python-nat-hole-punching icon indicating copy to clipboard operation
python-nat-hole-punching copied to clipboard

Why so complicated?

Open piegamesde opened this issue 2 years ago • 5 comments

I tried implementing this and only got a lot of really frustrating errors (the accept call tends to hang up indefinitely). But I tried a simpler method which does seem to work:

  1. Bind a port (with SO_REUSEADDR)
  2. Find out the IP addresses and exchange them over an external channel
  3. Connect to the peer with the bound socket. If there is more than one IP address to contact, bind a new socket on the same port and connect from there.

You can easily reproduce this with two nc commands. There are no listen and accept calls, making thins a lot easier. The first connect may fail on a firewall, but it will try again and once the peer has sent their first message the packets will go through. It doesn't matter that there is no accept call, if both sides connect to each other it works nevertheless. The usual caveats apply: both sides need to synchronize temporally with the precision of one timeout. If any firewall sends RST packets, it's game over (this holds especially true on loopback connections on the same machine).

My question is: did you try this approach, what do you think of it and how does it compare to the one implemented here? Is there something big that I'm missing (I didn't test with NATs yet for example, but there's no reason it shouldn't work).

piegamesde avatar Sep 10 '21 13:09 piegamesde

Could you add the nc commands you are using? That way we can try your proposal.

Periodic1911 avatar Sep 10 '21 17:09 Periodic1911

I think it was as simple as a well-timed nc -p 12345 $PEER_IP 12345 on both sides. We then looked at the strace to see that only bind and connect were called.

piegamesde avatar Sep 10 '21 21:09 piegamesde

Well, I found an issue with my approach: it doesn't work on devices without firewalls: The packets will be let through to the kernel, which will notice that no one is listening on that port and then send an RST packet. This is nothing that can't be worked around, but it certainly is annoying.

piegamesde avatar Sep 17 '21 00:09 piegamesde

I've now combined the approach with a naive listen/accept on a different port. It now requires twice as many connection attempts, but at least it works at all settings. It would be great to have the listener socket on the same port as the connect sockets, but I did not manage to get this to work at all.

piegamesde avatar Sep 22 '21 21:09 piegamesde

Well, I found an issue with my approach: it doesn't work on devices without firewalls: The packets will be let through to the kernel, which will notice that no one is listening on that port and then send an RST packet. This is nothing that can't be worked around, but it certainly is annoying.

setsockopt(SOL_IP, IP_TTL, ttlvalue) works on Linux for the regular user and could be changed mid-connection.

ValdikSS avatar Jul 19 '22 01:07 ValdikSS