Skip to content

Decentralised NAT Signalling #365

@tegefaulkes

Description

@tegefaulkes

Requirements of this design

  1. Maintaining active connections between nodes to form a 'strongly connected' network.
  2. Decentralised connection forming and NAT punching.
  3. Decentralised message relaying.
  4. Possibly caching known connection chains to speed up connection forming.

Additional context

Specification

We need to be able to hole punch a connection to the target node in a decentralised way. This can happen in stages.

  1. Holding connections to form a strongly connected graph.
  2. Discovery of a connected path.
  3. Forming a direct connection using a connected path.

Holding connections

To ensure that a connection path is available we need to have an environment where a chain of connections to the target is likely to exist. To do this each node needs to hold active connections to other nodes. the distribution of these connections needs to ensure that the overall network is strongly connected. Bonus points for minimising the number of hops.

Using the kademlia structure we can maintain active connections to the N closest nodes. but we can also hold connections between other nodes across the network. The closest nodes should maintain strong connections locally in the network while the extra connections will form shortcuts between parts of the network that are not closely related. Note I mean close the kademlia sense of closeness.

Discovery

The discovery process is very similar to the Kademlia FIND_NODE process. the difference here is that we only search over the active connections Proxy connections. Using this process we should be able to find a chain of connections that reaches our target Node.

Generally the idea is that we are walking the graph of active connections to find a chain of connections that leads to our target. Naively this would be a pretty expensive and exhaustive search but utilising the Kademlia structure we can ensure that we only walk closer to our target at each step of the search.

Forming direct connections

Given a chain of connections A>B>C>D we should be able to form a direct connection of A>B. We can form incrementally closer connections by using each step of the chain as a signaller to punch a connection to the next part of the chain. I will refer to this increasingly closer connection as a Feeler.

With the Chain A>B>C>D we can start by requesting B to signal a hole punch between A and C. If the hole-punch is successful then we now have a connection chain of A>C>D. We can then repeat the step use C as the new signaller to form a connection between A and D thus completing the desired direct connection.


    
 ┌─┐     ┌─┐     ┌─┐     ┌─┐    This is the existing connection chain.
 │A├─────►B├─────►C├─────►D│    We start out by asking Node B to signal
 └─┘     └─┘     └─┘     └─┘    hole punching to node C thus creating a
                                connection between A and C.


 ┌─┐     ┌─┐     ┌─┐     ┌─┐
 │A├─────►B├─────►C├─────►D│    With an active connection to C we can use
 └┬┘     └─┘     └▲┘     └─┘    C to act as a signaller to form a connection
  │    Feeler     │             A and D.
  └───────────────┘

 ┌─┐     ┌─┐     ┌─┐     ┌─┐    We now have a direct connection through
 │A├─────►B├─────►C├─────►D│    the NAT between A and D.
 └┬┘     └─┘     └─┘     └▲┘
  │         Feeler        │
  └───────────────────────┘

At each step the 'signalling' node is directly connected to both nodes and thus is aware of the 'NAT-ified' ports and addresses. It can easily coordinate a hole-punch between the two nodes. So long as we can find a connection chain we can form a connection between two nodes through NATs without a centralised signaller.

If we fail to form a more direct connection then an alternative chain will need to be found. Given a graph that is sufficiently connected then there should be multiple routes to the target. If for some reason a direct connection can't be formed but the target node is holding a connection. This process should ideally connect us with a node that IS holding an active connection to the target node and can act as a relay for said node. It's possible that not all nodes will like to act as a data relay so we may need a way to negotiate with the target node to connect to a relay node that will relay data.

Full process

We can combine the discovery and connection forming to create increasing closer connections to our target. the process will look something like this.

  1. get list of active connections sorted by distance to target.
  2. Pick N out of that list in order of closeness to target.
  3. form a direct connection with the newly found nodes using the previous connections as a signaller.
  4. repeat from step 1.

Eventually this should form a direct connection to our target. We at any point in time concurrently have alpha Feelers walking closer to the target during this process.

If we had existing discovery knowledge about the network such as know connection chains that lead to the target then that can be used to greatly speed up the process. if we had knowledge of an existing connection chain that leads to the target then we could just do the connection forming process without doing any discovery. Likewise if we had a partial chain that we know leads closer to our target then we can use that as a starting point and reduce how much we need to search.

Additional Resources

Sub-Issues & Sub-PRs created

  1. Decentralized Signalling #618
  2. MDNS integration and NodeGraph structure expansion #537

Metadata

Metadata

Labels

designRequires designenhancementNew feature or requestepicBig issue with multiple subissuesr&d:polykey:core activity 4End to End Networking behind Consumer NAT Devices

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions