Concepts / Encryption and trust

Encryption and trust

NAT traversal

Also called NAT-T, ESP in UDP, UDP encapsulation, UDP 4500, ICE

Getting traffic through NAT: wrapping protocols in UDP, whose ports NAT can map, and punching holes between peers.

NAT rewrites ports, and ESP has none, so IPsec wraps ESP in UDP 4500 (RFC 3948) when IKE detects NAT on the way, or always when told to (encap = yes in strongSwan). The same wrapping gets ESP past firewalls that pass only TCP and UDP. Two peers both behind NAT meet with hole punching, helped by STUN and, when that fails, a TURN relay; ICE (RFC 8445) tries every way and keeps the best. Keepalives every 20 seconds or so keep the NAT's mapping from expiring.

How NAT traversal works, step by step

Two devices each behind NAT cannot be called from outside: the router drops any packet that matches no mapping. NAT traversal is how video calls, games and peer-to-peer apps meet anyway. Follow the NAT traversal lab, where alice (10.0.1.10) is behind aliceRouter and bob (10.0.2.10) behind bobRouter, both facing an internet network 10.0.4.0/24 with a rendezvous server at 10.0.4.10:

  1. Learn your public address. alice sends a UDP datagram from port 5000 to the rendezvous server, which replies with the address and port it arrived as: 10.0.4.2 5000, aliceRouter's outside address. This is what a STUN server does (RFC 8489): it reports the server-reflexive address. bob learns he is 10.0.4.3 6000.
  2. Swap addresses. Each tells the server who it wants, and the server passes back the peer's reflexive address. But knowing bob's address is not enough: a packet alice sends to 10.0.4.3:6000 cold is dropped, because bobRouter has no mapping expecting it.
  3. Punch the hole. Both send to each other at the same time. alice's first datagrams open a mapping on aliceRouter expecting replies from 10.0.4.3:6000; bob's do the mirror. Once each router has seen an outbound packet, the other's packets match an established mapping and get in. This is hole punching, and it works because these NATs keep endpoint-independent mapping (RFC 4787): one outside port per inside port, whatever the destination.
  4. Keep it open and fall back. A UDP mapping expires when idle, so peers send keepalives every 15 to 25 seconds. When a NAT refuses to cooperate, a TURN relay forwards the media, and ICE (RFC 8445) tries every candidate and keeps the pair that works. IPsec uses the same idea: NAT-T wraps ESP in UDP 4500, in the site-to-site IPsec lab.

What it looks like on a Linux machine

Asking the rendezvous server what you look like from outside, with socat:

$ echo alice bob | socat -t1 - UDP4:10.0.4.10:3478,sourceport=5000
you 10.0.4.2 5000, peer 10.0.4.3 6000

alice is seen as her router's address 10.0.4.2, port 5000; the server also hands back where bob last checked in. On the router, conntrack shows the mapping, the reply half written as the router expects it:

$ conntrack -L -p udp
udp      17 29 src=10.0.1.10 dst=10.0.4.10 sport=5000 dport=3478 src=10.0.4.10 dst=10.0.4.2 sport=3478 dport=5000 mark=0 use=1

After both sides send, the hole-punch mapping to bob appears and is marked assured:

$ conntrack -L -p udp --orig-port-dst 6000
udp      17 119 src=10.0.1.10 dst=10.0.4.3 sport=5000 dport=6000 src=10.0.4.3 dst=10.0.4.2 sport=6000 dport=5000 [ASSURED] mark=0 use=1

The idle timeout that forces keepalives is a sysctl:

$ cat /proc/sys/net/netfilter/nf_conntrack_udp_timeout
30

The lab also has carol behind a carrierNat set to endpoint-dependent mapping (symmetric NAT): conntrack shows her getting a different outside port for the rendezvous server than for alice, so the port alice was told is already wrong and the punch fails.

What goes wrong, and how you see it

Symmetric NAT defeats the punch. With endpoint-dependent mapping the public port differs per destination, so the peer sends to a port that was never opened. In conntrack -L -s 10.0.3.10 the two entries show different dport= on their reply halves. The fix is a TURN relay; ICE picks it automatically. See NAT mapping.

You sent before the peer opened its side. A one-sided packet reaches the far router and is dropped, because no mapping expects it; a capture on the peer shows nothing arriving. Both sides must send at once, which is why hole punching needs a signalling step first.

The mapping expired. With no keepalive the UDP entry times out after 30 seconds and the next packet is dropped as new. Media stops; sending a keepalive within the timeout holds the entry open.

IPsec ESP cannot cross NAT. ESP has no ports for NAT to map, so it is dropped. IKE detects NAT and switches to UDP 4500 (NAT-T); if ip xfrm state shows no encap, forcing UDP encapsulation fixes it.

Questions people ask

What is the difference between STUN and TURN? STUN only tells a peer its public address so the two can talk directly; TURN relays the traffic through a server when a direct path is impossible. TURN always works but costs the relay's bandwidth, so ICE prefers STUN.

Why does hole punching fail on some networks? Symmetric (endpoint-dependent) NAT gives a new port per destination, so the address a peer learns is stale. Carrier-grade NAT often behaves this way; the answer is a relay.

Is NAT traversal a security hole? No. The peer still had to send out first to open its own mapping; nothing unsolicited gets in. It uses the stateful rule, it does not bypass it.

See it on real machines

Learn it on real machines.

Every lab is a small network of real Linux machines in your browser. Start with a free account: Google or email, no card.

Open this lab