Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make a connection between two computers behind NAT

Tags:

python

nat

I'm trying to establish a connection between two computers behind NAT. I can't open any port. I would like to use a broker who will be on the internet and who will be able to communicate with the two others. After, I would like to establish a tunnel between those two. I'm using Python and I'm trying to code this broker. How can I begin this ?

like image 203
Mathieu Lepage Avatar asked Oct 08 '14 22:10

Mathieu Lepage


1 Answers

What you want to do is possible, and almost routine (at least for UDP). It's called NAT hole punching. It won't work with every NAT in the world, but it works with many, including most setups of most current home routers.

The basic idea is that, with most routers, if you send an outgoing packet, it will forward any incoming packets from the same endpoint back to you. Otherwise, NAT wouldn't work at all, right? So, if both you and I try to talk to each other's public addresses at the same time, one of us will have "punched a hole" in his NAT in time to receive the message. The other one will probably miss the initial message, because he punched his hole too late, but he'll receive the next reply the other guy sent, and after that, everything is working.

There are a few tricks here, however.

First, you need to know your public address. All you can see directly is your private address inside the NAT, so you need a server outside the NAT that can tell you where you're coming from. There's a standard for this, called STUN. Not only are there multiple free implementations you can build and run yourself, there are multiple open STUN servers on the internet; google for an updated list.

At minimum, you have one private address and your public address, and you really want to give the other guy all of them, not just one. (If you only give me your public address, and it turns out that we're on the same NAT, we may not be able to talk to each other.) It gets even more complicated if you have multiple NICs, or you're on a corporate LAN with multiple layers of NAT, etc. But if you give me a whole slew of addresses, how do I know which one to use? Simple, I can just try to connect to each of them from each of my local addresses, and the first one that works is the one I keep using. We obviously need some kind of protocol for this so you can tell me when one of them has worked, but it can be dead simple.

Also, note that when I say "address" here, that doesn't just mean IP address, but IP address plus port. After all, NATs can and do translate ports along with addresses, and the "hole" that you punch in a NAT is often port-specific. So, you need to do the negotiation using the same source and destination ports you're going to use for the real communication. (Actually, there are some tricky bits here, which are explained by the linked documents, but let's skim over them for now.)

When hole punching doesn't work, and you need to fall back to proxying through a server, you don't want that code to look completely different; you'd end up having to write everything twice, and having huge problems with debugging. Fortunately, there's a standard solution for this, called TURN, which makes it look to the peers like they're actually talking directly even where they're talking through a relay. Again there are free implementations, but of course there aren't open TURN servers for you to use (because that would cost a lot of bandwidth).

Meanwhile, once you know your public address, you have to tell me what it is, and vice versa. Obviously we need some side channel to talk on, like a special "introducer" or "lobby" server. Typically, you're spawning a peer-to-peer connection as an offshoot of some server-mediated connection. For example, we might be in an IRC channel together, or an XMPP/Jabber chat network, and decide we want a peer-to-peer communication (to avoid eavesdropping, or to share giant spreadsheet files, or to play a game without huge lag).

There's a standard called ICE that ties this all together; as long as we have a shared side channel and a list of STUN (and possibly TURN) servers, ICE lets us negotiate a peer-to-peer connection if possible (falling back to TURN if not).

ICE support is part of SIP and a number of other protocols, so if you're using a library for one of those protocols, you probably just need to find out how to give it a list of STUN and TURN servers and that's it. If not, there are probably libraries for doing ICE. If not, there are definitely libraries for doing STUN and TURN (and if there weren't, they're both pretty simple protocols), so you can do the negotiation yourself on top of them.

Of course you don't have to use any of these standards, but you should definitely read what they do and why they do things that way. (Also, keep in mind that people are now making routers specifically to make ICE work better, and that won't be true for anything different that you invent.)


I've linked to Wikipedia articles above because they start off with a pretty simple overview of how the technologies work and the rationale behind them. There are better sources for the detailed reference specifications, sample code to get started, libraries to use, etc., but generally Wikipedia has links to everything else you need.

like image 70
abarnert Avatar answered Oct 23 '22 21:10

abarnert