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 ?
-
There are a hundred ways to do this. The computers behind the NAT will have to initiate the connection to commonly-visible internet broker. An easy option for experimentation is zeromq which has several tools useful for routing messages. You could even just setup an asyncio server that forwards requests between the two servers. – tdelaney Oct 08 '14 at 22:50
-
Well but is it possible to just connect the server to the broker, then connect a client to this broker and make a tunnel beetween those two ? Without using the broker again – Mathieu Lepage Oct 08 '14 at 22:55
-
No, all traffic will have to go through the broker. I'm assuming that these machines are behind different NATs and that you don't want to do port-forwarding behind the NAT. If so, the machines are not visible to each other and will need some sort of common agent/broker to communicate. – tdelaney Oct 08 '14 at 23:09
-
Strange. That's how SIP works i thank that will be possible. – Mathieu Lepage Oct 08 '14 at 23:14
-
I'm not an expert on SIP, but the SIP server stays in the conversation if it can't glue the two endpoints together. SIP nodes have protocols to advertise their external addresses but they still depend on NAT port-forwarding (or masquerade). I have no idea whether some NATs can do this automatically when they see SIP traffic, but the step is still there. – tdelaney Oct 08 '14 at 23:31
-
I don't think SIP has any support for port-forwarding. Some SIP clients might do so, presumably using UPnP port mapping, but I think that's outside the SIP protocol. Anyway, in other P2P protocols, I've seen arguments about whether to start with UPnP and only try hole punching if you can't get a public address on one side or the other, or to start with hole punching and treat UPnP as a fallback that's tried if hole punching fails (but maybe before TURN). – abarnert Oct 09 '14 at 18:49
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.

- 354,177
- 51
- 601
- 671
-
1Thank you very much for your answer. I got a last question. I see how to have the public ip of all clients. But how to have the port ? – Mathieu Lepage Oct 09 '14 at 09:00
-
1@MathieuLepage: Good question. Because NATs can remap ports, you have to get your public port via STUN and announce that through whatever side channel or introducer you're using, just like the address. Some protocols (including SIP) try to do tricks with adjacent port ranges, but many NATs will break those, so if you can avoid it, do. Anyway, if you're using ICE, it takes care of this along with the rest of the negotiation. – abarnert Oct 09 '14 at 18:46
-
-
1@MathieuLepage: Not usefully. ICE relies on STUN to find addresses for hole punching, and on TURN for setting up relays when hole punching doesn't work; if it has neither, it can only negotiate between hosts that could have already connected directly in the first place. – abarnert Oct 09 '14 at 21:09
-
So this is the solution. I find ZeroC who works with Python. Have you ever tried it ? Is it a solution ? Thank you again – Mathieu Lepage Oct 09 '14 at 22:30
-
1@MathieuLepage: I think ZeroC's Ice and ICE are completely unrelated. If you're looking for a good library to do ICE for you, you might want to look at [Software Recs](http://softwarerecs.stackexchange.com). I've used libnice via GObject, and I've heard good things about the PJSIP utilities and their Python bindings, but that's about all I can offer. – abarnert Oct 10 '14 at 00:09