A datacenter classic: Two bare-metal boxes sit in the same VLAN, link LEDs blink happily, but the ping dies with Destination Host Unreachable. On one server someone configured a /24, while on the other someone hurriedly slapped a /23 into the Netplan or NetworkManager config. To the first machine, its neighbor simply does not exist in the local L2 segment — it blindly shoves the frame to the default gateway, and if proxy ARP isn't active there, traffic disappears into the void.
Subnetting is often taught in training courses like a theoretical math lecture. You sketch zeros and ones on graph paper, compute bitwise AND operations by hand, and in the end still have no clue what actually happens on the switch or inside the Linux kernel.
Yet the operating system couldn't care less about academic ballast. The kernel does not care if an address was historically called "Class C". Ever since RFC 4632 introduced CIDR over thirty years ago, only two parameters exist:
A sequence of bits and a cutting edge.
An IP address is nothing more than a continuous number sliced at a defined point. The network sits in the front, the host in the back.
The prefix — the number after the slash — merely marks the knife:
/24means: 24 bits for the network, 8 bits for devices./25shifts the edge by one bit: twice as many networks, but only half as many hosts per segment.
Once you internalize that principle, you need neither binary tables nor IP calculators in daily work. You only need to know how the cutting edge runs through the respective byte.
The Tyranny of Powers of Two
The fundamental misunderstanding with IPv4 is not math, but geometry. A subnet is not a flexible sausage from which you can slice off twenty arbitrary addresses. It is a rigid block aligned strictly to powers of two.
When slicing a /24 into smaller subnets, you cannot decide that the server VLAN starts at .10 and ends at .40. The bits dictate the grid: You either allocate a 32-address block (/27) that starts squarely at 0, 32, 64, or 96, or the kernel ignores what you intended. A subnet that does not start on its own bit boundary simply does not exist in routing tables.
In production, this boils down to a handful of standard cut patterns:
When building point-to-point links — such as transit networks between two routers or firewalls —, nobody relies on the historical /30 anymore. Burning four IPs for two usable addresses was pure waste. Linux and standard routing OSs have supported RFC 3021 for decades: a /31 provides exactly two addresses without any network or broadcast overhead.
For host routes and isolated VPN endpoints, you stick with /32. WireGuard demonstrates this cleanly: AllowedIPs = 10.200.0.4/32 binds the route strictly to a single device. No ARP noise, no subnet overhead.
If you need actual multi-host segments, practical sizing typically begins at /29 (six usable addresses) — classic for small DMZs or public transit subnets. Everything above that simply doubles: /28 yields 14 hosts, /27 gives 30.
The mental model doesn't need to be binary at all. What matters is treating the address space like a filesystem: you can split a directory into two halves, but you cannot lay a single file across two separate partitions. Anyone attempting to route a /26 starting at .32 discovers on the first ip route add that the kernel ruthlessly snaps the mask back to the nearest valid network boundary.
IPv6: An End to Scarcity Reflexes
Anyone attempting to apply IPv4 penny-pinching reflexes to IPv6 immediately manufactures problems. The biggest mental stumbling block during migration: people start counting hosts again. Transit networks get planned as /126 or server segments as /120 to avoid "wasting" addresses.
Don't do it. With IPv6 you stop counting hosts; you allocate networks.
In LANs and server VLANs, exactly one functional size exists: /64. The upper 64 bits belong to routing; the lower 64 bits are the Interface ID. Squeezing this prefix breaks SLAAC RFC 4862, cripples Router Advertisements, and produces erratic failure modes that are nearly impossible to decipher in syslog. RFC 7421 spends pages documenting why anything tighter than /64 on a local segment is broken design. The sole real-world exception is backbone point-to-point links, where RFC 6164 specifies a /127 to prevent ping-pong routing loops between two routers. Everywhere else, it remains a /64.
Nibble Boundaries Over Mental Arithmetic
With IPv6 you no longer calculate — provided you align to nibble boundaries.
Each hexadecimal character represents exactly 4 bits. If you align your subnet boundaries along these 4-bit steps (/48, /52, /56, /60, /64), you never have to juggle powers of two or subtract masks. You simply increment the corresponding hex digit.
A business uplink typically arrives as a /48. That leaves 16 bits free to construct subnets: exactly 65,536 full-fledged /64 segments. With the common /56 prefix on fiber or DSL connections, you retain 8 bits for local networks — 256 subnets, numbered cleanly from 00 to ff.
In practice, carving subnets out of a /56 looks as straightforward as this:
Prefix from ISP: 2001:db8:beef:1200::/56
VLAN 10 (Management): 2001:db8:beef:1210::/64
VLAN 20 (Servers): 2001:db8:beef:1220::/64
VLAN 30 (DMZ): 2001:db8:beef:1230::/64
VLAN 40 (Guests): 2001:db8:beef:1240::/64
An added benefit: broadcasts are gone. Where IPv4 burns two addresses in every single subnet for network ID and broadcast, IPv6 has zero broadcast waste. Resolution from IP to MAC is handled by Neighbor Discovery RFC 4861 via multicast. Even the ::0 on an interface is technically a valid host address, even if people traditionally skip it out of habit.
Terminal Tooling
Nobody calculates subnet masks in their head during an outage when minutes count. It comes down to quick terminal verification and dependable values for automation scripts.
For IPv4, ipcalc does the job:
$ ipcalc 10.20.30.70/26
Address: 10.20.30.70 00001010.00010100.00011110.01 000110
Netmask: 255.255.255.192 = 26 11111111.11111111.11111111.11 000000
Wildcard: 0.0.0.63 00000000.00000000.00000000.00 111111
=>
Network: 10.20.30.64/26 00001010.00010100.00011110.01 000000
HostMin: 10.20.30.65 00001010.00010100.00011110.01 000001
HostMax: 10.20.30.126 00001010.00010100.00011110.01 111110
Broadcast: 10.20.30.127 00001010.00010100.00011110.01 111111
Hosts/Net: 62 (Class A, Private Internet)
You can safely ignore that the tool still babbles about "Class A" at the bottom — old habits die hard in open-source CLI utilities. What matters are Network, HostMin, HostMax, and the broadcast address. When writing Bash provisioning scripts, pass flags like -n or --json to parse values cleanly into variables instead of hacking through stdout with awk.
For IPv6, ipcalc quickly hits limitations depending on distribution packaging. You are better off using sipcalc. The tool expands addresses and displays boundaries without ambiguity:
$ sipcalc 2001:db8:beef:1200::/56
-[ipv6 : 2001:db8:beef:1200::/56] - 0
[IPV6 INFO]
Expanded Address - 2001:0db8:beef:1200:0000:0000:0000:0000
Compressed address - 2001:db8:beef:1200::
Subnet prefix (masked) - 2001:db8:beef:1200:0:0:0:0/56
Prefix length - 56
Network range - 2001:0db8:beef:1200:0000:0000:0000:0000 -
2001:0db8:beef:12ff:ffff:ffff:ffff:ffff
The nibble boundary is immediately apparent: the allocation spans from 1200 to 12ff. Exactly 256 subnets of /64.
Yet the single most important diagnostic command remains querying the kernel itself. When a machine cannot reach the box in the adjacent rack, staring at static interface files rarely helps. Look directly into the Forwarding Information Base (FIB):
$ ip route get 10.20.30.130
10.20.30.130 via 10.20.30.1 dev eth0 src 10.20.30.70 uid 1000
cache
ip route get interrogates the kernel stack: Where does this packet actually go?
If via 10.20.30.1 pops up for an IP that was supposed to be local, your interface prefix chopped the destination out of the Layer 2 broadcast domain. The kernel is doing precisely what the mask ordered it to do. Nothing more, nothing less.
Side-by-Side Comparison
When running both protocols on the same network, the mental model shifts:
| Metric | IPv4 | IPv6 |
|---|---|---|
| Mindset | Save addresses, count hosts | Allocate networks, assign segments |
| Boundaries | Rigid powers of two (/24, /27, /29, /31) |
Nibble boundaries in 4-bit steps (/48, /56, /64) |
| Standard Segment | Typically /24 (254 hosts) |
Always /64 (SLAAC standard) |
| Point-to-Point | /31 (RFC 3021) or legacy /30 |
/127 (RFC 6164) only in core, otherwise /64 |
| Host Route | /32 |
/128 |
| Overhead | 2 addresses per subnet (Network ID + Broadcast) | No broadcast waste |
| L2 Resolution | ARP (Broadcast interrupts every interface) | Neighbor Discovery (Multicast via Solicited-Node) |
Routing fundamentally doesn't change between the two worlds: the kernel compares bits against the mask. If you place your cutting edges cleanly on powers of two and nibbles, you never have to calculate by hand or debug in the dark.