桥接服务器已经跑起来了,masquerade 规则也在,但客户端上的浏览器还在转圈。一切看起来都该正常工作,于是第一个小时都花在检查隧道和凭据上。然后你去看 NAT 规则的计数器,发现那个零从来没动过。一条从未计过包的规则也从未匹配过任何包,而在桥接服务器上原因出在结构上:客户端流量从虚拟 tap 设备出去,规则却指向你从旧教程里抄来的物理网卡。

证据是计数器,不是表里的条目

存在的规则和匹配的规则是两回事,把它们分开的正是计数器。用详细计数器列出 NAT 表,在改动任何东西之前先读每条规则旁边的数字。流量在走而计数器停在零,说明这条规则被跳过了,不管它的地址看起来多合理。连接仍然失败而计数器却在涨,那就要往别处查:转发、回程路由,或者远端。

iptables -t nat -L POSTROUTING -v -n

先读接口匹配旁边的计数器。如果源地址段对而出口侧错,修法只是规则里的一个词,而不是另写一条新规则。

找出内核实际记录的名字

在写规则之前,用接口列表而不是凭记忆确认隧道设备的名字。在桥接的 OpenVPN 部署里,该设备通常是服务器创建的 tap 接口,它的名字属于你的本地配置,而不是通用常量。趁隧道还在的时候确认它存在,因为只在客户端连上之后才出现的设备,会让先前的测试看起来像规则问题。

ip -brief link

把名字记下来。这次排查里后面每条命令都要引用它,这里打错一个字,产生的正好是原来那个零计数器。

按隧道设备重写规则

把规则指向隧道设备,并保留客户端地址段作为匹配条件,这样它既覆盖隧道流量,又 不打扰主机本身的连接。把地址段和设备名换成你自己的,因为下面两个都只是示例,谁也不能在安装之间照搬。

iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o tap0 -j MASQUERADE

然后检查同一症状掩盖的另外两个条件。看看转发是否开启,如果没有,就在发行版自己的配置里开启,而不是用一条重启后就消失的 echo。顺便读一下 forward 链的计数器,如果它丢弃隧道流量,就单独放行那个接口,别去动链的默认策略。

带计数器读一遍 forward 链只需要一条命令,却能在你碰下一条规则之前排除掉剩下可能性的一半。

为什么物理网卡永远匹配不上

网桥是一个由 tap 设备和一块物理接口拼成的软件交换机。客户端帧进入这个交换机,然后作为网桥的成员而不是网卡上的包,朝物理网络出去。因此内核把这个包归属到网桥及其端口,这就是你想到的那块网卡从不出现在匹配里的原因。物理接口本身没有任何损坏,它只是不是内核给这个包归属的设备。

接下来人们遇到的另一个问题来自同一套结构:网桥上的帧过滤与 IP 过滤是两套机制。如果需求是帧级的限制,那它 属于网桥过滤,而不是 IP 表规则。

端到端验证,然后持久化

启动一个客户端,ping 一下服务器局域网里的东西;这条路径只考验转发,不涉及地址转换。然后再请求一个公网地址,让 masquerade 规则必须被用到,再读一次计数器:它应该为这次测试而动,而不是为上一次。在隧道设备上抓包,既能看出帧在哪里出现,也能看出它带着哪个地址。

最后一步:把能用的规则写进重启时会读取的配置,并记下这台主机的网桥实际使用的设备名。手敲的东西重启后就没了,同样的故障会再次出现,而那时已经没有任何线索能解释它。

如果重写之后计数器还是不动,就把规则里的源地址段和客户端实际拿到的地址对比一下,因为桥接服务器可能从你并没打算用的地址段里分配地址。

相关文章