Linux Netfilter:连接跟踪如何跟踪 NAT 更改的连接?

idl*_*n89 5 networking linux nat

目前我正在深入研究 Linux 的 Netfilter 架构的细节。我已经熟悉 Netfilter 钩子、表、链、不同的内核模块等。
但是我不了解 NAT 结合连接跟踪的一个细节。

我尝试用一​​个小 SNAT 示例来解释我的问题。在这个例子中,我将忽略传输层,因为我认为没有必要理解我的问题。如果我错了,请纠正我!

本地网络上有一台 IP 地址为 192.168.2.55 的客户端计算机和一个外部 IP 地址为 193.157.56.3 的 NAT 网关。现在客户端想要与服务器通信,IP 地址为 217.254.1.76 在 Internet 上。
因此,客户端将一个 src = 192.168.2.55/dst = 217.254.1.76 的数据包发送到 NAT 网关,该网关也是默认网关。连接跟踪跟踪这个新连接并创建两个新元组:

IP_CT_DIR_ORIGINAL:src = 192.168.2.55,dst = 217.254.1.76
IP_CT_DIR_REPLY:src = 217.254.1.76,dst = 192.168.2.55

IP_CT_DIR_ORIGINAL 和 IP_CT_DIR_REPLY 是访问两个元组数组的宏。

在 POSTROUTING 钩子处,NAT 要求对现有连接进行连接跟踪,如果成功,则更改数据包标头中的源地址。它还悄悄地创建了一个 DNAT 规则来恢复回复的目标地址。
现在我说到我的问题了。NAT 将 IP_CT_DIR_REPLY 中的目标地址更改为其外部 IP 地址 193.157.56.3。所以元组看起来像这样:

IP_CT_DIR_ORIGINAL:src = 192.168.2.55,dst = 217.254.1.76
IP_CT_DIR_REPLY:src = 217.254.1.76,dst = 193.157.56.3

这就是连接跟踪可以跟踪 PREROUTING 钩子中的回复的原因,因为有一个现有的回复元组。但是在跟踪数据包后,NAT 将目标地址更改为客户端的地址 192.168.2.55。
现在我的问题是:连接跟踪如何在 POSTROUTING 钩子中跟踪这个数据包?没有 src = 217.254.1.76/dst = 192.168.2.55 的回复元组,因为 NAT 改变了它。
有什么我错过了吗?

A.B*_*A.B 8

您应该conntrackhttp://conntrack-tools.netfilter.org/安装通常打包为 conntrack 或 conntrack-tools的命令。它将主要显示相同的内容,/proc/net/nf_conntrack但可以做更多。

在 NAT 网关上以事件模式运行 conntrack:(conntrack -E或者您可以选择conntrack -E --proto tcp --orig-port-dst 443限制为 HTTPS)。现在,您之前使用 HTTPS 请求的示例将提供类似于以下内容的内容:

    [NEW] tcp      6 120 SYN_SENT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 [UNREPLIED] src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798
 [UPDATE] tcp      6 60 SYN_RECV src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798
 [UPDATE] tcp      6 432000 ESTABLISHED src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
 [UPDATE] tcp      6 120 FIN_WAIT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
 [UPDATE] tcp      6 60 CLOSE_WAIT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
 [UPDATE] tcp      6 30 LAST_ACK src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
 [UPDATE] tcp      6 120 TIME_WAIT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
[DESTROY] tcp      6 src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
Run Code Online (Sandbox Code Playgroud)

nat 表是特殊的,因为每个流*在创建状态时使用一次[NEW]。其他所有内容都被找到的 conntrack 条目短路。POSTROUTING 中的 SNAT 规则没有为回复“悄悄地创建 DNAT 规则”。它将 conntrack 条目更改为回复 dst=193.157.56.3,并告诉 netfilter“我更改了一些内容”,这就是大部分内容。其他所有内容(包括源 ip 更改)都由 conntrack(模块 nf_conntrack、nf_conntrack_ipv4)和 nat(模块 nf_nat、nf_nat_ipv4 和这里可能还有一些)处理,而不是由 iptables 处理。考虑条目是连接状态数据库。

当收到回复数据包时,没有存储关联的数据包会通过 conntrack 条目进行查看(实际上是双向查找关联,无论是在原始部分还是在回复部分,这都无关紧要),并且找到匹配,因为条目是之前创建的。然后用这个连接关联更新打包信息。尽管稍后不再需要查找此数据包的条目,即使其某些属性(源或目标...)发生更改,信息也可直接使用。处理此问题的一些宏/内联函数在skbuff.h中定义。寻找_nfctnfct。数据包(又名 skbuff)在查找之后,在skb->_nfct.

所以你可能错过了一些事情:

  • iptables 不是 netfilter。它是 netfilter 的用户。nftables 是 netfilter 的另一个用户。conntrack 和 nat(例如模块 nf_nat)是 netfilter 的一部分。
  • POSTROUTING 钩子永远不会看到回复数据包,因为连接不是新的:不再为此流调用 nat 表,并且数据包被识别为该流的一部分。
  • conntrack 和 nat 的大部分处理都是由 conntrack 和 nat 完成的,而不是由 iptables 完成的。iptables 可以使用 conntrack(例如:)-m conntrack --ctstate ESTABLISHED或 nat(nat 表中的任何内容都必须)的资源。对于上面的示例,单独的 conntrack 条目具有“取消 SNAT”数据包的信息,实际上它看起来类似于具有最初入站连接的 DNAT。
  • 对于现有连接的数据包部分,通常不需要多次查找 conntrack 条目,查找后将“索引”附加到数据包。

通过运行几次iptables-save -c并查看计数器如何为 nat 表中的规则递增:仅针对第一个数据包,您可以确信 nat 表在第一个数据包之后没有看到其他数据包。

*看看NAT部分:

该表与“过滤器”表略有不同,因为只有新连接的第一个数据包会遍历该表:此遍历的结果然后应用于同一连接中所有未来的数据包。