你在 IPsec 主机上抓了包,看到 ESP 数据包发出,却没有看到任何回应,于是断定对端已经坏了。抓包并不支持这个结论。ESP 只保护对话的一个方向,在 IP 层不做任何确认,因此缺少返回的 ESP,既可能意味着应用根本没有话要说,也同样可能意味着隧道已经死了。在责怪任何一端之前,先阅读内部交换和接收侧的状态。

误区:每个受保护的数据包都会换来一个回复

ESP 不是请求-响应协议。它加密并认证策略所选中的内容,接收方验证、解密并交付该内部数据包,而自身不会生成任何数据包。目标忽略的 UDP 查询,或被入站策略丢弃的 ICMP 回显,两个方向都保持沉默,这才是正确结果。把这种沉默当作传输故障,会让你去寻找一个根本不存在的网络问题。

单向抓包实际证明的只是:在该观测点上,发送方的协议栈加密了某个东西。它既不能说明数据是否到达,也不能说明对方是否有意回复。有用的问题不是“为什么没有回复”,而是“内部对话本应产生什么”。

两个 SA,两个 SPI,一次对话

IPsec 安全关联是单向的,因此一次正常的受保护交换会运行一个入站关联和一个出站关联,二者标识符不同、状态独立。按出站数据包的 SPI 去寻找返回流量,是范畴错误,因为任何回复都在对端自己的出站 SA 之下、带着自己的 SPI 传输。应当改用流量选择器和对端地址来配对。

重新协商密钥会让画面在几秒内变得复杂,因为旧状态和替代状态可能重叠。如果症状始于一次密钥更换前后,请把关联标识符和时间戳一起收集,而不只是连接名称。

在任何东西回复之前,接收方必须做什么

StageWhat to look atWrong conclusion if skipped
Sender encryptsoutbound SA packet counter"the tunnel is up"
Peer receivesinbound SA counter and replay window"the peer is down"
Inner deliverytarget host or service logs"routing is broken"
Application repliesthe app's own trace"the reply was dropped"
Protected returnthe peer's outbound SA"ESP only works one way"

每一行都会独立失败,而每种失败有不同的负责人,因此单次抓包无法把它们区分开。入站计数器是数据到达的唯一直接证据,因为未通过认证或重放检查的数据包会被静默丢弃,不产生任何响应。因此,入站计数器没有变化的对端,根本没有向你提供关于其策略的任何信息。

交付是与解密分开的一步。状态可能有效,数据包之后仍可能被入站策略或一条不包含内部目的地的路由丢弃,所以要检查目标主机是否看到了明文,而不是假设计数器干净就说明数据包去了你想去的地方。

原生 ESP 不是 UDP

原生 ESP 是 IP 协议 50,既不是 TCP 也不是 UDP,完全没有端口。因此,允许 IKE 走 UDP 500 的防火墙规则对数据路径毫无说明;围绕端口编写的过滤器可能让隧道完成协商,随后丢弃其中承载的一切。请在你实际观测到的模式下单独测试数据路径。

在 NAT 之后形态会改变:受保护的载荷走在 UDP 内部,通常使用 4500 端口,正如 RFC 3948 所定义。在编写任何规则之前,先记录当前生效的模式,因为路径上的设备可能对原生 ESP 和 UDP 封装的 ESP 区别对待,某些中间设备会放行其中一种而丢弃另一种。

读取属于本次事件的计数器

在 strongSwan 主机上,swanctl --list-sas 会报告每个关联双向的数据包与字节计数器,足以区分“从未到达”和“已到达但无人应答”。在 Linux 上,/proc/net/xfrm_stat 统计聚合性失败,例如缺少状态、缺少策略和协议错误。这些总数自启动起累积,属于单个网络命名空间,因此一个大数字并不构成归因。

在正确的命名空间中,围绕事件窗口取两次读数并比较增量,否则你会把上周的错误安到今天下午的工单上。把命名空间和两个时间戳一并写进工单,让下一位读者能够复现比较,而不是相信一个总数。

要说出阶段,而不是怀疑对象

有用的结论会说明数据包停在哪里:从未收到、收到后被丢弃、已交付但无人应答,或已应答却在返回途中丢失。每一种都指向不同的负责人和不同的下一条命令。“VPN 服务器没有响应”把四种不同的故障压缩成一句话,而抓包从未证明过它。

保持抓包范围狭窄,并且不要把原始内核状态写进工单,因为它可能携带不该离开主机的密钥材料。计数器的增量和时间戳才是值得交给下一位同事的部分。

相关文章