一个方向能完整跑完、另一个方向却打印出一排排星号的 traceroute 输出,看起来像非对称转发,其实通常不是。每个显示出来的跳点都需要探测包到达、路由器决定回应,以及回应返回并与探测包匹配。这三环里任何一环断了,那一行就变成星号,而隧道仍在照常承载数据。

这条追踪首先是关于诊断的证据,其次才是关于传输的证据。值得问的是三环里哪一环失败了,而答案来自数据包,不来自输出的形状。

星号是缺了回应,不是缺了跳点

traceroute 通过发送 TTL 每轮多走一个路由器的探测包向外推进。丢弃过期探测包的路由器本应返回一条写明自身地址的 ICMP Time Exceeded 消息,工具会把那个名字打印出来。工具真正等待的是这条消息在超时之内到达。如果路由器从另一个源地址回应、限制生成错误的频率,或者干脆无声地丢掉探测包,这一轮就什么也打印不出来。

这些结果没有一条说明前进路径坏了。星号是关于邮件没人回的汇报,不是关于道路空无一物的判断。

用哪种探测方式,决定你撞上哪道过滤

Linux 上的默认方式通常是高位 UDP 目的端口,Windows 的 tracert 发 ICMP 回显,而 Linux 可以改用 ICMP 或 TCP。对状态防火墙或云安全组来说,它们看起来是不同种类的流量。拿一侧的 UDP 追踪和另一侧的 ICMP 追踪相比,比的是两个不同的实验。先把方式、地址族、源地址和超时固定下来,再称结果有差别。

为了公平比较,应从真正承载流量的主机上跑追踪,而不是从替它们转发的网关上跑。路由器自己生成的追踪使用它自己的源地址和出站策略,可能比客户端看到的任何东西都更严格。记下确切的命令,让下一个人能复现。

超时值是实验的另一半,也很容易在记录里漏掉。在拥塞路径上等两秒会报星号,等五秒才会在同一处报出跳点。让各次运行的取值完全一致,比较才仍然是在说网络。

回程是一条自己的路由

ICMP 错误是一个普通的包,有自己的源和目的,它需要一条回到追踪端的路由。如果隧道策略带了 LAN 前缀却没带产生该错误的路由器地址,消息就会从一条到不了你的路径出去。即使路由存在,回包过滤也可能把它丢掉,因为包是从意料之外的接口上、意料之外的地址来的。这是诊断平面上的不匹配,不是应用故障。

一直放宽每个选择器直到追踪跑完,只会让图更漂亮、策略更糟。修好那条具体的诊断地址,或者接受星号,换个方式测试应用。

用一次真实请求确认隧道

在服务自己的端口上做一次 TCP 握手,检验的是真正重要的方向上的转发、回程路由和过滤。下面的命令在两端各跑一次,比较两边各能打开什么。把服务端口和隧道接口名换成你自己的配置。

traceroute -I -n -w 2 203.0.113.40
traceroute -T -p 443 -n -w 2 203.0.113.40
curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' --max-time 5 http://203.0.113.40:8080/health
ss -tan state established

先读两次 traceroute 的结果:如果 ICMP 那次显示跳点而 TCP 那次不显示,差别在方式而不在路径。再把 curl 的结果当作应用层答案,因为一次带着合理状态码完成的交换意味着去程和回程对真实流量都可用。套接字列表显示连接到底有没有建立,这是任何追踪都告诉不了你的。

一条隧道双向都能跑交互式会话,只是某个 traceroute 打印星号,它是健康的,只是不上镜。把两侧的命令输出留作记录,并把缺失的跳点当作关于诊断的开放问题,而不是关于 VPN 的判决。

相关文章