# nf\_conntrack连接跟踪机制

## 1、前言

前段时间接手的一个连接跟踪表满导致网络不通的问题。

&#x20;  **问题介绍：**

* 网络不通，ping，ssh均失败。
* 查看dmesg日志有报错：kernel: nf\_conntrack: nf\_conntrack: table full, dropping packet &#x20;
* sysctl net.netfilter.nf\_conntrack\_count达到了net.netfilter.nf\_conntrack\_max的值
* 但查看  cat /proc/net/nf\_conntrack | wc -l    只有200多。

&#x20;   **主要问题：nf\_conntrack\_count计数与/proc/net/nf\_conntrack下的记录数不一致**

为进一步调查这个问题，对linux系统的连接跟踪模块进行了学习。本文梳理一下目前对nf\_conntrack模块的一些个人理解。

## 2、连接跟踪机制

* ### **背景知识：netfilter（这部分主要参考其他介绍资料）**

&#x20;      Netfilter是一个内核架构，是集成到linux内核协议栈的一套防火墙系统，可通过用户空间（iptables等）的工具来把相关配置下发给Netfilter。

&#x20;      **Netfilter相关背景：五链、四表、hook点、hook函数**

&#x20;      **五链**：**内核在网络上定义的五个关键位置**，进路由(PREROUTING)、进系统(INPUT) 、转发(FORWARD)、出系统(OUTPUT)、出路由(POSTROUTING)。

![](https://img-blog.csdnimg.cn/20200506213444537.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MzgyMDYy,size_16,color_FFFFFF,t_70)

&#x20;      **PREROUTING链:**&#x5728;对数据包作路由选择之前，应用此链中的规则。

&#x20;      **INPUT链：**&#x5904;理入站数据包，当接收到访问防火墙本机地址的数据包(入站)时，应用此链中的规则。

&#x20;      **OUTPUT链：**&#x5904;理出站数据包，当防火墙本机向外发送数据包(出站)时，应用此链中的规则。

&#x20;      **FORWARD链：**&#x5904;理转发数据包.当接收到需要通过防火墙发送给其他地址的数据包(转发)时，应用此链中的规则。

&#x20;      **POSTROUTING链:**&#x5728;对数据包作路由选择之后，应用此链中的规则。

&#x20;      **四表：即每个链中存储的规则。**&#x6570;据包到了上述的“链”处时，就会去对应“表”中查询设置的规则，然后决定是否放行、丢弃、转发还是修改等等操作。具体的四表为：

| **表名（优先级从上到下）** | **功能**                       |
| --------------- | ---------------------------- |
| Raw 表           | 决定数据包是否被状态跟踪机制处理             |
| Mangle表         | 修改数据包的服务类型、TTL、并且可以配置路由实现QOS |
| Nat表            | 用于网络地址转换（IP、端口）              |
| Filter表         | 过滤数据包                        |

表和链的对应关系：

![](https://img-blog.csdnimg.cn/20200506213817541.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MzgyMDYy,size_16,color_FFFFFF,t_70)

&#x20;      **Hook点：五链对应的位置我们又称之为hook点。**&#x6BCF;个hook点上针对不同的协议、不同的hook函数优先级，分配了一系列hook函数。数据包每到一个hook点，各协议会**按照hook函数优先级从小到大的顺序执行hook函数**。

&#x20;      **Hook函数：在hook点上设置的数据包处理函数。**

&#x20;      关于**netfilter中协议类型，hook点，hook函数，优先级的关系**，下图可以直观体现：

&#x20;      ![](https://img-blog.csdnimg.cn/20200506213943451.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MzgyMDYy,size_16,color_FFFFFF,t_70)

&#x20;      下图对更加详细的列出了各个阶段的hook函数

&#x20;      ![](https://img-blog.csdnimg.cn/20200506214026328.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MzgyMDYy,size_16,color_FFFFFF,t_70)

* ### **nf\_conntrack连接追踪模块**

&#x20;      nf\_conntrack模块维护着系统的一个链接追踪表。内部用1个哈希表记录已建立的连接，包括其他机器到本机、本机到其他机器、本机到本机（例如 ping 127.0.0.1 也会被跟踪）。如果连接进来比释放的快，把哈希表塞满了，新连接的数据包会被丢掉，同时dmesg日志会打印kernel: nf\_conntrack: nf\_conntrack: table full, dropping packet信息，外部表现为网络不通。

&#x20;        **nf\_conntrack\_count模块内部维护者四张表：**

1. **Confirm表**：系统正式连接追踪表，即/proc/net/nf\_conntrack看到的列表
2. **Expired表**：期望值表（暂没有深入了解）
3. **Unconfirm表**：记录已经进入连接追踪系统，但还没有加入正式追踪表的连接
4. **Dying表**：记录已经在正式追踪表超时删除，等待系统最终删除的连接块

&#x20;       net.netfilter.nf\_conntrack\_count统计的是上述所有表中连接的数量，而/proc/net/nf\_conntrack看到的信息只有confirm表的信息。**所以就要确认其他连接是存在哪张表里面了。首先要先理解连接加入confirm表和从confirm表删除的机制**

* **连接加入到nf\_conntrack模块流程**

&#x20;      其中conntrack对数据包进行追踪的hook函数即上图中的红框（输入连接）、蓝框（输出连接）所对应的函数。下面分析输入连接的跟踪ipv4\_conntrack\_in和ipv4\_confirm函数（输出过程的跟踪连接机制同理）：

**当数据包经过了PRE\_ROUNTING hook点：**

![](https://img-blog.csdnimg.cn/20200506214238908.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MzgyMDYy,size_16,color_FFFFFF,t_70)

**当数据包经过INPUT hook点：**

![](https://img-blog.csdnimg.cn/20200506214322205.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MzgyMDYy,size_16,color_FFFFFF,t_70)

* **连接删除出nf\_connntrack模块流程**

&#x20;      通过初步代码分析，确认系统是通过nf\_ct\_delete函数进行的连接删除工作，后续利用dump\_stack函数，打印出调用栈，其删除实际是通过工作队列进行处理的，具体流程如下

**当系统调度到worker\_thread**

![](https://img-blog.csdnimg.cn/20200506214419368.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MzgyMDYy,size_16,color_FFFFFF,t_70)

## 3、连接跟踪机制可能出的问题

&#x20;       通过2部分介绍发现：在连接加入系统confirm表以及连接从系统中删除的流程中，均有可能出现nf\_conntrack堆积造成问题。

&#x20;       **从连接加入角度分析：**&#x8FDE;接跟踪块在PRE\_ROUNTING hook点执行完nf\_ct\_add\_to\_unconfirmed\_list后，会加入unconfirm列表，而此时nf\_conntrack\_count值已然增加；那么此时有两种情况造成其不能加入confirm列表：

1. &#x20;**走到INPUT hook点之前被NF\_DROP或者NF\_STOLEN跳过之后的hook函数流程**
2. &#x20;**执行ipv4\_confirm流程没有走完（这个概率较小）。**

&#x20;      对于1中提到的情况，通过本地构造测试，返回NF\_DROP会直接丢弃该包，nf\_conntrack\_count也会对应清除；返回NF\_STOLEN则会使数据包一直在unconfirm列表中，nf\_conntrack\_count不减少。同时网上也找到了因错误返回NF\_STOLEN造成nf\_conntrack\_count堆积的案例：<https://www.spinics.net/lists/netfilter-devel/msg11924.html>

&#x20;      该问题通过修改NF\_STOLEN处理机制，即同步到NF\_DROP来解决，但新版本内核并没有合入该改动。

&#x20;     **从连接删除角度分析：**&#x53EF;以看出当confirm列表中的连接块超时之后，会通过工作队列进行删除工作。其中会先将链接从confirm列表删除，加入dying列表，此时有两种情况会出现nf\_conntrack\_count计数与confirm列表不同步的问题：

1. **连接块的连接引用计数不为1**
2. &#x20;**连接快引用计数为空**

&#x20;      **通过上述代码分析，可以发现连接块在“加入confirm表”以及“从confirm列表删除”的过程中，均有可能出现nf\_conntrack\_count计数与confirm列表不对应的现象。具体要在问题环境通过conntack工具查看，这个工具的使用和操作技巧，下一篇再具体介绍。**

**参考资料：**

<https://blog.csdn.net/wuruixn/article/details/7957368>

<https://www.cnblogs.com/codestack/p/10850669.html>

<https://blog.csdn.net/jasonchen_gbd/article/details/44873089>
