本文通过一个简单的 NAT 例子,介绍 Linux 的 conntrack 流。

本文只讨论 IPv4、TCP 和 SNAT,不展开 conntrack 状态机、Netfilter Hook、DNAT、NAT Hairpin 等内容。

What

先把核心概念说清楚:

conntrack 流是 Linux 内核为一次双向网络通信维护的一条状态记录。

假设内网客户端访问 Internet 服务器:

1
2
3
内网客户端                               Internet 服务器
10.0.0.2:50000 ----------------------> 203.0.113.10:443
10.0.0.2:50000 <---------------------- 203.0.113.10:443

Linux 会把两个方向关联成同一条 conntrack 流:

1
2
3
4
5
6
7
ORIGINAL 方向:

10.0.0.2:50000 -> 203.0.113.10:443

REPLY 方向:

203.0.113.10:443 -> 10.0.0.2:50000

所以:

1
2
3
4
请求方向 + 返回方向


一条 conntrack 流

conntrack 记录中可以包含:

1
2
3
4
5
原始方向
回复方向
协议状态
超时时间
NAT 映射

Linux 内核的 conntrack 接口也明确区分了 tuple-origtuple-reply 两个方向。

需要注意:

conntrack 流不是一个数据包,也不等于应用程序的 socket。

它是 Linux 内核对一次双向通信维护的状态记录。

Why

假设一台 Linux 机器作为 NAT 网关:

1
2
3
4
5
6
7
8
9
10
内网客户端
10.0.0.2


Linux NAT 网关
公网地址:198.51.100.1


Internet 服务器
203.0.113.10

内网客户端发送:

1
10.0.0.2:50000 -> 203.0.113.10:443

经过 SNAT 后,源地址被修改为网关的公网地址:

1
198.51.100.1:62000 -> 203.0.113.10:443

服务器看到的客户端是:

1
198.51.100.1:62000

因此服务器返回:

1
203.0.113.10:443 -> 198.51.100.1:62000

问题来了:

Linux 网关收到这个返回包后,怎么知道应该把它转发给哪个内网客户端?

网关必须记住下面这组关系:

1
2
3
4
5
198.51.100.1:62000

│ 对应

10.0.0.2:50000

这就是 conntrack 的作用。

它为这次通信保存 NAT 映射,使返回包可以执行反向转换:

1
2
3
4
5
6
7
NAT 前:

203.0.113.10:443 -> 198.51.100.1:62000

NAT 后:

203.0.113.10:443 -> 10.0.0.2:50000

如果没有 conntrack,Linux 网关只看到:

1
目的地址:198.51.100.1:62000

却不知道它原来对应:

1
10.0.0.2:50000

返回包就无法正确送回内网客户端。

基础 Example

完整过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1. 内网客户端发送

10.0.0.2:50000
->
203.0.113.10:443

2. Linux 网关执行 SNAT

198.51.100.1:62000
->
203.0.113.10:443

3. 服务器返回

203.0.113.10:443
->
198.51.100.1:62000

4. Linux 根据 conntrack 记录执行反向 NAT

203.0.113.10:443
->
10.0.0.2:50000

conntrack 在其中保存:

1
2
3
4
5
10.0.0.2:50000

│ SNAT 映射

198.51.100.1:62000

这样,正向数据包和返回数据包就能使用同一套 NAT 映射。

How

简化地说:

1
2
3
4
5
6
7
8
9
10
11
12
13
第一个数据包


创建 conntrack 流


确定并记录 NAT 映射


后续数据包复用这条映射


连接结束或超时后删除记录

对于 nftables 的有状态 NAT,通常只有一条连接的第一个数据包经过 NAT 规则并建立 NAT 绑定,后续数据包根据 conntrack 中已经保存的信息完成转换。

记忆图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
内网客户端
10.0.0.2:50000

│ 发送请求

┌─────────────────────────────┐
│ Linux NAT 网关 │
│ │
│ conntrack 记录: │
│ │
│ 10.0.0.2:50000 │
│ ↕ │
│ 198.51.100.1:62000 │
└─────────────────────────────┘

│ SNAT 后发送

Internet 服务器
203.0.113.10:443

可以简单记成:

NAT 修改了地址,conntrack 记住了地址是怎么修改的。

正向报文使用 NAT 映射:

1
内网地址 -> 公网地址

返回报文使用反向映射:

1
公网地址 -> 内网地址

这就是 conntrack 流在 NAT 中最核心的作用。


参考资料:

  1. Linux Kernel Documentation:conntrack Netlink Specification
  2. Netfilter nftables Manual:NAT chain