TPROXY's different behavior in different kernel

Q: 某代理软件如果不关闭tproxy重启软件会断网是不是应该让代理软件接管对防火墙的修改操作

A: 摘录聊天记录如下

silver: tproxy这个其实还是防火墙……防火墙这个就tm麻烦了

你看你有iptables还有nft

然后iptables还有iptables-legacyiptables-netfilter

用户机器上有可能是个iptables-legacybinary但他内核实现是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.cxt_TPROXY.c
  • 5.x+的版本xftables彻底被干掉了就剩下nftables的实现了

所以在一个4.x的内核上你就能体验到两种不同实现的差异如果tproxy重定向到的那个端口上没有一个listening socket

  • 如果走了nftables的实现那么规则无效继续执行其他规则换句话说网络是正常的: nft_tproxy.c#L143
  • 如果走的是xftables的老实现那么丢包: xt_TPROXY.c#L184

我搞了个测试环境复现了下换句话说你必须用nftables不能用iptables-natiptables-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也可以但这个操作不保险

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

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