
How NAT Traversal Works - signa11
https://tailscale.com/blog/how-nat-traversal-works/
======
zajio1am
The article (like many others) distinguish between easy NAT (endpoint-
independent) and hard NAT (endpoint-dependent symmetric NAT), but does not
discuss how real-world NAT devices behave (whether they are predominantly easy
NATs or hard NATs).

In reality, home routers are generally Linux-based and share netfilter NAT
implementation. And how this implementation behaves? Well, it is a symmetric
NAT masquerading as a port-restricted cone NAT. While it is symmetric NAT, it
tries to reuse internal port as external port, and therefore often ends with
the same port on different NAT mappings to different external machines.

Therefore, NAT traversal often works by accident in simple cases, but suddenly
stops to work when it becames more complicated (e.g. when two clients behind a
NAT try to connect to the same server).

Linux netfilter has an option to randomize ports in NAT mappings to avoid this
appearance of working, but it is unlikely that home routers allow to configure
this.

~~~
dave_universetf
The article makes the distinction, because it's a useful empirical distinction
to draw :). While you're correct that linux is, strictly-speaking, an
endpoint-dependent implementation, it goes to sufficient effort to reuse
mappings in an endpoint-independent way that you can do useful things against
it by treating it as an EIM NAT.

Similarly, commercial systems like JunOS try to stick to EIM behavior for as
long as they can, and only shift to EDM behavior when load makes it infeasible
to do otherwise. There again, strictly speaking that means you should abandon
all hope... But in practice, NAT traversal still works much more often than
not, and we can use that to build useful systems while we wait for IPv6
adoption to continue.

------
RabbitmqGuy
This is the kind of content that makes me visit hn, long & insightful

------
iforgotpassword
I got lost at the point where an easy Nat connects to a hard one. Ok, the hard
one can reach the easy one with the info obtained from the stun server, but
why does the easy one have to do port scanning on the hard one? if the easy
one receives the packet from the hard one, just send a packet back to the
source IP:port? the hard one should have created an entry in its Nat map and
firewall at that point since it just sent data to the easy one. What am I
missing?

~~~
fred256
It's not the NAT part on the easy side, it's the firewall on the easy side
that needs to be open.

Even if the easy side knows (through STUN) that its public address is
2.2.2.2:1234, that doesn't mean it'll accept any packets addressed to
2.2.2.2:1234.

For it to accept packets from e.g. 5.5.5.5:7890, it first needs to send a
packet there, to create the connection table entry in the firewall.

But... it doesn't know what port 5.5.5.5 is going to be sending _from_ (since
that's the whole point of 5.5.5.5 having a "hard", i.e., destination-
dependent-mapping, NAT), so it can't preemptively send a packet to open the
firewall.

At least, this is my understanding.

~~~
dave_universetf
Author here. You are correct, the challenge in an easy/hard pair is the
stateful firewall built into the easy side. Even thought the _NAT_ is easy (we
can discover our public ip:port), the firewall still wants to see
transmissions to/from the correct ip:port on the hard side in order to let the
traffic through. The hard side defeats that by not letting us discover what
the correct ip:port should be, so we can't punch through the firewall
component.

So, we're left with two options: make one of the NATs vanish via the port-
mapping protocols, or use the birthday paradox to find a usable ip:port in
reasonable time.

~~~
iforgotpassword
Thanks to both of you. So it would only work the way I assumed if the easy one
is that full cone type that doesn't have a firewall at all.

~~~
dave_universetf
Yup, exactly right. In the article, I tried to call those "trivial" NATs,
because from a NAT traversal POV they might as well not exist. You do need to
do a tiny bit of work to get them up and running, but once that's done, you
effectively have an open port on the internet that anyone can connect to.

------
dmarinus
This is a really nice explanation! I was also considering the birthday paradox
to guess through a hard nas. It seems that the cgnat I'd like to traverse uses
certain port ranges which could ease the guessing (I've read about nat
traversal helpers to predict the ip address and ports). It would also be nice
to do some research on NAT ALGs, it would be cool to fake a FTP connection to
open an incoming port on the NAT gateway!

------
devxpy
Natural follow up question - does NAT traversal actually work?

I've tried to replicate the NAT traversal paper[1] a couple of times in
python, but it never really got it to work.

Maybe my ISP's network topology just inlcudes multiple NATs which is messing
things up.

[1] [https://bford.info/pub/net/p2pnat/](https://bford.info/pub/net/p2pnat/)

~~~
dave_universetf
author here. NAT traversal definitely still works, but it's very situational.
I'd estimate the simple techniques get you successful p2p in 90% of cases, but
those 90% are unevenly distributed: for a particular pair of endpoints, the
success rate is either 100% or 0%, and it's little consolation to the 0% folks
to know that they're the exception :)

The paper you linked seems to have reasonably modern techniques for UDP
traversal, so it should work. One thing that paper doesn't make clear, IMO, is
that you need to STUN on both ends to discover your WAN ip:port, and you need
to run the STUN traffic from the same socket as the one you're using for the
NAT traversal.

If you have Tailscale, you can run `tailscale netcheck` to characterize your
network a little bit, which might give you some answers as to why the
traversal doesn't work. Successful traversal depends on the properties of both
endpoints, so you'd want to netcheck from both ends. The usual culprit is
MappingVariesByDestIP=true, which indicates a hard NAT.

~~~
devxpy
Thanks for the insider info.

I didn't run a STUN server, but I exchanged the WAN ip:port via a server and
some simple TCP messages.

------
thunkpad
Has anybody here ever used something like XMPP as a sidechannel? Sounds
interesting and I kind of want to try setting up something really silly just
for fun.

~~~
dave_universetf
As I said in the article, many years ago I played with using XMPP as the side
channel for NAT traversal software. XMPP was handy for this because you can
just define your own extension that chat programs will ignore, but XMPP itself
will still carry those extensions end to end. The idea I had at the time was
to have the p2p software signed into your XMPP account, and it would use that
as a federated message bus between all your devices, and the devices of your
friends.

Honestly, the idea only made sense back when Google Talk ruled the earth,
because it gave XMPP a network effect I could utilize. Sadly, since then, chat
has fallen into a dozen different silos, so the "piggybacking" benefits are
zero. And if you're not piggybacking on an established network, you can build
a side channel protocol that's much simpler than what you'd make with XMPP.

~~~
thunkpad
Thanks for the explanation! XMPP may not be the flavor du jour but it's got
the old gears turning in new ways...

~~~
dave_universetf
If I were trying this again today, I'd probably poke at what Matrix can do in
terms of non-user-visible messages. It seems to be a successor to XMPP in
terms of the communication model, so if it can do broadcast+unicast of non-
visible messages, and supports sending to yourself as well as others, it might
be a good modern replacement!

~~~
thunkpad
I'm actually just now gearing up to homelab a Matrix set up! I'll keep this in
mind.

------
api
So HN rediscovers NAT traversal yet again...

~~~
dang
Please don't post unsubstantive comments, and especially not supercilious
ones, which are poisonous in addition to noise.

If you know more than the rest of the community, it would be great to share
some of what you know so we all can learn. If you don't want to do that,
that's fine, but then please don't post.

