ping 超时:沿着数据包的路径排查
本文目录
从一次超时开始

你在电脑上 ping 一台服务器,请求超时。隧道软件的状态看起来正常,却不足以解释数据包去了哪里。
下面使用假设的拓扑和规则讲排查方法。标记冲突和引擎未启动,分别是需要证据支持的可能解释。文中的地址与规则用于演示;判断真实故障时,需要对照自己的软件版本、运行状态和报文记录。
| 节点 | 本文中的角色与示例地址 |
|---|---|
| 客户端 C | 隧道地址 100.75.237.122,发起请求 |
| 中转机 A | 接收隧道流量,内网接口地址 172.16.0.1 |
| 服务器 B | 内网地址 172.16.0.20,本次请求的目标 |
请求预期从 C 经隧道到 A,再由 A 转发给 B;回包则需要回到 C。这里讨论 IPv4 和 Linux 上可由内核转发的路径,不覆盖所有用户态隧道实现。命令中的尖括号内容都是需要替换的参数,终端输出应以实际检查为准。
先别急着问是谁把它丢了。要先确定:它有没有选对出口、到中转机后被当成本地包还是转发包、该做的地址改写是否发生,以及隧道的数据引擎是否仍在工作。
先弄清请求方向,再检查回程。本文分别说明正常时应具备的条件,以及什么证据能够支持或排除一个解释。
先按路径排查
先记录源地址、目标地址、探测命令和发生时间。检查时尽量对照同一次请求,避免把另一条流量的计数或抓包当作本次请求的证据。
每一步先回答一个问题:C 是否选中了隧道;A 是否收到并转发;B 是否收到请求;B 的回复是否回到 A;A 是否把回复送回 C。前一段正常,只缩小了范围,不能替后一段背书。
还要保留两种可能:请求没到,或者回复没回来。单看 C 上一次超时,无法区分它们。
1. 路由表:先确认出口
电脑的路由表记录了目的地址段、出口网卡和下一跳。发包前,内核先据此决定包从哪里出去、交给谁。
在一种常见配置中,Wi-Fi 提供默认路由:没有更合适路由的目标,经由家用路由器发送。实际配置仍需查表确认。
隧道软件可能添加对端地址或远端网段的路由。本例真正要查询的是 B 的地址 172.16.0.20;只验证能够访问 A 的隧道地址,不能证明去 B 也会选中隧道。
对普通目的地址查表,同一张路由表中的更具体前缀优先于默认路由;同前缀还要看相应的路由选择条件。若使用策略路由,则先由 ip rule 的 priority 和 selector 决定查哪些表。不能把不同表里的前缀混在一起,只挑最长的一条。
因此,不能仅凭某个程序先添加了路由,就判断去 B 的流量会走 Wi-Fi。应查看针对当前源地址、目标地址及其他条件的实际选路。
如果去 B 的请求实际选择了与设计不符的出口,应先解释这个选路结果,再继续排查隧道对端。注意区分去 B 的内层流量和承载隧道的外层流量:后者经 Wi-Fi 发出,本来就可能是正常路径。
接下来在对应的 Linux 节点上,先看规则,再按当前流量的条件查询。
先看静态配置:
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>;不能随意拿后续阶段才写入的标记代替它。查询帮助解释内核会怎样选路,实际流量仍需与抓包等证据对应。ip-route 手册
2. INPUT 与 FORWARD:本地处理还是继续转发
对于本例中从 A 的虚拟接口进入、由 Linux 内核处理的包,需要在经过前置处理和路由判断后,区分本地接收与继续转发。这里假定没有 DNAT 改变目标:
这个包的最终地址,是不是我自己的 IP?
- 是 → 交给我自己处理 → INPUT(到站车)
- 不是 → 我帮它转给别人 → FORWARD(过路车)
如果目的地是服务器 A,当前机器就是 A → INPUT,它自己处理。 如果目的地是服务器 B,当前机器是 A → FORWARD,它要把这包转出去。
两条路径应用的规则不同。目的地为本机时走 INPUT;目的地为另一台机器时必须走 FORWARD,并受转发规则约束。
因此,A 能响应自己的服务,并不能说明它允许把去 B 的包转出去。需要检查转发是否启用、到 B 的路由是否有效,以及相关 FORWARD 规则是否允许这类流量。链计数变化只能辅助判断,还要确认对应的是本次请求。
3. NAT:回包能否找到来路
回到开头的拓扑。C 以隧道地址 100.75.237.122 发起请求,经 A 转发给 B。先假设 A 尚未改写源地址。
A 收到包后,若目的地是 B 而非自己、已启用转发,且没有被更早的规则丢弃,才会进入 FORWARD 并继续转发。
如果 A 保持原源地址转发,发出的请求包含:
从谁来的:你的隧道地址
要去哪: 服务器 B 的内网 IP 地址
B 看到源地址 100.75.237.122。这个地址不在它可直接回达的局域网内,回包路径便成为问题。
只有查过 B 与相关网关的路由,才能知道回复会去哪里。若 B 没有更具体的回程路由,可能把回复交给默认网关;网关仍可能转发、丢弃或通过其他路径处理,不能仅凭地址不在同一局域网就认定结果。
一种可行设计,是为 B 或其网关配置经 A 返回隧道网段的路径;另一种设计,是由 A 改写源地址。下面讨论后者,假定本例明确依赖 A 的 SNAT 完成回程。
这一操作是 SNAT:改写源地址。
在这种设计下,A 把源地址从 100.75.237.122 改为自己的内网地址 172.16.0.1。B 的回复先发给 A,再由 A 根据相应连接的 NAT 状态进行回程改写和转发。回程规则、连接状态和隧道仍需正常,不能只看到请求改了地址就认定全部链路已通。
检查 A 的出口和 B 的入口,能确认实际出现的是哪个源地址。若仍是 C 的隧道地址,而设计又依赖 SNAT,就有理由继续查地址改写条件;如果源地址已经符合预期,则要转向回复的生成与回程。
4. 标记冲突:SNAT 的前提被另一条规则擦掉
即使包已走到 A 的转发链,SNAT 仍可能没有发生;这里需要检查 packet mark 是否是 NAT 规则的匹配条件。
先了解这个匹配条件:数据包标记(packet mark)。
packet mark 是 Linux 在本机处理数据包时关联的标记,可供规则匹配和策略路由使用;它不是自动写进 IP 报文、随包传到 B 的字段。普通抓包看不到这个本机标记。iptables 扩展手册中的 MARK
假设当前软件安装的规则采用以下流程,具体链、标记值和掩码必须以实际规则为准:
- 包从虚拟网卡进来,在 FORWARD 链上给它打个标记(比如 0x40000)
- 包走到最后一步(POSTROUTING,发出去之前的处理点),检查这个标记
- 有标记 → 做改寄件人
再假设 A 上还有一个策略程序,也会修改 packet mark。
如果它在 mangle 表的 POSTROUTING 中清掉某几位,就需要检查这些位是否与后面的 NAT 条件重叠。
只有当策略软件的清除掩码覆盖 0x40000,且 NAT 规则按相同的 mark/mask 匹配时,才会发生这里所说的冲突。
满足这些条件时,可能出现如下顺序:
包从虚拟网卡进来
→ FORWARD 链 → 隧道软件打上标记(done)
→ mangle POSTROUTING → 策略软件清掉那几位标记(gone)
→ nat POSTROUTING → 隧道软件查标记 → 没了 → 不改寄件人(broken)
若规则顺序确为上述顺序,计数可作为规则被使用的辅助证据;再结合流量条件、实际规则与必要的跟踪记录,判断 FORWARD 中写入的标记是否在 mangle POSTROUTING 被清除。若这一条件成立,进入 nat POSTROUTING 时,NAT 的匹配条件就不成立,包会保持原源地址出网,回到上一节的回包失败。
这类故障可能来自两个程序共享同一标记位但没有协调。验证依据是:哪条规则写入标记、哪条规则清除标记、它们所在的表和顺序。
在 A 上核对 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 才是补强层。
5. 在线状态与不同 ping,分别证明了什么
状态命令正常、内部 ping 有延迟,但系统 ping 或 ssh 不通,这种现象首先要求我们确认探测覆盖了哪段路径。
控制面负责协调身份、密钥或网络信息;数据面承载实际流量。不过,不能把软件自带的所有探测都归为只测控制面。以 Tailscale 为例,官方文档说明 tailscale ping --tsmp 会经过 WireGuard,但不经过两端操作系统协议栈。它与普通系统 ping 覆盖的环节不同。Tailscale CLI 的 ping 说明
这只是说明探测模式的区别,不是确认本例使用了 Tailscale。检查时应记录具体软件、版本、命令、参数和目标。测到 A 的隧道地址,也不能证明 A 后面的 B 已经可达。
还可能需要检查数据引擎是否成功启动。例如执行过停止操作后,新的启动命令可能因权限或配置问题失败。这种解释需要启动日志与运行状态支持,不能只凭业务不通就认定引擎停止,也不能断言引擎停止后某种内部 ping 必然仍然正常。
可以对照服务状态、启动错误和接口情况,判断请求是否真正进入数据路径,再回到前面的逐跳检查。一次在线显示、一次超时或一次重启,都不足以独自说明整条链路。
什么时候可以说已经找到原因
在这个假设拓扑里,需要把请求和回复串起来:C 是否选中预期隧道,A 是否收到请求并转发,出 A 时的源地址是否符合设计,B 是否收到并回复,回复是否经 A 回到 C。
若怀疑标记冲突,至少应找到具体的写入规则、清除规则和 NAT 匹配条件,确认它们会作用于同一类流量,并将规则顺序、计数变化和接口上的报文对应起来。计数不足以说明标记的中间值时,需要使用与实际环境相符的跟踪方法,不能凭规则名称就下结论。
如果 A 的出口已出现符合预期的源地址,原先的“没有执行 SNAT”解释就需要重新考虑;若 B 没有发回响应,则还要检查 B 上的处理。每得到一份证据,都应允许它否定自己最初的猜测。
修正配置后,也需要用相同的源、目标和探测方式再次检查。在涉及 NAT 的场景中,应确认测试采用的新连接,而不是误把既有连接的状态当作新规则效果。记录改动前后的路径与结果,才能说明哪一步发生了变化。
若考虑使用不依赖 packet mark 的源地址改写规则,还必须确认它匹配的来源、目标和出口范围,以及是否符合原来的回程设计。不能因为一条宽泛规则让某次 ping 通了,就认定问题已经被正确修复。
真正有帮助的排障记录,需要同时保留没有命中的假设。例如选路正常,为什么暂时排除出口错误;A 已发出请求,为什么转向回程。这样下一次出现相似现象时,别人才能复查你的判断,而非照着最后一条规则重复修改。
ping 超时只是观察的起点。查清哪一段与预期不同,再让规则、日志和报文支持解释,才能逐渐把“网络不通”变成一个可以处理的具体问题。