远程 shell 依然有响应,小探测也都有回应,随后文件复制却爬得很慢并停住,而没有任何人指得出的错误。用更大的负载做 ping 会立刻复现,而同样的传输从本地网络发起却正常。加密本身一点也不慢;只是某些数据包根本没有到达。两种不同的拆分都能造成这个特征,而它们留下的证据并不相同。

一个数据包可能被拆分的两个位置

加密之前,路由器可以拆分过大的内层数据包,并分别保护每一段。加密之后,被拆分的是已经封装完成的外层数据包,远端必须先把它重新组装起来,才能解密任何内容。症状相同,接收端要做的活不同,你能调的旋钮也不同。

这个区别很重要,因为改错一侧只会改变一个数字,而故障位置毫发无损。在接口上被拒绝的数据包根本不会变成外层数据包,所以外层任何设置都救不了它。

尺寸决定究竟在哪里做出

隧道接口携带一个 IP MTU,这个值可能在 IPsec 阶段看到数据包之前很久就把过大的内层包拒掉。在外层头部上设一个位并不会重新审视那个更早的决定,因为包已经被拒了。这就是为什么抬高内层 MTU 有时反而更糟。在改动两者中的任何一个之前,先读接口值和外层行为,因为更省事的那个改动往往才是能解释症状的。

先检查接口值,再看外层行为。两个数字、两个阶段,通常只有其中一个是责任方。

外层预算不是内层预算

每种封装都要在头部上花掉字节,而花多少取决于传输方式、地址族,以及是否启用 NAT 穿越。一个能放进本地链路的内层包,仍可能生成一个放不进后续路径的外层包。要实测这部分开销,而不是假设它,因为每个可选头部都会记到账上。启用 NAT 穿越的隧道比裸隧道付得更多,这点差距足以让昨天还能用的传输断掉。

当外层包带有禁止分片位时,无法转发它的跳点会把它丢掉,并且应当回一条 ICMP 消息。如果这条消息在某个地方被过滤掉,发送方就会不停重试,而用户看到的是没有解释的停滞。正是这种静默丢弃,通常让尺寸问题被报成速度问题。

IPv6 去掉了一个选项

IPv6 路由器不在途中分片,所以外层包要么放得下,要么带着 Packet Too Big 消息消失。这条消息成了唯一的反馈通道,把它挡住就会破坏之后每一次大传输的路径发现。IPv4 路由器在该位清零时可以分片一个大包,这会把故障藏起来,直到别的什么东西把这一位置位。

在双栈环境里,同一条隧道对两个协议族可能表现不同,所以只在 IPv4 上做一次测试并不能了结 IPv6 的抱怨。只演练一个协议族的检查,会让一半答案没有被测量。

TCP 分段策略不是通用答案

限制最大分段长度对 TCP 流有帮助,当问题只限于这些流时它是正当的控制手段。它对 UDP 毫无作用,而 DNS、部分语音协议以及越来越多的网页流量都跑在 UDP 上。

先应用它,再测试当初失败的那个工作负载。如果 UDP 那种情况仍然失败,那么分段策略从来就不是答案,接口预算仍然需要处理。先确定失败负载所用的协议,再挑属于它的那个控制手段。

动手的顺序

先记录还能通过的最大负载,然后是接口 MTU,接着是外层包是否带禁止分片位,最后是 ICMP 反馈能否回到发送方。这个顺序无需猜测就能收窄故障。每条观察都要带上时间和端点,因为路径一变,这四个数字全都会动。

改一个值,重测原来的传输,并把结果留下来。一条能通过大探测、却仍在应用层卡住的隧道,已经转到另一个问题上去了,这是值得写下来的进展。下一个人更想要数字,而不是结论。

相关文章