Subnetting and CIDR
On this page
Slicing the IP Pie#
An IPv4 address such as 192.168.1.15 is a 32-bit number written as four octets. Every address has two parts: the network portion (which network it belongs to) and the host portion (which machine on that network). Subnetting is choosing where that dividing line falls, and carving a large block into smaller networks sized for real needs. You need it to design a cloud VPC, read a firewall rule, or work out why two machines that "should" talk cannot.
Why Subnet?#
- Security: Web servers in one subnet, databases in another, and a firewall or security group between them. Subnet boundaries are where you enforce policy.
- Smaller broadcast domains: ARP and DHCP broadcasts reach every host on a subnet. Thousands of hosts on one flat network waste bandwidth and CPU, and one misbehaving device affects everyone.
- Organization and routing: Grouping addresses by site, function or environment keeps routing tables and firewall rules short and readable.
The Subnet Mask and the AND Operation#
A subnet mask has 1 bits over the network portion and 0 bits over the host portion. 255.255.255.0 is twenty-four 1s followed by eight 0s. A host finds its network by a bitwise AND of address and mask:
Address 192.168.10.77 11000000.10101000.00001010.01001101
Mask /27 255.255.255.224 11111111.11111111.11111111.11100000
AND 192.168.10.64 11000000.10101000.00001010.01000000 <- network address
Every time a host sends a packet, it does this for the destination too. If the destination's network matches its own, it delivers directly on the local link (ARPing for the destination). Otherwise it sends the packet to its default gateway. A wrong mask therefore produces a very specific failure: a host with /16 instead of /24 thinks remote machines are local, ARPs for them, gets no answer, and cannot reach them, while everything else works.
CIDR Notation#
CIDR writes the mask as a count of 1 bits after a slash: 192.168.10.0/27. With a /N prefix, the block contains 2^(32-N) addresses. The first is the network address (host bits all 0) and the last is the broadcast address (host bits all 1); neither is assigned to a host on a normal subnet.
| Prefix | Mask | Addresses | Usable hosts | Typical use |
|---|---|---|---|---|
| /32 | 255.255.255.255 | 1 | 1 | a single host in firewall rules or routes |
| /31 | 255.255.255.254 | 2 | 2 | point-to-point router links (RFC 3021) |
| /30 | 255.255.255.252 | 4 | 2 | point-to-point links, older style |
| /29 | 255.255.255.248 | 8 | 6 | small public IP allocations |
| /28 | 255.255.255.240 | 16 | 14 | small management networks |
| /27 | 255.255.255.224 | 32 | 30 | |
| /26 | 255.255.255.192 | 64 | 62 | |
| /25 | 255.255.255.128 | 128 | 126 | |
| /24 | 255.255.255.0 | 256 | 254 | the classic LAN |
| /22 | 255.255.252.0 | 1,024 | 1,022 | large office or Wi-Fi |
| /16 | 255.255.0.0 | 65,536 | 65,534 | a whole VPC |
| /8 | 255.0.0.0 | 16,777,216 | 10.0.0.0/8 private space |
|
| /0 | 0.0.0.0 | all | "anywhere": default route, allow-all rules |
Doing the Math by Hand: The Block-Size Method#
To find the subnet of 192.168.10.77/27 without binary:
- The "interesting" octet is the one where the mask is neither 255 nor 0. For
/27that is the fourth octet, mask value 224. - Block size = 256 - 224 = 32. Subnets start at multiples of 32: .0, .32, .64, .96, ...
- 77 falls in the block starting at .64. Network =
192.168.10.64. - Broadcast = next block start minus one = .95.
- Usable hosts =
.65to.94, 30 addresses.
So 192.168.10.77 and 192.168.10.100 are not on the same /27 (100 falls in the .96 block) and need a router between them, even though they look adjacent.
The same method works in any octet. For 10.4.37.9/20: the interesting octet is the third, mask 240, block size 16; 37 falls in the block starting at 32, so the network is 10.4.32.0/20 and the broadcast is 10.4.47.255.
Letting Python Check Your Work#
The standard ipaddress module does all of this and more:
import ipaddress
iface = ipaddress.ip_interface("192.168.10.77/27") # an address WITH its prefix
net = iface.network
print(net, net.netmask, net.hostmask)
print("network: ", net.network_address)
print("broadcast:", net.broadcast_address)
hosts = list(net.hosts())
print("usable: ", hosts[0], "-", hosts[-1], f"({len(hosts)} hosts)")
print(ipaddress.ip_address("192.168.10.100") in net)
vpc = ipaddress.ip_network("10.20.0.0/16")
print(list(vpc.subnets(new_prefix=20))[:3]) # split into /20s
print(ipaddress.ip_network("10.0.0.0/16").overlaps(ipaddress.ip_network("10.0.128.0/17")))
192.168.10.64/27 255.255.255.224 0.0.0.31
network: 192.168.10.64
broadcast: 192.168.10.95
usable: 192.168.10.65 - 192.168.10.94 (30 hosts)
False
[IPv4Network('10.20.0.0/20'), IPv4Network('10.20.16.0/20'), IPv4Network('10.20.32.0/20')]
True
Two details trip people up. ip_network("192.168.1.5/24") raises ValueError: 192.168.1.5/24 has host bits set, because a network must be written with its network address; use ip_interface for "this host and its subnet", or pass strict=False. And hostmask (0.0.0.31) is the same thing Cisco ACLs and OSPF call a wildcard mask.
VLSM: Right-Sizing Each Subnet#
Variable Length Subnet Masking means using different prefix lengths inside one block, sized to actual demand, instead of equal slices. The procedure: sort requirements from largest to smallest, give each the smallest block that fits (hosts + 2, rounded up to a power of two), and always allocate on a boundary that is a multiple of the block size. Largest-first matters, because it keeps the big blocks aligned.
import ipaddress, math
def plan(block, needs):
free = [ipaddress.ip_network(block)]
out = []
for name, hosts in sorted(needs.items(), key=lambda kv: -kv[1]): # largest first
prefix = 32 - math.ceil(math.log2(hosts + 2)) # +2: network and broadcast
for i, candidate in enumerate(free):
if candidate.prefixlen <= prefix:
sub = next(candidate.subnets(new_prefix=prefix))
free.pop(i)
free[i:i] = sorted(candidate.address_exclude(sub)) # keep the leftovers
out.append((name, sub, sub.num_addresses - 2))
break
else:
raise ValueError(f"no room for {name}")
return out
for name, net, usable in plan("10.20.0.0/22", {"office": 300, "wifi": 100, "servers": 50,
"mgmt": 12, "wan-link": 2}):
print(f"{name:9} {str(net):17} usable={usable}")
office 10.20.0.0/23 usable=510
wifi 10.20.2.0/25 usable=126
servers 10.20.2.128/26 usable=62
mgmt 10.20.2.192/28 usable=14
wan-link 10.20.2.208/30 usable=2
Everything fits in the first three-quarters of the /22, leaving 10.20.3.0/24 and a few small blocks free for growth. In real designs, leave headroom: a subnet that is 90% full on day one will be a migration project within a year.
Summarization (Supernetting)#
The reverse of subnetting is summarization: advertising several contiguous networks as one larger prefix. 10.1.0.0/24 through 10.1.3.0/24 collapse into 10.1.0.0/22 (ipaddress.collapse_addresses does this). Routers carry one route instead of four, and firewall rules get shorter. It only works if you allocated contiguously and on aligned boundaries, which is a strong argument for planning address space per site or per environment up front. See Routing.
Subnetting in the Cloud#
A cloud VPC is typically a /16 carved into public and private subnets per availability zone. Things that surprise people:
- Reserved addresses: providers reserve some addresses in every subnet. AWS, for example, reserves five: the network address, the next three (VPC router, DNS, future use) and the broadcast address. A
/28gives 11 usable addresses, not 14. - Overlap blocks connectivity: VPC peering, transit gateways and VPNs cannot route between networks with overlapping CIDRs. Two teams that both chose
10.0.0.0/16cannot be connected later without renumbering. Maintain an organization-wide IP plan. - Kubernetes eats addresses: with some CNI plugins every pod receives a VPC address, so a
/24subnet can run out of IPs long before nodes run out of CPU. See Container & Kubernetes Networking. /32in security groups means exactly one address;0.0.0.0/0means the whole internet. Open SSH or database ports to0.0.0.0/0are one of the most common cloud misconfigurations.
Public vs. Private Addresses#
Because IPv4 has only about 4.3 billion addresses, RFC 1918 reserves three ranges for private use that are never routed on the public internet:
10.0.0.0/8(16.7 million addresses)172.16.0.0/12(172.16.0.0to172.31.255.255, a range people often misremember as all of172.x)192.168.0.0/16
Hosts using them reach the internet through NAT, which translates private source addresses to a public one. Pick unusual private ranges (not 192.168.0.0/24 or 192.168.1.0/24) for corporate networks, because remote workers' home routers use those, and a VPN into an identical range is unroutable.
IPv6 Subnetting#
IPv6 uses the same prefix notation with 128 bits. Standard practice is a /64 for every subnet, regardless of host count, because SLAAC requires a 64-bit interface identifier. A site typically receives a /48 (65,536 subnets of /64) or a /56 (256 subnets). IPv6 prefixes are usually split on 4-bit (hex digit) boundaries, so allocation reads naturally: 2001:db8:abcd:0100::/56 for one building, 2001:db8:abcd:0101::/64 for its first floor. See IPv4 and IPv6 Addressing.
Practice#
Without a calculator, find the network address, broadcast address and usable range for 172.16.45.130/26 and for 10.9.200.17/21. Then verify both with ipaddress.ip_interface(...).network, and plan a /24 for departments needing 60, 28, 12 and 2 hosts using the VLSM function above.
Check your understanding
4 quick questions on this lesson. Get them all right and the lesson is marked complete.