How network address translation shares one IP across many devices.
A trick that bought IPv4 a few extra decades. It rewrites headers, tracks state, and makes "private" networks reach the public one without owning a single public address.
What is NAT?
Many private hosts, one public address.
Network Address Translation (NAT) rewrites packet headers so many private hosts can share one public IP. RFC 1631 (1994) standardised it; today every home router and most enterprise gateways do NAT. STUN, TURN, and ICE are the protocols WebRTC uses to traverse NATs; CGNAT is what mobile carriers run.
The IPv4 address space holds about 4.3 billion addresses. There are far more devices than that. NAT — formalised in RFC 3022 — is the workaround: a router rewrites packet source addresses on the way out, records the original mapping, and reverses it on the reply. To the public internet your home of fifteen devices looks like one.
This trick has consequences. It breaks end-to-end addressing — the principle the IP layer was designed around. It complicates protocols that embed addresses in payloads (FTP, SIP). It introduces state into a protocol designed to be stateless. But it works, and it has been working since 1994.
How NAT rewrites a packet, step by step
A packet's journey, step by step.
One private host opens a connection to a public service. Step through the frames; watch source and destination get rewritten on the way out, and the mapping looked up on the way in.
| internal | external port | peer |
|---|---|---|
| 10.0.1.4:50122 | 41010 | 8.8.8.8:443 |
Source NAT vs Destination NAT: two directions, two purposes
Rewriting the source on the way out, the destination on the way in.
SNAT (source NAT) rewrites the source address on the way out. This is the classic case — many private hosts share a public IP for outbound traffic. Cloud NAT GWs, home routers, and cellular CGNAT all do SNAT.
DNAT (destination NAT) rewrites the destination on the way in. This is how you publish a service: external traffic arrives at the public IP, the router rewrites the destination to a private host. Port forwarding, AWS load balancer health checks, Kubernetes Service IPs — all DNAT under the hood.
Hide the inside.
Many sources collapse to one or a few public IPs. State is keyed by (src, dst, src-port, dst-port). On reply, lookup by external port → restore the original.
Expose one inside.
Public IP:port maps to a specific internal host:port. The mapping is configured up-front, not dynamic. Often paired with SNAT on return so internal hosts see the LB, not the client.
PAT (port-based NAT): one IP, tens of thousands of conversations
One IP, 65,535 conversations.
If many hosts share one public IP, how does the router know which reply belongs to which inside host? The trick is the source port. NAT rewrites the source port to a unique value (typically picked from the ephemeral range, 1024–65535) and remembers the mapping in something close to a hash table. The reply, addressed to that external port, gets translated back.
This caps a single public IP at roughly 64,000 simultaneous flows to any single destination port pair. AWS NAT Gateway gets around this with multiple ENIs and 55,000 ports per IP; CGNAT carriers shard ranges; busy egress fleets need pools of EIPs. Run out of ports and connections start failing in confusing ways.
# A single Linux box doing PAT with iptables MASQUERADE iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # Conntrack table — one row per active flow conntrack -L -p tcp --dport 443 | head -3 tcp 6 src=10.0.1.4 sport=50122 dst=8.8.8.8 dport=443 [ASSURED] src=8.8.8.8 sport=443 dst=203.0.113.5 dport=41010
NAT is stateful: the connection table
Every active flow needs an entry in the connection table.
Conntrack stores one entry per active flow with timers — TCP idle, UDP unreplied, ICMP ping. Linux's net.netfilter.nf_conntrack_max bounds the table. Hit the limit and new connections silently fail with mysterious resets. Long-lived TCP idles are kept alive by an ESTABLISHED timer (5 days by default, 350s in many cloud NATs) — TCP keepalives are required if you want to span that.
UDP is harder. The router has no FIN to learn from. It guesses with a short timer (30s default) and expects either side to send something periodically. VoIP, gaming, and DTLS protocols send keepalives specifically to keep NAT mappings alive.
NAT traversal: reaching behind a NAT with STUN, TURN, and ICE
How peer-to-peer apps connect when both sides are behind NATs.
NAT breaks unsolicited inbound. For peer-to-peer apps (long-lived sockets, WebRTC video, BitTorrent, gaming, mesh VPNs) this is fatal. Three workarounds, in order of how desperate:
Discover your public address
Ask a public server "what address do I look like to you?" Useful when both peers have cone-NAT — they can guess each other's external (IP, port) and try directly.
Relay traffic when direct fails
If direct fails, both peers connect outbound to a relay server. Always works; doubles bandwidth on the relay; the fallback when STUN cannot punch through.
Negotiate the best path
The orchestration: gather all candidates (host, srflx, relay), exchange via signaling, race connectivity checks, pick the winner. WebRTC's bread and butter.
Carrier-grade NAT (CGNAT): two layers of translation
Why your phone sits behind a NAT inside another NAT.
Mobile carriers and cable ISPs ran out of public IPv4 long ago. Their solution: stack another NAT in front of yours. Your home router NATs your devices to a 100.64.0.0/10 (RFC 6598) carrier-private address; the carrier's CGNAT rewrites that to a real public IP. Two translations, two state tables.
CGNAT effectively prevents most inbound — port forwarding does not work, peer-to-peer punches through inconsistently, and game console NAT-types degrade. The proper fix is IPv6, where every device has a public address again and NAT becomes optional. Adoption is now over 50% of Google's traffic but uneven by region; until then, NAT is a permanent part of how the internet works.
NAT types: Full Cone, Restricted, Port-Restricted, Symmetric
The four behaviours that decide if peer-to-peer works.
RFC 3489 classified NAT behaviour into four types. The classification matters because peer-to-peer protocols (WebRTC, BitTorrent, multiplayer games) succeed or fail based on which combination two endpoints have:
- Full Cone
- Once an internal host sends out, any external host can send back to that mapped public IP/port. The most permissive; trivial for peer-to-peer. Rare in practice.
- Restricted Cone
- Inbound is allowed only from external IPs the internal host previously sent to. Different external port from the same IP is OK.
- Port-Restricted Cone
- Inbound is allowed only from the exact (external IP, external port) pair the internal host previously sent to. Most home routers default here.
- Symmetric NAT
- The mapped public port is unique per destination. Sending to two different external hosts produces two different public ports for the same internal socket. Almost impossible to traverse with simple STUN; requires TURN relays. Common on mobile carrier networks (CGNAT) and corporate firewalls.
Why this matters for WebRTC. Two Symmetric NATs cannot connect peer-to-peer. WebRTC's ICE protocol falls back to a TURN relay (a server in the middle that both peers can reach), at the cost of adding ~30-100ms of latency and consuming server bandwidth. Public TURN services (Twilio, Xirsys) charge per GB. Most production WebRTC deployments budget ~10-15% of connections for TURN fallback.
Two phones, two NATs, one video call
The whole traversal stack, on one concrete example.
Phone A sits at 192.168.1.7 behind a home router whose public address is 198.51.100.20. Phone B sits at 192.168.0.12 behind a different router at 203.0.113.74. They want to send video directly to each other. Neither can dial in. Each phone's address is private and means nothing outside its own LAN, and each router drops unsolicited inbound packets — a packet from B aimed at 198.51.100.20 arrives at router A, the router checks its connection table, finds no mapping, and discards it. Both phones are servers that nobody can call.
STUN fixes the first half of the problem: neither phone knows what it looks like from outside. Phone A sends a UDP packet to a public STUN server. The server reads the source address off the packet as it arrived — say 198.51.100.20:62004, the mapping router A just created — and writes it back in the reply. Now A knows its public address. Both phones do this, swap the results through the call's signaling server (an ordinary outbound connection, so NAT permits it), then fire UDP packets at each other's discovered address. A's outbound packet toward B's public address opens a mapping in router A that admits B's incoming packets; B's does the same on its side. Within a few round trips both directions are open. That is hole punching.
Symmetric NAT breaks this. The mapping A learned from STUN was created for the path to the STUN server. A symmetric NAT allocates a fresh external port per destination, so when A sends to phone B it gets a different port — the address B is aiming at was never valid for this conversation. B's packets arrive at a port with no mapping behind it and are dropped. Port-prediction tricks occasionally beat a NAT that allocates sequentially, but against most symmetric NATs (common on mobile carriers and corporate firewalls) direct connection is simply off the table.
TURN is the fallback. Both phones open outbound connections to a relay server on the public internet, and the relay forwards media between them. From each router's point of view this is ordinary outbound traffic, so it always works — at the cost of every video frame transiting a server someone has to pay for, plus the extra hop's latency. ICE runs the whole ladder automatically: gather candidates, try direct paths first, fall back to the relay only when punching fails.
The reason most calls still connect directly fits in one line: most home routers are cone NATs, where one socket gets one public mapping regardless of destination — so the address STUN discovers is the address that works, and hole punching succeeds.
NAT in production: three case studies
Where NAT shapes the design.
Tailscale — punching holes through NAT. Tailscale's mesh VPN connects user devices that all sit behind different NATs. Their "How NAT Traversal Works" blog post (2020) is the canonical modern reference. Their measurements: ~70% of connections succeed peer-to-peer using STUN; the remaining ~30% use Tailscale's DERP relay servers (their TURN equivalent), trading latency for connectivity. This split is typical of any mesh-VPN or peer-to-peer system at scale.
AWS NAT Gateway — managed CGNAT. AWS's NAT Gateway service costs ~$0.045/hour plus $0.045/GB processed (us-east-1, 2024) — roughly $32/month per gateway plus traffic. At Pinterest scale (terabytes per day egress), NAT Gateway charges run into hundreds of thousands of dollars annually; many shops route private-subnet egress through their own EC2-based NAT instances or use VPC endpoints to avoid NAT entirely for AWS-API traffic.
Mobile carriers — CGNAT shaping the modern internet. Most US mobile carriers (Verizon, T-Mobile, AT&T) put subscribers behind CGNAT. The IPv4 address you see your phone use is shared with hundreds or thousands of other subscribers. This is why home-server access from mobile is brittle, why some game-console NAT types are stuck on "Strict," and why IPv6 deployment was driven hardest by mobile networks (T-Mobile reached >95% IPv6 traffic by 2020).
NAT was always a workaround. It became infrastructure. Once you understand how it tracks state, why it breaks unsolicited inbound, and how protocols like ICE and WebRTC route around it, the modern internet stops feeling like magic and starts feeling like a series of compromises that mostly work.
Further reading on NAT
Primary sources, in order.
- RFC 3022Traditional IP Network Address TranslatorThe original NAT spec. Short and clear; hasn't aged much.
- RFC 8445ICE — Interactive Connectivity EstablishmentHow WebRTC traverses NATs. The procedure is complex; the spec is the only place it is fully written down.
- Semicolony guideVPC networkingWhere the cloud NAT GW lives, and what it costs.
- Semicolony guideTCP, in slow motionThe state machine NAT must track to do its job correctly.