两个配置文件都报告已连接,其中一个却在悄悄承载本该走另一个的流量。一台主机上跑两个客户端是受支持的安排;两个客户端各自的前缀都没有归属则不是。这件事的重点不在于把第二条隧道拉起来,而在于决定哪个进程有权认领哪条路由。按下面的顺序走一遍,配置自然从答案里长出来。

先问清第二条隧道是做什么用的

三种情况在状态行上看起来一模一样,却需要不同的设计。按目的地拆分,是让一个前缀走办公配置、另一个走实验室配置,两者都不认领默认路由。按应用拆分通常保留一条完整隧道,再用策略规则引导特定流量。故障切换则在同一时刻只保持一条隧道在线,这意味着第二条不能自动启动。

在打开任何配置文件之前先把情况写下来,因为那个答案决定了后面的一切。

最常见的坏配置是两条完整隧道都推送了默认路由,谁最后连上谁赢,另一条继续装作没事。这种状态从状态行看像是刻意为之,却白白浪费一天去查路由表。先把情况命名清楚,它就从“不太可能”变成“不可能”。

先看地址池,再看路由

两台服务器很容易从同一个私有网段分配地址,一旦如此,客户端就会在同一个前缀里出现两个接口。这时任何目的地都无法按地址归到某个接口上,因为两者看起来同样正确。与其发明一条规则来打破平局,不如给其中一个地址池重新编号。

也要检查本地网络,因为家庭或办公 LAN 与隧道网段重叠时,会从另一侧制造同样的歧义。在责怪客户端之前,先拿主机上的地址和两份服务器配置比对。实验室服务器上改地址池是五分钟的修法,而为重叠前缀做策略路由绕行,则是给后来每个读配置的人加一笔永久税。先决定你要哪一种,再决定在哪里动手。

两套地址族意味着这轮检查要做两遍。IPv6 的地址池和 IPv4 一样会悄无声息地撞车。

给每个配置文件一个归属

每个配置一个实例,每个实例一份配置文件,再加一个能标识这一对的名称。Debian 风格的打包里,@ 符号后面的名字决定读哪个文件,所以两个配置共用同一个实例名时,永远不可能同时生效。映射关系要从已安装的 unit 确认,而不是照着教程抄。

systemctl cat [email protected]
ip -4 addr show

然后再决定由谁来安装路由,这是策略问题,不是语法问题。

当两台服务器推送的集合互不兼容时,要么加上route-nopull并且只列出每个配置真正需要的前缀,要么让其中一条保留默认路由,把其余流量交给策略规则引导。两个进程都安装默认路由,最后只会有一个赢家,而且没人说得清为什么。一旦两条隧道分别承载不同来源的流量,回包就可能从错误的那条出去;站得住的做法是用连接标记在回包上恢复,并把它引进自己的路由表。标记的范围要收窄,别让隧道自己的加密包也撞上。

逐条隧道验证,然后停掉一条

两条都起来之后,做三项读数:每条隧道设备上的地址、通往各自背后主机的路由,以及两条之外的目的地会走哪条路。一旦规则介入,普通的 route 列表就不再描述最终的查找结果,所以要问内核它实际会选什么。然后停掉一个实例,看看什么消失了。如果停掉第一条会扰乱第二条,说明两者之间还有共享的东西:一条路由、一个设备名,或者一条没人拆开的防火墙规则。

ip rule show
ip route get 203.0.113.9

把停掉的配置重新拉起来,再重复同样的检查,因为一条重启后只工作一次、之后再也不工作的隧道,是穿着路由外衣的生命周期缺陷。

在部署笔记里写清哪个配置拥有哪个前缀,就写在地址池范围旁边。两条隧道是刻意安排的,下一位读者在动任何一个配置之前,需要先看见边界。没有这条笔记,等共享前缀终于出问题时,值班的人只能把边界重新发现一遍。

相关文章