两个对等节点通过灯塔找到彼此,却建不起直连路径,而第一反应往往是把所有 punchy 选项全部打开。这种反应产出的配置带着三处改动,却不知道哪一处起了作用。这些设置回答的是不同的问题:一个维持已经存在的映射,另一个尝试在相反方向上握手。在碰任何一个之前,先确定你要回答的是哪个问题。

先确认端点,再谈时机

永远不会出现在预期地址上的对等节点,靠 punch 调参救不回来。先看灯塔对这个节点知道什么,以及节点自己的配置对外声明了什么,因为过期的 bootstrap 地址或误发布的私有地址会让任何穿越技巧失效。还要确认证书是被信任的,因为身份不受信任时,会话在 NAT 起作用之前就已经中止了。然后再确认 UDP 到底能不能到达灯塔。时序值是最该最后看的东西,而不是最先看的。

记录你正在测试的端点对,这样后续比较用的是同一对节点,而不是另外两台机器。

punch 保住映射,respond 打开方向

punch 选项会定期向已有隧道的对等节点的底层地址发送空包。它的作用并不光鲜:阻止有状态的 NAT 或防火墙让直连路径所依赖的映射过期。respond 选项改变收到灯塔通知时的行为,让主机尝试朝正在访问它的那个节点反向握手。这是两种失效方式不同的不同机制。

当直连路径能用、但随后衰减时,选 punch。当路径在某个方向上根本形不成时,选 respond。两个都打开然后指望运气,正是五分钟的修复变成一周调参的方式。

延迟决定何时,而不是是否

延迟值挪动的是时机:一个作用于收到通知后发出的包,另一个作用于反向握手尝试。两者都不能让被阻断的方向变得可用。同时改两个,就会毁掉你正要做的比较。

在机制正确之前别动它们。之后只调一个,并把旧值写下来。

当接力节点才是诚实的答案

有些 NAT 组合无论怎么安排时机都支撑不了直连路径。两侧都是对称型 NAT,或者防火墙直接把未经请求的入站流量全部丢弃,那就没有可打洞的余地。

一个地址可达的接力节点能给这些节点一条不依赖穿越成功的路径。它会消耗经过该节点的带宽,所以这是一项关于容量的决定,而不是权宜之计。有意识地配置接力节点,并确认对等节点能够到达它,因为接力节点自己若处在难缠的 NAT 之后,就什么也改变不了。然后就别再为那一对调 punch 设置了,因为直连路径本来就不会出现。

在空闲一段时间后,测试失败的那一对

复现出问题的条件:把这一对节点空闲同样长的时间,或者重放过去会失败的握手序列。然后在这两个节点之间实际跑一次应用,而不是向灯塔发一次请求。灯塔的回应只能证明控制路径,证明不了数据路径的任何事情。

记下改了哪项设置、时间,以及直连路径是出现了,还是流量仍留在接力节点上。如果留下了,答案就是接力节点加一条容量备注,这完全是好结果。如果出现了,下一个问题就是它能不能扛住一小时的空闲,因为映射会衰减,正是 punch 存在的理由。

相关文章