Nodes on a peer-to-peer network can be categorized into two groups: public and non-public. Public nodes are those nodes that have unobstructed access to the internet, whereas non-public nodes are located behind some kind of firewall. This applies to most nodes in home and in corporate network, as well as mobile phones. In most configurations, both public and non-public nodes can dial connections to other public nodes. However, it’s not possible to establish a connection from the public internet to a non-public node.
Dialing a non-public node
Here are a few methods that nodes can use to dial a non-public node:
- UPnP (Universal Plug and Play): A protocol spoken between routers and computers inside the network. It allows the computer to request that certain ports be opened and forward to that computer.
- Port forwarding: Manually configuring a port forwarding on a router.
In many settings, UPnP is disabled by the router or a firewall. UPnP may also not work depending on the router’s firmware.
Manually opening a port requires technical expertise and does not enforce authentication or authorization.
Possible solution: hole punching
Relaying is a mechanism used to send information between two ends. In the case of non-public nodes:
Node A maintains a permanent connection to a relay node, R, and when node B wants to connect to node A, it first establishes a connection to node R, where R forwards all the packets on the connection. Relaying adds additional latency and is resource intensive as node R needs to handle a lot of traffic. Using a relay node also requires technical expertise.
What if we could use node R to help facilitate a direct connection between node A and node B?
In the case where the other options aren’t sufficient, networks can use a technique called hole punching to establish connections with non-public nodes.
Each node connects to a relay node and shares its external address and port information. The server temporarily stores the node’s information and relays each node’s information to the other. Clients can use this information to establish direct connections with each other.
Take two nodes,
B, that would like the dial each other:
- The first packet of both nodes (e.g., in the case of TCP, an SYN) passes through their respective routers.
- The routers add a 5-tuple to their router’s state table.A router state table (routing table) is data store within a router that lists the routes to particular network destinations. The 5-tuple structure includes the source IP address, source port, destination IP address, destination port, and transport protocol.
PacketB“punch holes” into their respective routers' firewalls.
- Both packets arrive at the opposite router.
A’s packet arrives at
Router_Bchecks its state table and finds a 5-tuple previously added through the packet sent by node B.
- The routers forward the packets through the “punched holes” to
B. The same occurs with
B’s packet; upon arriving at
Router_A, it matches a 5-tuple in
Router_A’s state table and thus forwards the packet to
The following use case diagram illustrates the above process.
Hole punching in libp2p
Inspired by the ICE protocol, libp2p includes a decentralized hole punching feature that allows for firewall and NAT traversal without the need for central coordination servers like STUN and TURN.
The following sequence diagram illustrates the whole process.
libp2p hole punching can be divided into two phases, a preparation phase and a hole punching phase.
Phase I: Preparation
AutoNAT: Determine whether a node is dialable, as in, discover if a node is behind a NAT or firewall.
This is equivalent to the STUN protocol in ICE.
Breaches out to
Other_Peers(e.g., boot nodes) on the network it is on and asks each node to dial it on a set of addresses it suspects could be reachable. A libp2p node has multiple ways of discovering its addresses, but the most prominent is using the libp2p Identify protocol.
Other_Peersattempt to dial each of
B’s addresses and report the outcome back to
- Based on the reports,
Bcan gauge whether it is publicly dialable and determine if hole punching is needed.
AutoRelay: Dynamically discover and bind to relay nodes on the network.
IPFS discovers the k-closest public relay nodes using a lookup method via Kademlia DHT):
B’s network can dial
Bindirectly through a public relay node. In the case of IPFS, each public node would serve as a
Bwould either perform a lookup on the Kademlia DHT for the closest peers to its Peer ID or choose a subset of the public nodes it is already connected to.
Circuit Relay: Connect to and request reservations with the discovered relay nodes. A node can advertise itself as being reachable through a remote relay node.
This is equivalent to the TURN protocol in ICE.
Relaycan limit the resources used to relay connections (e.g., by the number of connections, the time, and bytes) via Circuit Relay v2. In the case of IPFS, this allows every public node in the network to serve as a relay without high resource consumption.
- For each discovered
- connects to the remote node and requests the Relay node to listen to connections on its behalf, known as a reservation;
Relayaccepts reservation requests,
Bcan advertise itself as being reachable through
Phase II: Hole punching
Circuit Relay: Establish a secure relay connection through the public relay node. Node
Aestablishes a direct connection with the relay node. Node
Bthen requests a relayed connection to node
Athrough the relay node, creating a bi-directional channel and uses TLS to secure the channel.
Aestablishes a relayed connection to
Relayusing the information contained in
B’s advertised address.
Afirst establishes a direct connection to
Relayand then requests a relayed connection to
Relayforwards said request to
Relayforwards the acceptance to
Bcan use the bi-directional channel over
Bupgrade the relayed connection with a security protocol like TLS.
DCUtR: Use DCUtR as a synchronization mechanism to coordinate hole punching.
Connectcontains the addresses of A. libp2p offers multiple mechanisms to discover one’s addresses, e.g., via the libp2p Identify protocol.
Connectmessage on the relayed connection and replies with a
Connectmessage containing its (non-relayed) addresses.
Ameasures the time between sending its message and receiving
B’s message, thereby determining the round-trip time between
Bon the relayed connection.
Awaits for half the round-trip time, then directly dials
Bvia the addresses received in
- As soon as
Syncmessage, it directly dials
Awith the addresses provided in
Bdial each other simultaneously, a hole punch occurs.
- This guide is a byproduct of the Hole punching in libp2p - Overcoming Firewalls blog post by Max Inden.
- Research paper on decentralized hole punching by Protocol Labs Research
- Keep up with the libp2p implementations page for the state on different hole punching implementations.