如何在抓包中读取一次全新的 IKE_SA_INIT 交换
全新的 IKEv2 协商在密钥出现之前就能读懂。抓下这次尝试、读头部、配对回包,并在 4500 端口上把 IKE 与 ESP 分开。
要从第一个数据包之前就开始的抓包入手,否则你读到的是对话的中段。已经在跑的协商只会显示加密交换和密钥更新,两者都不像开头。开头正是无需密钥即可解码的部分,所以它能很好地回答可达性问题。按顺序做四项检查,就能从一份新抓包走到“是哪一侧先停止说话”这样站得住脚的结论。
第一,确认抓包从开头开始
隧道建立之后才开始的抓包,只会包含封装后的数据流和周期性 keepalive,两者要么已加密,要么几乎是空的。重启客户端并录下这次尝试本身,或者在抓包运行时故意触发一次新连接。如果无法制造一次新的尝试,就在记录里写明这一点,因为无论怎样解码,都找不回你从未录下的开头。
把抓包范围限定在两个端点和 UDP 500 与 4500 上,让文件小到可以通读。在繁忙主机上,不加过滤的抓包会把协商埋进无关流量里。
tcpdump -i any -n -s 0 -w fresh-ike.pcap 'udp port 500 or udp port 4500'这次尝试有意思时就留下文件,因为同一份抓包能回答后面两个问题:交换是什么,以及有没有回音。标注开始时间和你所过滤的端点对。缺少这两项信息的抓包,一周后就没用了:地址已经变了,也没人记得它录的是哪次尝试。
第二,先读头部再看载荷
IKE 头部承载安全参数索引、交换类型和报文 ID,在一次全新协商里这些取值很有辨识度。发起方会临时编出一个非零 SPI,初始请求的响应方 SPI 为零,因为还没见过任何回复;交换类型是 IKE_SA_INIT,报文 ID 为零。除此之外的取值,都属于同一协商的后续阶段,或者完全是另一次尝试。
这次初次交换中的 proposal、密钥交换和 nonce 载荷都是明文可读的。可读不等于经过认证,此时双方都还没有证明自己的身份。
私有 Diffie-Hellman 值从不经过线路,所以抓包无法让你推导会话密钥。认证发生在下一次交换,并由第一次交换谈成的内容加以保护。如果有人问某一方是否可信,这次交换回答不了。它只能说明双方就某个 proposal 达成一致,并生成了一个文件里并不包含的共享秘密。
第三,把请求与它的响应配对
按 SPI 对、交换类型和报文 ID 配对,然后看响应标志位。重传只是重复原来的报文 ID,并不是第二次登录尝试。按报文而不是按交换来计数,正是把健康协商读成幽灵循环的原因。
响应也可能是拒绝:cookie 请求、不可接受的组通知,或者关于 proposal 的错误。这些都是合法答复,说明路径通而策略不通。要解码这条通知,而不是把每个响应都当成成功。当通知点名某个组或某个 proposal 时,要改的是配置而不是防火墙。
方向同样重要。无响应的请求和从未到达客户端的响应是两种故障,只有后者看起来才像客户端侧超时。
第四,把 4500 上的 IKE 与其他流量分开
4500 端口承载三类在摘要视图里长得一样的东西:四字节零 non-ESP 标记之后的 IKE、标记位置是非零 SPI 的封装 ESP,以及短 keepalive。正是这个标记把 IKE 与封装 ESP 分开,所以在把流量叫作 IKE 之前先读那几个字节。抓包标签写着 4500,那只是端口号,不是协议。
封装 ESP 是没有密钥就无法解释的加密载荷,它的出现可能是隧道正常的迹象,而不是故障。
同一次尝试要同时盯住两个端口,因为 IKE 即便在 500 上开启,也可能在 NAT 探测之后转到 4500。只按 500 过滤的轨迹,恰好在协商成功并迁移时看起来毫无声息。扩大过滤范围,并在从“没有”下结论之前重读同一时间窗。
单个观察点上的沉默意味着什么
客户端没有响应,只能证明那个观察点看到了什么。过滤、路由、错误的远端地址、服务停止和协议不匹配,都会产生同样空白的轨迹。
一条相关的 ICMP unreachable 会补充证据,但要读出其中引用的报文,并记下发出它的设备地址。
路由器的拒绝与对端返回的认证错误是两回事,只有后者才能证明服务是活的。把同一次尝试与服务端日志或经批准的观察点关联起来,才能知道请求是否到达、响应是否发出。抓包要限定范围并注意保密,因为可读的握手段落并不意味着文件其余部分无害。真正有价值的结果,是一组配对的请求与响应,或者一处有名有姓的观察点上记录下来的空白。