2

So I was just reading about hole punch techniques for UDP and TCP. I read that TCP hole punch can easily be accomplished if a UDP connection has already been established. So here is my theoretical setup. I have two clients A and B both behind a NAT. They do not have a server to help them with the hole punch. Both have dynamic dns set up so that the other client can be hard coded to point to the others external address, also both are hard coded to use the same internal port, which we will say for example is 22321. Now these clients know that if they are both started and not connected that they want to connect so once every minute on the minute they send a UDP datagram to the other client to every single port, starting with the highest ports since my guess is a NAT will be inclined to use ports above 49152 as those are the dynamic ports. After each has done this there will be a record entered into the NATs state. If the clients are lucky then one of the two clients will hit the others external port after it has been opened by the NAT, and the datagram will get through and the listening client could then notify the sending client of the successful connection. If they were not so lucky then the clients just need to repeat the process, this time each starting from opposite ends to cut the time in half, when one gets through to the other then the receiver notifies the sender that the connection has been established. Could this methodology work? Or perhaps even be simplified by tracking the RST responses from the NAT?

We have clients A and B which are behind a NAT and want to communicate. both clients are listening on UDP port 22321 and both clients know the others global IP address, we will say that A has an ip address of 1.1.1.1 and B has a global ip address of 2.2.2.2. So A sends a packet to 2.2.2.2 on every single port, the contents of which need be basically nothing, for every single packet that passes through the NAT the NAT will change the outgoing IP from A's local address i.e. 192.168.1.16 to the global address 1.1.1.1. As the NAT forwards this packet to client B it creates a state entry for each of those packets so that if a packet is recieved from 2.2.2.2 on any of those ports it will know to forward it to client A (192.168.1.16) on port 22321. All of those packets would reach B's NAT at 2.2.2.2 and be discarded as unsolicited packets. Client B does the exact same process targeting client A's global ip address 1.1.1.1 when it hits a port on the NAT that client A has sent a packet out of to client B's NAT (basically any of them at this point) it will forward the packet on to client A. and Client A would be able then respond using that connection from then on out. Now the problem with this is negotiating who goes first and then when the second client should start, so instead they both just start at the exact beginning of a minute and quite likely the first attempt will fail to yield a connection but if they both then immediately repeat the process then there should be 1 or likely even many successful connections, each client could then just discard all but the first connection that succeeded.

0

1 Answer 1

2

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

22
  • Thanks, for the reply, I'm having trouble understanding why you would have to create 50,000 datagram sockets bound to 50,000 different ports, but it has been a while since I posted this and I don't deal with this stuff to often, mostly I do programming at the application layer, I'm going to thing about this some more now, and I'm happy someone besides me finds this interesting haha. 10 seconds to try everything isn't that bad for a worst case scenario on upload bandwidth since it only has do be done once per start up. If you try it out, please let me know how it goes. Jun 15, 2015 at 18:43
  • If you don't bind a datagram socket to a port and someone sends a packet to that port, even if you try to receive from that port after the fact, you won't get the packet they left. You have to bind first, then they send you the packet, then you receive. Or you can just wait (block) until the packet arrives, but with logistics like that, you don't have time to wait around. Jun 15, 2015 at 18:55
  • Crap. Apparently I can only bind an application to 4092 ports. And any ports I'm not bind-ed to, any packets sent to those ports are thrown away. So it won't work (unless you keep binding, unbinding, and resending to 4000 ports). Can I get best answer? Jun 15, 2015 at 20:27
  • That is only true by default, You can actually change the MaxUserPorts setting in the registry to be able to bind to every single port, you will likely be getting the best answer yes. but it would be awesome if you try that really quick. MSDN documentation on the registry key I am referring to can be found here: technet.microsoft.com/en-us/library/cc938196.aspx Jun 15, 2015 at 20:57
  • Listening on more than one address is not a good idea because symmetric NAT only accepts from the socket that it sends from, and you can't afford to send 65k packets from more than one local port. That being said, my code does brute force through my local network and is synchronized to shoot at every port each second. Note that depending on your computer and Wireless/Ethernet connection quality, it might take more than one second to hit every port. Jun 17, 2015 at 7:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.