Skip to content(if available)orjump to list(if available)

How NAT Traversal Works (2020)

How NAT Traversal Works (2020)

84 comments

·January 5, 2025

dennis-tra

This is an excellent article!

The tribal knowledge seems to be that you shouldn't do TCP-based hole punching because it's harder than UDP. The author acknowledges this:

> You can do NAT traversal with TCP, but it adds another layer of complexity to an already quite complex problem, and may even require kernel customizations depending on how deep you want to go.

However, I only see marginally added complexity (given the already complex UDP flows). IMO this complexity doesn't justify discarding TCP hole punching altogether. In the article you could replace raw UDP packets to initiate a connection with TCP SYN packets plus support for "simultaneous open" [0].

This is especially true if networks block UDP traffic which is also acknowledged:

> For example, we’ve observed that the UC Berkeley guest Wi-Fi blocks all outbound UDP except for DNS traffic.

My point is that many articles gloss over TCP hole punching with the excuse of being harder than UDP while I would argue that it's almost equally feasible with marginal added complexity.

[0] https://ttcplinux.sourceforge.net/documents/one/tcpstate/tcp...

dfawcus

The existence of stateful firewalls, and the fact that most NAT filters are EDF rather than EIF means that simultaneous open (send) is necessary even for UDP.

Hence the added complexity of doing a simultaneous open via TCP is fairly minor. The main complication is communicating the public mapping, and coordinating the "simultaneous" punch/open. However that is generally needed for UDP anyway...

One possible added complexity with TCP is one has to perform real connect() calls, rather than fake up the TCP SYN packet. That is becase some firewalls pay attention to the sequence numbers.

LegionMammal978

Yeah, I've gotten somewhat annoyed by the name of 'NAT traversal' for these methods. It seems to make some people think that cutting out NAT will lead to a beautiful world of universal P2P connections. But really, these methods are needed for traversing between any two networks behind stateful firewalls, which will pose a barrier to P2P indefinitely.

Also, wouldn't it be easier for stateful firewalls to block simultaneous TCP open (intentionally or not)? With UDP, the sender's firewall must create a connection as soon as it sends off the first packet, even if that packet bounces off the other firewall: the timing doesn't have to be particularly tight. But with TCP, the firewall might plausibly wait until the handshake is complete before allowing incoming packets, and it might only allow the 3-way SYN/SYN-ACK/ACK instead of the simultaneous SYN/SYN/ACK/ACK.

spacechild1

> But really, these methods are needed for traversing between any two networks behind stateful firewalls, which will pose a barrier to P2P indefinitely.

That's true. The actual problem are symmetric NATs where every peer sees a different port number. This makes traditional NAT-traversal impossible and you have to resort to port guessing/scanning. See for example https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&d...

ffsm8

People honestly thought for a while that devices behind a NAT were secured unless ports were specifically routed to them, hence the term "nat hole punching" was coined.

You're probably right that it makes less sense from today's perspective

Uptrenda

I think this is a really good point. As someone who has implemented TCP hole punching myself and now has a very good implementation for it I will say that obviously a major benefit of using TCP is you don't have to subsequently roll a poorman's TCP on-top of UDP once the hole is open. The other issue with TCP hole punching though is it looks very similar to a SYN flood compared to UDP packets. This may mean lower success rates for some networks. Though in practice I haven't seen much filtering so far.

TCP hole punching is very fun. The way I do it is to use multiple NTP readings to compute a "clock skew" -- how far off the system clock is from NTP. Then the initiator sets a future meeting time that is relative to NTP. It honestly gets quite accurate. It even works for TCP hole punching between sockets on the same interface which is crazy if you think about it.

The reason I wanted to support this strange, local-based punching mode is if it works that efficiently to be able to succeed in host-based punching then likely it will be fast enough to work on the LAN and Internet, too. My code is Python and my very first attempt at this was eye opening to say the least. Due to how timing-sensitive TCP hole punching is I was having failures from using Python with old-school self-managed sockets. I was using threading and a poormans event loop (based on my C socket experience)... which is ah... just not the way to do it in Python.

The only way I could get that code to work was to ensure the Python process had a high priority so other processes on the system didn't deprioritize it and introduce lag between the punching attempts. That is how time-critical the code is (with an inefficient implementation.) My current implementation now uses a process pool that each has its own event loop to manage punching. I create a list of tasks that are distributed over time. Each task simply opens a connection that is reused from the same socket. I determined this code was the best approach (in Python anyway) after testing it on every major OS.

You are right about TCP and UDP hole punching difficulty being similar. The main difficulty to both is the NAT prediction step. I haven't written code yet for symmetric NAT bypass but I am starting to see how I'd integrate it (or possibly write a new plugin for it.)

Uptrenda

I did just think of another drawback for TCP vs UDP punching that I think puts a major point in UDP's favour. It may have been touched on others already. But TCP would require the router to record connection state. This is bad because the table for routers is very small and some of these punching techniques are quite aggressive. Like the algorithm that tries to bypass symmetric NATs. If you're opening hundreds of TCP connections its possible you might even DoS the router. For UDP its plausible optimizations for state management would make it less likely that your punching would render the whole router inoperable. This is only speculation though.

duskwuff

> If you're opening hundreds of TCP connections its possible you might even DoS the router.

This was sometimes an issue for underpowered home/SOHO routers in the mid-2000s, but most modern routers have enough memory to support decently sized connection-tracking tables.

In any case, both TCP and UDP require connection tracking; there's no inherent advantage to UDP.

ocdnix

Fascinatingly effective, but maybe I'm the only one getting the heebie-jeebies when someone suggests implementing this in production corp networks. Sure it's super convenient, but the thought of bypassing all traditional NATs and firewalls, and instead relying solely on a software ACL, seems super risky. Maybe I just don't understand how it works, but it seems that a bad actor getting access to a stray VM with Tailscale on it in, say, your AWS testing env, essentially has an clear path all the way into your laptop on the internal corp network, through the kernel, into user space and into the Tailscale ACL code as the sole arbiter of granting or blocking access. Would I even know someone unauthorized made it that far?

dfawcus

That is why many of us keep repeating that NAT is not a security mechanism.

Punching through NAT, and most associated state tracking filters, is very easy.

I've implemented such in a production corp environment, as a product to be sold. There is no magic here, it is all well understood technology by the practitioners.

If you actually want to have packet filtering (a firewall) then deploy a firewall instance distinct from any NAT, and with appropriate rules. However that only really helps for traffic volume reduction, the actual security gain from a f/w per se is now minimal, as most attacks are over the top: HTTP/HTTPS, POP/IMAP etc.

cyberax

> That is why many of us keep repeating that NAT is not a security mechanism.

You can say that in general, network firewalls are not a security mechanism. They are at most a means to prevent brute-force attacks from outside of the network.

dijit

to be completely fair with you, everyone misinterprets NAT as a security mechanism, because traditionally it is deployed alongside a stateful firewall.

In reality, of course, the stateful firewall is doing all of the heavy lifting that NAT is getting the credit for. Tailscale does not get rid of the firewall in fact it has a much more comprehensive setup based on proper ACLs.

Though I’m definitely the first to admit that their tooling around ACL’s could be significantly improved

tptacek

I think they mostly interpret NAT as a security mechanism because that's what it originally was; "NAT" was a species of firewall, alongside "stateful" and "application layer". And NAT obviously does serve a security purpose; just not the inside->out access control function we're talking about here.

avianlyric

> think they mostly interpret NAT as a security mechanism because that's what it originally was; "NAT" was a species of firewall

That’s simply wrong. NAT is, and always has been for the sole purpose of Network Address Translation, I.e. allowing a large IP address space to hide behind a much smaller IP address space (usually a single IP address), for the purpose of mitigating IP address exhaustion.

NATs were meant to be a stop gap solution between IPv4 running out, and the rollout of IPv6. But we all know how that panned out.

The “firewall” like aspects of a NAT are purely incidental. The only reason why a NAT “blocks” unsolicited inbound traffic is because it literally has no idea where to send that traffic, and /dev/null is the only sensible place to direct what’s effectively noise from the NATs perspective.

The fact that NATs shares many of basic building blocks as a very simple stateful firewall is just a consequence of both NATs and firewalls being nothing more than stateful packet routing devices. The same way any standard network switch is (they internally keep a mapping of IP to MAC address of connected devices based of ARP packets, which incidentally blocks certain types of address spoofing, but nobody calls a network switch a firewall).

dijit

I mean, it really isn’t a security mechanism of any kind. Any security properties at all are completely accidental.

One need only disable stateful firewalling and use that to see how completely dire the situation would be. As all outbound connections open up your host to the internet.

Hilift

> production corp networks.

Networking has long been the toxic wasteland of security and misconfiguration. Now combine that with newer host-based networking models for containers. The Windows network stack is substantially different now due to that, and more complex. Since Wireguard has been part of Linux, everyone and their brother now has a VPN, somewhere connecting to a VPS. It's probably worse than you think because you don't know what you don't know.

rixed

This is to go through NAT, which are devices designed to work around the rarefaction of IPv4 addresses.

Firewalling is a different concept, but since you raise that issue of connectivity wrt. security, I have to say that what makes /me/ sad and anxious is to see how internet security has always been hinging on bloquing paquets based on the destination port.

Doing what's easy rather than what's correct, exemplified and labelled "professional solutions"...

navigate8310

I'm rather more curious as to why you stylized "bloquing paquets"?

antod

They are emphasizing the queuing implementation.

aborsy

Maybe the OP is French? :)

Muromec

That’s how all voip worked since forever and we also have a bunch of standard and public facing infrastructure to make it easier. All the ice, turn and friends.

It still needs something on the inside to talk to outside first, so the actual firewall should whitelist both outbound and inbound connections.

Than again, if you rely on perimeter, it’s a matter of time when someone figures out what’s your equivalent of high wiz jacket is.

totallywrong

It's no different from traditional VPNs. The tailnet admin has control over the routes that are exposed to clients and ACLs are available to further limit access. It's an overlay network, it doesn't magically give you access to user space on people's laptops.

ray_v

Given how tailscale works and many of the features (the SSH features especially) it's not terribly hard to imagine a critical flaw or misconfigured setup giving access to userspace

avianlyric

Everything beyond tailscales core VPN features are opt-in. The risk of misconfiguring Tailscale is the same (arguably it’s much smaller) as just misconfiguring SSH on a machine.

At the end of the day, Tailscale works just like any other VPN, from the perspective of the type of data that can traverse between machines connected to the same virtual network. Tailscales use of a P2P wireguard mesh is just an implementation detail, it’s no more or less risky that having every machine connect to a central VPN gateway, and bouncing all their traffic off that. Either way, all the machines get access to a virtual network shared by every other machine, and misconfigured ACLs could result in stuff getting exposed between those machines, which shouldn’t be exposed.

If anything the Tailscale mesh model is much more secure, because it forces every device to use a true zero trust model. Rather than the outdated, “oh it managed to connect to the VPN, it must be safe then” model that traditional VPNs often end up implementing.

rainsford

I'm not sure how to compare the risk and attack surface of traditional NATs and firewalls vs Tailscale's ACL code, but I'm not sure Tailscale is obviously the riskier choice there. I think more traditional network devices are more familiar and more of a known quantity, but there's a lot of janky, unpatched, legacy network devices out there without any of the security protections of modern operating systems and code.

It's also worth considering that exploitability of ACL code is just one factor in comparing the risk and Tailscale or similar solutions allow security conscious setups that are not possible (or at least much more difficult) otherwise. For example, the NAT and firewall traversal means you don't have to open any ports anywhere to offer a service within your Tailscale network. Done correctly, this means very little attack surface for a bad actor to gain access to that stray VM in the first place. You can also implement fairly complex ACL behavior that's effectively done on each endpoint without having to trust your network infrastructure at all, behavior that stays the same even if your laptop or other devices roam from network to network.

Not to say I believe Tailsclae is bulletproof or anything, but it does offer some interesting tradeoffs and it's not immediately obvious to me the risk is worse than legacy networks (arguably better), and you gain a lot of interesting features and convenience.

miki123211

And for whatever it's worth, Tailscale is written in a language that makes buffer overflow and memory corruption vulnerabilities extremely unlikely.

bradleyjg

You don’t want to be hard on the outside, soft on the inside. Especially because you probably aren’t that hard on the outside!

Defense in depth.

snthpy

A bit OT:

A read a bit about this space a few weeks ago after not knowing anything about it beforehand. My impression is that ip6 dices all of this and NAT traversal isn't necessary anymore. So why isn't ip6 more popular and how do I get started with it for my home network and tailscale VPN?

randunel

I wish there was a tailscale-like equivalent without connectivity encryption, for devices which encrypt at the application layer (like almost the entire internet does). We don't always need the lower layers to be encrypted, this is especially computationally expensive for low power devices (think IoT stuff running a tailscale like tunnel).

GRE tunnels exist and I actually use them extensively, but UDP hole punching is not handled so hub-and-spoke architecture is needed for them, no peer to peer meshes with GRE (ip fou).

Are there equivalent libraries out there which do UDP hole punching and unencrypted GRE tunnels following an encrypted handshake to confirm identity?

jamilbk

Yes, the established standard here is known collectively as Interactive Connectivity Establishment (ICE) [1] which WebRTC relies on -- there are a few good libraries out there that implement it and/or various elements of it [2] [3].

libp2p [4] may be what you're after if you want something geared more towards general purpose connectivity.

[1] https://datatracker.ietf.org/doc/html/rfc8445

[2] https://github.com/pion/webrtc

[3] https://github.com/algesten/str0m

[4] https://libp2p.io

randunel

Thank you for the resources! I will study them.

FWIW, libp2p also enforces transport encryption, quote:

> Encryption is an important part of communicating on the libp2p network. Every connection must be encrypted to help ensure security for everyone. As such, Connection Encryption (Crypto) is a required component of libp2p.

Muromec

Turn, stun, ice is what does hole punching for voip, so you can reuse libraries from voip for that

wmf

You could try to bring back Teredo.

Uptrenda

It's not UDP but I do TCP hole punching here: https://github.com/robertsdotpm/p2pd and every other major method of NAT traversal.

It's written in Python. Though its not based on using the default interface like most networking code. I wanted the possibility to be able to run services across whatever interfaces you like. Allowing for much more diverse and useful things to be built. Its mostly based on standard library modules. I hate C extension crap as it always breaks packages cross-platform.

unit149

[dead]

zerox7felf

> So, to traverse these multiple stateful firewalls, we need to share some information to get underway: the peers have to know in advance the ip:port their counterpart is using. > [...] To move beyond that, we built a coordination server to keep the ip:port information synchronized

This is where I wish SIP lived up to its name (Session Initiation Protocol, i.e. any session, such as a VPN one...) and wasn't such a complicated mess making it not worth the hassle. I mean it was made to be the communication side-channel used for establishing p2p rtp streams.

Muromec

Yeah, sip is doing so many things that its scary to load all them in your head at the same time.

Its like http, but its also statefull, bidirectional, federated and works over udp too.

Just looking at the amount of stuff (tls over udp included) baresip implements to barely sip. And it isnt even bloated, the stuff has to be there.

rixed

> Its like http

and for the same reason: both were initialy designed to be simple...

dfawcus

(2020)

Previous discussion:

  (2022) https://news.ycombinator.com/item?id=30707711
  (2020) https://news.ycombinator.com/item?id=24241105

dang

Thanks! Macroexpanded:

How NAT traversal works (2020) - https://news.ycombinator.com/item?id=36969018 - Aug 2023 (106 comments)

How NAT traversal works (2020) - https://news.ycombinator.com/item?id=30707711 - March 2022 (37 comments)

How NAT Traversal Works - https://news.ycombinator.com/item?id=24241105 - Aug 2020 (28 comments)

(p.s. your links weren't clickable because lines that are indented with 2 or more spaces get formatted as code - see https://news.ycombinator.com/formatdoc)

apitman

This is the article I sent people to for NAT traversal

This may be the only way we ever have to build p2p apps. IPv6 doesn't have enough steam since NAT and SNI routing solve most problems for most people.

And ISPs are very much not incentivized for that to change.

vinay_ys

Interesting blast from the past. We built an oblivious p2p mesh network that did this in 2010. Back then, nobody cared about security as much as we thought they should. Since then, nobody still cares about security as much as they should. Devices have increased and their value has increased, and still, they are quite insecure. Truly secure endpoints with hardware root-of-trust and secure chains of trust for authn/authz and minimal temporary privileges is still hard, and network perimeter security theater is still ongoing in home networks, corp networks and even large production datacenter networks. Only reason we don't find these to be the primary root-cause for security breaches is because more easier attack chains are still easily available!

yyyfb

The fact that this emerged instead of IPv6 is a true testament to the power of "good enough hackery"

null

[deleted]

binary132

Really clear and clean exposition on what can be a hairy and badly-discussed subject, thanks for posting!

michidk

Such a great explanation. Wish I would have had something like this back in my gamedev days.