文章目录
  1. 1. 问题与解释
  2. 2. 再次梳理流程
  3. 3. 参考

问题与解释

我在PREROUTING上做了一个REDIRECT端口改写,相应的服务处理请求后会返回应答。

按照我以往的认识,认为回包的流量应该先后经过OUTPUTPOSTROUTING,所以我利用iptables -t nat -nvL去查看NAT表在OUTPUT链和POSTROUTING链上的packge计数器,结果发现没有上涨,这让我陷入了沉思。

经过谷歌后找到了完美的解释:linux-netfilter-how-does-connection-tracking-track-connections-changed-by-nat

实际上我是知道OUTPUT链会先过conntrack表恢复原始IP关系的,但是超出我理解的是NAT表压根就不会再执行。

上述URL中给出了解释:NAT表只在连接状态是NEW的时候(也就是TCP的第一个握手包)才会执行计算,一旦改写关系存入了conntrack,那么这条连接后续的通讯就不会再过POSTROUTINGOUTPUT上面的NAT表了,而是直接换成了匹配conntrack来复原连接之前的改写状态。

因此,如果我们想看到回包的package计数器增长,就应该去看OUTPUT或者POSTROUTING上面的filter表计数,一定会看到上涨。

再次梳理流程

如果我们是服务端,那么SYN包到达的时候,在PREROUTING链的NAT表执行过之后(可能做DNAT或者REDIRECT),路由表将决定是FORWARD还是INPUT

  • 如果INPUT,那么conntrack记录就此生成(原理如下),当回包的时候会首先根据conntrack作地址复原,并且是不会经过OUTPUT/POSTROUTINGNAT表(但是会经过filter表)的。
  • 如果FORWARD,那么conntrack记录不会立即生成,需要经过POSTROUTING之后才知道是否做了SNAT/MASQUERADE,此时才会生成conntrack记录(原理如下)。当收到上游回包的时候,不会过PREROUTINGNAT表,而是直接根据conntrack复原为原始IP地址,然后直接FORWARD->POSTROUTING(不会过NAT表)送回原始客户端。

具体原理如下:

如上图所示,Netfilter 在四个 Hook 点对包进行跟踪:

  • PRE_ROUTINGLOCAL_OUT:调用 nf_conntrack_in() 开始连接跟踪, 正常情况下会创建一条新连接记录,然后将 conntrack entry 放到 unconfirmed list

    为什么是这两个 hook 点呢?因为它们都是新连接的第一个包最先达到的地方,

    • PRE_ROUTING 是外部主动和本机建连时包最先到达的地方
    • LOCAL_OUT 是本机主动和外部建连时包最先到达的地方
  • POST_ROUTINGLOCAL_IN:调用 nf_conntrack_confirm()nf_conntrack_in() 创建的连接移到 confirmed list

    同样要问,为什么在这两个 hook 点呢?因为如果新连接的第一个包没有被丢弃,那这 是它们离开 netfilter 之前的最后 hook 点:

    • 外部主动和本机建连的包,如果在中间处理中没有被丢弃,LOCAL_IN 是其被送到应用(例如 nginx 服务)之前的最后 hook 点
    • 本机主动和外部建连的包,如果在中间处理中没有被丢弃,POST_ROUTING 是其离开主机时的最后 hook 点

参考

文章目录
  1. 1. 问题与解释
  2. 2. 再次梳理流程
  3. 3. 参考