在 Linux 上使用 Kerio:给 kvnet 配置服务器期望的 MAC
每次连接都会重建 kvnet,因此一次性的地址修改会丢失。读取客户端记录的值,并在连接后的钩子中应用它。
在 Kerio 隧道真正承载流量之前,三件事必须一致:kvnet 接口上的地址、客户端日志中记录的值,以及服务器策略或 DHCP 所期望的值。三者不一致时,隧道依然能完成认证、依然能起来,而这正是故障令人困惑的原因。接口是按连接创建的,所以手工设置的地址只属于那次会话,不会带到别处。这就是问题的形状,解决手段是钩子而不是配置文件。剩下的问题是到哪里读取这个值、又在哪里应用它。
地址为什么总是被改回去
kvnet 是客户端在连接开始时创建、在连接结束时移除的虚拟适配器。因此手工的 ip link set 只能活到该会话结束,下一次连接会从客户端分配的地址重新开始。在客户端无法应用期望地址的机器上,每次重连都会重新引入同样的不一致,租约或访问规则于是再次拒绝这个设备。
这个现象本身就具有诊断价值。一条隧道在某次会话里正常、下一次却失败,不是凭据问题,因为凭据在连接之间不会改变。
从客户端读取这个值,而不是从接口读取
Linux 客户端会把协商细节写进调试日志,它打算使用的地址会作为连接序列中最后一个地址形式的标记出现。把它与内核实际持有的值比较一下,只差一条 readlink:cat /sys/class/net/kvnet/address。
如果两者不同,说明客户端知道正确的值却没能应用它,而一个在接口出现之后应用该值的钩子正好补上这个缺口。如果日志里根本没有地址,问题就更早,属于客户端版本或服务器配置,而不属于脚本。
在接口出现之后再应用
NetworkManager 用户可以把脚本放进它的 dispatcher 目录,只在 VPN 接口起来时执行。由 systemd 管理的客户端需要更小心:随服务启动运行的 drop-in 通常在适配器存在之前就触发了,所以钩子必须等待接口,而不是假定它已经在。那时再设置地址;如果链路处于 up 状态而内核对修改报错,就先把它 down 掉,设置好地址再重新 up。把提取出的标记用 MAC 模式再读一遍,然后才交给 ip link,以免某行意外的日志变成命令行参数。
任何以 root 身份根据客户端日志运行的东西都应当有狭窄的归属:文件归 root 所有、其他人不可写,除了已校验的地址之外不做任何 shell 拼接。把模式校验留在脚本内部。不要让日志内容决定执行哪条命令。
要校验的是租约,而不是链路
ip -4 -o addr show dev kvnet 应当显示预期范围内的地址,ip neigh show dev kvnet 应当显示网关,VPN 后面还应有一个服务能响应。然后断开并重连一次;如果地址又变回去,说明钩子触发的事件不对,或者根本没有触发。
接口带着正确地址出现却始终拿不到租约,指向的是服务器一侧。那是另一项检查,通常也属于另一个团队。再次修改钩子之前,先确认账号被允许、地址池里仍然有空闲地址。
升级之后重新审视这个绕行方案
如果客户端版本已经能正确设置地址,你的钩子就会继续应用一个过期的值,那是修复本身带来的新故障。也不要从别的机器或论坛帖子里复制 MAC;要使用这个客户端为这个账号记录的值,并记下你验证它的日期。
让脚本短到可以一口气读完。解析日志的绕行方案本质上都是临时的。能留下来的,是未来的读者在没有原帖的情况下也能看懂的那种。