On this page
01 Why models slip on subnet arithmetic
Language models predict text, and subnet plans are text that looks the same whether the numbers are right or wrong. The common failures are consistent: a subnet that starts on a boundary its prefix length does not allow, two subnets that overlap by one block, a broadcast address given as usable, and wildcard masks that are the netmask rather than its inverse. Each is easy to miss in review because the output is well formatted. This procedure, in the routing section, checks the numbers with tools. It complements the broader method for verifying AI-written configurations.
02 The check, step by step
List every prefix
Extract each network and prefix length from the plan into one list, including the parent block it was carved from.
Recompute with a library
Parse each prefix with a CIDR library, such as the standard ipaddress module in Python, in strict mode. A strict parser rejects a network address that is not aligned, which catches the most common error immediately.
Check containment and overlap
Confirm each subnet is inside the parent block and that no two overlap. Sort by network address and compare each end with the next start.
Recompute sizes and usable ranges
For each subnet, compute the number of addresses, the first and last usable host and the broadcast address, and compare with the plan. Plans often claim one extra usable host.
Convert wildcard masks back
For each ACL entry, turn the address and wildcard back into a range and compare it with what the entry was meant to match. A netmask pasted where a wildcard belongs matches far more than intended.
Test with addresses
Feed the ACL or firewall rule the first and last address of each intended range and one address just outside, in a lab device or a policy analysis tool, and confirm each result.
$ python3 -c "import ipaddress; ipaddress.ip_network('10.20.7.0/23')"
ValueError: 10.20.7.0/23 has host bits set
$ python3 -c "import ipaddress; print(ipaddress.ip_network('10.20.7.0/23', strict=False))"
10.20.6.0/23The first command fails because a /23 must start on an even third octet; the second shows the network the plan actually describes. In a VLAN plan, that single error puts two VLAN subnets and their gateways in the same address space.
03 Worked example: a generated /22 VLAN plan, recomputed
A campus team asked an assistant to carve 10.20.4.0/22 into five VLAN subnets: users, servers, printers, management and guest, with usable host counts. The answer arrived as a neat table with gateways and DHCP ranges. Recomputing it took under a minute and found two errors that would have reached production.
| VLAN | As generated | Recomputed | Verdict |
|---|---|---|---|
| 10 Users | 10.20.4.0/23, 512 usable | 10.20.4.0 to 10.20.5.255, 510 usable | Range right, count wrong |
| 20 Servers | 10.20.6.0/24, 254 usable | 10.20.6.0 to 10.20.6.255, 254 usable | Correct |
| 30 Printers | 10.20.7.0/25, 128 usable | 10.20.7.0 to 10.20.7.127, 126 usable | Count wrong |
| 40 Management | 10.20.7.128/26, 62 usable | 10.20.7.128 to 10.20.7.191, 62 usable | Correct |
| 50 Guest | 10.20.7.0/23, 510 usable | Host bits set; parent is 10.20.6.0/23 | Misaligned and overlaps 20 and 30 |
$ cat plan.txt
10.20.4.0/23 10.20.6.0/24 10.20.7.0/25 10.20.7.128/26 10.20.7.0/23
$ python3 - <<'EOF'
import ipaddress as ip
parent = ip.ip_network('10.20.4.0/22')
nets = []
for s in open('plan.txt').read().split():
try:
n = ip.ip_network(s)
except ValueError as e:
print('FAIL', s, e); continue
print('ok ', s, 'in parent' if n.subnet_of(parent) else 'OUTSIDE', n.num_addresses - 2, 'usable')
nets.append(n)
nets.sort(key=lambda n: n.network_address)
for a, b in zip(nets, nets[1:]):
if a.overlaps(b): print('OVERLAP', a, b)
EOF
ok 10.20.4.0/23 in parent 510 usable
ok 10.20.6.0/24 in parent 254 usable
ok 10.20.7.0/25 in parent 126 usable
ok 10.20.7.128/26 in parent 62 usable
FAIL 10.20.7.0/23 10.20.7.0/23 has host bits setThe strict parser rejected the guest subnet before the overlap test ran, which is the right order: an unaligned prefix has no single meaning to test against. Had the plan been written as 10.20.6.0/23, the overlap loop would have flagged it against both the server and printer rows. The usable counts came out two lower than generated on every row because the model counted the network and broadcast addresses as hosts, a habit worth checking on every plan. The remaining space, 10.20.7.192/26, was unallocated and unmentioned; a good plan says so.
04 The errors worth looking for
| Error | Example | How the check catches it |
|---|---|---|
| Misaligned network | 10.20.7.0/23 | Strict parse fails: host bits set |
| Overlap | 10.20.4.0/23 and 10.20.5.0/24 | Sorted comparison: second starts inside first |
| Wrong usable count | A /27 listed with 32 hosts | Recomputed: 30 usable plus network and broadcast |
| Netmask as wildcard | 0.0.0.255 intended, 255.255.255.0 written | Converted range covers the wrong bits entirely |
| Reserved space used | Documentation range 192.0.2.0/24 in production | Check against the special-purpose registry |
05 Wildcard mask arithmetic, checked by hand and by tool
A wildcard mask is the bitwise inverse of a netmask: a 0 bit means the address bit must match, a 1 bit means it is ignored. For a contiguous prefix the wildcard is 255.255.255.255 minus the netmask, so a /22 (netmask 255.255.252.0) has wildcard 0.0.3.255 and matches a block of 1,024 addresses. The two errors generated ACLs make most are pasting the netmask where the wildcard belongs, and pairing a correct wildcard with a base address that is not aligned to it.
| Prefix | Netmask | Wildcard mask | Block size | Matches from 10.20.4.0 |
|---|---|---|---|---|
| /22 | 255.255.252.0 | 0.0.3.255 | 1,024 | 10.20.4.0 to 10.20.7.255 |
| /23 | 255.255.254.0 | 0.0.1.255 | 512 | 10.20.4.0 to 10.20.5.255 |
| /24 | 255.255.255.0 | 0.0.0.255 | 256 | 10.20.4.0 to 10.20.4.255 |
| /25 | 255.255.255.128 | 0.0.0.127 | 128 | 10.20.4.0 to 10.20.4.127 |
| /26 | 255.255.255.192 | 0.0.0.63 | 64 | 10.20.4.0 to 10.20.4.63 |
| /27 | 255.255.255.224 | 0.0.0.31 | 32 | 10.20.4.0 to 10.20.4.31 |
The second error is subtler. An entry of permit ip 10.20.4.0 0.0.7.255 any looks like the /22 above with one digit off. The wildcard 0.0.7.255 ignores the low three bits of the third octet, and 4 is binary 00000100, so the entry matches 10.20.0.0 through 10.20.7.255: twice the intended range, including whatever lives in 10.20.0.0/22. Some platforms normalise the base address silently; others accept the entry as typed and match exactly this. Neither warns you.
$ python3 - <<'EOF'
import ipaddress as ip
def wild(base, wc):
b, w = int(ip.ip_address(base)), int(ip.ip_address(wc))
lo = b & ~w & 0xFFFFFFFF
return ip.ip_address(lo), ip.ip_address(lo | w), bin(w).count('1')
print(wild('10.20.4.0', '0.0.3.255'))
print(wild('10.20.4.0', '0.0.7.255'))
print(wild('10.20.4.0', '255.255.252.0'))
EOF
(IPv4Address('10.20.4.0'), IPv4Address('10.20.7.255'), 10)
(IPv4Address('10.20.0.0'), IPv4Address('10.20.7.255'), 11)
(IPv4Address('0.0.0.0'), IPv4Address('255.255.252.0'), 22)The first result confirms the intended /22. The second shows the one-digit slip doubling the range downwards to 10.20.0.0, with eleven ignored bits where ten were meant. The third, the netmask pasted as a wildcard, produces a range that means nothing, which is why the check must print the range and never just accept that the entry parsed. Wildcard masks need not be contiguous. 10.20.0.0 0.0.254.255 matches every even-numbered third octet under 10.20, which no prefix length can express; when a generated ACL uses one, check it by listing the ignored bit positions rather than by converting to a prefix.
06 Keeping the check in the change process
Make the recomputation part of review, not an optional extra: attach the tool output to the change ticket so the reviewer sees the numbers checked, not just the plan. The same principle runs through peer review for AI-assisted network changes: generated work is welcome, unverified generated work is not.
If a number decides where a packet goes, a tool must have computed it or checked it. A model may have suggested it.
07 Pitfalls in checking generated address plans
- Point-to-point links. RFC 3021 allows a /31 with two usable addresses and no broadcast. A model that applies the usual minus-two rule reports zero hosts and may "fix" the link to a /30.
- IPv6 alignment. Generated IPv6 plans put subnets on boundaries that are not nibble-aligned, or list a /64 with a host count. Parse them with the same strict library; the alignment rule is identical in 128 bits.
- Gateway assumptions. Plans assign the first usable address as the gateway. Confirm it against the site convention before the DHCP scope excludes the wrong address.
- Reserved space by accident. Models trained on documentation reproduce 192.0.2.0/24 and 203.0.113.0/24 as if they were free. The IANA special-purpose registry (RFC 6890) is the check.
- Mixed notations. One ACL with prefix lengths on some lines and wildcards on others invites a mixed-up mask. Normalise to one form before review.
- Rounding in summaries. A summary route generated to cover several subnets is often one bit too wide. Recompute the supernet from the members and compare.
08 Expert tips
- Ask for the plan as one prefix per line, without prose; it pastes straight into the checker and gives the model less room to explain an error away.
- Print every range as first address to last address in the ticket. A reviewer sees an overlap in two adjacent lines faster than in any table of prefixes.
- Test ACLs with three addresses per entry, the first, the last and one just outside, in a lab device or a policy tool, and attach the results.
- Keep the parent block in the check. A subnet that is aligned and non-overlapping but outside the parent is still wrong, and it is the error strict parsing cannot see.
- Record the failures. Which errors a tool makes, and how often, decides how much of its output can be trusted next time; the addressing section of the dual-stack transition guide is where the IPv6 side of the same checks starts.
09 Questions
Can I trust AI chatbots with subnetting?
Use them for a first draft, never as the final answer. Recompute every prefix with a CIDR library or calculator and check alignment, containment and overlap.
How do I check a subnet is aligned?
Parse it strictly with a CIDR library. The network address must be a multiple of the block size; a strict parser raises an error when host bits are set.
What is a wildcard mask?
The bitwise inverse of a netmask, used in many ACL syntaxes. 0.0.0.255 matches a /24; writing the netmask 255.255.255.0 there matches something entirely different.
Which address ranges should never appear in production plans?
Documentation ranges such as 192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24 (RFC 5737), and other special-purpose blocks listed in the IANA registry described by RFC 6890.
How do I test an ACL before deployment?
Run the first and last address of each intended range and one just outside through a lab device or a configuration analysis tool, and compare the results with the intent.
Can a wildcard mask be non-contiguous?
Yes, in most ACL syntaxes. A wildcard such as 0.0.254.255 keeps one bit in the third octet and ignores the rest, so it matches alternating /24 blocks that no prefix length can express. Generated ACLs rarely intend this. Convert each entry to its matched range, or list the ignored bit positions, before approving it.