I wrote a program that sets-up peer-to-peer links. The programm, which can be found at http://basyl.co.uk/code/punch/doc/files/Readme-txt.html, is in two parts: a server that runs on a public host; and a client that is used by each end of the desired peer-to-peer link.
I have access to two public servers: 'bonn' (home.contextshift.co.uk) and 'entropy' (home2.contextshift.co.uk)
If the server is on bonn and clients are run on bonn, entropy and my home PC (behind NAT), a punched connection from entropy can talk to my PC without problem. However, a connection from bonn to the PC fails; data from the PC reaches bonn, but data from bonn back through the NAT hole never arrives.
If the server is on entropy and again, clients are run on bonn, entropy and my PC, punched connections work fine between all clients.
This is confusing as the server is not involved in the peer-to-peer data flow. If you are still with me, here is the flow:
- Client-A connects to Server on a TCP link and gets a unique token;
Client-B connects to Server on a TCP link and gets a unique token;
Client-A and Client-B receive updates over their TCP link telling them who else is attached;
Client-A (or B) sends a request to the Server over a newly created UDP link passing its token and the name of Client-B;
The Server identifies Client-A from the token and forwards the request to Client-B over its TCP link, including A's UDP address/port number in the request;
Client-A (or B) sends a confirmation to the Server over a newly created UDP link, passing its token and the name of Client-A;
The Server identifies Client-B from the token and forwards the request to Client-A over its TCP link, including B's UDP address/port number in the request;
A and B now have the UDP address/port of the other and can ping each other and exchange data.
As you can see, the Server never talks on the UDP links created by the Clients for their requests, only on the TCP links.
So in summary, the client doesn't work on a particular host when the server is on the same host. Any suggestions for reasons for this behaviour or for ways I could investigate this further?
Note that this test is artificial because the point of the hole punching is to talk between two hosts both behind NAT. This in fact works, wherever the server is, so the problem might be considered academic.
Note also that before writing the program, I tried using a public app called 'NatCheck'. This also failed in a similar way, although I didn't investigate it much - it required three public hosts and I modified it to use just my two. When it failed to work, I assumed that I had screwed up in some way and discarded the app.
Any comments on the code also highly welcome (I will probably post it on the code review site).
Could it be that the server sees clientA as being on 127.0.0.1 (or maybe a non-routable LAN address?) and not its public IP-address?
If clientB tries to contact clientA using the wrong address, it's obvious why the UDP-datagram doesn't get to the intended recipient.
Some output from
tcpdump
from both the server (AKA clientA) and clientB would help a lot. You might want to use-i any
and maybe-s 0
.I can't quite follow all that, but it sounds like you want to use an intermediary server to discover the source UDP ports for Clients A and B so that A and B can simultaneously send UDP datagrams at each-other, thereby opening NAT rules and (eventually) allowing the traffic through.
Here's the problem: NAT can map the source port to whatever it wants. When B sends a datagram to the server, there's no guarantee that the source port seen by the server will be the same one that is used when B sends a datagram to A.
There are a lot of reasons why the NAT might change the port number, and a security conscious one will randomize just to prevent what you are trying to do. So while you may be able to make double punching (NAT to NAT) work sometimes, you cannot do so every time.
In some corner cases, NATs only accept traffic from the target IP address:port used to punch the hole. Since it is not the one of the remote NAT-ed peer, they block the traffic from it. This could explain your issue.