How we discover our own diallable IP address and port

While debugging an issue with a user, I found myself describing how reflexive public IP address discovery works, the role of AutoNAT, and when AutoRelay kicks in.

Here are my notes for the benefit of the community:

  1. As we connect to peers in the network, we learn the addresses of us they observe via the Identify protocol. This is basically the source ip:port tuple of the socket.
    • We use SO_REUSEADDR and SO_REUSEPORT socket options when dialing and listening, so the address we dial peers from is the same address they can use to dial us back, as long as we’re not behind a NAT.
    • In other words, we don’t dial from ephemeral (random) ports locally. But if we’re behind a NAT, the peer might see the source port changing as the NAT updates mappings.
  2. We track the observed addresses peers report to us reflexively.
    • We “activate” an address when we observe it 4 times from different peers, and we expire addresses we haven’t seen in a while.
    • The component that does this in go-libp2p is the ObservedAddrSet.
    • This logic is a bit arbitrary and needs to be made flexible/pluggable.
  3. AutoNAT has two components: the client and the service.
    • AutoNAT is a reflexive dialback gadget. AutoNAT clients request dial backs from peers running the AutoNAT service, to confirm they are indeed dialable on addresses they suspect are valid inbound addresses (as per the logic in point 2).
    • AutoRelay needs AutoNAT to know when to kick in. AutoRelay peers are all AutoNAT clients. They are capable of requesting dial backs to determine their NAT status (public, behind a NAT, unknown).
    • Bootstrappers, relay nodes, etc. offer the AutoNAT service.
    • When AutoNAT clients connect to any peer in the network, they try to open a stream for AutoNAT.
      • If the other party NACKs the stream, they are not an AutoNAT service and we cannot request a dialback from them.
      • If the other party ACKs the stream, they are an AutoNAT service and we request a dialback from them.
  4. After 3 peers (confidence threshold) have confirmed they can dial back to us on our observed addresses, AutoNAT makes the determination that we are publicly diallable, and we advertise our observed addresses to other peers.
    • The AutoRelay subsystem deactivates.
  5. If we do not reach this confidence threshold, AutoNAT assumes we are running behind a NAT and are not diallable via a pinhole.
    • The AutoRelay subsystem activates, finds relays in the network, connects to them, and advertises our relay addresses to all connected peers.

Hope this summary was useful.

8 Likes

is there any project that actually uses this NAT traversal ?

Yes, IPFS, Filecoin, Textile, OpenBazaar, etc. use this mechanism to reflexively discover our own addresses. Note that what’s described here is not a NAT traversal mechanism: that’s the circuit relay subsystem with autorelay.

1 Like

Ok I am wondering, if there are two peers that want to exchange a file (f.e. in a file sharing service based on libp2p) there is a need to connect peers directly with each other. How is that done if they are behind NATs ?

if there are two peers that want to exchange a file (f.e. in a file sharing service based on libp2p) there is a need to connect peers directly with each other

They won’t be able to directly connect to each other in this case, so direct P2P communication won’t be possible. However they may still talk with each other if they both connect to some other node that will relay their messages to each other – and that’s exactly what @raul explained in the OP.

  • I’ll try to detail what will happen in this particular case (nodes A and B are interchangeable in the following):
    1. Node A will connect to the network and find, based on the algorithm described in the OP, that it is behind a NAT and cannot receive any direct connections
    2. Node A will connect to some relay node C in the network and publish that relay’s address in the DHT as a way to receive connections
      • i.e.: Rather than publish /ip4/A.A.A.A/tcp/A/p2p/NODE-ID-A as its public address it will publish /ip4/C.C.C.C/tcp/c/p2p/NODE-ID-C/p2p-circuit/p2p/NODE-ID-A instead
    3. Some time later node B wants to connect to node A and finds that the published DHT entry telling it to first connect to the relay node C and ask it to relay all traffic to node A
    4. Node B does this and a two-way connection is established

It is not clear to me whether node A will try to establish a direct connection to node B if node B has a public non-relay address, but it does not matter for “just” establishing any connection between A and B; it would likely be a very useful optimization however.

2 Likes

Thanks four your answer. So those peers still are some point of centralisation.

And where does a new peer get the DHT from ?

Thanks four your answer. So those peers still are some point of centralization.

Kind of, but the selection of these relay nodes is completely up to the NAT-ed client and any (publicly-reachable) node may become a relay.

And where does a new peer get the DHT from ?

go-IPFS ships with a list of known bootstrap nodes provided by Protocol Labs. If that list doesn’t work for you for any reason you can also provide your own by changing the relevant config section (if you create a private network that is actually a mandatory step). When connected to any of the bootstrap nodes your node will then start a discovery process to find other nodes (XOR and random-walk iirc) until it is satisfied with the number of peers it has.

It is not clear to me whether node A will try to establish a direct connection to node B if node B has a public non-relay address

Can someone answer this, please. Assuming both A and B are behind NATs, will they eventually establish a direct connection or will everything go through C?
If you can also post the code from the libp2p library where this process occurrs, I would appreciate it greatly.