一次 ping 超时:网络包在哪一跳被拦下

从一次超时开始
你在电脑上 ping 一台服务器。回车按下去,屏幕显示 Request timeout。
先别急着问是谁把它丢了。要先确定:它有没有选对出口、到中转机后被当成本地包还是转发包、该做的地址改写是否发生,以及隧道的数据引擎是否仍在工作。
下面按包实际经过的顺序看。每一段都给出对象、判断条件,以及出错时包会怎样偏离原来的路径。
先按路径排查
图里的每个位置都是一个独立的判断点;前一段正常,不能替后一段背书。
1. 路由表:先确认出口

电脑的路由表记录了目的地址段、出口网卡和下一跳。发包前,内核先据此决定包从哪里出去、交给谁。
开机连上 Wi-Fi,系统自动加了一条规则:去任何地址(0.0.0.0/0),都走 Wi-Fi 网卡,交给你家路由器。
你装了 VPN 软件之后,隧道软件又加了一条规则:去 100.x.x.x 的流量,走虚拟网卡。
现在表里有两行路由。对常规的目的地址查表,先看最长前缀:去 100.x.x.x 的更具体路由通常会优先于 0.0.0.0/0。若前缀相同,还要比较 metric/preference;若机器使用策略路由,则还必须看 ip rule 的 priority 和 selector。
因此,不能仅凭某个程序先添加了路由,就判断 100.x.x.x 会走 Wi-Fi。只有实际查表选中了 Wi-Fi,才会出现隧道流量从错误出口发出的情况。
这时不是"路由坏了",而是实际选路与预期出口不同。
判断点是 100.x.x.x 实际命中的路由和出口:若命中 Wi-Fi,问题在出口选择,不在隧道对端。
不需要猜规则谁先写入;检查要按包的 selector 和证据层级进行。
先看静态配置:
ip rule show
ip route show table all
ip rule show 回答是否有按 priority 和 selector 选择其他表的策略规则;ip route show table all 用来核对各表中的前缀、metric/preference 和出口。
再按正在排查的包做路由查询:
ip route get <目标地址>
ip route get <目标地址> from <源地址> iif <入接口> mark <fwmark>
裸的 ip route get <目标地址> 只验证本机输出包会选哪条路由和出口。A 上的转发包则应按已知事实补上 from <源地址>、iif <入接口>,若 mark 已知也补上 mark <fwmark>;这些 selector 可能让策略路由得到不同结果。
最后核对 POSTROUTING 的规则位置和计数:
iptables -t mangle -nvxL POSTROUTING --line-numbers
iptables -t nat -nvxL POSTROUTING --line-numbers
这两项显示规则在链中的位置和精确 packet/byte counter,用来对照 mark 的改写条件、NAT 的匹配条件与顺序。nat POSTROUTING 的 counter 只能解释为新连接首包的规则证据,不能逐包证明既有连接重新遍历了 NAT。若 selector、规则与计数仍不足以关联这一条流量,抓包或在环境中明确启用的 netfilter trace 才是补强层。
2. INPUT 与 FORWARD:本地处理还是继续转发
包到达服务器网卡后,内核首先判断目的地址是否属于本机:
这个包的最终地址,是不是我自己的 IP?
- 是 → 交给我自己处理 → INPUT(到站车)
- 不是 → 我帮它转给别人 → FORWARD(过路车)
如果目的地是服务器 A,当前机器就是 A → INPUT,它自己处理。 如果目的地是服务器 B,当前机器是 A → FORWARD,它要把这包转出去。
两条路径应用的规则不同。目的地为本机时走 INPUT;目的地为另一台机器时必须走 FORWARD,并受转发规则约束。
因此,A 能响应自己的服务,并不能说明它允许把去 B 的包转出去。这里应确认包是否进入 FORWARD 链,以及该链是否放行。
3. NAT:回包能否找到来路

你的电脑有一个"怪地址"(比如隧道分配的 100.75.237.122),你用它 ping 一台内网服务器(服务器 B)。包不直接去 B——它先被你的隧道软件送到了中转服务器(服务器 A),由 A 再转发给 B。
A 收到包后,若目的地是 B 而非自己、已启用转发,且没有被更早的规则丢弃,才会进入 FORWARD 并继续转发。
包从 A 的物理网卡发出,内容是这样的:
从谁来的:你的隧道地址
要去哪: 服务器 B 的物理地址
B 看到源地址 100.75.237.122。这个地址不在它可直接回达的局域网内,回包路径便成为问题。
B 只能把回包发给默认网关——路由器。路由器也不认识 100.75.237.122。丢掉了。
正常情况下,A 要在转发出物理网卡前改写源地址。
这一操作是 SNAT:改写源地址。
A 应该这样做:把包从虚拟网卡拿过来,把寄件人从"100.75.237.122"改成"172.16.0.1"(A 自己),再发出去。B 看到是从 A 来的,认识,回包。A 收到之后走隧道回传给你。
判断结果是:若 SNAT 生效,B 的回包先回 A,再由 A 通过隧道回传;若未生效,B 与默认网关都可能不知道如何到达 100.75.237.122。
4. 标记冲突:SNAT 的前提被另一条规则擦掉
即使包已走到 A 的转发链,SNAT 仍可能没有发生;这里需要检查 packet mark 是否是 NAT 规则的匹配条件。
问题出在 Linux 内核里的一个机制:打标记(packet mark)。
标记是在包上写入一个数字,供后续规则判断。
隧道软件的流程是:
- 包从虚拟网卡进来,在 FORWARD 链上给它打个标记(比如 0x40000)
- 包走到最后一步(POSTROUTING,发出去之前的处理点),检查这个标记
- 有标记 → 做改寄件人
但中转机器上还跑着另一个网络策略软件。它也在用同一套标记系统。
它在 mangle 表的 POSTROUTING 里执行了一行命令:清掉某几位标记。
只有当策略软件的清除掩码覆盖 0x40000,且 NAT 规则按相同的 mark/mask 匹配时,才会发生这里所说的冲突。
满足这些条件时,可能出现如下顺序:
包从虚拟网卡进来
→ FORWARD 链 → 隧道软件打上标记(done)
→ mangle POSTROUTING → 策略软件清掉那几位标记(gone)
→ nat POSTROUTING → 隧道软件查标记 → 没了 → 不改寄件人(broken)
若规则顺序确为上述顺序,计数可作为规则被使用的辅助证据;再结合 selector 才能判断 FORWARD 中写入的标记是否在 mangle POSTROUTING 被清除。若这一条件成立,进入 nat POSTROUTING 时,NAT 的匹配条件就不成立,包会保持原源地址出网,回到上一节的回包失败。
这类故障可能来自两个程序共享同一标记位但没有协调。验证依据是:哪条规则写入标记、哪条规则清除标记、它们所在的表和顺序;计数只能作为辅助证据,nat 表的计数还要按新连接首包理解。
5. 控制面与数据面:在线状态不等于业务可达

你装完隧道软件,用它的状态命令看——所有机器都在线。用它的内部 ping 测——也能测到延迟。
但系统的 ping 不通,ssh 也连不上。
这并不矛盾,因为隧道软件的控制面和数据面承担不同工作。
控制面:负责公布"谁在线"、“延迟多少”、“路径是什么”。它使用自己的通信协议,因此状态与内部 ping 正常,只能说明控制面仍可通信。
数据面:负责加密、发送和解密 SSH、HTTP 等实际流量,依赖底层加密引擎。引擎停止,数据面就停止。
你之前跑了个停止命令——引擎停了。又跑了个启动命令——没有管理员权限,引擎没拉起来。
所以控制面还通着(你能看见状态),但数据面已经断了(你的 SSH 到不了)。
这里的正常/异常边界是:状态和内部 ping 正常,但系统 ping、ssh 不通时,不能据此排除数据引擎、路由、转发或 NAT。
把故障串回包路径
一次 ping 的路径可以这样拆开:
包从应用层发出来 → 查路由表决定走哪个口 → 防火墙决定放不放行 → NAT 决定要不要改寄件人 → 出网线 → 到对方网卡 → 对方路由表决定交给本地程序还是转发 → 对方防火墙 → 对方 NAT……
每一层都可能卡住。
本例的故障点不是抽象的"网络不通",而是可定位的配置覆盖:一个程序写入状态,另一个程序在后续处理阶段改变了这个状态。
- 路由表——实际查表是否选择了预期的虚拟网卡,而不是仅根据规则写入先后判断
- 转发——策略软件的 mangle POSTROUTING 清除掩码是否覆盖隧道软件的标记
- NAT——NAT 是否以该 mark/mask 为条件,以及该条件在进入 nat POSTROUTING 时是否仍成立
- 引擎——控制面正常不代表数据面正常,引擎停了就是停了
对这类问题,先还原顺序:规则是否命中正确路由,包是否进入 FORWARD,标记是否仍在,NAT 是否执行,数据引擎是否运行。不要只根据任一组件的在线状态下结论。
一种替代路径是使用不以 packet mark 为条件的源地址改写规则。它能否适用,取决于规则的来源、目的与出口范围,以及回程设计是否与该改写一致;这些条件未确认前,不能把它当作必然可解的方案。