Linux 基础知识:连接跟踪 conntrack 入门


连接跟踪是许多网络功能及应用的基础,例如:Kubernetes Service、ServiceMesh sidecar、 软件四层负载均衡器 LVS/IPVS、Docker network、OVS、iptables 主机防火墙等等,都依赖连接跟踪功能。
不过这里的链接需要同 TCP 协议中的连接区分开来,它指的是通信的两个端点之间用于传输数据的连接,因此它不止可以用来跟踪 TCP 的连接,还可以跟踪 UDP、ICMP 协议报文这样的「连接」。
概念
连接跟踪,顾名思义,就是跟踪(并记录)连接的状态。
如下图所示,这是一台 IP 地址为 10.1.1.3 的 Linux 机器,能看到这台机器上有两条连接:
机器访问外部 HTTP 服务的连接(目的端口 80)。
机器访问外部 DNS 服务的连接(目的端口 53)。

连接跟踪所做的事情就是发现并跟踪这些连接的状态,具体包括:
从数据包中提取元组(tuple)信息,辨别数据流(flow)和对应的连接(connection)
为所有连接维护一个状态数据库(conntrack table),例如连接的创建时间、发送 包数、发送字节数等等
回收过期的连接(GC)
为更上层的功能(例如 NAT)提供服务
需要注意的是,连接跟踪中所说的“连接”,概念和 TCP/IP 协议中“面向连接”( connection oriented)的“连接”并不完全相同,简单来说:
TCP/IP 协议中,连接是一个四层(Layer 4)的概念。
TCP 是有连接的,或称面向连接的(connection oriented),发送出去的包都要求对端应答(ACK),并且有重传机制
UDP 是无连接的,发送的包无需对端应答,也没有重传机制
CT 中,一个元组(tuple)定义的一条数据流(flow )就表示一条连接(connection)。
后面会看到 UDP 甚至是 ICMP 这种三层协议在 CT 中也都是有连接记录的
但不是所有协议都会被连接跟踪
本文中用到“连接”一词时,大部分情况下指的都是后者,即“连接跟踪”中的“连接”。
原理
当加载内核模块 nf_conntrack 后,conntrack 机制就开始工作。网络数据包通过 Netfilter 时的工作流向,conntrack(椭圆形方框)在内核中有两处位置(PREROUTING 和 OUTPUT 之前)能够跟踪数据包。

每个通过 conntrack 的数据包,内核都为其生成一个 conntrack 条目用以跟踪此连接,对于后续通过的数据包,内核会判断若此数据包属于一个已有的连接,则更新所对应的 conntrack 条目的状态(譬如更新为 ESTABLISHED 状态),否则内核会为它新建一个 conntrack 条目。
所有的 conntrack 条目都存放在一张表里,称为连接跟踪表(conntrack table)。连接跟踪表存放于系统内存中,可用 cat /proc/net/nf_conntrack 命令查看当前跟踪的所有 conntrack 条目,conntrack 维护的所有信息都包含在条目中,通过它就可以知道某个连接处于什么状态。
如下则为表示一条状态为 ESTABLISHED 的 TCP 连接。
cat /proc/net/nf_conntrack...ipv4     2 udp      17 11 src=x.x.x.x dst=x.x.x.x sport=43365 dport=53 packets=1 bytes=52 src=10.224.2.49 dst=10.224.2.50 sport=53 dport=43365 packets=1 bytes=52 mark=2 zone=65520 delta-time=18 use=2...应用
conntrack 是许多高级网络应用的基础,譬如经常使用的 NAT(Network Address Translation,网络地址转换)、iptables 的状态匹配等。
NAT 
如下图所示,机器自己的 IP 10.1.1.3 可以与外部正常通信,但 192.168 网段是私有 IP 段,外界无法访问,源 IP 地址是 192.168 的包,其应答包也无法回来,因此:
当源地址为 192.168 网段的包要出去时,机器会先将源 IP 换成机器自己的 10.1.1.3 再发送出去,进行 SNAT(对源地址 source 进行 NAT)。
收到应答包时,再进行相反的转换,进行 DNAT(对目的地址 destination 进行 NAT)。

当 NAT 网关收到内部网络的请求包之后,会做 SNAT,同时将本次连接记录保存到连接跟踪表,当收到响应包之后,就可以根据连接跟踪表确定目的主机,然后做 DNAT,DNAT + SNAT 其实就是 Full NAT,如下图所示。

部署 Kubernetes 时有一条配置 net.bridge.bridge-nf-call-iptables = 1,很多同学不明其意,笔者结合 conntrack 说明这个配置的作用。
首先 Kubernetes 的 Service 本质是个反向代理,访问 Service 时会进行 DNAT,将原本访问 ClusterIP:Port 的数据包 NAT 成 Service 的某个 Endpoint (PodIP:Port),然后内核将连接信息插入 conntrack 表以记录连接,目的端回包的时候内核从 conntrack 表匹配连接并反向 NAT,这样原路返回形成一个完整的连接链路。
但是 Linux Bridge(Linux 网桥)是一个虚拟的二层转发设备,而 iptables conntrack 工作在三层。所以问题来了,如果直接访问同一网桥内的地址,会走二层转发,不经过 conntrack,由于没有原路返回,客户端与服务端的通信就不在一个 「频道」 上,不认为处在同一个连接,也就无法正常通信。
设置 bridge-nf-call-iptables 这个内核参数 (设置为 1),表示 bridge 设备在二层转发时也去调用 iptables 配置的三层规则 (包含 conntrack),所以开启这个参数就能够解决上述 Service 同节点通信问题。
这也是为什么在 Kubernetes 环境中,大多都要求开启 bridge-nf-call-iptables 的原因。
四层负载均衡(L4LB)
再将范围稍微延伸一点,讨论一下 NAT 模式的四层负载均衡。
四层负载均衡是根据包的四层信息(例如 src/dst ip, src/dst port, proto)做流量分发。
VIP(Virtual IP)是四层负载均衡的一种实现方式:
多个后端真实 IP(Real IP)挂到同一个虚拟 IP(VIP)上
客户端过来的流量先到达 VIP,再经负载均衡算法转发给某个特定的后端 IP
如果在 VIP 和 Real IP 节点之间使用的 NAT 技术(也可以使用其他技术),那客户端访 问服务端时,L4LB 节点将做双向 NAT(Full NAT),数据流如下图所示:


内核实现
请移步参考:https://arthurchiao.art/blog/conntrack-design-and-implementation-zh
相关内核参数
参数名称 参数含义 建议值

net.netfilter.

nf_conntrack_buckets


net.netfilter.

nf_conntrack_max

连接跟踪表容量,可视业务场景调整。

计算桶占用率= [nf_conntrack_count] / [nf_conntrack_buckets]。

通过调整buckets值,将占用率调整至0.7以下,查看参数:

sysctl net.netfilter.nf_conntrack_buckets

sysctl net.netfilter.nf_conntrack_max


net.netfilter.

nf_conntrack_tcp_timeout_close

连接跟踪表里处于close状态连接的表项的过期时间

缩短过期时间可加快回收,查看参数:

sysctl net.netfilter.nf_conntrack_tcp_timeout_close

net.netfilter.

nf_conntrack_tcp_be_liberal


参数值为 0 或 1。

  • 0:表示关闭,所有不在TCP窗口中的RST包都被标志为无效。

  • 1:表示开启,只有不在TCP窗口内的RST包被标志为无效。

容器场景下,开启这个参数可以避免 NAT 过的 TCP 连接带宽受限。

net.netfilter.

nf_conntrack_count

nf_conntrack表示当前连接数

查看:

sysctl net.netfilter.nf_conntrack_count


conntrack 命令
conntrack命令支持多种参数,用于执行不同的操作:
-L, --list:列出连接跟踪表中的所有条目。-G, --get:获取单个连接跟踪条目的信息。-D, --delete:从连接跟踪表中删除条目。-I, --create:创建一个新的连接跟踪条目。-U, --update:更新已存在的连接跟踪条目。-E, --event:监听连接跟踪事件。
输出结果解析
conntrack -L 输出结果解析:
协议:表示连接使用的协议,如tcp、udp等。
协议号:表示连接使用的协议号,如6代表tcp连接、17代表udp连接等。
TCP状态计数器,可以理解为超时时间(Timeout):表示连接条目在连接跟踪表中保持的剩余时间(秒)。当这个时间到达0时,条目将被自动从跟踪表中移除。
对于TCP连接,该数字表示连接跟踪条目在内核连接跟踪表中的剩余时间。这个计时器的值会根据连接的状态(如ESTABLISHED、TIME_WAIT等)和配置的超时设置变化。计时器的值为零时,连接跟踪条目会从表中删除。
对于非TCP连接(如UDP),因为UDP是无连接的,所以这个计数器代表连接跟踪条目在没有任何新的数据包更新的情况下,还可以在连接跟踪表中存活多长时间。
连接状态:描述TCP连接的当前状态。
ESTABLISHED:已建立连接
TIME_WAIT:等待足够的时间以确保远程TCP接收到连接终止请求的确认)
CLOSE_WAIT:CLOSE_WAIT状态表示对端(远程主机)已经关闭了连接的一半(发送了一个FIN包),并且本地端(你的计算机)已经接收到这个关闭请求,但是本地应用程序还没有关闭(或者说还没有调用close来关闭连接)。简单来说,CLOSE_WAIT状态意味着TCP连接在等待本地应用程序去关闭连接。
SYN_SENT:客户端已发送一个连接请求(SYN包)给服务器,并等待服务器的确认。这表示客户端已经开始了TCP三次握手过程
SYN_RECE:服务器收到客户端的SYN包,并回应一个SYN+ACK包,等待客户端的确认。这个状态表示服务器端已经响应了连接请求,正在进行三次握手的第二步。
FIN_WAIT_1:一方(通常是客户端)决定关闭连接,并发送一个FIN包给对方,等待对方的确认。这标志着连接关闭过程的开始
FIN_WAIT_2:在发送FIN包并收到ACK包后,连接进入FIN_WAIT_2状态。在这个状态下,连接的关闭一方等待对方的FIN包。
CLOSE_WAIT:当一方收到另一方的FIN包,即对方请求关闭连接时,它会发送一个ACK包作为回应,并进入CLOSE_WAIT状态。在这个状态下,等待本地应用程序关闭连接。
CLOSING:在同时关闭的情况下,当双方几乎同时发送FIN包时,连接会进入CLOSING状态,表示双方都在等待对方的FIN包的确认。
LAST_ACK:当处于CLOSE_WAIT状态的一方发送FIN包,并等待对方的最终ACK包时,连接进入LAST_ACK状态。
TIME_WAIT:在收到对方的FIN包并发送ACK包后,连接进入TIME_WAIT状态。这个状态持续一段时间(2倍的MSL,最大报文生存时间),以确保对方收到了最终的ACK包。这也允许老的重复数据包在网络中消失。
CLOSED:连接完全关闭,两端都释放了连接的资源。
源地址(src)、目的地址(dst):发送数据包的主机的IP地址。src=源IP dst=目的IP:表示数据包的源和目的IP地址。
源端口(sport)、目的端口(dport):发送数据包的主机的端口号。sport=源端口 dport=目的端口:表示数据包的源和目的端口号。
[UNREPLIED] / [ASSURED]:
[UNREPLIED]:表示从源到目标的连接请求尚未收到回复。
[ASSURED]:表示连接已经被确认,不会因为短时间内没有数据包而被清除。
Mark(标记):mark字段用于表示特定的连接跟踪条目被打上的标记(或称为标签)
这个标记是一个整数值,通常由防火墙规则(如iptables)设置,用于对特定的数据包或连接进行分类或执行特定的处理逻辑。
可能的取值:mark的取值范围是从0到4294967295(即2^32-1),其中0通常表示未被标记的连接。非零的值则根据实际的防火墙规则和策略而定,不同的值可以代表不同的分类或处理逻辑。例如,在某些配置中,mark可以用来区分经过VPN的流量、被特定规
Use(使用):use字段表示当前有多少个内核组件正在引用这个连接跟踪条目。简而言之,它表明这个连接跟踪条目的“使用度”或“引用计数”。
可能的取值:use的取值是一个正整数,起始值至少为1,表示至少有一个引用(即连接跟踪本身)。如果有多个内核组件因为某些原因(如特定的路由或防火墙规则)同时需要跟踪这个连接,use的值会相应增加。一般而言,大多数情况下use的值会比较低,除非系统配置导致多个组件需要引用同一个连接跟踪条目。
常见用法
命令 说明
conntrack -L -p tcp 查看所有 TCP 连接
conntrack -L -p udp 查看所有 UDP 连接
conntrack -L -p tcp --state ESTABLISHED

显示具有特定状态的连接条目


conntrack -L -s 192.168.201.111 显示来自或发送到特定IP的连接条目
conntrack -E

监听连接跟踪事件

实时显示连接跟踪事件,如新建(NEW)、更新(UPDATE)和销毁(DESTROY)事件。

conntrack -D --src 192.168.1.2 删除指定源地址的连接
conntrack -D --state TIME_WAIT 删除所有处于特定状态的连接

到顶部