NAT Traversal Explained for Peer-to-Peer Apps
By SRIFT maintainers · Published
Reviewed by SRIFT maintainers · last reviewed against the SRIFT 4.3.2 source.
Short answer
NAT traversal is the set of techniques that let two devices behind network address translators open a direct connection even though neither can accept an unsolicited inbound packet. The core trick is UDP hole punching: each side learns its public mapped address from a STUN server, the two swap addresses through a signaling server, and both send packets to each other at once so that each NAT sees outbound traffic and admits the replies.
It works when a NAT keeps the same public port for a socket regardless of destination (endpoint-independent mapping, RFC 4787). It fails when the NAT picks a new port for every destination (the old “symmetric NAT”) or when UDP is blocked outright; in those cases traffic has to go through a relay such as TURN.
- Main technique
- UDP hole punching, run by ICE connectivity checks
- Works with
- Endpoint-independent mapping on at least one side, and compatible filtering
- Defeated by
- Address-and-port-dependent mapping on both sides, or blocked UDP
- Fallback
- A relay (TURN) that forwards traffic for both peers
This guide covers NAT behaviour and hole punching. The servers that help, and how to configure them, are in STUN vs TURN.
Why NAT blocks inbound connections
A NAT rewrites the source address of outgoing packets. A laptop at 10.0.0.5:5000 sends a UDP packet; the router replaces the source with its own public address and a port it chooses, say 198.51.100.7:40001, and records that pairing as a mapping. When a reply comes back to 198.51.100.7:40001, the router looks up the mapping and forwards it to the laptop.
The asymmetry follows directly. A packet arriving at the public address with no matching mapping has nowhere to go, so the NAT drops it. Two devices that are both behind NATs therefore cannot simply connect: each one's first packet arrives at the other's router as unsolicited traffic. Servers avoid the problem by having a public address and listening on it; ordinary devices on home, mobile and office networks do not.
Mappings are also temporary. RFC 4787 requires a UDP mapping to survive at least two minutes of idle time and recommends a default of five minutes or more, so any long-lived peer-to-peer path must send keepalives to stop the router forgetting it.
Mapping and filtering: the RFC 4787 vocabulary
Early STUN (RFC 3489) sorted NATs into four types: full cone, restricted cone, port-restricted cone and symmetric. Those labels turned out to be, in RFC 4787's words, “inadequate at describing real-life NAT behavior”, because they bundle two independent properties together. RFC 4787 separates them.
Mapping behaviour: which public port do you get?
- Endpoint-independent mapping (EIM). One internal socket gets one public address and port, reused for every destination. Whatever a STUN server reports is also what a peer will see.
- Address-dependent mapping. The public port is reused only for the same destination IP address; a different destination host gets a new mapping.
- Address-and-port-dependent mapping (APDM). Every distinct destination IP and port gets its own mapping. The port the STUN server saw is useless to a peer, because the peer is a different destination.
Filtering behaviour: whose packets are let in?
- Endpoint-independent filtering (EIF). Once a mapping exists, any external host may send to it.
- Address-dependent filtering (ADF). Only hosts the internal device has already sent to may send back, from any port.
- Address-and-port-dependent filtering (APDF). Only the exact IP and port the internal device sent to may reply.
RFC 4787 makes endpoint-independent mapping a MUST (REQ-1). For filtering it leaves a choice: endpoint-independent when application transparency matters most, address-dependent when stricter filtering matters more. Not every deployed device follows these requirements, which is why traversal software has to cope with all six behaviours.
| RFC 3489 label | Mapping | Filtering | Hole punching |
|---|---|---|---|
| Full cone | Endpoint-independent | Endpoint-independent | Works; inbound is effectively open once mapped |
| Restricted cone | Endpoint-independent | Address-dependent | Works |
| Port-restricted cone | Endpoint-independent | Address-and-port-dependent | Works, if both sides send |
| Symmetric | Address-and-port-dependent | Usually address-and-port-dependent | Fails unless the other side is permissive |
You can observe mapping behaviour yourself: send binding requests from one socket to two STUN servers on different IP addresses and compare the reflexive addresses returned. Identical ports suggest endpoint-independent mapping; different ports mean the mapping depends on the destination. RFC 5780 defines a fuller STUN-based discovery procedure for both mapping and filtering.
UDP hole punching, step by step
- Learn the mapped address. Each peer sends a STUN Binding request (RFC 8489) from the very socket it will use for the connection. The NAT creates a mapping, and the STUN server reports the public address and port it saw. In ICE terms this is a server-reflexive candidate.
- Exchange candidates. Both peers send their candidates (local addresses, reflexive addresses and any relay addresses) to each other through a signaling channel.
- Send towards each other. Peer A sends to B's reflexive address. If B's NAT has not yet seen B send to A, it drops the packet. The packet still did its job: A's NAT now has filter state that admits packets from B's public address.
- The reply gets through. B sends to A's reflexive address. B's NAT records the outbound packet, and A's NAT admits it because of step 3.
- Both holes are open. A's next packet passes B's NAT. From here the path carries traffic both ways for as long as keepalives refresh the mappings.
In WebRTC nobody codes these steps by hand. ICE (RFC 8445) pairs every local candidate with every remote one and sends STUN connectivity checks from both ends on a schedule; those checks are the punching packets. A check that arrives from an address ICE did not know about produces a peer-reflexive candidate, which is how ICE recovers when one side's real public port differs from the one it advertised. The STUN vs TURN guide covers the candidate types in detail.
Why symmetric NAT defeats hole punching
Hole punching relies on one assumption: the public port the STUN server saw is the port the peer will see. Under address-and-port-dependent mapping that assumption is false. When A sends to B, A's NAT allocates a fresh port, say 40017 instead of 40001. B, meanwhile, is sending to 40001, where A's NAT holds no state for B, so those packets are dropped.
The connection can still succeed if B's side is permissive. A's packet arrives at B's NAT from port 40017. If B's NAT uses endpoint-independent mapping and does not filter on port (endpoint-independent or address-dependent filtering), the packet gets through. B's ICE agent learns 198.51.100.7:40017 as a peer-reflexive candidate and replies to it, which A's NAT admits. It fails when B's NAT filters on port (it expects 40001) or when both sides use address-and-port-dependent mapping, because then neither side can know the other's port in advance.
Port prediction and its limits
Some NATs allocate ports sequentially, so in principle a peer could guess the next port and aim at a small range of them. RFC 5128 describes such techniques. They are fragile: other traffic through the same NAT consumes ports between the probe and the guess, a carrier-grade NAT shares its port space among many subscribers, and NATs that randomize port allocation (RFC 6056 describes the reasons to randomize) give nothing to predict. ICE does not specify port prediction, and browser WebRTC stacks rely on TURN instead.
Carrier-grade NAT, double NAT and IPv6
CGNAT and double NAT
Many mobile and some fixed-line ISPs no longer give each customer a public IPv4 address. A carrier-grade NAT translates many subscribers onto a shared pool, and the customer's side often uses addresses from 100.64.0.0/10, the shared address space reserved for exactly this by RFC 6598. If the customer's own router also does NAT, packets cross two translators: a double NAT.
Three practical consequences follow:
- Behaviour compounds. The path is only as permissive as its strictest NAT. An endpoint-independent home router behind an address-and-port-dependent CGN behaves like the CGN.
- No port forwarding. UPnP or a manual port forward on the home router opens nothing on the carrier's NAT, so “just forward a port” is not an option.
- Port budgets. RFC 6888 requires a CGN to support limiting the external ports per subscriber. Applications that open many connections can exhaust that allowance and see new mappings fail.
RFC 6888 also requires a CGN to meet RFC 4787 for UDP (including endpoint-independent mapping) and recommends endpoint-independent filtering. Where operators follow it, hole punching through a CGN works; where they do not, the only signal you get is failed connectivity checks. Two subscribers behind the same CGN additionally depend on hairpinning (RFC 4787 REQ-9), which lets a NAT route a packet sent to its own public address back inside.
IPv6: no NAT, still a firewall
With IPv6 every device can have a globally routable address, so there is no translation and no mapping to discover. Reachability is still not automatic. RFC 6092 recommends that home gateways run a stateful filter that drops unsolicited inbound traffic, admitting only packets that match state created by an inside host. That is exactly the filtering half of a NAT, so peers still have to send to each other first, and ICE's simultaneous checks do that. IPv6 removes the mapping problem; it does not remove the need for hole punching.
Address families add one more case: a peer with only IPv4 and a peer with only IPv6 have no common path at all. A TURN server reachable over both families can bridge them.
When a relay is unavoidable, and how SRIFT handles it
A relay is the only option when:
- both peers sit behind address-and-port-dependent mapping, or one does and the other filters on port;
- outbound UDP is blocked, as on many corporate and guest networks;
- a firewall permits only specific ports or only traffic through a proxy;
- the peers share no address family.
TURN (RFC 8656) solves all of these the same way: each peer makes an ordinary outbound connection to the TURN server, over UDP, TCP or TLS, and the server forwards packets between the two allocations. Because both connections are client to server, no NAT behaviour matters. The cost is that every byte crosses the relay, so the relay operator pays for the bandwidth and sees both IP addresses and the traffic volume. With WebRTC the payload remains DTLS-encrypted end to end. For locked-down offices, see WebRTC behind corporate firewalls.
You can try the whole sequence from any two browsers with a browser-to-browser transfer. To see which candidate pairs ICE actually tried, open chrome://webrtc-internals in Chromium or about:webrtc in Firefox during a connection; the guide to failed WebRTC connections covers the common causes.
Frequently asked questions
Can I make hole punching work by changing my router settings?
Sometimes. Routers that offer an option for endpoint-independent (sometimes called 'full cone' or 'open') NAT behaviour make traversal more likely. It does not help when the restrictive NAT is your ISP's carrier-grade NAT or a corporate firewall you do not control.
Does UPnP or NAT-PMP replace hole punching?
Where they are enabled, port mapping protocols such as UPnP IGD, NAT-PMP or PCP let an application ask the router for an inbound port, which removes the need to punch on that router. They only reach the first router, so they do not help behind a carrier-grade NAT, and many networks disable them.
Is a relayed connection less secure than a direct one?
Not if the payload is encrypted end to end, as WebRTC's DTLS is. The relay can see both IP addresses, timing and traffic volume, and it adds latency, but it cannot read DTLS-protected content.
References
- RFC 4787: NAT Behavioral Requirements for Unicast UDP (IETF)
- RFC 5128: State of Peer-to-Peer Communication across NATs (IETF)
- RFC 8489: Session Traversal Utilities for NAT (STUN) (IETF)
- RFC 8445: Interactive Connectivity Establishment (ICE) (IETF)
- RFC 8656: Traversal Using Relays around NAT (TURN) (IETF)
- RFC 6598: IANA-Reserved IPv4 Prefix for Shared Address Space (IETF)
- RFC 6092: Recommended Simple Security Capabilities in IPv6 CPE (IETF)
Try a direct transfer
Open a session in one browser, join it from another device, and send a file. No account, nothing to install on either side.
Related resources
- STUN vs TURN: How WebRTC Gets Through NATWhat STUN and TURN servers do, how ICE picks between host, server-reflexive and relay candidates, and what a TURN relay can and cannot see.Explainer
- Why WebRTC Connections Fail, and How to Fix ThemWhy two browsers sometimes cannot connect directly: ICE failures, blocked UDP, symmetric NAT and missing TURN, with a diagnosis checklist.Troubleshooting
- How Peer-to-Peer File Transfer Works, Step by StepFrom signaling to the last chunk: how two devices find each other, open a direct encrypted channel, stream a file and fall back when the path is blocked.Explainer
