IPsec 子 SA 已建立,但数据包计数器始终为零
已安装的子 SA 上数据包计数器为零,通常意味着没有任何流量与它匹配。沿计数器、路由、选择器和来源,追踪一条固定下来的流。
隧道已经起来,重密钥计时器正在倒计时,而两个方向仍然读数为零个数据包。这两个数字并不能说明对端拒绝了你的隧道:一条已安装、计数器安静的子 SA,正是一条尚未被任何流量匹配的关联的正常状态。要做的是先证明某个数据包本应匹配,再找出它在哪里离开了选择器所描述的路径。只做一次,按顺序做,并让每次运行的请求保持完全一致。
零计数器究竟证明了什么
子 SA 是一对选择器,加上 用于加密落入这些范围之内流量的密钥与 SPI。安装它只意味着两端就这些范围和派生密钥达成一致;即使一个包都没到,内核也会把它保留数小时。因此问题从来不是隧道是否存在,而是你测试发出的包是否落入了两端协商的范围。如果一个都没有落入,计数器就停在零,关联会在生命周期结束时被闲置过期。
这个区别改变了你先检查什么。被拒绝的提议、认证失败或垂死的 IKE SA 都会出现在日志或 SA 列表里,而不是表现为安静的零。协商干净却随后闲置的隧道,反而指向匹配、路由或测试流量的来源。
先能描述,再发送这条流
在受保护的子网里选一个目标,并向它打开一条新连接;一小时前加载的页面可能来自缓存,而缓存中的应答从不触碰隧道。然后在请求还只是设想时写下四个值:应用将要绑定的源地址、目标地址、地址族,以及你正在观察的子 SA 的名称或选择器对。正是这份记录把平坦的计数器变成可检验的断言。
源地址是大多数人跳过的一项,而它恰恰最常让测试失败。一台笔记本同时有有线地址、Wi-Fi 地址和隧道地址,具体绑定哪一个取决于路由查找,而只有其中一个可能落在协商范围之内。使用能固定源地址的工具,例如 ping -I 10.1.0.5 10.2.0.9 或 curl --interface 10.1.0.5,这样数据包就无法悄悄挑走另一个地址。当固定源的那次运行让计数器变化、不固定时毫无变化,你找到的是路由问题,而不是 IPsec 问题。
该读哪些数字,它们又住在哪里
在 Linux 上,ip -s xfrm state 会为每条关联打印一行 lifetime current:,其字节总数和数据包总数只属于这条 SA,随后是一行 stats:,用于重放与错误计数。在 strongSwan 对端上,swanctl --list-sas 会为每个已安装的子 SA 输出一行 in 和一行 out,每行都以字节数和数据包数结尾;更早的 stroke 工具把同一对称为 bytes_i 与 bytes_o。两个方向要分开读,因为保持平坦的那个方向才是结论。把它们相加,恰好会掩盖你正在寻找的不对称。
两种视图都是按关联计算的,所以一次重密钥会安装新的 SPI 对,你的基线也随之归零,而没有任何东西坏掉。要在重密钥计时器触发之前记录你正在观察的 SPI,否则你会拿新的 SA 去比旧的数字。如果出方向增长而入方向保持平坦,说明选择器匹配上了、应答却从未返回,这把调查转向对端的转发及其自身策略。strongSwan 的 IKEv2 配置文档 覆盖了子 SA 的生命周期以及这些数字背后的重密钥时机。
观察点是与数据包来源相互独立的变量。硬件卸载可能让关联计数器递增,而接口上的软件抓包却什么都看不到,因为被卸载的包会跳过抓包所在的挂接点,入方向也会以同样的方式分叉。当两者不一致时,用计数器回答是否有流量匹配,用抓包回答数据包长什么样。
路由、选择器,以及数据包在哪里诞生
把三样东西并排比较:内核为你的目标选出的路由、把流量导入隧道的那条策略,以及子 SA 协商出的选择器对。ip route get 10.2.0.9 from 10.1.0.5 会指出内核将使用的接口和源地址,而这往往不是你设想的那个组合。ip xfrm policy 显示本地安装的模板与选择器,而 swanctl --list-sas 显示对端同意了什么。把包从物理接口送出去的查找根本到不了策略,计数器自然动不了。
不匹配有好几种形态,从计数器的角度看完全相同。IPv4 选择器之外的 IPv6 目标、来自错误子网的源地址,以及比子 SA 更窄的策略,都会产生同样的零。先修最窄的那个原因,因为仅仅为了动一个数字而放宽选择器,会改变隧道所保护的东西。
接下来是数据包的来源,也就是网关给自己运维人员设下的陷阱。网关自身产生的流量按出方向策略匹配,而它为客户端转发的包按转发策略匹配,所以从这台机器上发出的 ping 可能走在一条任何客户端都不会走的路径上并取得成功。
一次可回滚的改动,以及一份经得起评审的报告
只改动一个元素,重复完全相同的请求,读取同一批计数器。清空所有防火墙规则或一次性替换全部选择器,会让数字变化,同时毁掉那个告诉你原因的证据。如果第二次运行仍然为零,你就是排除了一种假设,而不是浪费了一个下午。
把结论写成别人可以复现的形式:子 SA 的名称、被接受的范围、你固定的源地址、你测试的目标、保持在零的方向,以及你正在读的 SPI。这句话把无人使用的关联与收到流量却将其拒绝的远端应用区分开来。如果改动没有让计数器变化,就把它回滚,并保留记录;下一个人从你停下的地方开始。这一切都不需要重建隧道里的任何东西,而这正是在动配置之前先检查匹配的意义。