12. Special-use addresses, private space, and NAT/PAT
12.1 Ranges you must recognize
| Range | Name | Meaning |
|---|---|---|
10.0.0.0/8 |
Private (RFC 1918) | Not routed on the internet |
172.16.0.0/12 |
Private (RFC 1918) | 172.16.0.0–172.31.255.255 |
192.168.0.0/16 |
Private (RFC 1918) | Home/office LANs |
100.64.0.0/10 |
CGNAT (RFC 6598) | Carrier-grade NAT shared space |
127.0.0.0/8 |
Loopback | 127.0.0.1 = this host; whole /8 is local |
169.254.0.0/16 |
Link-local (APIPA) | Self-assigned when DHCP fails; not routed |
0.0.0.0/8 |
"This network" | 0.0.0.0 = "any"/unspecified/default route |
255.255.255.255 |
Limited broadcast | This subnet only, never forwarded |
224.0.0.0/4 |
Multicast | 224.0.0.1 all-hosts, 224.0.0.251 mDNS |
240.0.0.0/4 |
Reserved (experimental) | Mostly unusable |
192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 |
Documentation (TEST-NET) | Safe to use in docs/examples |
198.18.0.0/15 |
Benchmarking | Lab/testing only |
12.2 Why NAT exists
IPv4 has ~3.7 billion usable addresses; there are ~15 billion connected devices. NAT (Network Address Translation) lets many private hosts share one public address. It also became a de-facto firewall (unsolicited inbound has nowhere to go) — which is both a feature and the reason peer-to-peer is hard (Ch. 46).
12.3 The kind you actually use: PAT / NAPT / "masquerade"
Port Address Translation (one public IP, many hosts) maps the full 4-tuple:
NAT table entry (conntrack):
inside: 192.168.1.50 : 51000 <-> 8.8.8.8 : 53
outside: 203.0.113.7 : 62000 <-> 8.8.8.8 : 53
Outbound: rewrite src 192.168.1.50:51000 -> 203.0.113.7:62000, fix checksums
Inbound: match dst 203.0.113.7:62000 -> rewrite dst 192.168.1.50:51000
The router allocates a unique outside port per flow, so two inside hosts both using source port 51000 to the same server get different outside ports. State times out (TCP established ~5 days on Linux conntrack default, UDP ~30 s, TCP after FIN ~1 min).
12.4 NAT variants (and why P2P cares — see Ch. 46)
- Full-cone: once
inside:port → outside:portexists, any external host can send tooutside:port. Easiest to traverse. - Restricted-cone: external host may reply only if the inside host sent to that host's IP first.
- Port-restricted-cone: … that host's IP and port.
- Symmetric: a different outside port per destination. Two different servers see two different source ports for the same inside socket. Hardest to traverse; needs TURN relay.
- Static NAT / port forwarding: fixed
outside:port → inside:host:portfor hosting a server behind NAT. - 1:1 NAT (NAT44) / hairpin NAT: map a whole public IP to a private host; hairpin = inside host reaching the public IP of another inside host.
- CGNAT (RFC 6598): your ISP NATs you inside
100.64.0.0/10, so you're behind two layers of NAT. Breaks port forwarding entirely; this is why IPv6 or services like Tailscale matter.
12.5 What NAT breaks
- Embedded addresses: protocols that put IPs inside the payload — FTP active mode, SIP, H.323, PPTP GRE — need an ALG (Application Layer Gateway) on the router to rewrite them, and ALGs are famously buggy.
- End-to-end IPsec AH (authenticates the IP header → NAT invalidates it); needs NAT-T (UDP/4500 encapsulation).
- Inbound connections without explicit port forwarding.
- Address-based logging/geo/abuse — thousands of users behind one IP.
- Connection count limits — a busy office can exhaust ~64k ports to a single popular destination IP.
12.6 War story
A SaaS provider IP-allowlisted a customer's office "public IP." It worked for a week, then broke randomly. The ISP had moved the customer to CGNAT, so their public IP now changed hourly and was shared with strangers. Allowlisting became impossible; the fix was a site-to-site VPN with a stable tunnel IP. Lesson: never assume a client's source IP is stable or unique — NAT, CGNAT, mobile networks, and proxies all break that assumption.
Try it yourself
curl -s https://api.ipify.org ; echo # your public (post-NAT) IP
ip route get 8.8.8.8 # which src IP/interface is used
sudo conntrack -L | head # Linux NAT/state table (conntrack-tools)
sudo iptables -t nat -L -n -v # NAT rules
# On a Linux router, classic masquerade:
sudo iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j MASQUERADE