0

I'm trying to send UDP packets from one client to another. Let C1 be the client I'm using to receive data and C2 the client who will send the data. What I'm doing:

  1. Ask a STUN server for the public IP addresses and ports of both clients.

  2. I inform each client of the others IP address and public port.

  3. C1 sends a packet to C2 on it's public address and port and starts listening for packets. At the same time C2 starts sending packets with the data I want to be transmited.

C1 just hangs on receiving a packet that never arrives. am I doing the UDP hole puching wrong?

I am testing this with both clients on the same computer on the same network but the server is on a server on another network with a public IP. What I'm trying to say is that the public address for both clients is the same while I'm testing, is this a problem?

UPDATE:

I know now that my router allows for hairpinning. I fowarded the UDP ports on my router and using the public IP address I can send packets from one client to the other.

This is the code I'm using to test if I can do hole punching in C#:

static UdpClient udpClient = new UdpClient(30001);
static IPAddress remoteIp;
static int remotePort;

static void Main(string[] args)
{
        // Send packet to server so it can find the client's public address and port 
        byte[] queryServer = Encoding.UTF8.GetBytes("something");
        udpClient.Send(testMsg, testMsg.Length, new IPEndPoint(IPAddress.Parse("199.254.173.154"), 30000));

        // Wait for a response from the server with the other client's public address and port
        IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);           
        byte[] dataJsonBytes = udpClient.Receive(ref sender);
        JObject json = JObject.Parse(Encoding.UTF8.GetString(dataJsonBytes));
        remoteIp = IPAddress.Parse(json["IP"].ToString());
        remotePort = Int32.Parse(json["port"].ToString());                                                    

        // Start spamming the other client with packets and waiting for response
        Thread sendingThread = new Thread(sendPacket);
        Thread receivingThread = new Thread(receivePacket);

        sendingThread.Start();
        receivingThread.Start();

        Console.ReadLine();
    }

    private static void sendPacket()
    {
        byte[] udpMessage = Encoding.UTF8.GetBytes("Forcing udp hole punching!!\n");
        while (true)
        {               
            udpClient.Send(udpMessage, udpMessage.Length, new IPEndPoint(remoteIp, remotePort));
            Thread.Sleep(100);
        }
    }

    private static void receivePacket()
    {
        IPEndPoint senderEp = new IPEndPoint(IPAddress.Any, 0);
        udpClient.Client.ReceiveTimeout = 500;
        while (true)
        {
            try
            {
                byte[] receivedData = udpClient.Receive(ref senderEp);
                Console.Write(Encoding.UTF8.GetString(receivedData));
            }
            catch (Exception)
            {
                Console.Write("Timeout!!\n");
            }
        }
    }

UPDATE 2:

With the help of @Tahlil I was able to figure out that the algorithm I used to check the NAT type was flawed and I was trying to do hole punching behind a symmetric NAT. So the problem was not noticing a mistake on my code... I recommend asserting the NAT type with certainty to all that face the same problem.

Thank you all for your help.

tttony
  • 4,944
  • 4
  • 26
  • 41
Pedro Leal
  • 183
  • 1
  • 10

2 Answers2

1

Some NAT doesn't allow packets from unknown sources. That's why we punch hole in the NAT so that the NAT can recognize that unknown IP and creates a mapping for it.

If C1 and C2 are in different network. Then if C2 sent a packet to C1s public IP:port then C2's NAT will create a mapping for C1's public IP:Port. After that any data from C1 in C2's public IP:Port would have been forwarded to C2.

But I think in your scenario the packets are not leaving the NAT because they are both under same network. Thats why the NAT is not creating any mapping. So your hole punching is failing.

So when you send data to C2's public IP address from C1, you are assuming by that time C2 has punched a hole for C1s address and NAT has created a mapping. But C2's hole punching has been failed as C1 is in same network.

And why do you need to punch hole if you are in same network? This is a bizarre intention as C1 and C2 can directly send packets to each others private IP and establish a connection. Hole punching is needed when C1 and C2 is in different network.

EDIT:

Hole punching will not work if the NAT combination of two clients is like Symmetric vs Symmetric. So first check what is the NAT types of both end.

Do not spam the other end with packets in order to punch a hole. NAT can block your IP if you flood it with packets. Do hole punching from one side and with low TTL value. So that the packets intended for hole punching leave the NAT but doesn't reach the other NAT. This way hole punching will be done successfully and other side NAT won't be flooded. From the other side try sending packet a little late so that hole punch is completed from the other side. When hole punched side receives first packet then stop the hole punching and start sending packet normally.

Tahlil
  • 2,680
  • 6
  • 43
  • 84
  • 1
    Thank you for your awnser Tahlil. This is intended to use with clients behind different NAT's but I'm testing it with computers on the same network because that's what I have. I'm going to do some tests having in mind what you told me (I'm trying to setup two computers in different networks) and I'll post the results. – Pedro Leal Sep 01 '15 at 09:46
  • Keep in mind that C2 needs to start sending packets after C1's hole punching is done. Or else C1 will not receive anything as C2 will be blocked by the NAT. – Tahlil Sep 01 '15 at 09:53
  • Ok, I didn't know that actually... That might be another thing that is causing packets to be dropped. Thank you – Pedro Leal Sep 01 '15 at 11:16
  • So I used a sencond computer behind another NAT and still no go... I posted the code I'm using to attempt udp hole punching. Please give it a look if you can... I really don't understand what I might be doing wrong. Thanks again for your awnser :) – Pedro Leal Sep 03 '15 at 14:12
  • I know that both NATs are full cone. I'll try again with the sugestion you gave me in your edit. Thank you for the help – Pedro Leal Sep 03 '15 at 15:26
  • If both NAT are full cone then you don't need hole punching. You exchange your IP information and when you send packet to each other both of them should receive. Do you know why hole punching is needed? – Tahlil Sep 03 '15 at 15:41
  • I'm sorry I got the names confused (I'm no expert on this matter as you have probably noticed ^^). Both NATs do port mapping and change the port bound to the sockets for a different public port, those are port restricted cone, right? – Pedro Leal Sep 03 '15 at 15:50
  • If client's public Port changes with destination change then it is Symmetric NAT. For PRC NAT, public port doesn't change with destination change. Read this answer http://stackoverflow.com/a/31382269/395131 – Tahlil Sep 03 '15 at 15:59
  • Thank you for the link I'll get better informed... It appears I'm all over the place... I'll get back to you – Pedro Leal Sep 03 '15 at 16:18
  • I used a public stun server to get each of the NATs type and for both it awnsered full cone. For what I understand from the awnser you linked me if I send a udp packet from client1 to a server and if I know the NAT's address and port mapping for example private port 3000 -> public port 50000 and private 192.168.1.40 -> public 88.x.x.x, client2 should be able to reach client one on 88.x.x.x:50000 without doing anything else right? No need to forwards any ports on the router, just send a packet to client1 on the mapped address and port. Thanks again for your patience and help @Tahlil – Pedro Leal Sep 04 '15 at 08:58
  • Yes. If the NAT type is full cone then you don't need to forward any port. Just gather Client1's public IP:Port info from STUN server and then client 2 needs to send data on that public IP:Port to reach client1. – Tahlil Sep 04 '15 at 09:51
  • That wasn't working but for what I gather the problem might be at the windows firewall level. I'll check it out ;) – Pedro Leal Sep 04 '15 at 10:01
  • Take TCP dump from both of your NAT. See if packets from client2 is reaching its NAT and leaving. Also see if the packet reached the NAT of client1 and if it is forwarded. – Tahlil Sep 04 '15 at 10:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/88780/discussion-between-tahlil-and-pedro-leal). – Tahlil Sep 04 '15 at 10:10
1

Of course C2 will hang. Because the C2 NAT likely rejected the original packet sent by C1 before C2 sent his packet. Build some retry logic into all of this to overcome these issues.

Also, if C1 and C2 are on the same subnet, then it's possible your NAT just doesn't allow hairpinning. "Hairpinning" is the ability to send internally through your external address. Not all NATs support this. Be prepared to try to connect with both internal candidate addresses as well as STUN discovered external addresses.

In any case, start with the answer I've given out before.

https://stackoverflow.com/a/8524609/104458

Community
  • 1
  • 1
selbie
  • 100,020
  • 15
  • 103
  • 173
  • Thank you for your awnser selbie, I had a mistake on the description, it's C1 that hangs even thought it had already sent the first packet (sorry for the mistake, I edited it). I had read your awnser before, in fact it helped me understand how this all works ;). I also have mediated everything with tcp just to make sure things happen in the desired sequence. I'll try setting up two computers in different networks to see if it's because of the NAT not allowing hairpinning. Thank you again. – Pedro Leal Sep 01 '15 at 09:55
  • TCP nat traversal is twice as hard as UDP NAT traversal. In TCP, you would likely need to have both endpoints simultaneously are connecting to each other with retry logic. – selbie Sep 01 '15 at 22:14
  • Still no go... I know that hairpinning is possible on my router because if I do port fowarding and send packets from one client to the other using the public address they passtrough. I wrote some code in java that sends each client's public port and address to the other. In my router I fowarded one port to access the socket open in the java code. I'm going to update my post with some code. Please give it a look if you can. – Pedro Leal Sep 02 '15 at 17:16