前段时间接手的一个连接跟踪表满导致网络不通的问题。
问题介绍:
主要问题:nf_conntrack_count计数与/proc/net/nf_conntrack下的记录数不一致
为进一步调查这个问题,对linux系统的连接跟踪模块进行了学习。本文梳理一下目前对nf_conntrack模块的一些个人理解。
Netfilter是一个内核架构,是集成到linux内核协议栈的一套防火墙系统,可通过用户空间(iptables等)的工具来把相关配置下发给Netfilter。
Netfilter相关背景:五链、四表、hook点、hook函数
五链:内核在网络上定义的五个关键位置,进路由(PREROUTING)、进系统(INPUT) 、转发(FORWARD)、出系统(OUTPUT)、出路由(POSTROUTING)。
PREROUTING链:在对数据包作路由选择之前,应用此链中的规则。
INPUT链:处理入站数据包,当接收到访问防火墙本机地址的数据包(入站)时,应用此链中的规则。
OUTPUT链:处理出站数据包,当防火墙本机向外发送数据包(出站)时,应用此链中的规则。
FORWARD链:处理转发数据包.当接收到需要通过防火墙发送给其他地址的数据包(转发)时,应用此链中的规则。
POSTROUTING链:在对数据包作路由选择之后,应用此链中的规则。
四表:即每个链中存储的规则。数据包到了上述的“链”处时,就会去对应“表”中查询设置的规则,然后决定是否放行、丢弃、转发还是修改等等操作。具体的四表为:
表名(优先级从上到下) |
功能 |
Raw 表 |
决定数据包是否被状态跟踪机制处理 |
Mangle表 |
修改数据包的服务类型、TTL、并且可以配置路由实现QOS |
Nat表 |
用于网络地址转换(IP、端口) |
Filter表 |
过滤数据包 |
表和链的对应关系:
Hook点:五链对应的位置我们又称之为hook点。每个hook点上针对不同的协议、不同的hook函数优先级,分配了一系列hook函数。数据包每到一个hook点,各协议会按照hook函数优先级从小到大的顺序执行hook函数。
Hook函数:在hook点上设置的数据包处理函数。
关于netfilter中协议类型,hook点,hook函数,优先级的关系,下图可以直观体现:
下图对更加详细的列出了各个阶段的hook函数
nf_conntrack模块维护着系统的一个链接追踪表。内部用1个哈希表记录已建立的连接,包括其他机器到本机、本机到其他机器、本机到本机(例如 ping 127.0.0.1 也会被跟踪)。如果连接进来比释放的快,把哈希表塞满了,新连接的数据包会被丢掉,同时dmesg日志会打印kernel: nf_conntrack: nf_conntrack: table full, dropping packet信息,外部表现为网络不通。
nf_conntrack_count模块内部维护者四张表:
net.netfilter.nf_conntrack_count统计的是上述所有表中连接的数量,而/proc/net/nf_conntrack看到的信息只有confirm表的信息。所以就要确认其他连接是存在哪张表里面了。首先要先理解连接加入confirm表和从confirm表删除的机制
其中conntrack对数据包进行追踪的hook函数即上图中的红框(输入连接)、蓝框(输出连接)所对应的函数。下面分析输入连接的跟踪ipv4_conntrack_in和ipv4_confirm函数(输出过程的跟踪连接机制同理):
当数据包经过了PRE_ROUNTING hook点:
当数据包经过INPUT hook点:
通过初步代码分析,确认系统是通过nf_ct_delete函数进行的连接删除工作,后续利用dump_stack函数,打印出调用栈,其删除实际是通过工作队列进行处理的,具体流程如下
当系统调度到worker_thread
通过2部分介绍发现:在连接加入系统confirm表以及连接从系统中删除的流程中,均有可能出现nf_conntrack堆积造成问题。
从连接加入角度分析:连接跟踪块在PRE_ROUNTING hook点执行完nf_ct_add_to_unconfirmed_list后,会加入unconfirm列表,而此时nf_conntrack_count值已然增加;那么此时有两种情况造成其不能加入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列表不同步的问题:
通过上述代码分析,可以发现连接块在“加入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