空的 VPN 表本身并不拦截任何流量
没有匹配路由的策略表不会丢弃数据包,而是继续查找。请添加终止动作,然后测试两种故障状态。
策略规则会把打上标记的数据包送往专用表,而人们通常以为,一旦 VPN 路由消失,这些数据包就会被拦下。但机制的实际行为并非如此。当表中没有匹配的路由时,查找会继续交给下一条规则而不会停止,同时 main 表里仍然保留着一条普通的默认上联路由。结果就是一套看起来是 fail-closed、实际却表现为 fail-open 的配置。
什么都没有坏;策略路由本来就是这么规定的。
表中没有任何内容时,查找仍会继续
100: from all fwmark 0x36 lookup 42 110: from all fwmark 0x36 lookup default 32766: from all lookup main
看这组规则:带标记的查找会进入表 42,后面的一条规则又把同一个标记送往名为 default 的表。只要表 42 里还有可用的默认路由,第一次查找就能给出路径。当那条路由消失、表 42 里也没有别的内容匹配时,查找并不会变成丢弃;处理会继续走到下一条规则,而那条规则也一无所获时,就落到 main 表。整个过程里,没有任何东西报告究竟是哪条规则给出了最终答案,于是能用的连接看起来就像一条能用的策略。
表 253 通常被称为 default,它是一张普通的路由表,通常是空的。它的名字并不是丢弃数据包的指令,也不等于表内的一条默认目的路由。
终止动作会结束查找
100: from all fwmark 0x36 lookup 42 110: from all fwmark 0x36 prohibit 32766: from all lookup main
终止规则可以接在 VPN 查找之后,取代对空表的查找。使用同一个标记时,放在表 42 规则之后的 prohibit 动作会在该表一无所获时结束查找,返回一个管理性拒绝,而不是再给 main 表一次机会。unreachable 和 blackhole 同样是终止动作,只是返回给发送方的错误行为不同。
throw 类型的路由则不同:在那张表里,它的行为就像什么都没匹配上,因此后面的规则仍然轮得到。这让 throw 适合有意为之的回落,却不适合作最终的拦截。
用真实的标记和接口来验证
ip -4 rule show ip -4 route show table 42 ip -4 route get 198.51.100.20 from 10.44.0.2 iif lan0 mark 0x36
去看真实的规则和表,而不是看示意图,然后用这条流真正拥有的源地址、标记和入接口上下文去查询。一条只读查找,只要告诉它目的地址、源地址、接口和标记,就能在不发出任何数据包的情况下报告内核会选择的路径。在查询里给出标记,并不能证明防火墙真的给真实流量打了标记;那是对标记规则的另一次检查。在下结论之前,先用计数器或日志确认这个值。
本地生成的请求需要自己的上下文,因为它们的标记路径常常与转发流量不同。请用应用真正会使用的出接口来检查它们。
两种故障状态,而不是一种
一种故障状态是 VPN 路由消失、流量回落到上联。路由仍然装着、但隧道已经死掉,是另一种状态:数据包继续被送进一条不可用的路径,而不是回落,症状是无声丢包,而不是看得见的直连。两种都要测,启动、重连和停服时的行为也要测。
这样的规则只在它存在、并且数据包仍然带着选中它的标记时才起作用。一个删掉规则的关机脚本,或者一条剥掉标记的路径,都会让开放的旧行为回来。这些转换值得演练。
IPv6 要单独应用这套策略,已批准的端点例外要写清楚,而不是靠默认。