对称型 NAT 与 VPN:测映射,而不是看标签
依赖端点的映射影响的是对等发现,而不是普通的客户端到服务器隧道。先测量端口,直接路径失败时再用中继。
一个支持帖以“对称型 NAT,VPN 在这里用不了”的结论收场,随后客户端被毫无理由地重装三次。这个标签有害,因为它回答了一个没人问的问题。向外部固定服务器发起连接的客户端,在发出第一个包的那一刻就建立了映射,而回包几乎被所有网络按其实现的规则放行。依赖端点的映射真正让问题变复杂的场景,是两个对等端同时尝试互相打洞,而那是另一项测试。
一个标签背后藏着两个问题
第一个问题是:这个网络后的主机能否向已知的公网地址和端口发起会话。对隧道客户端来说这是常态,答案几乎总是可以。路由器记录内部源、分配外部端口,并把回包放行,因为它属于一个从内部发起的会话。
第二个问题是:位于不同网络后的两台主机能否在不经过中间服务器的前提下找到共同路径并互相发送。这取决于每个网络如何分配那些外部端口,而映射行为只在这里才有意义。
把两者混为一谈就会得出经典的误诊:某个对等功能失败,有人跑一次 NAT 类型测试,看到最差的那一档,就断言普通隧道也用不了。失败的一直不是隧道。
映射与过滤是两种不同的属性
RFC 的术语比厂商使用的名称更精确,而且它能终结争论,所以值得写进工单。映射指的是外部端口如何分配。过滤指的是哪些入向包被允许通过。一个网络可能在一方面严格、在另一方面宽松,而正是这种组合决定了对等协议能否存活。
| Behaviour | What it means | Effect on a direct peer path |
|---|---|---|
| Endpoint-independent mapping | One external port for every destination | Discovery works if the port is advertised |
| Address-dependent mapping | New port per remote address | Works only if both sides probe the same pair |
| Address-and-port-dependent | New port per remote address and port | Usually needs a relay |
第三行就是大多数消费级路由器和移动运营商口中的“对称”。注意,这个标签只描述分配。具备这种映射和宽松过滤的网络仍然可以接受发往它刚分配的端口的入向包,所以打洞在有些网络上成功,在另一些上失败。
过滤有自己的三种情形,每种都有自己的名字。明明想说的是“依赖地址和端口的过滤”,却在工单里写“对称型 NAT”,会把下一个人送到手册的错误章节。两者在测试中的表现不同,所以这个错误不是学术问题。
去测量映射,而不是给它命名
你可以从自己控制的任意 UDP 服务的服务器侧直接观察被分配的端口。在公网主机上抓包,从客户端发送,然后读出该包到达时的源端口:
sudo tcpdump -ni eth0 -c 10 'udp port 51820'
sudo wg show wg0 endpoints
sudo conntrack -L -p udp --dport 51820
端点那一行是三行中最有用的,因为 WireGuard 会记录从每个对等端收到的最后一个地址和端口。把这个端口与客户端认为自己正在使用的端口做对比。只要发生任何转换,两者就会不同,而关键问题是这个值是否保持稳定。
对第二个目标端口或第二台服务器重复观察,再比较一次。如果外部端口随目标而变化,映射就依赖地址和端口。如果它保持不变,网络只给了你一个映射,发现就有机会。
基于 STUN 的工具一步就能报告同样的分类,当网络允许出站 UDP 到公网 STUN 服务器时可以使用。不允许时,上面的抓包方法仍然有效,除了你已经掌握的主机之外不需要任何东西。
选择与故障相匹配的修复
如果失败的是对等功能,受支持的答案是中继、双方都能到达的会合服务,或者另一种拓扑。TURN 正是为此存在,而自带中继的产品是在告诉你,它预计有些网络会不友好。
如果失败的是普通隧道,映射行为就不是你的问题,上面这些一条都不适用。检查客户端是否真的在发送、服务器是否看到包、回包是否返回。这个顺序就是三次观察,完全不需要知道路由器的 NAT 分类。
不起作用的是到处乱改本地端口,直到有东西连上。改客户端的源端口并不会改变网络分配映射的方式。第五次尝试碰巧成功是假阳性,它会被当作解决方案引用好几个月。
记录架构,而不是结论
有用的记录会写清:用的什么协议、会话朝哪个方向发起、路径上有没有中继、线路上观察到了什么。“对称型 NAT,用不了”是没用的,因为它同时错在隧道上、又对等端路径毫无信息。
如果你测过,就把测得的端口稳定性和过滤行为写进去。这两个事实让别人不必重做整个排查,就能预判新功能在那个网络上能不能用。它们也比那种在每个帮助页上含义都略有不同的厂商分类更耐放。
最后,写这段时把 RFC 4787 的术语放在手边。使用精确的术语只花一句话,却能阻止下一个读者重复同样的误诊,而这正是写这份记录的全部意义。