FUN WITH LINUX

How to calculate ip-networks using bit-operators in C

16 January 2015

Sometimes we have to calculate networks. We are interested in stuff like: networkid, subnetmask, min. hostip, max. hostip, how many hosts can we address in this network aso.. There are many tools for calculating networks. And they are perfectly good. If you need it in a c programm, there are libraries for that too. But I think it’s not neccessary to use libraries for that. If you calculate it manually, it’s just a matter of working with bits in a 32bit address, so it might be an quite easy task…

I wrote this little programm to demonstrate how it works:

``````doctor@tardis> ./calciprog 192.168.10.0 255.255.255.0
Network: 192.168.10.0/24
Wildcard: 0.0.0.255
Hostmin: 192.168.10.1
Hostmax: 192.168.10.254
Hosts: 254
NetID-Bytewise: 0 10 168 192 -> 698560
``````

On Linux, using the gcc and the gnu-standard-library, there is a header-file called “netinet/in.h”. In this file we find the following definition:

``````/* Internet address. */
{
};
``````

This is the struct we are looking for. We can use functions like the following to convert our IP-String into a binary address:

``````inet_pton(int af, const char *src, void *dst);
``````

## What exactly do we calculate?

Mostly we are interested in the following things:

• Network ID
• Number of possible IPs
• First Ip in our Network
• Last Ip in our Network

For this I defined the following datatype:

``````typedef struct ipcalcsubnet{
unsigned int hosts; /* max. possible hosts */
struct in_addr netid; /* network id */
struct in_addr hostmin; /* first possible ip of this network */
struct in_addr hostmax; /* maximal possible ip of this network */
} ipnet;
``````

## What do we need to calculate all the network stuff?

We need a Ip-Address of the network, or a Networkid and we need the Subnetmask of the network. Let’s assume we stored the netid(192.168.10.0) and the subnetmask(255.255.255.0) in our struct(above).

## Let’s do the bit-stuff

``````/*
let's say, that we filled in our netid the address 192.168.10.3
and a subnetmask 255.255.255.0. then 192.168.10.3 is not the real network-id.
it's just a ip of the network 192.168.10.0/24.
We can easily calculate the netid:
just do a bitwise AND on the netmask and ip
*/
/*
so if our subnetmask is 255.255.255.0, our wildcard would be 0.0.0.255
Just do a bitwise NOT on the netmask
*/
/*
it's easy to calculate too:
Just do a bitwise OR on net netid and the wildcard-address
*/
/*
the first useable hostip in our network is calculated by netid + 1
if our netid is 192.168.10.0 and our subnetmask is 255.255.255.0
then our first host would be 192.168.10.1
But at this point we do have a problem. We have to take care about little-endian
and big-endian.
We do have a 32bit-Adress. Lets assume our netid is 192.168.10.0.
so netdata->netid.s_addr would have the decimal value 698560.
that's in binary: 00000000000010101010100011000000
0 is in binary: 00000000
10 is in binary: 00001010
168 is in binary: 10101000
192 is in binary: 11000000
therefore if we just increase our netid.s_addr by one we would get:
193.168.10.0
That's why we have to change the byte-order, increase the 32bit by 1
and then change the byte-order back. our address is stored in host-byte-order,
so we have to use the function htonl() to change it into network-byte-order. if it's in
network-byte-order we can increase it and then we use ntohl() convert it back
to host-byte-order:
*/
/*
we have to change our address to network-byte-order, decrease it and
change back to host-byte-order again:
*/
/*
hosts will store the maximal number of useable ips in our network.
it's calculated by wildcard -1. here we do not store a network-address!
this is just an integer-value which stores the number of hosts. that's why
we convert our wildcard-address to network-byteorder, decrease it and store
this value into hosts.
in our example the wildcard is 0.0.0.255 which is in decimal 255.
255 decreased by one would be 254. so we can use 254 ips in our network
*/
``````

Now we calculated all the important things. But it would be neat to give out the netid/subnetmask in cidr-notation. To remember: in the cidr-notation we count all the 1-bits in the subnetmask. 255.255.255.0 would be 24 for example. The big question is, how access the bits in our 32bit address?

``````int cidr = 0;

/*
in CIDR-notation our address looks like:
192.168.10.0/24
So our subnetmask is just a short integer.
this integer is easily calculated:

just count all the 1-bit's of our

00000000111111111111111111111111
-here we have 24 1-bits-

But how can we count all the 1-bits?
with this tricky function:
let's assume a netmask of 255.255.255.0
stored in host-byte-order it would look like:
00000000111111111111111111111111

now let's do a bitwise AND with 0x01

00000000111111111111111111111111
00000000000000000000000000000001
our result would be:
00000000000000000000000000000001

then right-shift all the bits by one digit:

00000000011111111111111111111111

We do this until our bitmask looks like:

00000000000000000000000000000000

perfect, because our loop stopps

so if we count all the loops of bitshifting
we are able to count all the 1-bits in our

*/
{
}
``````

## How to easily split a 32bit-address in 4 bytes

``````/* this union splits 32bit-adresses into 4 bytes
it's very important to use unsigned here. otherwise
we would not be able to store values up to 255.
*/
typedef union {
uint32_t val;
unsigned char byte;
} uIp32;

uIp32 b;
printf("NetID-Bytewise: %u %u %u %u -> %u \n",b.byte,b.byte,b.byte,b.byte,b.val);
``````