Linux系统nf_conntrack连接跟踪机制简介

1、前言

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

   问题介绍:

  • 网络不通,ping,ssh均失败。
  • 查看dmesg日志有报错:kernel: nf_conntrack: nf_conntrack: table full, dropping packet  
  • sysctl net.netfilter.nf_conntrack_count达到了net.netfilter.nf_conntrack_max的值
  • 但查看  cat /proc/net/nf_conntrack | wc -l    只有200多。

    主要问题:nf_conntrack_count计数与/proc/net/nf_conntrack下的记录数不一致

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

2、连接跟踪机制

  • 背景知识:netfilter(这部分主要参考其他介绍资料)

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

       Netfilter相关背景:五链、四表、hook点、hook函数

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

Linux系统nf_conntrack连接跟踪机制简介_第1张图片

       PREROUTING链:在对数据包作路由选择之前,应用此链中的规则。

       INPUT链:处理入站数据包,当接收到访问防火墙本机地址的数据包(入站)时,应用此链中的规则。

       OUTPUT链:处理出站数据包,当防火墙本机向外发送数据包(出站)时,应用此链中的规则。

       FORWARD链:处理转发数据包.当接收到需要通过防火墙发送给其他地址的数据包(转发)时,应用此链中的规则。

       POSTROUTING链:在对数据包作路由选择之后,应用此链中的规则。

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

表名(优先级从上到下)

功能

Raw

决定数据包是否被状态跟踪机制处理

Mangle

修改数据包的服务类型、TTL、并且可以配置路由实现QOS

Nat

用于网络地址转换(IP、端口)

Filter

过滤数据包

表和链的对应关系:

Linux系统nf_conntrack连接跟踪机制简介_第2张图片

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

       Hook函数:在hook点上设置的数据包处理函数。

       关于netfilter中协议类型,hook点,hook函数,优先级的关系,下图可以直观体现:

       Linux系统nf_conntrack连接跟踪机制简介_第3张图片

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

       Linux系统nf_conntrack连接跟踪机制简介_第4张图片

  • nf_conntrack连接追踪模块

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

         nf_conntrack_count模块内部维护者四张表:

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

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

  • 连接加入到nf_conntrack模块流程

       其中conntrack对数据包进行追踪的hook函数即上图中的红框(输入连接)蓝框(输出连接)所对应的函数。下面分析输入连接的跟踪ipv4_conntrack_in和ipv4_confirm函数输出过程的跟踪连接机制同理):

当数据包经过了PRE_ROUNTING hook点:

Linux系统nf_conntrack连接跟踪机制简介_第5张图片

当数据包经过INPUT hook点:

Linux系统nf_conntrack连接跟踪机制简介_第6张图片

  • 连接删除出nf_connntrack模块流程

       通过初步代码分析,确认系统是通过nf_ct_delete函数进行的连接删除工作,后续利用dump_stack函数,打印出调用栈,其删除实际是通过工作队列进行处理的,具体流程如下

当系统调度到worker_thread

Linux系统nf_conntrack连接跟踪机制简介_第7张图片

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

        通过2部分介绍发现:在连接加入系统confirm表以及连接从系统中删除的流程中,均有可能出现nf_conntrack堆积造成问题。

        从连接加入角度分析:连接跟踪块在PRE_ROUNTING hook点执行完nf_ct_add_to_unconfirmed_list后,会加入unconfirm列表,而此时nf_conntrack_count值已然增加;那么此时有两种情况造成其不能加入confirm列表:

  1.  走到INPUT hook点之前被NF_DROP或者NF_STOLEN跳过之后的hook函数流程
  2.  执行ipv4_confirm流程没有走完(这个概率较小)。

       对于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

       该问题通过修改NF_STOLEN处理机制,即同步到NF_DROP来解决,但新版本内核并没有合入该改动。

       从连接删除角度分析:可以看出当confirm列表中的连接块超时之后,会通过工作队列进行删除工作。其中会先将链接从confirm列表删除,加入dying列表,此时有两种情况会出现nf_conntrack_count计数与confirm列表不同步的问题:

  1. 连接块的连接引用计数不为1
  2.  连接快引用计数为空

       通过上述代码分析,可以发现连接块在“加入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

你可能感兴趣的:(网络子系统,内核,内核,netfilter)