最近排查的一个网络问题,两个 IP 之间的网络不通,经过在 Linux 上一个一个 interface 上抓包,发现包丢在了本地的 bridge 上。
Bridge 就是一个简单的二层设备,虽然是虚拟的,但是应该逻辑也很简单,怎么会丢包呢?
经过一通乱查,发现 Bridge 的包跑到了 iptables 里面去,被 iptables 的 FORWARD chain DROP 了。
说到这里跑个题,我有一个排查 iptables 是哪一条 rule 丢包的妙计,就是 watch -d "iptables -nvL | grep DROP
,watch 会监控引号中的脚本,脚本会过滤出来所有会丢包的 rule,-d
参数很关键,它可以让 watch
每次对比和上一次命令的不通,然后高亮出来。一眼定位到问题。
话说回来,bridge 一个二层的设备怎么会跑到 iptables 里面去?iptables 可是 IP tables,这是三层呀。
在 Linux 中有一个机制,可以让 layer 2 的 bridge 代码调用 iptables, arptables, ip6tables 等。这样能做的事情就比 BROUTING chain (Bridge Routing1) 更多。可以在 bridge 上通过 iptables 做 dnat, stateful firewall, conntrack 等2。
如果不需要 bridge 上的包跑到 iptables 上过一遍,可以通过 kernel 参数关闭:
sysctl -w net.bridge.bridge-nf-call-iptables=0
0
的意思是 bridge 的包不会去 iptables,1
就是会去 iptables,默认是 1
. 也是执行完这行命令,网络果然就通了。
Bridge call iptables, 之前是在 kernel 实现的一个功能,但是显然这样会有性能问题。后来就独立出来作为一个独立的 kernel module 了 (br_netfilter
3)。(如果使用 physdev
4就会自动启用这个 module)。
另外,nftables
和 iptables-nft
也会受到影响,layer violation 会有很多复杂的问题。新的 kernel module – nf_conntrack_bridge
5 可以做到直接在 bridge layer 实现 connection track. nftables6 是下一代的 iptables。
前面说过这个功能是一个 kernel module,所以在关闭的时候有一个小小的问题。即我们关闭的时候,可能还没有这个 module load,所以会告诉我们无法设置这个参数:error: "net.bridge.bridge-nf-call-iptables" is an unknown key
。如果后来创建一个 bridge,那么这个 module 会自动 load,那么包就又会跑到 iptables 里面去。
libvert 给出的解决方案7是,通过 udev 来创建一个事件,每次创建 bridge 就执行 sysctl
来 disable net.bridge.bridge-nf-call-iptables
.
- ebtables https://linux.die.net/man/8/ebtables ↩︎
- ebtables/iptables interaction on a Linux-based bridge:https://ebtables.netfilter.org/br_fw_ia/br_fw_ia.html ↩︎
- https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-3.18.y&id=34666d467cbf1e2e3c7bb15a63eccfb582cdd71f ↩︎
physdev
: https://manpages.debian.org/bookworm/iptables/iptables-extensions.8.en.html#physdev ↩︎- nf_conntrack_bridge: https://www.kernelconfig.io/config_nf_conntrack_bridge ↩︎
- nftables wiki: https://wiki.nftables.org/wiki-nftables/index.php/Main_Page ↩︎
- https://wiki.libvirt.org/Net.bridge.bridge-nf-call_and_sysctl.conf.html ↩︎
怎么看着像是运行了 docker 的结果。原来 br_netfilter 模块是干这事的啊,但我很不喜欢它,因为它会把 iptables 弄成我不理解的样子。(docker 的 ip{,6}tables 功能被我关掉了。)
我不知道对此例有没有效果,但是推荐个工具 pwru,查丢包挺好用的。
其实是 VM 都接到了 host 的 bridge 上,不过和 docker 差不多的网络结构。
感谢推荐,我也尝试过这个工具,就是跑起来有一些麻烦,好像是对 kernel 版本有要求?需要一些 BTF 之类的,尝试到一半在 iptables 发现了端倪就直接破案了。(目前还没有成功运行过 pwru T T)
啊对,pwru 这些用 ebpf 的工具都会要求比较新的内核,跑不起来就算啦。我是在 Arch Linux 上用的所以都能用~(然后就发现 docker 开始给我的 ip6tables -A FORWARD -P DROP 了……
watch 默认的 update interval 是 2s,我一般会加上
-n 1
,有时候会刚好错过嗯,但是如果是 iptables, 它的数据是累加的,-n 2 反而不容易错误,因为有 -d 高亮和上次的对比。如果是 -n 1 的话还容易错过一些,比如这一秒增加了 10,下一秒马上刷新,这个 diff 就不会高亮了。
不过我明白你说的那种 -n 1 的场景。适合 gauge 这种非累加的值。
需要看 history 的话,
while true; do xxx; sleep 0.5; done
这种也可以记录下指标的变化历史。嗯,确实是这样。