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也可以,但这个操作不保险。

或者说在路由层面通过两个路由(有点像是所谓的旁路由方案),但依然折腾。

可能在一个不能直接互联的网里,这种事情就是无法避免的吧。我大多数的网络知识都来自于这种攻防对抗,但我并不会因此而感谢这种对抗。