LAN 里的客户端经由隧道解析,而路由器自己的查询却悄然不这样做。 软件包更新、时间同步和动态 DNS 都从普通上行链路出去,而每一个被转发的查询都走 VPN,界面上没有任何迹象表明存在两条不同的路径。 原因不是策略有 bug,而是 哪个钩子看得见数据包 的问题。 两条流,一个解析器地址,只有其中一条穿过路由器。 把它们分开,就是全部的修复。

两条流,一个解析器地址

第一条流是人们想象中的那条。 客户端向路由器的解析器提问,解析器向上游转发,而被转发的数据包在出去时穿过路由器。 任何观察转发流量的人都能看到那个数据包。

第二条流从不来自任何地方。 它由路由器自己创建,所以既没有入站数据包,也没有可匹配的转发步骤。

两者可能指向同一个解析器地址,这正是差异容易被忽略的原因。 同样的目的,不同的来源,不同的链。 看清来源之后,剩下的诊断就顺理成章。

转发的数据包与本地生成的数据包进入方式不同

转发的流量在从另一个接口到达时被评估。 策略路由软件包把它暴露为 prerouting 链,放在那里的规则能看到每一个穿过路由器的数据包。 所以客户端查询的表现与策略描述完全一致,也因此路由器对它视而不见。 规则并没有错,它只是挂在一个路由器永远到不了的钩子上。 把它搬过去,同样的匹配就开始生效。

本地生成的流量由路由器上的进程创建,进入的是输出路径。 它由路由器自己的决定来路由,只有策略覆盖了那条链,它才会遇到策略。 没有可匹配的入站接口,也没有可观察的转发决定。

这是数据包诞生位置的属性,而不是某个特定软件包的属性。 为一条链写的规则,对另一条链根本没有意见。 再怎么调规则也改变不了这一点。

先选输出链,再收紧匹配

修复的办法是把同样的意图也挂到输出链上,或者在你在意路由器自身查询时,把规则搬过去。 在以策略路由软件包为基础的 OpenWrt 配置里,这意味着在规则的链设置中选择 output 而不是 prerouting。 如果两类流量都需要覆盖,就保留两条规则。

然后收紧规则匹配的范围。 在解析器实际使用的端口上匹配目的解析器地址,比把路由器产生的一切都送进隧道要小得多。

让两条规则各自明确,而不是试图写一条覆盖两条链的规则。 分开的规则更易读,也让你在调试时可以停用路由器的路径而不打扰客户端流量。 它们还让计数器一次只说明一件事。

把隧道自身的传输排除在规则之外

隧道的外层连接同样由路由器创建。 一条把所有本地生成的流量都送进隧道的规则,会让隧道经过它自己,于是隧道根本起不来。

第二条受害者是管理访问。 由路由器发起的会话或对它们的回复,可能被一条过宽的本机规则拖进隧道,从而无法从 LAN 访问。

把隧道的端点排除在匹配之外,并让规则只限于解析器地址。 这对 DNS 的需求已经足够,也小到可以推理。

两类流量都要测,不能只测一类

观察路由器发出自己的查询,并记下是哪个接口承载了它。 再从一台 LAN 主机重复查询,记下它走的是哪条路径。 策略规则上的计数器不用猜就能让两个答案都显现。

把结果当作两个独立的发现。 一个经隧道解析的客户端说明不了路由器的事,一个解析正确的路由器也说明不了另一台监听器上的客户端。 在变更记录里把它们写成两行。

端口也要检查:一条匹配目的端口 53 的规则,会漏掉通过 853 端口上的 DNS over TLS 或 HTTPS 端点到达的解析器。 按上游实际使用的传输来匹配,泄漏才会因正确的理由被堵上。

相关文章