The NAT device will keep a table with currently open connections so that it can send return packets to the internal host that opened the connection. An entry in the state table could look something like this:
Internal Source IP | Internal S-Port | External S-Port | Destination IP | D-Port
192.168.1.12 10123 10123 203.0.113.1 5555
When a packet is received (from the external side) the NAT device will check the Source IP, Source Port and Destination Port of the packet and compare it to the Destination IP
, D-Port
and External S-Port
fields in its connection table. When it finds a match it will forward the packet to Internal Source IP
on Internal S-Port
.
UDP hole punching depends on the fact that Internal and External Source Ports are the same. This is normally the case unless you have a second internal host that uses the same Source port to connect to the same external Destination/D-Port combination.
When we assume that Internal and External S-Port are the same then both clients A,B can communicate their source port to the rendezvous server which will then relay the information back to the other client respectively.
Yes. Yes it is possible. The network has to be able to handle all the packets (without exceptions - packet loss means death), the machines have to be able to send out packets fast enough, the other end must not have "flooding protection" and the address has to not change every time you open a new connection. This method managed to connect my laptop behind my University's WiFi with my friends laptop behind his WiFi and it sent a packet from my home WiFi through my University's symmetric NAT. If you're using a phone, you should probably send packets as fast as possible because phones are slow but with laptops you can synchronize the barrage of UDP packets with Thread.sleep()
/**
* Shoots empty UDP packets at every port between "start" and "end".
*/
public static void sendBarrage(int start, int end, InetAddress target) {
final ByteBuffer to_send = ByteBuffer.allocate(0);
// Start at a random value to avoid re-traversing failed paths on repeated attempts.
final int starting_point = start + generator.nextInt(end - start);
// Go from starting_point down to 1024.
for (int port = starting_point; port >= start; --port) {
try {
if (made_connection == false) {
myChannel.send(to_send, new InetSocketAddress(target, port));
} else {
break;
}
} catch (java.nio.channels.ClosedChannelException cce) {
Application.printerr("Channel closed while on port: " + port);
break;
} catch (IOException e) {
Application.printerr("Error sending in port: " + port);
continue;
}
}
// Go from 65535 down to say starting_point, not including starting_point.
for (int port = end - 1; port > starting_point; --port) {
try {
if (made_connection == false) {
myChannel.send(to_send, new InetSocketAddress(target, port));
} else {
break;
}
} catch (java.nio.channels.ClosedChannelException cce) {
Application.printerr("Channel closed while on port: " + port);
break;
} catch (IOException e) {
Application.printerr("Error sending in port: " + port);
continue;
}
}
}
^ Make sure you call this method in a loop, it might take a few attempts. Keep in mind that this method does NOT work with cell phone tower networks (unless you get insanely lucky and the public ip address does not change). It might work better if one side holds the port open while the other guesses rather than having them both brute force both sides.
Also, if only one side holds the ports open, this method might be good: https://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf
Best Answer
The only way to establish a connection between two hosts when they are both behind different NATs is to either configure port-forwarding on the NAT devices, or use a third-party server hosted on a public address externally (in which case there is no direct TCP connection between hosts).