The internet is composed of countless networks, bound together into shared address spaces by foundational transport protocols.
As traffic moves between network boundaries, it’s very common for a process called Network Address Translation to occur. Network Address Translation (NAT) maps an address from one address space to another.
NAT allows many machines to share a single public address, and it is essential for the continued functioning of the IPv4 protocol, which would otherwise be unable to serve the needs of the modern networked population with its 32-bit address space.
For example, when I connect to my home wifi, my computer gets an IPv4 address of
10.0.1.15. This is part of a range of IP addresses reserved for internal use by private networks. When I make an outgoing connection to a public IP address, the router replaces my internal IP with its own public IP address. When data comes back from the other side, the router will translate back to the internal address.
While NAT is usually transparent for outgoing connections, listening for incoming connections requires some configuration. The router listens on a single public IP address, but any number of machines on the internal network could handle the request. To serve requests, your router must be configured to send certain traffic to a specific machine, usually by mapping one or more TCP or UDP ports from the public IP to an internal one.
While it’s usually possible to manually configure routers, not everyone that wants to run a peer-to-peer application or other network service will have the ability to do so.
We want libp2p applications to run everywhere, not just in data centers or on machines with stable public IP addresses. To enable this, here are the main approaches to NAT traversal available in libp2p today.
Automatic router configuration
If you router supports one of those protocols, libp2p will attempt to automatically configure a port mapping that will allow it to listen for incoming traffic. This is usually the simplest option if supported by the network and libp2p implementation.
Support for automatic NAT configuration varies by libp2p implementation. Check the current implementation status for details.
When an internal machine “dials out” and makes a connection to a public address, the router will map a public port to the internal IP address to use for the connection. In some cases, the router will also accept incoming connections on that port and route them to the same internal IP.
Libp2p will try to take advantage of this behavior when using IP-backed transports by using the same port for both dialing and listening, using a socket option called
If our peer is in a favorable network environment, they will be able to make an outgoing connection and get a publicly-reachable listening port “for free,” but they might never know it. Unfortunately, there’s no way for the dialing program to discover what port was assigned to the connection on its own.
However, an external peer can can tell us what address they observed us on. We can then take that address and advertise it to other peers in our peer routing network to let them know where to find us.
This basic premise of peers informing each other of their observed addresses is the foundation of STUN (Session Traversal Utilities for NAT), which describes a client / server protocol for discovering publicly reachable IP address and port combinations.
One of libp2p’s core protocols is the identify protocol, which allows one peer to ask another for some identifying information. When sending over their public key and some other useful information, the peer being identified includes the set of addresses that it has observed for the peer asking the question.
This external discovery mechanism serves the same role as STUN, but without the need for a set of “STUN servers”.
The identify protocol allows some peers to communicate across NATs that would otherwise be impenetrable.
While the identify protocol described above lets peers inform each other about their observed network addresses, not all networks will allow incoming connections on the same port used for dialing out.
Once again, other peers can help us observe our situation, this time by attempting to dial us at our observed addresses. If this succeeds, we can rely on other peers being able to dial us as well and we can start advertising our listen address.
A libp2p protocol called AutoNAT lets peers request dial-backs from peers providing the AutoNAT service.
AutoNAT is currently implemented in go-libp2p via go-libp2p-autonat.
Circuit Relay (TURN)
In some cases, peers will be unable to traverse their NAT in a way that makes them publicly accessible.
Libp2p provides a Circuit Relay protocol that allows peers to communicate indirectly via a helpful intermediary peer.
This serves a similar function to the TURN protocol in other systems.