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.

  1. Alice to Rendezvous: Alice sends a UDP packet from her local IP 192.168.1.10:5000 to the Rendezvous Server at rendezvous.example.com:12345. Her NAT creates an entry: map 203.0.113.1:60000 to 192.168.1.10:5000.
  2. Bob to Rendezvous: Bob sends a UDP packet from his local IP 192.168.1.20:5001 to rendezvous.example.com:12345. His NAT creates an entry: map 192.0.2.5:60001 to 192.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.

  1. 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 endpoint 192.0.2.5:60001.
  2. 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 endpoint 203.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.

Want structured learning?

Take the full Udp course →