Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal for NAT UDP hole-punching #433

Closed
2 of 4 tasks
cannium opened this issue Sep 25, 2018 · 4 comments
Closed
2 of 4 tasks

Proposal for NAT UDP hole-punching #433

cannium opened this issue Sep 25, 2018 · 4 comments
Assignees

Comments

@cannium
Copy link
Contributor

cannium commented Sep 25, 2018

Reason

To increase connectivity between nodes as much as we can.

Relay is good, but is not so "peer-to-peer", and at the cost of community maintained relay servers. Also, as we have tested, many of our customers are behind some kind of NATs. Hopefully hole-punching would solve some other percents of the problem.

Technical details

Hole-punching is better understood at protocol level. To track an NAT mapping, a gateway(or some other device) would record at least 3 addresses(and ports): local internal address, local external address and remote external address. For an outbound packet, the gateway would replace its local internal address with local external address; for a inbound packet, the gateway would replace its local external address with local internal address. From the perspective of a internal node, the connection is between local internal address and remote external address.

If a gateway only do some translations, the problem would be much easier. But a gateway would also apply some restrictions to inbound packets, mainly for security reasons. This wiki lists 4 types of NATs and their restrictions for inbound packets, and theoretically UDP hole-punching could solve 3 types. For TCP packets, I observed additional restrictions, like sequence number check, so UDP is a better choice.

The diagram below is a punching example.
udp hole-punching

Node1 and Node2 are two nodes behind different NAT gateways, with internal address 192.168.0.1:4001 and 10.0.0.2:4001, respectively. A helper node with public IP 3.0.0.1 would assist connection establishment, but not relay data for them.

For the first step, the public IP node would see Node1 has external address 100.100.0.1:10000, Node2 has 200.200.0.1:20000, and send this information to one another side. Knowing the other's external address, Node1 would send packets to 200.200.0.1:20000, on receiving Node1's packet, NAT1 would allow inbound packets from 200.200.0.1:20000 (otherwise normal communication is disturbed). Node2 and NAT2 are the same. The first 1 or more packets might be dropped because of timing (inbound packets not already allowed), but eventually a 2-way communication would be established.

And for multiple layers of NATs, the situation is similar, only take longer time.
multiple-layer-nat

Limitations

  • Hole-punching relies on gateways to map to a same external port for the same internal address. In other words, peers should observe same external addresses for a node. I think Keep more information when tracking observed addresses (in identify) #416 would help to probe the environment.
  • UDP packets are generally of lower priority on the internet; some service providers even fully disable UDP, but this would be very rare.

Implementation

I noticed inside libp2p ecosystem, we already have port reuse and QUIC transport, both are great technologies. The missing parts include:

I'd like to know if someone is also digging on the problem, or some work is done or on-going? I plan to code for it very soon.

@overbool
Copy link

overbool commented Sep 25, 2018

It's a good choice for UDP hole-punching. We had done TCP hole-punching experiments and found it's so difficult.

@cannium
Copy link
Contributor Author

cannium commented Sep 28, 2018

Inspired by this comment(ipfs/kubo#5511 (comment)), I read a little about ICE(https://tools.ietf.org/html/rfc8445) and found the problem already solved systematically. https://gortc.io/ looks like a promising golang implement.

@cannium
Copy link
Contributor Author

cannium commented Nov 21, 2018

I posted the final piece of hole-punching logic at #490

@aarshkshah1992 aarshkshah1992 self-assigned this May 12, 2020
@marten-seemann
Copy link
Contributor

Closing since we now have proper hole punching in libp2p, which works on TCP and on QUIC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants