如何读懂抓包中的第一次 IKEv2 交换
按 SPI 与消息 ID 配对 IKE_SA_INIT 请求和应答,区分 4500 端口上的 IKE 与封装 ESP,并看清静默抓包究竟能证明什么。
抓包若在会话中途开始,IKEv2 的轨迹通常就像噪声,因为录制开始时双方已经在更新密钥或交换加密载荷。不妨从一次全新的连接尝试开始,这样文件里的第一条消息就是开启协商的那一条。下文的阅读顺序以此为前提,并标出一条数据包不再能证明远端任何情况的界线。跳过这一步,正常工作的隧道就会被误判为端口被封。
确认你看到的是开启协商的那次交换
IKEv2 协商以 IKE_SA_INIT 开始,交换类型 34,消息 ID 为 0。发起方会选择非零的 SPI,而响应方的 SPI 在该请求中为零、在应答中非零。这三条事实能把全新尝试与重新协商密钥、或与恰好排在抓包开头的 IKE_AUTH 消息区分开来。
头部字段可读,提案、密钥交换和 nonce 载荷同样可读。可读并不等于已认证:此时双方都还没有证明身份,而 Diffie-Hellman 的私有值绝不会出现在线路上。
把字段打印成表格,而不是在解码窗格里滚动,因为重传和 SPI 不匹配在列中一目了然。
tshark -r ike.pcapng -Y "isakmp" -T fields -e frame.number -e ip.src -e udp.srcport -e isakmp.exchangetype -e isakmp.messageid -e isakmp.initiator_spi -e isakmp.responder_spi
成对地读输出。消息 ID 为 0 的请求,紧随其后带同一 ID、且响应方 SPI 非零的应答,就是一次交换——即使因请求被重传而出现三次。三条相同的请求不是三次登录;正是把它当作三次登录,才会得出客户端在死循环的结论,其实它只是有耐心。按消息 ID、再按时间戳排序,整段对话就会浓缩成一张简短的尝试清单。
先读拒绝,再读成功
有应答并不自动意味着进展。响应方可能回一个 cookie 通知,等发起方重试;也可能回 INVALID_KE_PAYLOAD,点名它更愿意接受的组。两者都以格式正确的 IKE 消息到达,而在只统计包数量的过滤器眼里,两者都像是已经完成的交换。
打开通知载荷,先读类型。cookie 挑战说明响应方在防御伪造的源地址,客户端必须带上 cookie 重发。组不合法说明两边的提案对不上,无论重试多少次都不会改变,直到某一方的配置发生变动。
只有第二次交换 IKE_AUTH 才携带加密身份和认证载荷。因此一次完成的 IKE_SA_INIT 只说明双方在算法和派生密钥上达成一致,对凭据是否会被接受一无所知。那里同样有两条消息,而且没有会话密钥时两条都不透明。
4500 端口不止一种协议
开头的交换通常走 UDP 500。一旦完成 NAT 检测,IKE 就转到 4500,并在每条消息前加一个四字节的零值非 ESP 标记。同一端口上的封装 ESP 则以非零的 ESP SPI 开头;而单字节的 NAT 保活是第三种形态,在别无事发生时出现。
只按端口匹配的过滤器会把三者混在一起,所以"4500 上有流量"几乎说明不了什么。在对远端下任何结论之前,先按协议拆开视图。
tshark -r ike.pcapng -Y "udp.port == 4500 && isakmp" -T fields -e frame.number -e isakmp.exchangetype tshark -r ike.pcapng -Y "udp.port == 4500 && !isakmp" -T fields -e frame.number -e esp.spi
静默描述的是观察点
有请求无应答只确定一件事:在那个抓包点上没有出现应答。过滤、把应答送往别处的路由、不可达的响应方,或者不喜欢该提案的响应方,都会给出同样的空结果,而通用的 UDP 探测无法把它们区分开。
ICMP 不可达是另一类证据。读它里面引用的那个包,确认是哪个设备发出的,因为路由器的管理性拒绝并不是来自对端的已认证错误。
tshark -r ike.pcapng -Y "icmp.type == 3" -T fields -e frame.number -e ip.src -e icmp.code
在有权限读取服务器日志的地方,把同一次尝试与日志对照:请求到了吗?服务回答了吗?回答停在哪里?这三个答案会把你引向路径问题或策略问题,也比第四次轮换凭据更省事。