Split 192.168.1.0/24 into four pieces and they start at .0, .64, .128, and .192. None can start at 50. Memorizing the subnetting table, where /26 means 64 and /27 means 32, makes the numbers feel familiar, but a nonstandard boundary or a VLSM split breaks that memory.
A CIDR prefix marks where the network bits end, and every boundary calculation reduces to a bit operation at that mark. The sections below walk through that bit operation and check each result with real output from Python’s ipaddress.
What the Number After /24 Means
CIDR writes the count of network bits after the address with a slash. 192.168.1.0/24 means the first 24 bits are network and the remaining 8 (32 - 24) are host. Since IPv4 addresses are 32 bits, prefixes run from /0 to /32: /0 (0.0.0.0/0) is the default route covering the whole internet, and /32 is a single host.
Spread out in binary, the boundary is visible. Here is 192.168.1.64/26:
addr 192.168.1.64 = 11000000.10101000.00000001.01000000
└──────── 26-bit network ──────┘└6 host┘
mask /26 = 11111111.11111111.11111111.11000000 (255.255.255.192)
The mask is a 32-bit value with all network bits set to 1 and all host bits set to 0. At /26, the first 26 bits are 1, so the last octet is 11000000 = 192, which makes the mask 255.255.255.192. Counting how many 1s are set replaces memorization.
The common mappings are the rule “n host bits, 2ⁿ addresses” repeated.
| Prefix | Mask | Host bits | Total addresses | Usable hosts |
|---|---|---|---|---|
| /24 | 255.255.255.0 | 8 | 256 | 254 |
| /25 | 255.255.255.128 | 7 | 128 | 126 |
| /26 | 255.255.255.192 | 6 | 64 | 62 |
| /27 | 255.255.255.224 | 5 | 32 | 30 |
| /28 | 255.255.255.240 | 4 | 16 | 14 |
| /30 | 255.255.255.252 | 2 | 4 | 2 |
In each block the first address is the network address and the last is the broadcast address, so neither can go to a host. That’s why usable hosts is always 2ⁿ - 2. The exception is /31, which under RFC 3021 uses both addresses as hosts on point-to-point router links with no broadcast. A /32 is a single host, used for host routes or loopbacks.
Why Subnet Boundaries Land Where They Do
Subnetting raises the prefix so the network portion grows and the host portion shrinks. Splitting 192.168.1.0/24 into /26 yields four subnets of 64 each. Get the exact answer from Python first.
import ipaddress
net = ipaddress.ip_network('192.168.1.0/24')
for s in net.subnets(new_prefix=26):
h = list(s.hosts())
print(f'{s} net={s.network_address} bcast={s.broadcast_address} '
f'usable={h[0]}-{h[-1]} ({len(h)} hosts)')
Actual output:
192.168.1.0/26 net=192.168.1.0 bcast=192.168.1.63 usable=192.168.1.1-192.168.1.62 (62 hosts)
192.168.1.64/26 net=192.168.1.64 bcast=192.168.1.127 usable=192.168.1.65-192.168.1.126 (62 hosts)
192.168.1.128/26 net=192.168.1.128 bcast=192.168.1.191 usable=192.168.1.129-192.168.1.190 (62 hosts)
192.168.1.192/26 net=192.168.1.192 bcast=192.168.1.255 usable=192.168.1.193-192.168.1.254 (62 hosts)
The start addresses are 0, 64, 128, 192. A /26 has 6 host bits, and those 6 bits are the range that varies within one subnet (0–63, i.e. 64 values), so a network’s start address must be a multiple of 64. That is why it cannot start at 50: the last octet of 50 is 00110010, whose host bits (the low 6) are not zero. To be a start address, every host bit must be 0. A valid subnet boundary is an address whose host bits are all 0.
Block Membership
Whether an address belongs to a block comes down to ANDing the address with the mask and checking whether the resulting network address equals the block’s network address.
Does 192.168.1.75 belong to 192.168.1.64/26?
addr 192.168.1.75 = 11000000.10101000.00000001.01001011
mask /26 = 11111111.11111111.11111111.11000000
AND result = 11000000.10101000.00000001.01000000 = 192.168.1.64 ✓
The result is 192.168.1.64, which matches the block’s network address, so yes. By contrast, ANDing 192.168.1.200 with the same mask gives 192.168.1.192, which does not match. Checked in Python:
import ipaddress
for ip in ['192.168.1.75', '192.168.1.200']:
a = ipaddress.ip_address(ip)
for s in ipaddress.ip_network('192.168.1.0/24').subnets(new_prefix=26):
if a in s:
print(f'{ip} -> {s}')
break
192.168.1.75 -> 192.168.1.64/26
192.168.1.200 -> 192.168.1.192/26
From Host Count to Prefix
In practice you usually go the other way: “I need 50 hosts, which block do I hand out?” Three steps:
- Add 2 to the host count (for the network and broadcast addresses).
- Find the smallest power of 2 that is at least that value → the number of host bits.
32 - host bits= prefix.
For 50 hosts: 50 + 2 = 52, the smallest power of 2 that holds it is 2⁶ = 64, so 6 host bits → /26. The same way:
- 10 hosts →
12→2⁴ = 16→/28(14 usable) - 100 hosts →
102→2⁷ = 128→/25(126 usable) - 500 hosts →
502→2⁹ = 512→/23(510 usable)
Point-to-point router links need only 2 hosts, so they use /30 or /31.
VLSM
A fixed-length mask forces every subnet to the same size. Carving a 100-host department and a 2-host router link to the same size wastes one badly. VLSM sizes each subnet to its own requirement and eliminates that waste.
Allocate the largest first. Placing small ones first throws the alignment off and fragments the space. Here is one 192.168.1.0/24 carved to fit, checked in Python.
import ipaddress
reqs = [('server farm', 100), ('office', 50), ('DMZ', 10),
('p2p A', 2), ('p2p B', 2), ('p2p C', 2)]
cursor = int(ipaddress.ip_network('192.168.1.0/24').network_address)
for name, need in reqs:
bits = 0
while (2**bits) - 2 < need:
bits += 1
net = ipaddress.ip_network((cursor, 32 - bits))
print(f'{name:12s} need {need:3d} -> {str(net):18s} ({net.num_addresses - 2} usable)')
cursor += net.num_addresses
server farm need 100 -> 192.168.1.0/25 (126 usable)
office need 50 -> 192.168.1.128/26 (62 usable)
DMZ need 10 -> 192.168.1.192/28 (14 usable)
p2p A need 2 -> 192.168.1.208/30 (2 usable)
p2p B need 2 -> 192.168.1.212/30 (2 usable)
p2p C need 2 -> 192.168.1.216/30 (2 usable)
The DMZ, with 10 hosts, lands on /28 (14) rather than /27 (30). Cutting only what is needed leaves an entire 192.168.1.224/27 block free at the tail for growth. Boundaries like this are easy to miss by hand, but the script above recomputes the host bits exactly each time and advances the cursor. This design works only on routing protocols that support VLSM (RIPv2, OSPF, EIGRP, IS-IS, BGP); RIPv1 and IGRP do not.
Supernetting
Supernetting goes the other direction, lowering the prefix to merge several smaller networks into one. It is the heart of route aggregation, which shrinks routing-table entries to save router memory and lookup time.
Folding four consecutive /24s, again verified in Python:
import ipaddress
nets = [ipaddress.ip_network(f'192.168.{i}.0/24') for i in range(4)]
print(list(ipaddress.collapse_addresses(nets))[0])
192.168.0.0/22
The /22 result comes out of the binary. From 192.168.0.0 to 192.168.3.255, the first 22 bits are common and only the low 2 bits of the third octet (0–3) vary.
192.168.0.0 = 11000000.10101000.000000 00.00000000
192.168.3.255 = 11000000.10101000.000000 11.11111111
└──────── 22 common bits ───┘
That 22-bit common prefix is 192.168.0.0/22. This aggregation is what lets ISPs and BGP routing keep the global routing table at a manageable size.