关于 tailscale NAT 打洞问题的理解

Kinnikuman · 8 小时前 · 1137 次点击

我的问题是由 tailscale nat traversal 这篇文章引起的,文章比较长。

问题是在 The benefits of birthdays 这一章节:

Rather than open 1 port on the hard side and have the easy side try 65,535 possibilities, let’s open, say, 256 ports on the hard side (by having 256 sockets sending to the easy side’s ip:port), and have the easy side probe target ports at random.

基础知识

两台机器在不同的地方使用 tailscale 组网,tailscale 是基于 WireGuard 的。

机器 A 的内网 ip 192.168.1.2, 公网 ip 2.2.2.2

机器 B 的内网 ip 192.168.2.2, 公网 ip 3.3.3.3

假设 A 是 Hard NAT(Symmetric NAT ), 而 B 是 Easy NAT ( Fullcone NAT )。

对于 Hard NAT 和 Fullcone NAT 的一点解释:

A 的某个程序使用 192.168.1.2:1234 向 4.4.4.4:5678 发出一个请求, 那么在 NAT 映射后,是 2.2.2.2:4321 发往 4.4.4.4:5678 ,这样 4.4.4.4:5678 返回的请求,到达 NAT 也会被正常映射到设备 A 的 1234 端口。这是由 iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 这种防火墙规则决定的。 但是对于 Hard NAT 的 A 来讲,如果是 5.5.5.5:5678 向 A 的 2.2.2.2:4321 发起一条请求,NAT 会检查源地址不是 4.4.4.4:5678 ,所以会 drop 掉这一条请求。

但对于 B 来讲,192.168.2.2:1234->3.3.3.3:4321 -> 4.4.4.4:5678, 建立了一个这样的 NAT 表,当其他的设备比如 5.5.5.5:9876 主动请求 3.3.3.3:4321 端口的时候,也会被 NAT 转发到 192.168.2.2:1234 ,这就是 Fullcone NAT ,表现为很容易 p2p 连接成功。

回到 The benefits of birthdays

假设 B 机器想要连接 A 机器,由于 A 是 Hard NAT ,无法直接选择一个合适的端口进行连接,但反过来是可以的,因为每一台运行 tailscale 程序的设备来讲,都会和 DERP 服务器进行着连接。

不管是谁连接谁,都先走一下 DERP 中继,这样起码就能获取到每一方的外部端口。理想情况下,两端都是 Easy NAT ,那它们使用连接 DERP 服务器的端口进行直连,基本上就可以 p2p 连接成功。

但 A 是 Hard NAT ,于是(下面是我的猜测) DERP 服务器将 B 的 ip 和端口告诉了 A ,虽然是 B 想主动连接 A ,但 tailscale 程序的实现是两端都进行尝试连接,这样 A 直连成功了 B ,192.168.1.2:1234->2.2.2.2:5678->3.3.3.3:8765->192.168.2.2:4321 ,这样它俩就建立了正常的直连。

文章中的举例是需要让 A 开 256 个端口连接 B ,B 通过遍历端口进行和 A 连接,当尝试了 1024 次时候,能和 A 成功连接的概率是 98% (生日悖论)。

我的疑问是,为什么需要 A 开很多端口进行尝试探测?我的猜测那种方案有什么不能实现的地方吗?

真实的实践

我尝试过很多次,对于复杂的网络环境,tailscale 有时候能打洞成功,有时候会失败。而且两端都是 Fullcone NAT, 并且双方都有 ipv6 地址。

tailscale ping

举报· 1137 次点击
登录 注册 站外分享
10 条回复  
swananan 初学 7 小时前
你是在问为什么 tailscale 可以打通 Hard NAT 吗? Hard NAT 蛋疼的地方在于,它是 Address and Port-Dependent Mapping 和 Address and Port-Dependent Filtering ,Address and Port-Dependent Mapping 意味着,A 和 DERP 中继构建的 mapping ,不会复用给 A 和 B 之间。导致 B 想和 A 打通,只能猜测 A 和 B mapping 中,新映射的 port 是多少。 所以 如果 A 能够开很多端口去尝试(即使用很多 socket ,确保有很多 local ip 和 local port 组合),B 就很有希望能连上 A 。
swananan 初学 7 小时前
另外关于存在成功率的问题,我觉得原因可能是很多 NAT 实现本质上是有状态的,会随着负载的情况,NAT 行为甚至有可能改变(取决于 NAT 实现)。另外,在绝大部分场景,对于开发人员来说,它还是一个完全黑盒。所以,有成功率波动我觉得也挺正常的。
lovemaostar 初学 7 小时前
你可以尝试映射任意一边的 udp 41641
wheat0r 小成 7 小时前
最后的实践令我疑惑。原则上来说两个节点都有 ipv6 ,就不用考虑 NAT 的问题,只需要解决防火墙的问题就可以。但是 DERP 本身可能需要 ipv6 地址,并且 ipv6 上有正确的监听。
FaiChou 初学 7 小时前
@wheat0r v6 确实没 nat ,但有时候 ts 就是不能用 v6 直连,即使 v6 可用也不行。现实网络环境太复杂了。
Ploter 小成 7 小时前
确实有遇到明明双端都有 IPv6 却无法直连的情况,不过一端重新连接 WIFI 就解决了,我以为是校园网的问题
mcluyu 小成 6 小时前
家里是移动宽带有 v6 ,v6 只开放给了路由器没有继续向下分配,ts 装在路由器上,v4 是大内网, 公司只有 v4 公网但是经过 NAT 到我的设备。 实际就是: 偶尔能打洞成功, 且每次升级后成功率会很低。 全靠 upnp 但是只要我使用手机热点,两边都有移动 V6 时, 几乎百分百成功,延迟速度都很好。
badgv 初学 6 小时前
@wheat0r derp 不需要有 v6 ,如果节点都有 v6 ,直接就会走 v6 直连,每个节点会提交自己这边的接口 ip 信息
Judoon 小成 6 小时前
基于近期观察,好像从某个版本之后,长时间没有流量或者请求就会断掉连接。 具体现象是 tailscale status 看到的状态是 relay 或者直接没有,而 ping 一下之后(不管是 tailscale ping 还是直接 ping ),看状态就是 direct 了 题主的问题应该是理论上 A 直接去连 B 就能打通,为什么需要 A 开很多端口让 B 来尝试吧?
12下一页
返回顶部