This was extracted from my distributed resource allocation post, originally added as an annotation to explain the concept of subnets. I was very happy with how it turned out so I decided to extract it into a separate post. Think of it as an intro to subnets.

Internet Protocol Version 4 (IPv4)

IPv4 defines how data is routed across a network of devices. In order to ensure accurate delivery, each device needs to be uniquely identified. The protocol refers to these identifiers as IP addresses. Here's an example address: 192.168.1.1.

IPv4 Address Space

The protocol requires all addresses to fit into 32 bits of memory. This means only 232 = 4,294,967,296 unique addresses can be stored; which is roughly 4.3 billion.

Heuristics

  1. The number 32 can be split into 4 equal groups; 8 + 8 + 8 + 8.
  2. This implies that 32 bits can also be split into four groups of 8 bits: 232 = 28 × 28 × 28 × 28.
  3. Each group of 8 bits is called an octet and an octet of memory can store numbers from 0 to 255.
  4. This is why an IPv4 address is a sequence of 4 octets that look like this: [0-255].[0-255].[0-255].[0-255]

Subnets

A subnet is a range of addresses that share the same binary prefix. Naturally, when you write out all 4.3 billion IP addresses in binary form, you're going to notice a pattern where a lot of addresses begin with the same sequence of numbers, which we call the prefix. This pattern of shared prefixes enables us to create families of networks, called subnets.

In a given subnet, the fixed prefix is called the network part. The variable part is called the host part. This is analogous to human families, where the network-prefix acts as a surname and the host part acts as the first-name. Can you see the (surname|network) and (firstname|host) pattern in these examples? 😉

192.168.0.0   -> 11000000.10101000.00000000.00000000
192.168.0.1   -> 11000000.10101000.00000000.00000001
192.168.0.2   -> 11000000.10101000.00000000.00000010
192.168.0.3   -> 11000000.10101000.00000000.00000011
192.168.0.4   -> 11000000.10101000.00000000.00000100
192.168.0.5   -> 11000000.10101000.00000000.00000101
192.168.0.6   -> 11000000.10101000.00000000.00000110
192.168.0.7   -> 11000000.10101000.00000000.00000111
192.168.0.8   -> 11000000.10101000.00000000.00001000
192.168.0.9   -> 11000000.10101000.00000000.00001001
...
Subnet example

CIDR Notation (192.168.0.0/24)

Thinking in base-2 is a lot of work, so humans invented the CIDR notation as a simple way to represent these subnets. Instead of writing out all family members like I did in the example above, we can just write:

192.168.0.0/24

This notation tells us that the network-prefix or network-portion is 24 bits. But in order to find the host-portion/host-bit/variable-bit in this subnet block, you only need to calculate 232-24 = 28 = 256 . This means, there are 256 unique addresses in this subnet block.

Subnet Masks

There's one last thing called a subnet mask that you'll see in the wild. I'm not going to go into the details of how they work, because I really think it's a party trick that doesn't help with intuition(at least for me). They are quite handy for programmatic manipulations of subnets though, so I'll implore you to take a look at them.