1、主要处理部分
再来回顾一下Netfilter的框架。如果不存在Netfilter框架,则包的流程就是图中的函数调用,比如到本地的是:
ip_rcv()->ip_rcv_finish()->ip_local_deliver()->ip_local_deliver_finish()
当加上Netfilter的时候,就会在钩子点的地方先调用注册的函数,然后再调用正常流程中的函数。还拿到本地的包举例:
ip_rcv()->ipv4_conntrack_defrag()->nf_conntrack_in()->其他钩子处理……->ip_rcv_finish()->ip_local_deliver()->ipv4_confirm()->其他钩子处理……->ip_local_deliver_finish
从会话跟踪的角度上看,当一个数据包流经Netfilter的时候,主要是经历了几个函数
当数据包流经LOCAL_OUT钩子点的时候,调用的是ipv4_conntrack_local,只不过ipv4_conntrack_local最终还是调用了函数nf_conntrack_in函数,所以这里没画出ipv4_conntrack_local函数。ipv4_conntrack_defrag主要是完成ip报文分片的重新组装,将属于一个IP报文的多个分片组成一个真正的报文。连接跟踪只跟踪完整的IP报文,这里不关心IP分片重组的函数ipv4_conntrack_defrag,所以这里着重分析nf_conntrack_in和ipv4_confirm函数。
来自网络的数据包(本地、内网、外网),都会经过函数nf_conntrack_in,首先来看nf_conntrack_in都做了哪些操作,
structsk_buff{}中有两个元素与连接跟踪有关系,分别是nfct和nfctinfo。nf_conntrack.h中提供了一个函数staticinlinestructnf_conn*nf_ct_get(skb,*ctinfo),此函数可获取到skb的指针nfct(与此数据相关联的连接),从而得知该数据包的连接状态和该连接状态的相关信息ctinfo。从连接跟踪的角度来看,这个ctinfo表示了每个数据包的几种连接状态:
IP_CT_ESTABLISHED
Packet是一个已建连接的一部分,在其初始方向。
IP_CT_RELATED
Packet属于一个已建连接的相关连接,在其初始方向。
IP_CT_NEW
Packet试图建立新的连接
IP_CT_ESTABLISHED+IP_CT_IS_REPLY
Packet是一个已建连接的一部分,在其响应方向。
IP_CT_RELATED+IP_CT_IS_REPLY
Packet属于一个已建连接的相关连接,在其响应方向。
流程中间有一部分是涉及到期望连接的部分,下文再讲。再来看下出口做的操作:
出口的函数主要是从‘非确认’的链表中摘下来,然后加入到确认的链表中(net->ct.hash[])。入口函数nf_conntrack_in中,查找是否存在tuple就是从net->ct.hash[]这个哈希链表中查找的。定时器超时的时间是在4层协议的回调函数packet中指定的,当超时的时候,会调用函数death_by_timeout(在初始化ct的时候,指定)。
2、连接跟踪的销毁
(1)death_by_timeout函数,当会话超时的时候,会调用这个回调。
超时销毁:当一个会话经历了入口和出口以后,会添加一个超时的timer,等到timer超时的时候,就会调用回调函数。
(2)内核程序主动进行销毁
对于会话的销毁,也主要是通过nf_ct_put->nf_conntrack_put函数来进行。nf_conntrack_put函数的作用是对于这条连接会话的引用次数减1,如果被引用次数为0,则释放该条会话的相关资源。当是一条新会话的时候,会将引用计数设置为1,当该会话被确认过了之后,该引用计数会被加1。
当释放skb的时候,调用kfree_skb函数,然后涉及到会话的部分最终也是调用nf_conntrack_put函数来对于会话的引用减1。
当Netfilter执行钩子函数的过程中,只要有一个返回NF_DROP,这个时候,就会调用kfree_skb的操作,这时候,如果此时Netfilter还没有执行到nf_conntrack_confirm函数,则这条会话可能会被释放掉(因为此时会话的引用计数为1)。调用关系如下:
从以上的流程图可以看出,最终都是通过nf_conntrack_put这个函数销毁的。