Subnetting questions show up in two forms. One is arithmetic: “what range does 10.20.37.140/20 belong to?” The other is design: “carve a VPC for three availability zones without boxing yourself in.” The arithmetic is quick once you see the pattern, and the design question is where interviewers find out whether you’ve actually run out of addresses before.
What the slash number means
An IPv4 address is 32 bits, written as four 8-bit octets. CIDR notation, like 10.20.0.0/16, says the first 16 bits are the network prefix and the remaining 16 identify hosts inside it. So the block has 2^16 = 65,536 addresses, from 10.20.0.0 to 10.20.255.255.
The same thing written as a subnet mask is the prefix as ones followed by zeros: /16 is 255.255.0.0, /20 is twenty ones, which is 255.255.240.0, and /28 is 255.255.255.240.
Two rules cover most questions:
- A
/nblock contains 2^(32 − n) addresses. Each extra bit of prefix halves the block:/24has 256,/25has 128,/28has 16. - A block always starts on a multiple of its own size. A
/20holds 4,096 addresses, which is 16 values of the third octet, so/20networks start at third octets 0, 16, 32, 48 and so on.10.20.37.0/20is not a valid network address, because 37 isn’t a multiple of 16.
Working one out by hand: 10.20.37.140/20
The quick method: find the octet where the mask stops being 255. For /20 that’s the third octet, with mask byte 240.
- Block size in that octet is 256 − 240 = 16.
- Network start: round the address’s third octet (37) down to a multiple of 16, giving 32. So the network is
10.20.32.0/20. - Broadcast: the next block starts at 48, so this one ends just before it, at
10.20.47.255. - Usable hosts: on a traditional LAN, the first address names the network and the last is broadcast, so hosts run from
10.20.32.1to10.20.47.254: 4,094 of them.
Step 2 is really a bitwise AND, and seeing it in binary explains why the shortcut works:
37 = 0010 0101
240 = 1111 0000
AND = 0010 0000 = 32The mask keeps the high four bits of the octet (network part) and zeroes the low four (host part). Rounding down to a multiple of 16 is the same operation.
Python’s standard ipaddress module will check your work, which is a handy habit when you’re writing infrastructure code:
import ipaddress as ip
host = ip.ip_interface("10.20.37.140/20")
print(host.network, host.network.broadcast_address, host.netmask)
for s in ["10.20.0.0/24", "10.20.0.0/28", "10.20.0.0/31", "10.20.0.0/32"]:
net = ip.ip_network(s)
hosts = list(net.hosts())
print(net, net.num_addresses, len(hosts), hosts[0], hosts[-1])
ip.ip_network("10.20.37.0/20")Output:
10.20.32.0/20 10.20.47.255 255.255.240.0
10.20.0.0/24 256 254 10.20.0.1 10.20.0.254
10.20.0.0/28 16 14 10.20.0.1 10.20.0.14
10.20.0.0/31 2 2 10.20.0.0 10.20.0.1
10.20.0.0/32 1 1 10.20.0.0 10.20.0.0
ValueError: 10.20.37.0/20 has host bits setTwo edge cases appear in that output. A /31 has two usable addresses and no broadcast, because RFC 3021 allows it for point-to-point links where a broadcast address is pointless. A /32 is a single host, which is how you write one address in a route table or a firewall rule. And the ValueError is the module enforcing the “starts on a multiple of its size” rule. (Pass strict=False if you want it to round down for you.)
Cloud subnets don’t give you every address
Interviewers who’ve worked on AWS like this one. In a VPC subnet, AWS reserves five addresses, not two: the network address, .1 for the VPC router, .2 for the DNS resolver, .3 held for future use, and the last address. A /24 gives you 251 usable addresses, and the smallest subnet AWS allows, a /28, gives you 11. Azure also reserves five per subnet; GCP reserves four. The exact list differs, but “you lose more than two” is the point.
That matters when subnets are small. A /28 for a NAT gateway is fine. A /24 for an EKS node group using the AWS VPC CNI is not, because that plugin gives every pod a real VPC address. A dozen nodes running 30 pods each needs well over 251 addresses, and the symptom is pods stuck in ContainerCreating with IP allocation errors rather than anything that says “subnet full”.
Designing a VPC that you won’t have to renumber
Suppose you’re given 10.20.0.0/16 for a production VPC across three availability zones, with public subnets for load balancers, private subnets for application nodes and pods, and isolated subnets for databases.
A layout that holds up:
private-a 10.20.0.0/20 4,091 usable on AWS
private-b 10.20.16.0/20
private-c 10.20.32.0/20
public-a 10.20.48.0/24 251 usable
public-b 10.20.49.0/24
public-c 10.20.50.0/24
data-a 10.20.52.0/24
data-b 10.20.53.0/24
data-c 10.20.54.0/24
(free) 10.20.64.0/18 16,384 addresses held backThe decisions behind it:
- Big private subnets. That’s where node and pod counts grow. A
/20per zone is generous now and still fine after the cluster triples. - Small public subnets. Only load balancers and NAT gateways live there.
- Aligned, contiguous blocks. Every allocated subnet sits inside
10.20.0.0/18, so one route or security rule can cover them all, and the first two private subnets merge into a single/19. Python’sip.collapse_addresseson10.20.0.0/20and10.20.16.0/20returns10.20.0.0/19: adjacent aligned blocks merge cleanly. - A reserved quarter. You can add a fourth zone or a new tier later without touching existing subnets. Most cloud providers won’t let you resize a subnet in place, so “we’ll make it bigger later” means migrating workloads.
The biggest design mistake isn’t inside the VPC at all. It’s picking a range that overlaps with something you’ll need to reach. Two VPCs that both use 10.0.0.0/16 can’t be peered, because a packet for 10.0.5.9 has no unambiguous destination. The same goes for the office VPN, a partner’s network, or the default Docker bridge (172.17.0.0/16), which regularly collides with corporate networks in the 172.16.0.0/12 private range and makes a laptop lose access to an internal service when Docker starts. Keep an organisation-wide allocation list and hand out blocks from it.
Interview exercise: will this new subnet fit?
The VPC above needs an extra subnet for a batch workload in zone b. A teammate proposes 10.20.24.0/22, “since there’s room after private-b”. Is it valid, and if not, what would you pick?
Answer
It overlaps. private-b is 10.20.16.0/20, whose block covers third octets 16 through 31 (16 + 16 − 1). 10.20.24.0/22 covers third octets 24 to 27, entirely inside private-b. The cloud API would reject it with an overlap error, and Python agrees:
ip.ip_network("10.20.16.0/20").overlaps(ip.ip_network("10.20.24.0/22")) # True
ip.ip_network("10.20.16.0/20").overlaps(ip.ip_network("10.20.64.0/22")) # FalseThe confusion comes from treating the subnet as if it ended where its current hosts end. A subnet owns its whole block whether or not addresses are in use. I’d take the new subnet from the reserved range, for example 10.20.64.0/22, which is aligned (64 is a multiple of 4) and doesn’t overlap anything. If the workload is large and long-lived, I’d give it a /20 from the same reserve so it doesn’t need to move later.