UDP against routers : hole punching

UDP against routers : hole punching

Hello there! Today, I ran a few tests with UDP networking (yes, my hobbies are really exciting), and my results made me think I could write another article… This is the first one related to networking, hopefully it’ll be understandable enough. For this first time, we’ll talk about UDP hole punching. Let’s go!

What is UDP hole punching ?

Networking notions

For those of you who aren’t interested in networking (yet), here’s a little schema of mine :

Network basics

Beautiful is it not (well no, it isn’t) ? Basically, your machine is connected to a router, which connects you to the rest of the world. With this very common setup, it is impossible for A and C to communicate directly. In order to do so, the routers must be configured accordingly, usually through port forwarding rules, and relay messages sent on the communication ports to the specified machines. That’s great, but not everyone enjoys going into the router’s configuration to allow home-made networking applications to work.

Punching a hole

Punching a hole makes the problem go away. It relies on a very simple thing : they are ports opened when receiving and when sending. This means that when you send a packet outside (through your router), a port will be automatically assigned to the outgoing transmission. Let’s explain the process with a list this time :

  1. Your machine sends to packet to 1.2.3.4:5000 (this is an example IP which happens to belong to Google).
  2. The packet goes through your router, which will handle the transmission from here.
  3. The router assigns a random port for the outgoing message, let’s say 12345.
  4. The message goes from your router (port 12345) to its destination (1.2.3.4, port 5000).

There it is, this random port. 12345 is used for the transmission, but more importantly, it is connected to your local machine : you punched a hole. This means that any message sent to your router, on port 12345 will be relayed to your machine. Point is : once you close your socket descriptor, the one you used to send the message, this assignation goes away (the hole is closed). The trick here will be to maintain the socket descriptor so that 1.2.3.4 can answer back!

Some code

The trick needs to be performed by both sides, client and server. For this reason, there will be two samples here. See you in the comments.

The server

Now, write some code, compile it, and run it on a distant server, which has a public IP. Keep it aside, and let’s program the client.

The client

Run your favourite C compiler on this, and run it on your local machine. Watch those funny little lines printing on the server terminal. You should recognise your router’s IP, and a port, always the same (if it differs, then you probably abandoned the socket of the client side).

Now, you’ll probably tell me : “this doesn’t help! the server isn’t my destination, I want to contact another machine behind another router!”. You’re right, that’s what you want to do. But the above code is a way to set it up! Let’s see…

Communicating over the Internet, behind routers

Here’s how we can actually punch a hole to make A and B communicate.

  1. Machine A sends a packet to a world-visible server, let’s call it S.
  2. Machine B does just the same.
  3. S receives the packet from A, which transited through A’s router (with the random port!)
  4. S receives the packet from B, which transited through B’s router (with another random port!)
  5. S sends B’s IP and random port to A.
  6. S sends A’s IP and random port to B.

From there, A and B know exactly how to contact each other : through the ports which were allocated when contacting S. And because they’re super kind and all, the client applications on these machines didn’t close any socket descriptor, meaning there’s still a random port associated for both of them. Now, another cute little schema :

Punching the UDP hole

There you go ! As long as A and B keep their descriptors opened, and don’t renew them, then A and B will be able to communicate through their routers, without any manual port forwarding configured. As it so happens, UDP hole punching is a very common mechanism, especially in VoIP or P2P software. Even though other solutions exist (STUN, UPnP), this one requires less setup, and works on most routers. Be careful though : this might fail on some NAT  types… You cannot win every time, right?

That’s all for today, see you next time!