UDP hole punching allows two peers, each behind a NAT, to establish a direct UDP connection.
Let’s see this in action. Imagine Alice and Bob want to play a game directly.
Alice’s machine has an IP 192.168.1.10 and is behind a NAT with public IP 203.0.113.1. Bob’s machine has 192.168.1.20 and is behind a NAT with public IP 192.0.2.5.
They both connect to a publicly accessible Rendezvous Server.
- Alice to Rendezvous: Alice sends a UDP packet from her local IP
192.168.1.10:5000to the Rendezvous Server atrendezvous.example.com:12345. Her NAT creates an entry: map203.0.113.1:60000to192.168.1.10:5000. - Bob to Rendezvous: Bob sends a UDP packet from his local IP
192.168.1.20:5001torendezvous.example.com:12345. His NAT creates an entry: map192.0.2.5:60001to192.168.1.20:5001.
Now, the Rendezvous Server knows:
- Alice’s public endpoint is
203.0.113.1:60000. - Bob’s public endpoint is
192.0.2.5:60001.
The server tells Alice about Bob’s public endpoint and Bob about Alice’s.
- Alice to Bob: Alice sends a UDP packet from her machine (which the NAT sees as coming from
203.0.113.1:60000) to Bob’s public endpoint192.0.2.5:60001. - Bob to Alice: Bob sends a UDP packet from his machine (which the NAT sees as coming from
192.0.2.5:60001) to Alice’s public endpoint203.0.113.1:60000.
When Alice’s packet arrives at Bob’s NAT (192.0.2.5:60001), the NAT looks at its mapping table. It sees an outgoing connection from 192.168.1.20:5001 to 203.0.113.1:60000. It associates the incoming packet from 203.0.113.1:60000 with this outgoing mapping and forwards it to 192.168.1.20:5001. Bob’s machine receives the packet.
The same happens in reverse for Bob’s packet arriving at Alice’s NAT. The NAT sees an outgoing connection from 192.168.1.10:5000 to 192.0.2.5:60001. It associates the incoming packet from 192.0.2.5:60001 with this mapping and forwards it to 192.168.1.10:5000. Alice’s machine receives it.
Now, both machines have an open "hole" in their NATs, allowing direct UDP communication.
The core problem this solves is that NATs, by default, block unsolicited incoming UDP packets. UDP hole punching exploits the fact that NATs typically create temporary mappings for outgoing packets, allowing subsequent incoming packets that match that mapping to pass through. It’s a handshake initiated by both sides towards each other’s public endpoint, creating the necessary state in their respective NAT devices. The Rendezvous Server acts as a neutral third party to exchange these public endpoint addresses.
The specific UDP ports used by the peers on their local network (5000 and 5001 in the example) are generally chosen by the application, often dynamically. The public ports (60000 and 60001) are the ones assigned by the NAT devices, which are usually ephemeral and unpredictable. The Rendezvous Server’s role is critical, as it’s the only entity that knows both peers’ public IP addresses and the ports their NATs have exposed for their outgoing connections.
A key detail often overlooked is that not all NAT types are compatible with hole punching. Symmetric NATs, for instance, assign a different public port for each distinct destination IP/port combination. This means the port Alice’s NAT exposes for a packet to Bob’s server might be different from the port it exposes for a packet directly to Bob’s public IP and port. This makes hole punching much harder, often requiring a more complex "turn" server or specific NAT traversal techniques.
The success of UDP hole punching is highly dependent on the type of NAT devices involved. If both peers are behind Full Cone NAT or Restricted Cone NAT, hole punching is generally successful. However, if one or both are behind a Symmetric NAT, the technique will likely fail without additional measures.
The next challenge is managing the connection state and handling potential packet loss inherent in UDP.