Subnetting isn’t hard but I often see even experienced network engineers looking for subnet calculators or trying to count in binary on their fingers. So how about a quick primer?

To understand subnets, remember what a subnet is and think about the IP address in binary.

A subnet describes the local network

A subnet is a range of IP addresses. All the devices in the same subnet can communicate directly with one another without going through any routers. Most of the time, a network interface is connected to only one subnet and has only one IP address. My laptop is on a subnet that also includes a server, a printer, a couple of other workstations, and a router. All of these devices are in the same subnet.

If I want to communicate with a device in my subnet, I can send packets to it directly. If it’s not on my subnet, I need to forward the packet to my router first. My computer knows that another device is in my subnet by looking at my own IP address and my subnet mask.

Suppose my IP address is 192.168.101.15 and my subnet mask is 255.255.255.0. There are 32 bits in the IP address and the same number in the mask. We always write those 32 bits as four 8-bit numbers, often called octets. The thing that can make it confusing is that we use decimal notation for each of those 8-bit numbers.

The decimal number 255 represented in binary is a string of eight 1s. So our example subnet mask is very simple. In binary, it’s three groups of eight 1s followed by eight 0s.

Let’s look at the IP address side by side with the mask:

192.168.101.15 = 11000000.10101000.1100101.00001111
255.255.255.0  = 11111111.11111111.1111111.00000000

Anywhere the mask has a 1 is a network portion of the address. Anywhere the mask has a 0 is a host portion of the address. The network portion is 192.168.101.0 and the host portion is xx.xx.xx.15.

Any other device with the same network portion is part of my subnet. So 192.168.101.1 is part of my subnet and 192.168.101.100 is part of my subnet, but 192.168.102.15 is not part of my subnet and I need to go through the router to reach it.

Saving time with CIDR notation

We’ve been talking about traditional netmask notation. But before we go any further, let me talk about another notation called CIDR (classless interdomain routing).

The trouble with the traditional network mask notation is that, looking at it, you might think you could have an arbitrary sequence of 1s and 0s. Then you could make any two addresses part of the same subnet. Fortunately you can’t do that because that would be really confusing.

All the 1s have to be on the left and all the 0s on the right. So all that really matters is how many 1s there are. You don’t even need to separately count the 0s because there have to be 32 bits altogether.

In our example, the mask is 255.255.255.0. That’s three groups of eight 1s followed by eight 0s. So we can say everything there is to know about this mask by simply specifying that there are 24 1s (3×8=24), and we write this as /24. In CIDR notation, our example address and subnet information can therefore be written as 192.168.101.15/24.

If you don’t like CIDR notation, that’s fine. I didn’t like it at first either, but it really does save a lot of time and space when writing addresses.

Understanding special host addresses

Think about just the host portion of the address. In our example (192.168.101.15/24) the host portion is .15. The mask is /24 and there are 32 possible bits in the address. That means eight bits are available for the host.

If I write 15 in binary, I get 0000 1111. (It’s conventional to write these 8-bit binary values as two groups of four with a space between because they’re easier to read that way.)

The lowest value in this subnet range is 0000 0000, which is the decimal number 0. The highest is 1111 1111, which is the decimal number 255. These are what I mean when I say all 0 and all 1. These two values are reserved for local broadcasts on the subnet. You can’t assign them to any host — though there’s an exception to this rule, which I’ll talk about in a moment.

Using common subnet masks

/24 is a very common subnet size. It’s easy to understand because the network portion of the address is the first three octets and the host portion is the last octet. You can just read off the subnet information. Any two addresses with the same first three octets are in the same subnet.

I really like using /24 subnets precisely because they are so easy to read and remember. One of my personal rules of network design is that when somebody calls me at 3 a.m. to troubleshoot a problem, it’s probably best if I don’t have to think too hard.

But sometimes you need to use something else. For example, you might want to conserve addresses or create a subnet with hundreds of devices on it. In both cases, you’ll want a different subnet size.

If you have a subnet with a mask of /25, then instead of eight free bits for the host portion of the address, you have only seven bits. 2^7 is 128. You can’t use either the all 0 or all 1 host addresses, which leaves 126 possible hosts.

This table shows /25 and several other useful subnet sizes.

common subnet masks table

I’ve included the old-fashioned class names in the table, but it’s important to know that the Internet no longer recognizes the concept of class.

Take a look at the last two entries, which break the rules for all 0 and all 1 host addresses being illegal. It was once true that the only way to get a point-to-point subnet with two devices was to use a /30 subnet. But the trouble with the /30 subnet is that it wastes fully half the range because every /30 subnet has four host addresses, of which two are the all 0 and all 1 addresses.

So RFC 3021 was introduced to allow the use of /31 subnets specifically for point-to-point links that don’t require a local broadcast. In these networks, any packet sent by one node must be intended for the other node, as there are no other possibilities.

The /32 subnet is used for things like internal loopback addresses where there is only ever one device in a subnet.

Determining addresses in a subnet range

So far, I hope this all sounds pretty simple. There’s only one hard part to figuring out subnets: knowing which addresses are included in a particular range. There are a few tricks that can help though.

The first trick is that the all-0 address is a binary number that necessarily ends in a zero, which means it’s always an even number. The all-1 address is always an odd number. So, except for the /31 subnet, the first usable address in any range is always an odd number.

The second trick is to remember the numbers in the middle column of the above table, the powers of two. But all you really need to remember are the powers of two up to 2^8. Then you count by the appropriate power of two.

Here’s what I mean. If I’m using a /28 subnet, then I first subtract 28 from 32, which is 4. I remember (or calculate if my memory fails me in the moment) that 2^4 is 16. Then I count by 16s. The first subnet is xx.xx.xx.0 and the first host in that range is xx.xx.xx.1. To get the second subnet, I add 16: xx.xx.xx.16.

This value tells me both that the first host in the second range is xx.xx.xx.17 and that the last host in the first range is xx.xx.xx.14. (Remember that it can’t be xx.xx.xx.15 because that would be the all-1 address.)

What’s the next subnet? I just add 16 again and get xx.xx.xx.32. So the last address in the second subnet is xx.xx.xx.30 and the first address in the third range is xx.xx.xx.33.

Ok, so determining subnet addresses isn’t actually all that hard. If you think for a minute about what you’re doing, you really don’t need one of those subnet calculators. And, in truth, thinking about what you’re doing is probably a good idea anyway.