Subnetting
Dividing an IPv4 network into smaller subnets, step by step, with worked examples you can check yourself.
Why subnet at all
A subnet (subnetwork) is a range of Internet Protocol (IP) addresses that share the same network prefix and sit together on one layer 2 segment, such as one virtual local area network (VLAN). Hosts in the same subnet reach each other directly; traffic to any other subnet goes through a router. Subnetting is the act of cutting a larger address block into several of these smaller ranges. It builds on the address and mask basics from IPv4 Addressing.
Organizations subnet for four practical reasons:
- Right-sized networks. A /16 holds 65,534 hosts. No single office segment needs that; a printer VLAN may need ten addresses and a point-to-point link between two routers only two.
- Smaller broadcast domains. Every broadcast (for example an Address Resolution Protocol (ARP) request) reaches every host in the subnet. Fewer hosts per subnet means less broadcast traffic and a smaller blast radius when something misbehaves.
- Security and control. Traffic between subnets passes a router or firewall, where access control lists can filter it. Putting servers, staff, guests and management interfaces in separate subnets is the basis of network segmentation (see Network Security Basics).
- Tidy routing. Subnets carved from one block can be advertised to the rest of the network as a single summary route, which keeps routing tables small (see route summarization below).
Borrowing host bits
An IPv4 address has 32 bits. The
prefix length — the number
after the slash, as in /26 — says how many of those
bits, counted from the left, form the network part. The remaining
bits are the host bits: they number
the individual addresses inside the subnet. With a /26 there are 26
network bits and 32 − 26 = 6 host bits.
To subnet, you borrow bits from the host part and make them network bits. Start with 192.168.10.0/24 and borrow two bits: the prefix becomes /26. Those two borrowed bits can take four values (00, 01, 10, 11), so the /24 turns into four /26 subnets. Each has six host bits left, so 64 addresses.
Try it: subnet mask and prefix length
Convert a dotted-decimal mask to its prefix length or back, with the bits, the wildcard mask and the number of hosts.
The formulas
Two formulas cover almost every subnetting question:
| Question | Formula | Example: /24 → /26 |
|---|---|---|
| How many subnets? | 2borrowed bits | 22 = 4 subnets |
| How many addresses per subnet? | 2host bits | 26 = 64 |
| How many usable hosts per subnet? | 2host bits − 2 | 64 − 2 = 62 |
Why minus two
Two addresses in every ordinary subnet cannot be given to a host:
- The network address is the first address of the subnet, with all host bits set to 0. It is the name of the subnet itself — the value that appears in routing tables, such as 192.168.10.64/26.
- The broadcast address is the last address, with all host bits set to 1. A packet sent to it is delivered to every host in the subnet (a directed broadcast), such as 192.168.10.127 for 192.168.10.64/26.
Everything in between is usable: the first usable host is the network address + 1, the last usable host is the broadcast address − 1. By convention many networks give the default gateway the first (or the last) usable address, but that is a local habit, not a rule.
The exceptions: /31 and /32
The formula would give a /31 (one host bit) 2 − 2 = 0 hosts, which would make it useless. Request for Comments (RFC) 3021 changed that for point-to-point links: on a link with exactly two ends there is nobody else to broadcast to, so both addresses of a /31 may be used as host addresses. This saves half the addresses compared with the traditional /30 for router-to-router links. A /32 has no host bits at all and identifies exactly one address; it is used for loopback interfaces and host routes.
import ipaddress
for prefix in (30, 31, 32):
net = ipaddress.ip_network(f"10.0.0.0/{prefix}")
print(f"/{prefix}: {net.num_addresses} address(es), mask {net.netmask}")
link = ipaddress.ip_network("10.0.0.0/31")
print("usable on a /31:", [str(host) for host in link.hosts()])
/30: 4 address(es), mask 255.255.255.252 /31: 2 address(es), mask 255.255.255.254 /32: 1 address(es), mask 255.255.255.255 usable on a /31: ['10.0.0.0', '10.0.0.1']
Python's hosts() follows RFC 3021 and returns both
addresses of a /31. The examples on this page use the standard
library module ipaddress; see
The Standard Library
if the module itself is new to you.
The block size method
Converting every address to binary works, but it is slow. The block size method (also called the magic number method) gives the same answer with only subtraction and multiplication. It works because subnets of the same size always start at a multiple of their size.
- Write the mask in dotted decimal and find the interesting octet: the one that is neither 255 nor 0. (If the mask is on an octet boundary, such as /16 or /24, the interesting octet is the one after the last 255, and the block size is 256.)
- Block size = 256 − the mask value in that octet. For 255.255.255.192 the block size is 256 − 192 = 64.
- List the multiples of the block size in the interesting octet: 0, 64, 128, 192. Each one is the start of a subnet.
- Find the subnet: the network address is the largest multiple that is not greater than the address's value in that octet. All octets to the right become 0.
- Broadcast address = the next subnet's start − 1 in the interesting octet, with all octets to the right set to 255.
- Usable range: network + 1 to broadcast − 1.
The mask values themselves follow a fixed pattern you will soon know by heart: 128, 192, 224, 240, 248, 252, 254, 255 (each adds the next bit: 128, +64, +32, +16, +8, +4, +2, +1).
Worked examples
Each example is worked out by hand first, then verified with Python. The Python code prints exactly the values in the table, so you can change the addresses and practice on your own.
Example 1: 192.168.10.0/24 into four subnets
Task: split 192.168.10.0/24 into four equal subnets.
- Four subnets need 2 borrowed bits (22 = 4). New prefix: /24 + 2 = /26.
- Mask: 255.255.255.192. Interesting octet: the fourth. Block size: 256 − 192 = 64.
- Subnets start at .0, .64, .128 and .192. Host bits: 32 − 26 = 6, so 26 − 2 = 62 usable hosts each.
| Subnet | Network | First usable | Last usable | 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 |
import ipaddress
block = ipaddress.ip_network("192.168.10.0/24")
for subnet in block.subnets(prefixlen_diff=2): # borrow 2 bits
first = subnet.network_address + 1
last = subnet.broadcast_address - 1
print(subnet, first, last, subnet.broadcast_address)
print("usable hosts per subnet:", subnet.num_addresses - 2)
192.168.10.0/26 192.168.10.1 192.168.10.62 192.168.10.63 192.168.10.64/26 192.168.10.65 192.168.10.126 192.168.10.127 192.168.10.128/26 192.168.10.129 192.168.10.190 192.168.10.191 192.168.10.192/26 192.168.10.193 192.168.10.254 192.168.10.255 usable hosts per subnet: 62
Example 2: which subnet is 172.16.45.130/20 in?
Task: a monitoring alert names host 172.16.45.130/20. Which subnet is it in, and what is its range?
- /20 = 8 + 8 + 4 network bits: mask 255.255.240.0. The interesting octet is the third (240), not the fourth.
- Block size: 256 − 240 = 16.
- Multiples of 16 in the third octet: 0, 16, 32, 48 … The address has 45 there; the largest multiple not above 45 is 32.
- Network address: 172.16.32.0 (the fourth octet becomes 0). The next subnet starts at 172.16.48.0, so the broadcast address is 172.16.47.255.
- Host bits: 32 − 20 = 12, so 212 − 2 = 4,094 usable hosts.
| Network | First usable | Last usable | Broadcast | Usable hosts |
|---|---|---|---|---|
| 172.16.32.0/20 | 172.16.32.1 | 172.16.47.254 | 172.16.47.255 | 4,094 |
import ipaddress
host = ipaddress.ip_interface("172.16.45.130/20")
net = host.network
print("mask: ", net.netmask)
print("network: ", net)
print("first: ", net.network_address + 1)
print("last: ", net.broadcast_address - 1)
print("broadcast:", net.broadcast_address)
print("usable: ", net.num_addresses - 2)
mask: 255.255.240.0 network: 172.16.32.0/20 first: 172.16.32.1 last: 172.16.47.254 broadcast: 172.16.47.255 usable: 4094
ip_interface accepts an address with host bits set (a
host and its prefix, as configured on an interface);
.network gives the subnet it belongs to.
Try it: is an address in this subnet?
Enter an address and a subnet to see whether the address belongs to it: both are ANDed with the mask and the network bits compared.
Example 3: network, broadcast and range of 10.20.30.40/27
Task: a ticket says a new server got 10.20.30.40/27. Which addresses share its subnet?
- /27: mask 255.255.255.224, interesting octet the fourth.
- Block size: 256 − 224 = 32. Multiples: 0, 32, 64, 96 …
- 40 lies between 32 and 64, so the network address is 10.20.30.32 and the broadcast address is 64 − 1 = 10.20.30.63.
- Usable: 10.20.30.33 to 10.20.30.62, which is 25 − 2 = 30 hosts.
| Network | Mask | First usable | Last usable | Broadcast |
|---|---|---|---|---|
| 10.20.30.32/27 | 255.255.255.224 | 10.20.30.33 | 10.20.30.62 | 10.20.30.63 |
import ipaddress
net = ipaddress.ip_interface("10.20.30.40/27").network
hosts = list(net.hosts())
print(net, net.netmask)
print("first:", hosts[0], " last:", hosts[-1], " count:", len(hosts))
print("broadcast:", net.broadcast_address)
print("10.20.30.64 in this subnet?", ipaddress.ip_address("10.20.30.64") in net)
10.20.30.32/27 255.255.255.224 first: 10.20.30.33 last: 10.20.30.62 count: 30 broadcast: 10.20.30.63 10.20.30.64 in this subnet? False
Example 4: VLSM for a small office
Giving every subnet the same size wastes addresses: a router-to-router link needs 2, not 62. Variable Length Subnet Masking (VLSM) means using different prefix lengths for different subnets carved out of the same block, so each subnet gets only the size it needs.
Task: from 192.168.50.0/24, create subnets for staff (60 hosts), guests (28), servers (12) and a wide area network (WAN) link between two routers (2).
- Sort the needs from largest to smallest. Big blocks have the strictest alignment (a /26 must start at a multiple of 64); placing them first means each later, smaller block automatically starts on a valid boundary.
- For each need, find the smallest subnet that fits with 2h − 2 ≥ hosts: 60 → h = 6 (62) → /26; 28 → h = 5 (30) → /27; 12 → h = 4 (14) → /28; 2 → h = 2 (2) → /30.
- Allocate from the start of the block, each subnet beginning right after the previous broadcast address.
| Need | Hosts | Subnet | Usable range | Broadcast |
|---|---|---|---|---|
| Staff | 60 | 192.168.50.0/26 | .1 – .62 | 192.168.50.63 |
| Guests | 28 | 192.168.50.64/27 | .65 – .94 | 192.168.50.95 |
| Servers | 12 | 192.168.50.96/28 | .97 – .110 | 192.168.50.111 |
| WAN link | 2 | 192.168.50.112/30 | .113 – .114 | 192.168.50.115 |
Addresses 192.168.50.116 to 192.168.50.255 remain free for growth.
import ipaddress
block = ipaddress.ip_network("192.168.50.0/24")
needs = [("Staff", 60), ("Guests", 28), ("Servers", 12), ("WAN link", 2)]
next_free = block.network_address
for name, hosts in sorted(needs, key=lambda need: need[1], reverse=True):
host_bits = 2
while 2 ** host_bits - 2 < hosts:
host_bits += 1
# strict checking raises ValueError if the start is not aligned
subnet = ipaddress.ip_network(f"{next_free}/{32 - host_bits}")
first = subnet.network_address + 1
last = subnet.broadcast_address - 1
print(f"{name:9}{hosts:3} {str(subnet):18} {first} - {last}")
next_free = subnet.broadcast_address + 1
print("free from", next_free, "to", block.broadcast_address)
Staff 60 192.168.50.0/26 192.168.50.1 - 192.168.50.62 Guests 28 192.168.50.64/27 192.168.50.65 - 192.168.50.94 Servers 12 192.168.50.96/28 192.168.50.97 - 192.168.50.110 WAN link 2 192.168.50.112/30 192.168.50.113 - 192.168.50.114 free from 192.168.50.116 to 192.168.50.255
Try it: a VLSM plan
Enter a block and the hosts each subnet needs. The plan follows the steps above: largest first, the smallest subnet that fits, each one right after the previous.
Cheat sheet: /24 to /30
These prefixes cover most local area network (LAN) and link subnets you will meet. The block size equals the number of addresses, because all of these fall in the fourth octet.
| Prefix | Mask | Block size | Subnets in a /24 | Usable hosts |
|---|---|---|---|---|
| /24 | 255.255.255.0 | 256 | 1 | 254 |
| /25 | 255.255.255.128 | 128 | 2 | 126 |
| /26 | 255.255.255.192 | 64 | 4 | 62 |
| /27 | 255.255.255.224 | 32 | 8 | 30 |
| /28 | 255.255.255.240 | 16 | 16 | 14 |
| /29 | 255.255.255.248 | 8 | 32 | 6 |
| /30 | 255.255.255.252 | 4 | 64 | 2 |
The same table, generated:
import ipaddress
for prefix in range(24, 31):
net = ipaddress.ip_network(f"192.0.2.0/{prefix}")
per_24 = 2 ** (prefix - 24)
print(f"/{prefix} {str(net.netmask):16} {net.num_addresses:4} {per_24:3} {net.num_addresses - 2:4}")
/24 255.255.255.0 256 1 254 /25 255.255.255.128 128 2 126 /26 255.255.255.192 64 4 62 /27 255.255.255.224 32 8 30 /28 255.255.255.240 16 16 14 /29 255.255.255.248 8 32 6 /30 255.255.255.252 4 64 2
Common mistakes
Python catches both errors for you. A network written with host bits
set is refused, and overlaps() tests two plans against
each other:
import ipaddress
staff = ipaddress.ip_network("192.168.50.0/26")
guests = ipaddress.ip_network("192.168.50.32/27") # a typo: should be .64
print("overlap:", staff.overlaps(guests))
ipaddress.ip_network("10.20.30.40/27") # host bits set
overlap: True ValueError: 10.20.30.40/27 has host bits set
The last line fails on purpose: 10.20.30.40 is a host address, not
the start of a /27. Pass strict=False (or use
ip_interface) when you want Python to find the network
for you.
Wildcard masks
Cisco access control lists (ACLs) and the Open Shortest Path First
(OSPF)
network command do not take a subnet mask but a
wildcard mask: the inverse of the subnet mask. A 0
bit means "must match", a 1 bit means "don't care". You get it by
subtracting each octet of the mask from 255: the wildcard for
255.255.255.192 (/26) is 0.0.0.63.
R1(config)# access-list 10 permit 192.168.10.0 0.0.0.63
R1(config)# router ospf 1
R1(config-router)# network 192.168.10.64 0.0.0.63 area 0
- Line 1: a standard ACL (number 10) that matches every source in 192.168.10.0/26 (.0 to .63).
- Line 2: starts OSPF process 1.
- Line 3: enables OSPF, in area 0, on every interface whose address lies in 192.168.10.64/26. Exam-relevant.
import ipaddress
net = ipaddress.ip_network("192.168.10.0/26")
print("mask: ", net.netmask)
print("wildcard:", net.hostmask)
mask: 255.255.255.192 wildcard: 0.0.0.63
Python calls the wildcard the hostmask: it has a 1 for
every host bit. ACLs are explained on
Network Security Basics,
OSPF on
Dynamic Routing and OSPF.
Route summarization: a preview
Subnetting borrows host bits to make smaller networks. Summarization (also called supernetting or route aggregation) does the reverse: it gives up network bits to describe several adjacent subnets with one shorter prefix. A branch office that owns 172.16.0.0/24 through 172.16.3.0/24 can be reached with a single route to 172.16.0.0/22, because the four networks differ only in the last two bits of the third octet.
import ipaddress
branch = [ipaddress.ip_network(f"172.16.{n}.0/24") for n in range(4)]
print(list(ipaddress.collapse_addresses(branch)))
[IPv4Network('172.16.0.0/22')]
Summaries work cleanly only when the subnets are contiguous and aligned — one more reason to plan subnets from one block, in order. How routers use these prefixes, including longest prefix match, is covered on Routing Basics.
Try it: summarize networks
Enter two or more networks to find the shortest prefix that covers them all, and whether that summary is exact or also covers addresses that are not yours.