hook 脚本触发后读取 ppp0,并把路由应用到当时拥有该名称的任意接口。有时它确实是你想要的那条隧道,有时却是前一分钟刚连上的另一个会话。下一次重连会把编号挪走,同一个脚本便悄悄配置了错误的链路。解决办法是从刚刚执行的这次调用中取接口,并把预期的配置文件一并带上。

从选项到 hook 追踪同一个会话

从会话被创建的地方开始。xl2tpd 用该隧道 pppoptfile 指定的选项文件启动 pppd,因此插件、单元编号以及任何配置文件标记都在那里决定,而不是在全局文件中。连接管理器也可能生成自己的选项,所以修改 /etc/ppp/options 并不能证明你关心的隧道发生了变化。链路建立后,pppd 会执行发行版分发器提供的通知脚本。这些脚本把接口名作为第一个参数接收,把配置文件标记作为第六个参数接收。

环境也会以 IFNAME 和 PPP_IPPARAM 之类的名字携带同样的信息,但它们是否可用取决于所安装的分发器。在依赖某个变量之前先读分发器:有些发行版保留上游的参数顺序,另一些会在调用你的脚本前过滤环境。只使用 $1 和第六个参数的脚本在两种安排下都能工作。

从调用中读取接口

传统 hook 首先接收接口。这个位置参数就是描述触发脚本的那个会话的接口标识。把它保存在顶部的变量里,然后用该变量处理路由、防火墙规则和拆除。

ipparam office-vpn

把配置文件标记加在定义选项的地方,而不是脚本里。在隧道的选项文件中写上这一行后,由该文件创建的每个会话都会把 office-vpn 作为第六个参数携带。脚本可以按标记分支,同时仍然作用于真实的接口。在代码中把两者分开,因为标记是稳定的,而接口是按会话分配的。

只有在拒绝才正确时才请求固定名称

固定名称看起来比读取参数更简单,但它改变了失败时的行为。unit 选项为出站连接请求一个编号,当该单元已被占用时 pppd 会退回动态分配。这个结果里没有任何东西为你的配置文件保留 ppp0。

ifname ppp-office

ifname 选项的行为不同:如果请求的名称已被占用或无法分配,pppd 会直接退出,而不是另选一个。这是策略选择,不是外观上的改名。当同一配置文件的第二个并发实例应当被拒绝时使用它,并确认已安装的 pppd 和你的连接管理器会把该选项传下去。

无论如何,脚本看到的名称才是应当处理的名称。配置好的名称并不能免除读取调用的必要性,因为重启后的会话仍可能呈现与上次不同的名称。

让拆除与你配置的会话对应

路由和防火墙规则的寿命比创建它们的脚本更长,因此拆除必须精确移除该会话拥有的对象。用配置文件标记和接口一起给规则打标签,并按该标签删除。如果两条隧道可能同时在线,删除所有提到 ppp 的东西正是让一次断线破坏另一条连接的根源。

测试能暴露这个缺陷的场景:在第一条仍连接时启动第二条 PPP 会话,然后重连第一条,观察 hook 报告的是哪个接口。只用一个会话成功运行,什么也证明不了关于名称分配的事。在 VPN 停止的情况下重复一次,确认拆除没有留下过期的路由。

把标记、接口和 hook 路径写进部署记录。重启后编号发生变化时,这三个值能解释为什么在没有任何配置修改的情况下脚本的行为不同。

相关文章