当 IKEv2 返回 TS_UNACCEPTABLE 时,比较真实的子网
选择器被拒是双方在覆盖范围上的分歧。把本地和远端的标签对调,然后比较各自提出的确切范围。
把 TS_UNACCEPTABLE 读作覆盖范围上的分歧,而不是配置文件损坏。网关是在说,双方无法就这条隧道可以承载哪些地址达成一致。那一刻 IKE SA 可能已经建立,所以客户端可能看起来已连接,但后面并没有任何子 SA。一旦这样框定,工作就变得机械了。写下双方预期的前缀,朝一个方向比较,然后不断收窄,直到它们相交。
先把预期的那几对写下来
在动配置之前,列出四个值:发起方的源范围、它的目标范围,以及从应答方看到的同一对值。同时记下各自所属的地址族。IPv4 范围和 IPv6 范围是不同的选择器类型,谁也不能折进对方的负载里。
把策略携带的任何协议或端口限制也加上。只允许 TCP 445 访问文件服务器的规则,所描述的范围比它所在的子网更窄。如果把它漏在笔记之外,协商结果就会因为你看不见的原因而显得自相矛盾。
比较之前先对调标签
本地和远端是相对标签。客户端的本地前缀是网关的远端范围,而网关的本地范围是客户端的远端范围。因此两端的截图永远对不上,而把二者逐字比较正是浪费一个下午的常见方式。
应答方可能不直接拒绝,而是把提议收窄到一个可接受的子集。这种结果比干脆失败更糟,因为它只工作一半。当策略允许 10.0.5.0/24 时却申请 10.0.0.0/16,你可能最终得到一个只覆盖那个 /24 的子 SA。只有一台主机应答,其余全部落在外边,而客户端里什么都不会说。在断定协商成功之前,要看被接受的选择器,而不是被申请的选择器。
请求默认路由的 road warrior 配置,经常落进这个坑。为分离访问而建的网关会把全范围提议收窄到自己的子网,客户端必须接受收窄后的结果,或者大声失败。
从一次尝试里读出拒绝
收集同一次连接尝试的两边日志。保存的配置只显示你配置了什么,而不是提议了什么;在 strongSwan 上,swanctl --log 会把收到的通知连同它携带的选择器一起打印出来。
然后检查实际装上了什么。swanctl --list-sas 显示子 SA 的选择器,ip xfrm policy 把同样的范围显示为内核策略条目。列表里出现一条选择器并非你所愿的子 SA,就是一次收窄事件;日志里有 TS_UNACCEPTABLE 却没有子 SA,就是拒绝。
既然在那儿,就把相近的名字区分开。INVALID_SELECTORS 抱怨的是已建立子 SA 内部的选择器,而 INTERNAL_ADDRESS_FAILURE 意味着应答方无法分配虚拟地址,那是地址池问题,不是覆盖问题。
检查是什么在悄悄缩小集合
有三样东西会在不声不响中缩小选择器集合。第一是应答方策略,通过子网列表、组规则或按用户授权,把提议缩成子集。第二是地址族:双栈客户端常常在 IPv4 之外同时提议 IPv6,于是为网关从未配置过的范围收到拒绝。
第三是协议和端口限制。在策略只允许 TCP 445 的地方,子 SA 可以起来,而 ICMP 和 UDP 留在外面;用户报告隧道坏了,可当初允许的本来就只有一种协议。
因此只靠放宽地址无法修好端口受限的策略,而这恰恰是最可能原封不动地熬过三轮排查的情形。
用合适的流量证明覆盖
子 SA 安装之后,发一个肯定落在被接受范围内的请求。看计数器而不是状态图标:当报文被匹配并保护时,ip -s xfrm state 以及 ip xfrm policy 背后的计数器会增长。真实流量之后字节数仍为零,说明内核从未选中那条策略。
这个零是路由或策略顺序问题,不是加密问题。从用户的角度看,它也和选择器被拒一模一样,所以重新打开配置之前值得先把计数器收下来。只朝一个方向增长的计数器,反而指向回程路径。
最后把被接受的那一对写进工单,放在最初申请的范围旁边。选择要像需求一样窄,因为用所有地址替换私有范围来消除报错,给出的访问权限远超账号获批的范围。