The idea: one public address, many private hosts

There are only about 4 billion IPv4 addresses, far fewer than devices. So a home or an office gets one public address from its ISP (here 203.0.113.7), and the devices inside use private addresses that are not routed on the Internet: 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 (RFC 1918). The router between them does network address translation (NAT): it rewrites addresses in every packet that crosses it, and keeps a table so it can undo the rewrite for the replies.

The canvas has three regions. On the left the private LAN with three hosts and their sockets. In the middle the home router: its NAT type, the port-forward rule, the translation table, and at the bottom the packet it is working on, with its header as it looks on the LAN side and on the WAN side (fields NAT rewrote are orange). On the right the Internet: servers, an outside host, and a peer that is itself behind another NAT. Each inside socket has its own colour, used by its packets and its table rows.

Going out: rewrite the source, remember it

When the laptop sends 192.168.1.10:51000 → 93.184.216.34:443, the router replaces the source with its own public address: 203.0.113.7:51000. This is source NAT; with a changing public address (a home line) Linux calls it masquerade. The router writes a row: protocol, inside address and port, public port, remote address and port. On Linux this table is conntrack (conntrack -L lists it). Because the TCP and UDP checksums cover the IP addresses, NAT recomputes them too.

The reply comes to 203.0.113.7:51000. The router looks up the row by public port and remote address, rewrites the destination back to 192.168.1.10:51000 and sends it into the LAN. The server never learns the private address. Demo: 1. One TCP connection out and back.

Laptop 192.168.1.10 sends to web server 93.184.216.34:443 through the home router. On the WAN side the source becomes 203.0.113.7:51000; the reply to 203.0.113.7:51000 gets its destination rewritten back to 192.168.1.10:51000 using the translation table row; a packet from outside to port 3389 matches no row and is dropped
The router swaps the private source for its public address on the way out and uses its table row to swap it back on the reply.

Choosing the public port

If the host's source port is still free on the public address, the router keeps it (port preservation). If another host already uses it, the router picks a different one. In Demo 2 the laptop and the phone both use port 51000: the laptop keeps 51000 and the phone gets 40000. Replies are told apart by the public port alone. A public address has 65 535 ports per protocol; Linux can reuse a port for different remotes, so the real limit is about 64 k connections to the same server per public address, which matters for carrier-grade NAT, not for a home.

Coming in: only replies get through

A packet from outside is addressed to 203.0.113.7. If no row has its destination port, the router cannot know which inside host it is for, so it drops it, silently. This is why a machine behind NAT cannot be reached from the Internet unless it spoke first, and why NAT looks like a firewall. It is not one by design (the filtering depends on the NAT type below, and IPv6 has no NAT at all), so real routers add a firewall on top. Demo: 3. Unsolicited inbound packets are dropped.

Port forwarding and hairpinning

To run a server behind NAT, you add a static port-forward rule (destination NAT): "TCP to 203.0.113.7:8080 goes to 192.168.1.20:80". The first SYN creates a row like any other, so the NAS's replies have their source rewritten back to 203.0.113.7:8080 (Demo 4). UPnP IGD and PCP let an application ask the router for such a rule itself.

Hairpin NAT (NAT loopback) is the case where a host inside uses the public address, e.g. because the NAS's DNS name points at it. The router must rewrite the destination to the NAS and the source to its own LAN address: without the second rewrite, the NAS would answer the laptop directly from 192.168.1.20:80, and the laptop, which called 203.0.113.7:8080, would reset that unknown connection. Demo: 10. Hairpin NAT.

Timeouts and keepalives

Rows are not removed when an application closes a socket; the router cannot see that. It only sees packets, so every row has a timer that each packet refreshes. TCP rows follow the handshake and the FINs; UDP and ICMP have no connection, so they only have the timer. The page uses the Linux defaults:

Row statesysctlTimeout
TCP SYN_SENT / SYN_RECVnf_conntrack_tcp_timeout_syn_sent / _syn_recv120 s / 60 s
TCP ESTABLISHEDnf_conntrack_tcp_timeout_established432 000 s (5 days)
TCP FIN_WAIT / LAST_ACK / TIME_WAIT_fin_wait / _last_ack / _time_wait120 s / 30 s / 120 s
UDP, one exchangenf_conntrack_udp_timeout30 s
UDP stream (traffic both ways for a while)nf_conntrack_udp_timeout_stream120 s (not modelled)
ICMP echonf_conntrack_icmp_timeout30 s

A UDP answer that arrives after its row expired is dropped (Demo 5). Home routers often use much shorter timers than Linux defaults. That is why VoIP, games, VPNs and push-notification connections send small keepalive packets every 15–30 s: not for the other side, but to keep the row in the NAT alive.

ICMP, and protocols that carry addresses

An ICMP echo has no ports, so NAT uses its query id the way it uses a port (Demo 6). ICMP error messages (e.g. "port unreachable") quote the header of the packet that caused them; NAT finds the row from that quote and rewrites the quoted header too. Protocols that write IP addresses into their payload, like FTP's PORT command or SIP, break under NAT unless the router has an application-level gateway (ALG) that rewrites the payload as well.

Cone and symmetric NAT

RFC 4787 describes a NAT by two choices. Mapping: does an inside socket get the same public port for every destination (endpoint-independent), or a new one per destination (address- and port-dependent)? Filtering: once a public port is mapped, who may send to it? The old names combine them:

Old nameMappingFiltering: packets accepted fromHole punching
Full coneendpoint-independentanyoneworks
Restricted coneendpoint-independentany port of an IP it sent toworks
Port-restricted coneendpoint-independentexactly the ip:ports it sent toworks if both sides send
Symmetricnew port per destinationexactly the ip:ports it sent tofails against a port-restricted peer

RFC 4787 asks for endpoint-independent mapping, since it is what lets peer-to-peer work; Linux masquerade behaves like a port-restricted cone as long as it can keep the port. Demo 9 shows why full cone is the least safe: once the call app has asked STUN, anyone who learns its public port can send to it.

STUN, hole punching and TURN

Two hosts that are both behind NATs cannot simply connect: each one's inbound packets are dropped. The trick is to make both NATs think the other side's packets are replies:

  1. Each host asks a STUN server "what address do you see me as?" (a Binding request). The answer, e.g. 203.0.113.7:50000, is its public address for that socket.
  2. Through a signalling server, the two hosts exchange these addresses.
  3. Both send to each other at the same time. The first packet in each direction may be dropped by the far NAT, but it creates a row (a hole) in its own NAT, and the other side's packets then match that row. Demo 7.
Sequence with lanes laptop, home router, STUN server, peer's NAT and peer: 1 the laptop learns 203.0.113.7:50000 from STUN; 2 both sides swap addresses through the app's server; 3 the peer's hello is dropped by the home router but opens a hole in the peer's NAT; 4 the laptop's hello passes through that hole; 5 the peer's answer is a reply and gets through
The first packet in each direction punches a hole in its own NAT, so the other side's packets arrive as replies.

This only works if the public port the peer was told about is the one the NAT uses for the peer. A symmetric NAT gives the peer's address a new port (Demo 8: STUN saw :40000, the peer is sent from :40001), so the peer's hole points at the wrong port. Then the call is relayed through a TURN server, which both sides can reach because both connect out to it. ICE (used by WebRTC) tries all these candidates and picks the best path that works.

Carrier-grade NAT and IPv6

Many ISPs, most mobile networks, no longer have one public address per customer. They put a second NAT in their own network (CGNAT): your router's "public" address is then from 100.64.0.0/10 and is translated once more, so a whole neighbourhood shares a few public addresses. Port forwarding on the home router then no longer helps. IPv6 gives every device a public address, so it needs no NAT; a stateful firewall on the router does the "only replies get in" part.

See also Internet Protocol Layers (NAT as one hop of a packet's trip through all the layers), TCP vs UDP and How an HTTP Connection Is Established.

What the page leaves out

IP fragments, ALGs in detail, NAT64 (IPv6 hosts reaching IPv4 servers), port-block allocation and logging in CGNAT, UPnP / PCP, the UDP stream timeout, randomised source ports (the page uses fixed counters so demos repeat), the firewall rules that usually sit next to NAT, and the ISP's routers between the home and the servers.