IPs and Subnets
The other day, I came across ipcalc - a tool that helps you calculate subnet-related info like broadcast IP, network ID, host range, etc - and I thought it'd be fun to learn what goes on under the hood. I ended up learning more about how IP addresses (and the internet) work - as well as what did go on under the hood.
NOTE: IP in this post refers to IPv4 since subnets in IPv6 are for entirely different reasons anyway
What, and Why Subnets?
Subnets (from sub-networks) are like networks-within-networks. Think of the internet as one large network, then think of how your work-place (or school) is part of the internet, but also sits in own corner, then think of how your department (or office) also has its own corner, while being part of the larger organization's network which is in turn, part of the bigger internet. Remember, networks within networks.
The “why” for subnets really comes down to efficiency. Imagine trying to talk to a computer in an office in your organization over the organization's network. Without subnets, there's a chance that the packets will go off into the larger internet and even visit a different country before finding its way to the recipient computer that is probably downstairs. With subnets, the router deduces that the recipient is on the same network and goes off to find it - thus calling off the unneeded trip.
So how do routers find the right recipient within the network? It turns out to be a combination of parts working together, and one important part is the Subnet Mask. Subnet masks are formatted like IP addresses (i.e., X.X.X.X), except only the router really bothers about them. Using the subnet mask, the router splits up a given IP address into two parts - one part identifies the computer (called the host) and the other identifies the network the host is on.
Another important part used by the router is the routing prefix. A routing prefix is a number between 0 and 32. It is usually written after a forward-slash ("/” e.g.,
192.168.43.21/24) and it tells us how many hosts a network can possibly take (using the formula:
2^(32-N), 0 <= n <= 32). It is also what helps the router calculate the subnet mask.
For example, take the IP address
192.168.43.21/24. We already know there is a computer on the network with the IP address 192.168.43.21, we also have the routing prefix (24) which means, we can make some calculations about the network - we are all routers on this blessed day. The formula above tells us that there are 256 (2^(32-24)) possible IP addresses on this network. It also turns that the first 24 bits belong to the network (leaving 32-24 bits) for the host. This also means that only the last 8 bits (the host bits) will be set to 0 in our subnet mask. We can then write our subnet mask as
11111111.11111111.11111111.00000000 or more realistically,
255.255.255.0 (because 255 in binary is 11111111).
The host bits and network bits are not fixed, and instead, depend on the routing prefix. For example, the same IP above but with the prefix bits set to 28 (i.e
192.168.43.21/28) will result in just 16 (2^(32-28)) possible addresses, and a subnet mask of 11111111.11111111.11111111.11110000 (i.e., 255.255.255.255.240). Similarly, if we get
192.168.43.21/16, then there are 65536 (2^(32-16)) possible addresses, and our subnet mask will have the last 16 bits set to 0, giving us 11111111.11111111.00000000.00000000 (i.e, 255.255.0.0).
The usefulness of subnet masks comes up because it helps the router (and network admins) to resolve a bunch of details about the network (such as the ones ipcalc calculates for you). Some of such details include:
Remember when we said the IP address gets split into two parts - the network and the host? The network part is creatively called the network ID, and the host part is the host ID. That is, the network ID identifies the network the host is part of. You calculate it by logically ANDing original IP address with the subnet mask. For example, given the IP as 192.168.43.21 and the subnet mask as 255.255.255.0,
192.168.43.21 = 11000000.10101000.00101011 255.255.255.0 = 11111111.11111111.00000000 ANDing the above gives us 192.168.43.0 = 11000000.10101000.00000000
The above can be calculated using any programming language of your choice. Here's how I did it in Go:
// nid is an array of 4 integers nid = 192 & 255 // 192 nid = 168 & 255 // 168 nid = 43 & 255 // 43 nid = 21 & 0 // 0 return fmt.Sprintf("%d.%d.%d.%d", nid, nid, nid, nid) // returns 192.168.43.0
The broadcast IP is an IP address on a network that can be used to address all the hosts on that network. That is, It broadcasts packets it receives to all the other devices. It is usually the last IP address on the network and to calculate it, you find the complement (logical NOT) of the subnet mask and OR the result with the original IP address. In Go, it is not as straightforward since complementing a positive integer gives a negative number. As a result, we have to add 256 to the final result to make it wrap around.
bid = 256 + (192 | ^255) bid = 256 + (168 | ^255) bid = 256 + (43 | ^255) bid = 256 + (21 | ^0) return fmt.Sprintf("%d.%d.%d.%d", bid, bid, bid, bid)
The host size represents the number of IP addresses possible on the network and the formula is 2^(32-N) where N is the routing prefix. Using same example from above, our host size becomes 2^(32-24) = 2^8 = 256. The network ID and the broadcast IP belong to the same network though, which reduces the number of IP addresses available for allocation by 2. Thus, the available host size becomes 2(32-N) - 2.
First and Last Hosts
The first host on the network is the available IP address that comes immediately after the network ID. Going by our example above, the first host is
The last host on the other hand is the available address that comes immediately before the broadcast IP. From the example above, the last host is
In a larger network, it often helps to know where the next subnet will start. The next network (ID) is resolved by calculating the IP address that comes after the broadcast IP. Sometimes, it's as simple as adding 1 to the broadcast IP (e.g., if the broadcast is 192.168.43.64, then the next subnet starts at 192.168.43.65). Other times as in our example, adding 1 to the broadcast IP (192.168.43.255) gives us 192.168.43.256, which is NOT a valid IP address! In such cases, we add the 1 to the previous decimal block in the IP address instead, which then gives us 192.168.44.0. Note that doing this resets the proceeding block back to 0, and it goes on and on.
That's for now!
I also ended up building my own naive version of an IP calculator, called ipgalc and you can find it at idoqo/ipgalc.
Also, here's an interesting subnet mask cheat sheet if you're looking for an extra resource.
Hopefully, the above explanation is mostly correct. I'm still confused as to how NOT complements work on integers (especially how they transform to negative numbers haha).