NAT and PAT
Network Address Translation rewrote the internet's addressing model as a workaround for IPv4 exhaustion. It is everywhere, it is invisible when it works, and it breaks a specific set of protocols that expect end-to-end addressing.
The three flavours
| Type | What changes | Typical use |
|---|---|---|
| Static NAT (1:1) | One private IP ↔ one public IP | Publishing an internal server |
| Dynamic NAT (pool) | Private IP ↔ any free public IP from a pool | Legacy outbound with a block of addresses |
| PAT / NAT overload (many:1) | Private IP:port ↔ public IP:unique port | Every home router and most corporate egress |
PAT is what almost everyone means by "NAT": thousands of internal hosts share one public address, distinguished by source port.
Client 192.168.1.20:51234 → 203.0.113.5:41200 → Server 198.51.100.10:443
The router keeps a translation table mapping the tuple back for the return packet. That table is state, so NAT is a stateful middlebox and inherits every stateful-middlebox problem.
Why it exists, and what it costs
IPv4 has ~4.3 billion addresses. NAT let the world postpone renumbering for thirty years. The bill:
- No inbound connections without a forwarding rule or a tunnel.
- No end-to-end addressing, which breaks protocols that embed IPs or ports inside payloads.
- State exhaustion — every translation holds a table entry and a port.
- Ambiguity in logs — the server sees the NAT address, not the client, unless something forwards the original.
Protocols that break, and why
| Protocol | Problem | Workaround |
|---|---|---|
| FTP (active) | Server connects back to a port the client did not open | Passive mode, or an ALG |
| SIP / H.264 | Addresses embedded in the payload | ALG, STUN, TURN, ICE |
| IPsec ESP | No ports to translate | NAT-T (UDP 4500 encapsulation) |
| ICMP | No ports; identifier is translated instead | Handled by most NATs, not all |
| DNS | A records pointing at private IPs leak out | Split-horizon DNS, not NAT |
| Peer-to-peer / WebRTC | Direct connection impossible | STUN/TURN relay |
ALGs (application layer gateways) try to fix this by rewriting payloads. They are the reason "NAT works except for that one application", and they are frequently the bug rather than the fix.
The table, and what exhaustion looks like
conntrack -L | wc -l
conntrack -S # insert_failed, drop counters
sysctl net.netfilter.nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count
Symptoms of a full table: new connections fail while established ones work, insert_failed climbs, and the failure looks random by client. Common in offices where everyone opens a video call at 9am.
Fixes in order: raise the limit (buys time), reduce connection churn (pooling, keep-alive), add more public IPs (more tuples), or move to IPv6.
Hairpinning and the internal-then-external problem
A client inside the network resolves api.example.com to the public IP and tries to connect. The packet goes to the router, which must translate it back inside — NAT hairpin / loopback. Many routers do not support it by default, so internal clients cannot reach an internal service via its public name while external clients can. The usual fixes:
- Split-horizon DNS returning the private address internally.
- Explicit hairpin/NAT-loopback configuration.
- Route internal clients to the internal load balancer directly.
This is one of the most common "it works from home but not from the office" causes, and it is a DNS problem wearing a NAT costume.
IPv6 and the end of NAT (mostly)
IPv6 restores end-to-end addressing, so NAT is not required. What remains is a stateful firewall with the same job as NAT's side effect, and NAT66 exists for multihoming but is discouraged. The practical translation of skills: what you learned as "port forwarding" becomes "firewall policy", and what you learned as "no inbound" becomes "default deny inbound, which you configured deliberately".
Where it goes wrong
- Port forward to the wrong internal IP after a DHCP lease changed. The fix is a static reservation, not a new rule.
- Overlapping private ranges across a VPN: both sides use
192.168.1.0/24, so half the destinations are ambiguous. Renumber one side; there is no clean NAT fix. - Assuming the server logs the client IP. Behind NAT it does not; you need
X-Forwarded-Forset by a proxy you trust, or PROXY protocol. - Hairpin missing, as above.
- ALG disabled to fix one application and enabled to fix another, on the same gateway.
- Conntrack full during a traffic spike, misdiagnosed as an application bug.
- NAT on the firewall and NAT on the router, each translating, producing a double-NAT that is invisible in the config of either device alone.
Related Notes
- Addressing Subnetting CIDR — private ranges and overlap risk
- Firewalls And ACLs — the policy layer NAT is often confused with
- Load Balancing — the modern alternative to port forwarding
- VPNs And Tunnels — where overlapping ranges hurt most