我的问题是由 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 连接成功。
假设 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 地址。
1
swananan 10 小时 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 。 |
2
swananan 10 小时 4 分钟前
另外关于存在成功率的问题,我觉得原因可能是很多 NAT 实现本质上是有状态的,会随着负载的情况,NAT 行为甚至有可能改变(取决于 NAT 实现)。另外,在绝大部分场景,对于开发人员来说,它还是一个完全黑盒。所以,有成功率波动我觉得也挺正常的。
|
3
lovemaostar 9 小时 53 分钟前
你可以尝试映射任意一边的 udp 41641
|
4
wheat0r 9 小时 30 分钟前
最后的实践令我疑惑。原则上来说两个节点都有 ipv6 ,就不用考虑 NAT 的问题,只需要解决防火墙的问题就可以。但是 DERP 本身可能需要 ipv6 地址,并且 ipv6 上有正确的监听。
|
6
Ploter 9 小时 18 分钟前
确实有遇到明明双端都有 IPv6 却无法直连的情况,不过一端重新连接 WIFI 就解决了,我以为是校园网的问题
|
7
mcluyu 8 小时 59 分钟前
家里是移动宽带有 v6 ,v6 只开放给了路由器没有继续向下分配,ts 装在路由器上,v4 是大内网, 公司只有 v4 公网但是经过 NAT 到我的设备。
实际就是: 偶尔能打洞成功, 且每次升级后成功率会很低。 全靠 upnp 但是只要我使用手机热点,两边都有移动 V6 时, 几乎百分百成功,延迟速度都很好。 |
9
Judoon 8 小时 49 分钟前
基于近期观察,好像从某个版本之后,长时间没有流量或者请求就会断掉连接。
具体现象是 tailscale status 看到的状态是 relay 或者直接没有,而 ping 一下之后(不管是 tailscale ping 还是直接 ping ),看状态就是 direct 了 题主的问题应该是理论上 A 直接去连 B 就能打通,为什么需要 A 开很多端口让 B 来尝试吧? |
10
Actrace 8 小时 32 分钟前
tailscale 现在是靠定时提交接口 IP 信息来维持联系,如果提交不上去,或者这个过程出现了什么问题,那么可能会导致一些问题。
|
12
Kinnikuman OP @swananan 我在附言中 append 了一条我的疑惑,原来写的可能表达不是很清楚。请再帮看下。
|
13
Chalice 2 小时 5 分钟前
easy nat 不等于 Nat2 ( fullcone nat ),easy nat 本质上是 Endpoint-Independent NAT mapping ,这里面包含了 Nat1 - Nat3 ( Port-Restricted Cone NAT ),而 Nat 3 还会校验 ip:port 的一致性( Nat2 只校验 IP )。
|