thoughts/net/subnets.md

4.9 KiB

Understanding IP Addresses, Subnet Masks, and CIDR

Disclaimer: Most, if not all, information in this article applies to both IPv4 and IPv6. However, an IPv6 address is 4 times the size of an IPv4 address, which is 4 times the math the author is willing to do. Due to this, all examples will be in the context of IPv4.


Cloudflare owns the number 16843009. At least, as far as networks are concerned they do. You'd be more likely to recognize it in the form 1.1.1.1. So how does the number 16843009 relate to the IP address 1.1.1.1? Let's start with what an IP address really is.

An IPv4 address is normally expressed as A.B.C.D, where each letter represents an octet in decimal form. Since there are 4 octets, an IPv4 address is exactly 32-bits long. If you take the IP address 10.42.7.19 and write it in binary form, you get 00001010.00101010.00000111.00010011. This is referred to as a bit string, which you'll notice contains exactly 32 bits as expected. Since an octet will always be 8 bits, the dot separators aren't needed in a bit string. This means that the bit string should really be written as 00001010001010100000011100010011. These bit strings can also be referred to as 32-bit integers. In other words, they are whole numbers that take up exactly 32 bits of memory. The example bit string in this case could be written in decimal form as the number 170526483. We now have 3 ways to describe the same IP address:

  • 10.42.7.19
  • 00001010001010100000011100010011
  • 170526483

You'll likely never need to write an IP address in any other form other than the first one (commonly referred to as dotted decimal form), so why is this important? For computers, it is fast and straight-forward to operate on IP addresses when they are bit strings. Combined with an important tool called a subnet mask, they can quickly compute the start and end of a range an IP address is in.

A subnet mask is a 32-bit string, similar to an IPv4 address, with the additional constraint that it must be a series of 1 bits immediately followed by a series of 0 bits. This means that as soon as the first 0 bit appears, all remaining bits must be 0. The following bit string is a valid subnet mask: 11111111111111111111111100000000. If you split up the 32 bits into 4 groups of octets, and convert those groups to decimal form, you get 255, 255, 255, and 0, written as 255.255.255.0. To drive the point home, both forms represent the same subnet mask:

  • 11111111111111111111111100000000
  • 255.255.255.0

Another way of writing a subnet mask is CIDR notation, which is of the form /N, where N represents the number of 1 bits at the beginning of the mask. This means that writing 10.42.7.19/24 indicates you have the IP address 10.42.7.19 with the subnet mask of 11111111111111111111111100000000 (or 255.255.255.0 as we determined above). You'll notice the bit string contains 24 leading 1 bits, the value of N.

So how does a computer use a subnet mask to obtain information from an IP address? For finding the first IP in a range defined by a subnet, the equation is very straightforward. Take the given IP address and subnet mask, and perform a bitwise AND on them. For example, using the IP address above, 10.42.7.19, and the subnet mask 255.255.255.0, the equation would look like this:

  00001010001010100000011100010011 (10.42.7.19)
& 11111111111111111111111100000000 (255.255.255.0)
  --------------------------------
  00001010001010100000011100000000 (10.42.7.0)

So we now know that the start of the range is 10.42.7.0. With this information, finding the end of the range is just as easy. First, compute the inverse of the subnet mask. This can be done using a bitwise NOT:

~ 11111111111111111111111100000000 (255.255.255.0)
  --------------------------------
  00000000000000000000000011111111 (0.0.0.255)

Finally, take the inverted subnet mask and the first IP address in the range, and apply a bitwise OR to them:

  00001010001010100000011100000000 (10.42.7.0)
| 00000000000000000000000011111111 (0.0.0.255)
  --------------------------------
  00001010001010100000011111111111 (10.42.7.255)

Putting this all together, we are given the IP address 10.42.7.19 and the subnet mask 255.255.255.0, which can be rewritten as 10.42.7.19/24. The range this IP address belongs to (with that subnet mask) comes out to 10.42.7.0-10.42.7.255. What makes these operations even more useful is they can be applied to any IP address in the range and get the same answer. In conclusion:

  • IPv4 addresses are 32-bit integers.
  • Subnet masks define the range an IP address is in.
  • The first IP address in the range can be found by applying a bitwise AND to the given IP address and the subnet mask.
  • The last IP address in the range can be found by applying a bitwise NOT to the subnet mask, then a bitwise OR to the inverted subnet mask and the first IP address.