P2P NAT Traversal— How to punch a hole

Branimir Malesevic
ITNEXT
Published in
6 min readJan 23, 2023

--

WWW or, as many call it, the Internet. Today we can’t imagine anything going on without it, from texting our loved ones, posting videos, and streaming to sending money across borders. And the best thing is that “everyone” has a chance to participate and be a part of it.

Source: https://realtechexperts.com/how-does-the-internet-work/

In order to be a participant, technically speaking, you need a stable connection which gives you an IP address and, voila, you can send requests to any computer you want. Is it though?

And when the server receives my request, he can identify my device through my IP and say “Hey, that’s penumbra! Sup buddy?”… Is it though?

When the question is clear enough, the answer always lies somewhere in between. Technically, the IPv4 address range (which is by far the most used one, at the time of writing) is limited to 2³² (4,294,967,296) unique addresses, which clearly is far too few for today’s modern needs. To overcome this critical limitation, some (http://www.sigcomm.org/sites/default/files/ccr/papers/2015/April/2766330-2766340.pdf) smart heads gathered around and proposed a solution called — NAT.

NAT explained

Network Address Translation (NAT) is a method used to remap one IP address space into another by modifying network address information in the IP header of packets while they are in transit across a traffic routing device.

NAT runs on your router, and translates the private addresses of your devices connected to it (PC, mobile, IoT) in the internal network into public IP addresses, to be used on the Internet.

NAT uses a technique called IP masquerading which enables the router to act as an agent between the Internet and a local network. This allows multiple devices on a local network to share a single public IP address for Internet access.

NATs can be classified as endpoint dependent and endpoint independent types.

A. Endpoint Independent Mapping (EIM): This type of NAT allows a device on the internal network to initiate a connection to any IP address on the Internet using the same mapping. It is also known as full-cone NAT. Any server that your local device tries to connect to will see the same IP:PORT mapping.

Endpoint independent mapping

B. Endpoint Dependent Mapping (EDM): This type of NAT maps a local IP address to a public IP address based on the destination IP address. It is also known as restricted-cone NAT or address-dependent NAT. Once a connection with a server is established, it will see IP:PORT1. If the device connects to a different server, it will see a different mapping IP:PORT2.

Endpoint dependent mapping

NAT Traversal

NAT Traversal is a collection of techniques used to establish a connection between two devices that are behind different NATs or firewalls. NAT Traversal is often used in P2P apps such as instant messaging, file sharing, and VoIP to allow direct communication between devices on different networks.

There are several methods of traversing NATs to allow direct communication between two peers. Some of them are:

  1. Hole Punching: This method uses a third-party server to coordinate the connection between the two devices. The devices send a connection request to the server, which then sends a message to each device containing the IP address and port of the other device. The devices can then establish a direct connection.
  2. UPnP: This method uses the Universal Plug and Play protocol to automatically configure the NAT or firewall to forward incoming connections to a specific device on the private network.
  3. STUN & TURN: These methods use a STUN server to determine the public IP address and port of a device on a private network. The STUN server then sends this information to the other device, which can then establish a direct connection. TURN servers are used to relay the traffic between the devices if direct connection is not possible.
  4. ICE: This method stands for Interactive Connectivity Establishment, it is a combination of STUN and TURN, it uses both of them to establish a connection, it uses STUN to find the public IP and port of the device, and if a direct connection is not possible, it uses TURN to relay the traffic.

We will try to focus on hole punching, since it’s the easiest way to understand how to implement NAT traversal.

Hole punching

Hole punching is a technique used to establish a direct connection between two devices that are behind different NATs.

The process of hole punching works by having the devices send a connection request to a publicly available server, which is called a rendezvous server, which then sends a message to each device containing the IP address and port of the other device. Knowing each other’s public interface, direct connectivity can be established between the peers.

Careful readers will notice one thing: if the NATs are of type EDM, the rendezvous server and peer A won’t see the same mapping to peer B, because it changes based on the destination. In this case, some smarter solutions need to come in, such as port guessing.

UDP hole punching is considered easier to implement because it does not require the devices to establish a full TCP connection before sending data. TCP is a connection-oriented protocol, which requires a three-way handshake before data can be sent. This process can be more complex and time-consuming compared to simply sending a UDP packet. Additionally, the NAT or firewall may not always allow the TCP connection to be established, which can make hole punching with TCP more difficult to implement. On the other hand, UDP hole punching has a drawback, which is the lack of reliability, since UDP is a connectionless protocol, it’s not guaranteed that the packets will reach their destination, so it’s not guaranteed that the devices can establish a direct connection successfully.

But why is it called hole punching? If we figured out the IP address and port, it’s simply sending a request to the device, right? Turns out, it’s not that easy.

NATs block any incoming traffic which hasn’t been requested from inside the local network. In other words, if a device on IP:PORT sends a packet to your public interface, the NAT will block the packet if the local device hasn’t previously sent a packet to IP:PORT. In that way the local machine actually punches a hole inside the NATs configuration to let packets from IP:PORT through.

The algorithm works as follows:

  1. Peer A sends a message to the rendezvous server which stores the translated public address of Peer A
  2. Peer B sends a message to the rendezvous server which stores the translated public address of Peer B
  3. The rendezvous has direct communication with the peers, so it exchanges the public addresses of the peers
  4. Peer A and B simultaneously send messages to the received addresses of each other to create a NAT hole
  5. When both NATs insert the rules for the outgoing IP address, each new incoming message will break through the NAT and reach the peers (i.e. the hole has been punched)
Hole punching with rendezvous server

Peerko — P2P Chat app

Peerko is a P2P chat application written in Rust that utilizes hole punching as the NAT traversal technique. It uses a custom protocol for peer discovery and message exchange.

It has two modes of operation:

  1. server mode — the actual rendezvous server
  2. client mode — the peer that connects to the rendezvous server to pick all connected peers and exchange messages

To run the rendezvous server in Docker:

docker run -it --rm -p 8000:8000 penumbra23/peerko:latest \
--name my-server --group chatting --port 8000 -s true

Assuming that the IP of the server is SERVER_IP, you can run any number of clients to exchange peer information with it:

docker run -it --rm -p 8000:8000 penumbra23/peerko:latest \
--name my-client-app --group chatting --port 8000 -b SERVER_IP:8000

The group argument introduces the feature of having different chat rooms, so that peers not belonging to the same group won’t know the address of different peers.

References:

--

--