How does UDP hole punching open a direct path between two peers behind different NATs, and why does endpoint-dependent ('symmetric') mapping defeat it?
answer
- an introducer both can reach
- send at the same time
- your outbound packet opens your NAT
- the advertised mapping must be reused
- new destination, new public port
basics
~20 sA rendezvous server tells each peer the other's public mapping, then both send at once, so each NAT sees an outbound session and admits replies. Endpoint-dependent mapping uses a new public port toward the peer, so the advertised address fails.
solid answer
~50 sBoth peers first talk to a public rendezvous server, which learns each one's public mapping and passes it to the other. Each peer then sends UDP packets to the other's public address and port. Peer A's outbound packet creates a session in NAT A toward B's mapping; B's outbound packet does the same in NAT B. The first packet may be dropped by the far NAT if it arrives before that NAT has sent anything outbound; once both have sent, each NAT treats the other's packets as replies. This relies on the NAT reusing the same public port toward the peer that it used toward the server, RFC 4787's **endpoint-independent mapping**. A NAT with endpoint-dependent mapping allocates a new public port toward the peer, so the address the other side is sending to is not mapped toward it and its packets are dropped. RFC 5128 records that the technique needs such endpoint-independent NATs.
go deeper
Remember that each peer must send to the other, because a NAT only admits packets that look like replies to something its own host sent.
Walk the steps with real mappings, explain why the first packet may be lost, and connect success to endpoint-independent mapping.
Explain which NAT pairings fail and why, including the one-sided case, and how mapping timers and keepalives decide whether a working path survives idle periods.
Judge when to invest in better direct-path success against provisioning relays, given that the NAT behaviour on the far side is outside your control.
## The setup **UDP hole punching** is the technique RFC 5128 (Informational, a survey of methods in use) describes for connecting two peers that are both behind NATs. It needs three parties: - **Peer A** at `10.0.0.5:4000` behind **NAT A**, public address `203.0.113.10`. - **Peer B** at `10.1.1.7:5000` behind **NAT B**, public address `198.51.100.20`. - A **rendezvous server S** at a public address, which both peers can reach. In ICE this role is split: a STUN server reports each peer's mapping, and the application's signalling carries it to the other peer. ## The punch, step by step 1. A sends a packet to S. NAT A creates a mapping, say `10.0.0.5:4000 -> 203.0.113.10:62000`. S sees and records `203.0.113.10:62000`. 2. B does the same; NAT B maps `10.1.1.7:5000 -> 198.51.100.20:31000`, and S records that. 3. S tells A about B's public endpoint and B about A's. 4. A sends from `10.0.0.5:4000` to `198.51.100.20:31000`. NAT A reuses `62000` and opens a session toward B's endpoint. If this packet reaches NAT B before B has sent anything to A, NAT B's filter may drop it. That loss is expected. 5. B sends from `10.1.1.7:5000` to `203.0.113.10:62000`. NAT B reuses `31000` and opens a session toward A. This packet arrives at NAT A, matches the session A opened in step 4, and is delivered to A. 6. A's next packet now matches NAT B's session too. The path is open in both directions with no server in it. The key insight is that **each peer's own outbound packet is what opens its own NAT** to the other. ## Why it works: two properties of each NAT | Property (RFC 4787 terms) | What hole punching needs | What happens without it | |---|---|---| | **Mapping** | endpoint-independent: the same public port toward S and toward the peer | the peer is told `62000`, but packets to the peer leave from a different port | | **Filtering** | any of RFC 4787's three filtering behaviours, because each side sends first | no extra requirement: the simultaneous outbound packets satisfy even address-and-port-dependent filtering | RFC 4787 REQ-1 makes endpoint-independent mapping a MUST, and its justification is exactly this: without it, a UDP relay is needed. ## Why endpoint-dependent mapping breaks it Suppose NAT A uses **address-and-port-dependent mapping**, what RFC 3489 called a "symmetric" NAT. In step 4, A's packet to B is a new destination, so NAT A allocates a new public port, say `62001`. Now: - B keeps sending to `203.0.113.10:62000`, a mapping that only admits traffic from S. NAT A drops it. - A's packets reach NAT B from `62001`, a port B never sent to. If NAT B filters on address and port, it drops them too. RFC 5128 states that hole punching works only when the NAT is endpoint-independent, and that a substantial fraction of deployed NATs are not. Two refinements: - **One-sided cases can still succeed.** If the other NAT does not filter on the sender's port (endpoint-independent or address-dependent filtering) and the endpoint-dependent NAT keeps the same public IP, A's packets from `62001` get through, B answers to the source it observed, and ICE records that address as a peer-reflexive candidate. - **Port prediction** ("N+1" in RFC 5128) guesses the next port a NAT will allocate, since many allocate in sequence. It is fragile: other traffic through the same NAT consumes ports in between. When both sides allocate per destination and filter by port, no direct path exists, and a **TURN relay** is the remaining option. ## Keeping the hole open A punched path is just a pair of NAT mappings, and mappings expire when idle. RFC 4787 REQ-5 says a UDP mapping timer MUST NOT expire in less than two minutes and recommends a default of five minutes or more, but many NATs use shorter values, an implementation choice. ICE therefore sends a keepalive on the selected pair whenever nothing has been sent on it for Tr seconds, which RFC 8445 recommends setting to 15. ## Variants - **Peers behind the same NAT.** RFC 5128 has peers try their private endpoints too; sending to each other's public endpoints instead only works if the shared NAT loops traffic back, which is the separate hairpinning behaviour. - **TCP hole punching.** Both sides send a SYN at the same time, relying on TCP's simultaneous open. RFC 5128 notes it is less reliable, because simultaneous open is not implemented correctly on many systems, NATs included, and a NAT may answer an early SYN with a reset.
- Why is losing the first hole-punching packet not a failure?Whichever packet arrives first at the far NAT may find no matching session, because the far peer has not yet sent its own outbound packet, and a filtering NAT drops it. As soon as the far peer sends, its NAT opens a session toward the first peer, and the retransmitted or following packets get through. Applications keep sending for a short period rather than relying on one packet.
- Does hole punching work when only one peer's NAT uses endpoint-dependent mapping?Sometimes. If the other NAT does not filter on the sender's port and the endpoint-dependent NAT keeps the same public IP address, packets from the newly allocated port are admitted, and the far peer simply replies to the source it saw. ICE learns that source as a peer-reflexive candidate. If the other NAT filters on address and port, the pairing fails and needs a relay.
Two residents in different buildings each have a doorman who only puts through calls from numbers the resident has dialled. If both dial each other at the same moment, each doorman sees an outgoing call and lets the return call in. A doorman who hands out a different extension for every number dialled breaks this: the other resident is calling an extension that was only meant for someone else.
saying these in an interview costs you the question
- Hole punching needs the rendezvous server to forward all of the peers' packets.
- The peer's incoming packet is what opens a hole in the local NAT.
- Symmetric NATs fail because they block every inbound UDP packet.
- Losing the first punch packet means the technique has failed.
- Once punched, the hole stays open as long as both peers are online.