NET-110 · Lesson 5 of 12 · 28 min
Subnetting step by step
▶ Video coming soon A video version of this lesson is on the way.
Sam's plan for Ridgeline is to stop running every staff device in one big network. The tills and card terminals should sit in their own network, the roastery equipment in another, the office computers in a third, the guest Wi-Fi stays separate, and the switches and access points should be managed from a small network of their own. Each of those networks needs its own range of addresses, carved out of one larger block without overlaps and without wasting space. Carving a block into smaller networks is called subnetting, and it is the single most practised skill in introductory networking.
This lesson works through subnetting by hand, so the arithmetic becomes automatic, and then checks every answer with Python. It covers why networks are subnetted, how borrowing host bits creates subnets, the block-size method for finding subnet boundaries quickly, how to choose a prefix for a required number of hosts, variable-length subnet masks for networks of different sizes, and summarizing several networks into one route.
Why subnet?
A single flat network is simple, but it has costs that grow with size. Every broadcast, such as an ARP question or a DHCP request, reaches every device, so a large flat network spends more and more of its capacity on chatter. Every device can reach every other device directly at layer 2, so a compromised laptop can probe the tills, and there is no place to put rules between groups. Faults also spread: a looping cable or a misbehaving device can disrupt the whole network at once.
Splitting one network into subnets, each in its own VLAN, fixes these problems. Broadcasts stay within their subnet. Traffic between subnets must pass through a router or firewall, where rules decide which groups may talk to which: tills may reach the payment processor but not the office file share, guests may reach the internet and nothing else. Problems are contained within one subnet. Address plans also become easier to read: at a glance, an address in 10.20.20.0/26 is a payment device and one in 10.20.40.0/27 is roastery equipment.
Borrowing host bits
Subnetting takes a network and moves the dividing line between network and host bits to the right, borrowing some host bits to number the subnets. Borrowing n bits creates 2 to the power n subnets, and each subnet has the host bits that remain. Take 192.168.10.0/24 and borrow 2 bits: the prefix becomes /26, there are 2 squared, or 4, subnets, and each has 6 host bits, so 64 addresses and 62 usable hosts.
| Subnet | Network address | First host | Last host | Broadcast |
|---|---|---|---|---|
| 1 | 192.168.10.0/26 | 192.168.10.1 | 192.168.10.62 | 192.168.10.63 |
| 2 | 192.168.10.64/26 | 192.168.10.65 | 192.168.10.126 | 192.168.10.127 |
| 3 | 192.168.10.128/26 | 192.168.10.129 | 192.168.10.190 | 192.168.10.191 |
| 4 | 192.168.10.192/26 | 192.168.10.193 | 192.168.10.254 | 192.168.10.255 |
Each subnet's network address is a multiple of its size, and each broadcast address is one less than the next subnet's network address. Once that pattern is clear, any subnet can be written down without converting anything to binary.
import ipaddress
block = ipaddress.ip_network('192.168.10.0/24')
for borrowed in (1, 2, 3, 4):
subnets = list(block.subnets(prefixlen_diff=borrowed))
first = subnets[0]
print('borrow', borrowed, '->', len(subnets), 'subnets of /' + str(first.prefixlen),
'with', first.num_addresses - 2, 'hosts each; first two:', subnets[0], subnets[1])borrow 1 -> 2 subnets of /25 with 126 hosts each; first two: 192.168.10.0/25 192.168.10.128/25 borrow 2 -> 4 subnets of /26 with 62 hosts each; first two: 192.168.10.0/26 192.168.10.64/26 borrow 3 -> 8 subnets of /27 with 30 hosts each; first two: 192.168.10.0/27 192.168.10.32/27 borrow 4 -> 16 subnets of /28 with 14 hosts each; first two: 192.168.10.0/28 192.168.10.16/28
The block-size method
The fastest way to subnet in your head is the block size, also called the magic number. Find the octet where the prefix falls, the interesting octet, and work out 256 minus the mask value in that octet. For /26 the mask is 255.255.255.192, the interesting octet is the fourth, and the block size is 256 minus 192, which is 64: subnets start at 0, 64, 128 and 192. For /27 the mask is 224 and the block is 32; for /28 the mask is 240 and the block is 16. The same works in other octets: a /20 has mask 255.255.240.0, block size 16 in the third octet, so its subnets start at x.x.0.0, x.x.16.0, x.x.32.0 and so on.
| Prefix | Mask | Block size | Addresses | Usable hosts |
|---|---|---|---|---|
| /24 | 255.255.255.0 | 256 | 256 | 254 |
| /25 | 255.255.255.128 | 128 | 128 | 126 |
| /26 | 255.255.255.192 | 64 | 64 | 62 |
| /27 | 255.255.255.224 | 32 | 32 | 30 |
| /28 | 255.255.255.240 | 16 | 16 | 14 |
| /29 | 255.255.255.248 | 8 | 8 | 6 |
| /30 | 255.255.255.252 | 4 | 4 | 2 |
To find which subnet an address belongs to, divide the interesting octet by the block size, ignore the remainder, and multiply back. For 10.20.20.77/26, the block is 64; 77 divided by 64 is 1 with a remainder, and 1 times 64 is 64, so the subnet is 10.20.20.64/26, its broadcast is 64 plus 64 minus 1, or 127, and its hosts are .65 to .126. With practice this takes a few seconds and needs no binary at all.
def subnet_of(address, prefix):
octets = [int(o) for o in address.split('.')]
index = prefix // 8 # which octet the prefix falls in (0-based)
if index == 4:
return address, address
bits_in_octet = prefix % 8
block = 256 >> bits_in_octet # 256 minus the mask value in that octet
start = octets[index] // block * block
net = octets[:index] + [start] + [0] * (3 - index)
bc = octets[:index] + [start + block - 1] + [255] * (3 - index)
return '.'.join(map(str, net)), '.'.join(map(str, bc))
for a, p in (('10.20.20.77', 26), ('10.20.40.19', 27), ('172.16.37.5', 20), ('192.168.1.200', 25)):
net, bc = subnet_of(a, p)
print((a + '/' + str(p)).ljust(18), 'network', net.ljust(14), 'broadcast', bc)10.20.20.77/26 network 10.20.20.64 broadcast 10.20.20.127 10.20.40.19/27 network 10.20.40.0 broadcast 10.20.40.31 172.16.37.5/20 network 172.16.32.0 broadcast 172.16.47.255 192.168.1.200/25 network 192.168.1.128 broadcast 192.168.1.255
Try it yourself
Find the block size
Write block_size(prefix) that returns the block size for a prefix length between 1 and 32: the number of addresses each subnet spans in the octet where the prefix falls. For /26 it is 64, for /20 it is 16 (in the third octet), for /24 it is 256 in the fourth octet but 1 in the third, so return 256 for /8, /16 and /24, and 1 for /32.
Then write subnet_starts(prefix) that returns the list of values the interesting octet takes at the start of each subnet, for example [0, 64, 128, 192] for /26.
def block_size(prefix):
return 256 - prefix
def subnet_starts(prefix):
return [0]
print(block_size(26), subnet_starts(26))Choosing a prefix for a number of hosts
Design usually starts from the other direction: you know how many devices a network must hold and need the smallest subnet that fits. Find the smallest number of host bits h for which 2 to the power h minus 2 is at least the number of hosts needed, then the prefix is 32 minus h. Always allow for growth: the guest Wi-Fi may serve forty phones on a quiet day and a hundred and fifty during a weekend festival.
For Ridgeline: the office needs about 25 devices today, so allow 60 and choose a /26 (62 hosts); the tills, card terminals and kitchen display need 8, allow 30, a /27 (30 hosts); the roastery equipment needs 6, allow 14, a /28; guests need up to 200, a /24 (254 hosts); and management, covering the switch, access points and firewall interfaces, needs 6, a /29 (6 hosts) is tight, so a /28. Lesson 12 finalizes these numbers.
import math
def prefix_for(hosts):
host_bits = max(2, math.ceil(math.log2(hosts + 2))) # +2 for the network and broadcast addresses
return 32 - host_bits
needs = {'office': 60, 'payments': 30, 'roastery': 14, 'guest Wi-Fi': 200, 'management': 12}
for name, hosts in needs.items():
p = prefix_for(hosts)
print(name.ljust(12), 'needs', str(hosts).rjust(3), '-> /' + str(p), 'gives', 2 ** (32 - p) - 2, 'hosts')office needs 60 -> /26 gives 62 hosts payments needs 30 -> /27 gives 30 hosts roastery needs 14 -> /28 gives 14 hosts guest Wi-Fi needs 200 -> /24 gives 254 hosts management needs 12 -> /28 gives 14 hosts
Using floating-point logarithms for this is safe for the sizes in a LAN, but a loop that counts up host bits until the subnet is big enough is just as short and avoids any doubt about rounding. The exercise below asks for that version.
Try it yourself
Smallest subnet for a number of hosts
Write prefix_for(hosts) that returns the longest prefix length (the smallest subnet) that provides at least hosts usable addresses, remembering that each subnet loses two addresses for the network and broadcast. Use a loop over host bits rather than logarithms. The smallest allowed subnet is a /30.
For 30 hosts the answer is 27; for 31 hosts it is 26.
def prefix_for(hosts):
return 32 - hosts // 8
print(prefix_for(30), prefix_for(31))Variable-length subnet masks
When every subnet must be the same size, the largest requirement forces the size of all of them, and a /24 for each of Ridgeline's five networks would waste hundreds of addresses. Variable-length subnet masking, or VLSM, gives each subnet its own prefix length. The method is to sort the requirements from largest to smallest, allocate the largest first at the start of the block, then place each smaller subnet at the next free boundary that is a multiple of its own size. Allocating largest first guarantees that every subnet starts on a proper boundary without gaps or overlaps.
import ipaddress
def vlsm(block, needs):
"""Allocate subnets from block, largest first. needs maps a name to a prefix length."""
free = ipaddress.ip_network(block)
next_start = int(free.network_address)
plan = []
for name, prefix in sorted(needs.items(), key=lambda item: item[1]): # small prefix = big subnet first
size = 2 ** (32 - prefix)
start = -(-next_start // size) * size # round up to a boundary
net = ipaddress.ip_network((start, prefix))
if not net.subnet_of(free):
raise ValueError('block too small for ' + name)
plan.append((name, net))
next_start = start + size
return plan
needs = {'guest Wi-Fi': 24, 'office': 26, 'payments': 27, 'roastery': 28, 'management': 28}
for name, net in vlsm('10.20.0.0/22', needs):
print(name.ljust(12), str(net).ljust(16), 'hosts', net.num_addresses - 2)guest Wi-Fi 10.20.0.0/24 hosts 254 office 10.20.1.0/26 hosts 62 payments 10.20.1.64/27 hosts 30 roastery 10.20.1.96/28 hosts 14 management 10.20.1.112/28 hosts 14
The five networks fit into 10.20.0.0 to 10.20.1.127, three eighths of the /22, leaving the rest free for growth. In practice Sam chooses a plan that is easier to read, putting each network in its own third octet that matches its VLAN number, such as 10.20.20.0 for VLAN 20. That uses more address space than a tightly packed VLSM plan, but private address space is plentiful, and an address that tells you its VLAN at a glance saves time in every future troubleshooting session. Both approaches are correct; the choice is a design trade-off.
Try it yourself
Do two subnets overlap?
Overlapping subnets are a classic design error. Write overlaps(plan) that takes a list of networks in CIDR notation and returns a list of tuples (a, b) for every pair that overlaps, in the order they appear in the plan (each pair once, with the earlier network first). Use ipaddress.ip_network(...).overlaps(other). Return an empty list when there are no overlaps.
import ipaddress
def overlaps(plan):
return []
print(overlaps(['10.20.0.0/24', '10.20.1.0/26', '10.20.0.128/27']))A few checks catch most VLSM mistakes before they reach a router. Every subnet's network address must be a multiple of its block size. No two subnets may overlap, which the exercise below automates. The total of all subnet sizes must not exceed the size of the block. And each subnet must actually hold the hosts it was sized for, with the gateway counted as one of them: a /29 for six devices leaves no room for the router interface that will be their default gateway, a classic way to come up one address short on installation day.
Summarizing routes
Subnetting has a mirror image. Several contiguous subnets can be described by one shorter prefix, called a summary route or supernet. 10.20.20.0/24, 10.20.21.0/24, 10.20.22.0/24 and 10.20.23.0/24 share their first 22 bits, so they can be advertised as one route, 10.20.20.0/22. Summarization keeps routing tables small and stable: a branch office can tell head office about one route instead of twenty. It only works cleanly when the subnets are contiguous and start on a boundary of the summary's size, which is another reason to plan addresses rather than hand them out as needed. Python's ipaddress.collapse_addresses performs the calculation.
import ipaddress
nets = [ipaddress.ip_network(n) for n in ('10.20.20.0/24', '10.20.21.0/24', '10.20.22.0/24', '10.20.23.0/24')]
print(list(ipaddress.collapse_addresses(nets)))
uneven = [ipaddress.ip_network(n) for n in ('10.20.21.0/24', '10.20.22.0/24')]
print(list(ipaddress.collapse_addresses(uneven))) # not on a /23 boundary, so no single summary[IPv4Network('10.20.20.0/22')]
[IPv4Network('10.20.21.0/24'), IPv4Network('10.20.22.0/24')]Worked problem
Subnetting a /24 by hand
A second Ridgeline cafe will receive 10.21.5.0/24. It needs subnets for 50 staff devices, 20 payment devices and 10 cameras, plus room to grow. Plan them by hand.
Show solution
- 50 hosts need 6 host bits (62 usable): /26, block size 64.
- 20 hosts need 5 host bits (30 usable): /27, block size 32.
- 10 hosts need 4 host bits (14 usable): /28, block size 16.
- Allocate largest first: staff 10.21.5.0/26 (hosts .1 to .62, broadcast .63).
- Payments at the next 32 boundary: 10.21.5.64/27 (hosts .65 to .94, broadcast .95).
- Cameras at the next 16 boundary: 10.21.5.96/28 (hosts .97 to .110, broadcast .111).
- Free for growth: 10.21.5.112 to 10.21.5.255, which is a /28 at .112 and a /25 at .128.
Answer: Staff 10.21.5.0/26, payments 10.21.5.64/27, cameras 10.21.5.96/28, with 10.21.5.112/28 and 10.21.5.128/25 left for later.
Common mistakes
MistakeForgetting the two reserved addresses when sizing a subnet.
InsteadUsable hosts are 2 to the power h minus 2; 32 addresses hold 30 hosts.
MistakeStarting a subnet at an address that is not a multiple of its block size.
InsteadEvery subnet must begin on a boundary of its own size.
MistakeAllocating small VLSM subnets first.
InsteadAllocate largest first so each subnet lands on a valid boundary.
MistakeSizing networks for today's devices only.
InsteadAllow room for growth; private address space is plentiful.
MistakeCreating overlapping subnets.
InsteadCheck the plan for overlaps, by hand or with a script, before configuring anything.
MistakeTrying to summarize subnets that are not contiguous or not aligned.
InsteadPlan contiguous, aligned blocks if you want to summarize them later.
Summary and key terms
- Subnetting splits one network into smaller ones to contain broadcasts, apply rules between groups and contain faults.
- Borrowing n host bits makes 2 to the power n subnets; each keeps the remaining host bits.
- The block size is 256 minus the mask value in the interesting octet; subnets start at multiples of it.
- Choose the smallest prefix whose 2 to the power h minus 2 hosts covers the need, with room to grow.
- VLSM gives each subnet its own size; allocate largest first on proper boundaries.
- Contiguous, aligned subnets can be summarized into one shorter prefix.
- Python's ipaddress module can split, check, allocate and summarize networks reliably.
- Subnetting
- Dividing a network into smaller networks by borrowing host bits.
- Subnet
- One of the smaller networks created by subnetting.
- Block size
- The number of addresses each subnet spans in the interesting octet; 256 minus the mask value there.
- Interesting octet
- The octet in which the prefix boundary falls.
- VLSM
- Variable-length subnet masking: subnets of different sizes within one block.
- Summary route
- One shorter prefix that covers several contiguous subnets.
- Supernet
- Another name for a summary covering several networks.
- Broadcast domain
- The set of devices that receive each other's broadcasts; one per subnet or VLAN.
Practice problems
Work each problem on paper first, then reveal the answer to check.
How many /27 subnets fit in a /24?
Show answer
8 (2^3).
What is the block size for /29?
Show answer
8 (256 - 248).
Which subnet contains 192.168.10.150/26?
Show answer
192.168.10.128/26 (hosts .129 to .190).
What prefix holds 100 hosts?
Show answer
/25 (126 hosts).
What is the broadcast address of 10.21.5.96/28?
Show answer
10.21.5.111.
What is the third octet block size for a /21?
Show answer
8 (mask 255.255.248.0).
Which subnet contains 172.16.37.5/20?
Show answer
172.16.32.0/20 (block 16 in the third octet).
Summarize 10.1.4.0/24 to 10.1.7.0/24 in one route.
Show answer
10.1.4.0/22.
Why allocate the largest VLSM subnet first?
Show answer
So every subnet starts on a boundary that is a multiple of its size, with no overlaps.
Why might a designer use one /24 per VLAN instead of tightly packed subnets?
Show answer
Readable addresses that match VLAN numbers, and plenty of private space, make operations easier.
Lesson quiz · 5 questions
Score 60% or more to complete this lesson.
Create a free account to save your progress, take quizzes and earn a certificate.