TPROXY's different behavior in different kernel
Q: 某代理软件如果不关闭tproxy,重启软件会断网。是不是应该让代理软件接管对防火墙的修改操作?
A: 摘录聊天记录如下
silver: tproxy这个其实还是防火墙……防火墙这个就tm麻烦了
你看你有iptables,还有nft
然后iptables还有iptables-legacy和iptables-netfilter
用户机器上有可能是个iptables-legacy的binary,但他内核实现是netfilter的
也有可能用户机器上既有nft 又有iptables-netfilter,然后他两个都配了规则(
如果是nft,有可能用户同时配了table ip/table ip6/table inet
tl; dr: linux firewall is messy
群友提到其他软件的用户会使用Namespace解决这个问题,这样就不需要改防火墙了,进程死掉的时候顺便也删掉规则。但:
exactly but如果想跑在路由器上,那对应版本的内核有没有namespace
uname -a: linux 2.6.32
gg
所以我觉得你的问题其实是在挂掉的时候怎么优雅处理tproxy
于是吃了个晚饭后翻源码:
silver: 调研了下,这个case是这样的。
- Linux <=3.x的版本,这个功能只有一个实现,也就是xftables项目的xt_TPROXY.c
- =4.x的版本,因为nftables也来了,所以有两个实现,nft_tproxy.c和xt_TPROXY.c
- 5.x+的版本,xftables彻底被干掉了,就剩下nftables的实现了。
所以在一个4.x的内核上你就能体验到两种不同实现的差异。如果tproxy重定向到的那个端口上没有一个listening socket:
- 如果走了nftables的实现,那么规则无效,继续执行其他规则,换句话说网络是正常的:
nft_tproxy.c#L143
- 如果走的是xftables的老实现,那么丢包:
xt_TPROXY.c#L184
我搞了个测试环境复现了下,换句话说你必须用nftables,不能用iptables-nat(iptables-nat出于兼容性还是用了xftables的实现),也不能用iptables-legacy,这样就算tproxy背后的socket没了,网络也正常
so tl; dr: blame upstream
群乌贼: 草
silver: 讲道理你想tproxy就是把一个应该丢到网络设备的包丢到了socket上(
所以socket没了内核肯定是知道的(
所以肯定内核做了什么操作才会丢包(
群友A: 云理解就是做没做的问题 吧(
silver: 对,老内核就是看到socket不存在就丢包,新内核就是看到socket不存在就当这条规则不存在,继续执行下一跳规则
所以如果你用4.x的话,用nft写规则;或者你用5.x+的内核就好了
当然这个事情解法很多。用tun/tap device结合路由规则就不需要考虑tproxy的问题,但涉及到策略路由,有一定门槛,而且对应用实现的速度要求就比较高了。
Per-device去分发PAC也可以,但这个操作不保险。
或者说在路由层面通过两个路由(有点像是所谓的旁路由方案),但依然折腾。
可能在一个不能直接互联的网里,这种事情就是无法避免的吧。我大多数的网络知识都来自于这种攻防对抗,但我并不会因此而感谢这种对抗。