linux内核协议栈 netfilter 之连接跟踪子系统核心实现nf

编程入门 行业动态 更新时间:2024-10-09 09:22:44

linux内核协议栈 netfilter 之连接跟踪<a href=https://www.elefans.com/category/jswz/34/1761079.html style=子系统核心实现nf"/>

linux内核协议栈 netfilter 之连接跟踪子系统核心实现nf

目录

1 连接跟踪入口 nf_conntrack_in()

1.1 连接跟踪信息块的获取 resolve_normal_ct()

1.1.1 nf_ct_get_tuple()

1.1.2 nf_conntrack_find_get()

1.1.3 init_conntrack()

2 连接跟踪子系统出口 nf_conntrack_confirm()

2.1 连接确认判断

2.2 连接确认 __nf_conntrack_confirm()【核心】

3 连接超时机制

3.1 定时器的初始化 nf_conntrack_alloc()

3.2 定时器超时回调 death_by_timeout()

3.2.1 连接删除 clean_from_llists()

3.3 定时器的更新nf_ct_refresh_acct()


1 连接跟踪入口 nf_conntrack_in()

数据包skb就是通过该函数进入连接跟踪子系统的,对于发送报文,从LOCAL_OUT点进入,对于接收报文,从PRE_ROUTING点进入。该函数的功能是实现对数据的连接跟踪,更新连接跟踪项的连接状态,目前就是根据五元组识别一条数据流。这个函数可以说是连接跟踪模块的重中之重。

首先它将数据包的类型按如下几个类型处理:

  1. 当这是一个新的数据流的第一个数据包时,则会根据该数据包的五元组信息创建一个新的连接跟踪项,并初始化该连接跟踪项的tuple_hash、helper的值,最后,将该连接跟踪项的原始方向的tuple_hash添加到unconfirmed链表中,且该连接不是期望连接
  2. 当这是一个新的数据流的第一个数据包时,则会根据该数据包的五元组信息创建一个新的连接跟踪项,并初始化该连接跟踪项的tuple_hash、helper的值,最后将该连接跟踪项的原始方向的tuple_hash添加到unconfirmed链表中,且该连接是期望连接。
  3. 当该数据包是原始方向的非第一个数据包,但当该数据包进入连接跟踪模块时,连接跟踪模块还没有收到reply方向的数据包。
  4. 当该数据包是原始方向的非第一个数据包, 且到改数据包进入连接跟踪模块时,连接跟踪 模块已经接收到reply方向的数据包。
  5. 当该数据包是reply方向的数据包。

主要也就对数据包进行上面五种分类

  1. 当是第一类数据包时,则创建一个新的连接跟踪项,并作相应的初始化工作,且将数据包的nfctinfo设置为 IP_CT_NEW;
  2. 当是第二类数据包时,则创建一个新的连接跟踪项,并作相应的初始化工作,且将数据包的nfctinfo设置为 IP_CT_RELATED;
  3. 当时第三类数据包时,将数据包的nfctinfo设置为 IP_CT_NEW;
  4. 当是第四种数据包时,将数据包的nfctinfo设置为 IP_CT_ESTABLISHED;
  5. 当时第五种状态时,将数据包的的nfctinfo设置为 IP_CT_ESTABLISHED+IP_CT_IS_REPLY;

然后再调用四层协议相关的packet函数,根据数据包的四层内容值,更新四层协议相关的状态值(比如tcp协议,就会在其nf_conntrack_protocol->packet设置四层相关的状态改变)。

当数据包是reply方向,且置位连接跟踪项的status的IPS_SEEN_REPLY_BIT后,发现IPS_SEEN_REPLY_BIT位原来的值为0,则会调用nf_conntrack_event_cache设置事件通知为IPCT_STATUS,在ipv4_confirm函数里,会调用函数nf_ct_deliver_cached_events

通过通知链的回调函数,将需要更新的status通过netlink机制发送给nfnetlink模块,由其再执行更新状态操作。

最后,为skb的nfct指针赋值,即将skb的nfct指向该数据包对应的连接跟踪项,当数据包再进入NAT模式时,即可以根据这个指针获取到数据包对应的连接跟踪项,从而实现NAT相关的操作。


struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {.l3proto	 = PF_INET,.name		 = "ipv4",.pkt_to_tuple	 = ipv4_pkt_to_tuple,.invert_tuple	 = ipv4_invert_tuple,.print_tuple	 = ipv4_print_tuple,.get_l4proto	 = ipv4_get_l4proto,....me		 = THIS_MODULE,
};struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly =
{.l3proto		= PF_INET,.l4proto 		= IPPROTO_TCP,.name 			= "tcp",.pkt_to_tuple 		= tcp_pkt_to_tuple,.invert_tuple 		= tcp_invert_tuple,.print_tuple 		= tcp_print_tuple,.print_conntrack 	= tcp_print_conntrack,.packet 		= tcp_packet,.new 			= tcp_new,.error			= tcp_error,...
};
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp4);struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly =
{.l3proto		= PF_INET,.l4proto		= IPPROTO_UDP,.name			= "udp",.pkt_to_tuple		= udp_pkt_to_tuple,.invert_tuple		= udp_invert_tuple,.print_tuple		= udp_print_tuple,.packet			= udp_packet,.new			= udp_new,.error			= udp_error,...
};
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp4);struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
{.l3proto		= PF_INET,.l4proto		= IPPROTO_ICMP,.name			= "icmp",.pkt_to_tuple		= icmp_pkt_to_tuple,.invert_tuple		= icmp_invert_tuple,.print_tuple		= icmp_print_tuple,.packet			= icmp_packet,.new			= icmp_new,.error			= icmp_error,.destroy		= NULL,.me			= NULL,...
};================================================================================
unsigned int nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff *skb)
{struct nf_conn *ct;enum ip_conntrack_info ctinfo;struct nf_conntrack_l3proto *l3proto;struct nf_conntrack_l4proto *l4proto;unsigned int dataoff;u_int8_t protonum;int set_reply = 0;int ret;/*如果数据包的nfct项不为空,则说明该数据包已经关联一个nf_conn项了有两种情况:1.数据在PRE_ROUTING的raw模块相关的hook函数时,将该数据包与un_conntrack关联了,因此就不再进行连接模块的跟踪了2.如果发送数据包的设备为loopback设备,说明数据包已经被跟踪一次了,此处也不再进行跟踪了。*/if (skb->nfct) {NF_CT_STAT_INC_ATOMIC(ignore);return NF_ACCEPT;}//从全局数组nf_ct_l3protos[]中找到该协议族注册的L3协议l3proto = __nf_ct_l3proto_find((u_int16_t)pf);//调用L3协议的get_l4proto()回调解析skb,获取L4协议//编号protonum和L4协议报文首部距skb起始位置的偏移量dataoffret = l3proto->get_l4proto(skb, skb_network_offset(skb), &dataoff, &protonum);if (ret <= 0) {pr_debug("not prepared to track yet or error occured\n");NF_CT_STAT_INC_ATOMIC(error);NF_CT_STAT_INC_ATOMIC(invalid);return -ret;}/*根据三层协议类型与四层协议号,在全局数组nf_ct_protos中,获取四层协议的相应的        nf_conntrack_l4proto 结构*/l4proto = __nf_ct_l4proto_find((u_int16_t)pf, protonum);//如果L4协议提供了校验回调error(),对skb进行校验if (l4proto->error != NULL &&(ret = l4proto->error(skb, dataoff, &ctinfo, pf, hooknum)) <= 0) {NF_CT_STAT_INC_ATOMIC(error);NF_CT_STAT_INC_ATOMIC(invalid);return -ret;}//核心函数,获取该skb所属的连接跟踪信息块nf_conn,见下文。//如果数据包skb属于reply方向,set_reply会被设置为1,否则为0ct = resolve_normal_ct(skb, dataoff, pf, protonum, l3proto, l4proto,&set_reply, &ctinfo);if (!ct) {/* Not valid part of a connection */NF_CT_STAT_INC_ATOMIC(invalid);return NF_ACCEPT;}if (IS_ERR(ct)) {//连接跟踪子系统本意不会过滤数据包,但是在一些异常情况也会丢包NF_CT_STAT_INC_ATOMIC(drop);return NF_DROP;}NF_CT_ASSERT(skb->nfct);//调用L4协议的packet回调函数决定连接跟踪子系统给Netfilter框架的返回值ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum);if (ret < 0) {/* Invalid: inverse of the return code tells* the netfilter core what to do */pr_debug("nf_conntrack_in: Can't track with proto module\n");nf_conntrack_put(skb->nfct);skb->nfct = NULL;NF_CT_STAT_INC_ATOMIC(invalid);return -ret;}/*对于一个新创建的连接跟踪项后,当我们第一次收取到reply方向的数据包后,则会设置nf_conn->status的IPS_SEEN_REPLY_BIT位为1,当设置成功且IPS_SEEN_REPLY_BIT位的        原来值为0时,则调用nf_conntrack_event_cache ,由nfnetlink模块处理状态改变的事件。*/if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))nf_conntrack_event_cache(IPCT_STATUS, skb);return ret;
}
EXPORT_SYMBOL_GPL(nf_conntrack_in);

梳理下关键步骤:

  1. 根据skb找到能够处理该skb的L3协议和L4协议;
  2. 如果有,调用L4协议的error()回调进行报文校验,校验通过继续,否则结束;
  3. 调用resolve_normal_ct()查询该skb是否属于某个已有连接,没有则创建一个;
  4. 调用L4协议的packet()回调,该回调的返回值将作为该Netfilter钩子的返回值,一般应该都是NF_ACCEPT。

1.1 连接跟踪信息块的获取 resolve_normal_ct()

对于连接跟踪子系统来讲,每个数据包都应该归属某一条“连接”。如上,数据包通过一些检查后,就会调用该函数检查数据包是否属于已有的连接,如果该数据包不属于任何一个连接,那么它是一个“新连接”的数据包,这时该函数就会创建一个“连接”对该数据包进行跟踪,具体实现细节如下:

  1. 若连接跟踪项不存在时,则根据已知条件创建一个nf_conn,并放入unconfirm表中,执行c)
  2. 若连接跟踪项已存在,则执行c)
  3. 更新连接跟踪项的状态
  4. 设置传入数据包的nfct指针
@dataoff: L4报文的偏移量
@set_reply: 输出参数,
/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
static inline struct nf_conn* resolve_normal_ct(struct sk_buff *skb,unsigned int dataoff, u_int16_t l3num, u_int8_t protonum,struct nf_conntrack_l3proto *l3proto, struct nf_conntrack_l4proto *l4proto,int *set_reply, enum ip_conntrack_info *ctinfo)
{struct nf_conntrack_tuple tuple;struct nf_conntrack_tuple_hash *h;struct nf_conn *ct;/*根据三、四层协议的nf_conntrack结构变量及skb,为该数据包计算相应的nf_conntrack_tuple值*/if (!nf_ct_get_tuple(skb, skb_network_offset(skb),dataoff, l3num, protonum, &tuple, l3proto, l4proto)) {pr_debug("resolve_normal_ct: Can't get tuple\n");return NULL;}//从全局的哈希表nf_conntrack_hash中寻找是否有一致的tuple_hashh = nf_conntrack_find_get(&tuple);if (!h) {//如果没找到,说明这是一个新的连接,新创建一个连接,顺带也就创建了tuple_hashh = init_conntrack(&tuple, l3proto, l4proto, skb, dataoff);if (!h)return NULL;if (IS_ERR(h))return (void *)h;}//tuple_hash就在连接跟踪信息块中,这里由tuple_hash获取连接跟踪信息块。//注意:连接跟踪信息块的分配是在init_conntrack()中完成的,这里只是转换ct = nf_ct_tuplehash_to_ctrack(h);//特别注意下面ctinfo的设定,它最后会被设置到skb->nfctinfo中,//表示当处理完该skb后,skb所属连接的状态/*根据tuple的dst.dir,确定当前进来的数据包对应于连接跟踪项的哪一个方向,然后设置set_reply的值*/if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) {//收到的是reply方向的数据包,记录状态为ESTABLISHED*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;/* Please set reply bit if this packet OK */*set_reply = 1;} else {//初始方向的数据包,但是在该连接的reply方向上也已经收到过了数据包//那么skb依然是属于ESTABLISHED连接的/*当是连接跟踪项原始方向的数据包时,则有以下几种情况1.当发送的数据包到达连接跟踪模块时,其reply方向没有收到对应的数据包之前a)连接跟踪项不是期望连接,此时将skb->nfctinfo设置为IP_CT_NEWb)连接跟踪项是期望连接,此时将skb->nfctinfo设置为IP_CT_RELATED2.当原始方向发送的数据包到达连接跟踪模块时,其reply方向已经收到过对应的数据包即连接跟踪项的状态的IPS_SEEN_REPLY_BIT位已经置位了。此时将skb->nfctinfo设置为IP_CT_ESTABLISHED*/if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {pr_debug("nf_conntrack_in: normal packet for %p\n", ct);*ctinfo = IP_CT_ESTABLISHED;} else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {pr_debug("nf_conntrack_in: related packet for %p\n", ct);*ctinfo = IP_CT_RELATED;} else {//初始方向的第一个数据包pr_debug("nf_conntrack_in: new packet for %p\n", ct);*ctinfo = IP_CT_NEW;}*set_reply = 0;}//设置skb中的引用计数和连接跟踪状态skb->nfct = &ct->ct_general;skb->nfctinfo = *ctinfo;//返回连接跟踪信息块指针return ct;
}

1.1.1 nf_ct_get_tuple()

        参见:《linux内核协议栈 netfilter 之连接跟踪子系统核心数据结构 struct nf_conn》

1.1.2 nf_conntrack_find_get()

       参见:《linux内核协议栈 netfilter 之连接跟踪子系统核心数据结构 struct nf_conn》

1.1.3 init_conntrack()

       参见:《linux内核协议栈 netfilter 之连接跟踪子系统核心数据结构 struct nf_conn》

2 连接跟踪子系统出口 nf_conntrack_confirm()

该接口的核心逻辑如下:

  1. 首先判断该数据连接跟踪项是否已经被确认,即是判断该连接跟踪项的 status 中的 IPS_CONFIRMED_BIT 位是否为 1 。
  2. 若连接跟踪项尚未有确认,则调用函数 __nf_conntrack_confirm() 进行确认。
  3. 调用nf_ct_deliver_cached_events()  触发连接跟踪项对应的消息通知回调,判断是否需要向 nfnetlink 模块发送相应的消息。
/* Confirm a connection: returns NF_DROP if packet must be dropped. */
static inline int nf_conntrack_confirm(struct sk_buff *skb)
{//skb所属连接的连接跟踪信息块struct nf_conn *ct = (struct nf_conn *)skb->nfct;int ret = NF_ACCEPT;if (ct) {if (!nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct))ret = __nf_conntrack_confirm(skb);//向外部模块发送缓存的事件nf_ct_deliver_cached_events(ct);}return ret;
}

2.1 连接确认判断

并不是每一个skb都需要执行确认,一条连接只有初始方向上的第一个skb需要被确认,并且连接必须是有效的才需要确认。

//检查该连接是否已经被确认过了
static inline int nf_ct_is_confirmed(struct nf_conn *ct)
{return test_bit(IPS_CONFIRMED_BIT, &ct->status);
}
//检查连接是否依然有效
static inline int nf_ct_is_dying(struct nf_conn *ct)
{return test_bit(IPS_DYING_BIT, &ct->status);
}

2.2 连接确认 __nf_conntrack_confirm()【核心】

该函数主要确认一个连接跟踪项,确认操作只会发生在连接跟踪项的状态不为reply时。具体操作如下:

  1. 调用CTINFO2DIR,判断是否是原始方向发送的数据包(仅对原始方向的数据包对应的连接跟踪项进行确认操作。)
  2. 若1中判断通过后,分别计算 original、reply 方向上的 tuple 变量对应的 hash 值,分别为 hash、repl_hash。
  3. 若连接跟踪项的original、reply方向上的tuple_hash变量均没有对应的网络命名空间的 net->ct.hash[] 链表中,则首先将original方向上的 tuple_hash 从 unconntrack 链表(net->ct.unconfirmed)中删除,然后将连接跟踪项的original、reply方向上的tuple_hash变量添加到相对应的 net->ct.hash[[] 链表中置位连接跟踪项的status中的IPS_CONFIRMED_BIT,并启动超时定时器,用于实现对连接跟踪项所占内存的超时回收功能。
/* Confirm a connection given skb; places it in hash table */
int __nf_conntrack_confirm(struct sk_buff *skb)
{unsigned int hash, repl_hash;struct nf_conntrack_tuple_hash *h;struct nf_conn *ct;struct nf_conn_help *help;struct hlist_node *n;enum ip_conntrack_info ctinfo;ct = nf_ct_get(skb, &ctinfo);/* ipt_REJECT uses nf_conntrack_attach to attach relatedICMP/TCP RST packets in other direction.  Actual packetwhich created connection will be IP_CT_NEW or for anexpected connection, IP_CT_RELATED. *//*该函数只对dir为IP_CT_DIR_ORIGINAL状态时的nf_conn进行confirm操作对于dir为IP_CT_DIR_REPLY状态的nf_conn,其状态已经设置过confirm,所以不需要操作*/if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)return NF_ACCEPT;//计算初始方向和reply方向的tuple的哈希值hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);spin_lock_bh(&nf_conntrack_lock);/* See if there's one in the list already, including reverse:NAT could have grabbed it without realizing, since we'renot in the hash.  If there is, we lost race. *///检查全局的hash表中是否已经保存了对应的tuple或者reply_tuple,//如果已经保存了,那么是一种异常,结束处理过程hlist_for_each_entry(h, n, &nf_conntrack_hash[hash], hnode)if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, &h->tuple))goto out;hlist_for_each_entry(h, n, &nf_conntrack_hash[repl_hash], hnode)if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, &h->tuple))goto out;//将初始方向上的tuple_hash从net->ct.unconfirmed 链表中删除,添加的时候也只添加了初始方向的tuplehlist_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode);//将tuple和reply_tuple都加入到网络命名空间的net->ct.hash中,完成一条连接的跟踪__nf_conntrack_hash_insert(ct, hash, repl_hash);/* Timer relative to confirmation time, not originalsetting time, otherwise we'd get timer wrap inweird delay cases. */ct->timeout.expires += jiffies;//激活超时定时器add_timer(&ct->timeout);atomic_inc(&ct->ct_general.use);//标记该连接已经被确认过了set_bit(IPS_CONFIRMED_BIT, &ct->status);NF_CT_STAT_INC(insert);spin_unlock_bh(&nf_conntrack_lock);help = nfct_help(ct);if (help && help->helper)nf_conntrack_event_cache(IPCT_HELPER, skb);
#ifdef CONFIG_NF_NAT_NEEDEDif (test_bit(IPS_SRC_NAT_DONE_BIT, &ct->status) ||test_bit(IPS_DST_NAT_DONE_BIT, &ct->status))nf_conntrack_event_cache(IPCT_NATINFO, skb);
#endifnf_conntrack_event_cache(master_ct(ct) ? IPCT_RELATED : IPCT_NEW, skb);return NF_ACCEPT;
out:NF_CT_STAT_INC(insert_failed);spin_unlock_bh(&nf_conntrack_lock);return NF_DROP;
}
EXPORT_SYMBOL_GPL(__nf_conntrack_confirm);

3 连接超时机制

一个连接被建立后,如果长时间没有数据交互,那么应该将该连接从连接跟踪子系统中清除,因为毕竟内存是有限的,不可能长时间跟踪这些空闲的连接。为了实现这个目的,连接跟踪子系统为每个连接维护了一个定时器,一旦该定时器超时,那么就将该连接从系统中清除。

3.1 定时器的初始化 nf_conntrack_alloc()

定时器是在连接跟踪信息块的分配函数nf_conntrack_alloc()中初始化的。

struct nf_conn *nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,const struct nf_conntrack_tuple *repl)
{
.../* Don't set timer yet: wait for confirmation */setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);
...
}

如上所见,定时器是在nf_conntrack_confirmed()中激活的。

3.2 定时器超时回调 death_by_timeout()

定时器超时回调函数为death_by_timeout(),其实现就是将连接跟踪信息块从系统中删除。

static void death_by_timeout(unsigned long ul_conntrack)
{struct nf_conn *ct = (void *)ul_conntrack;struct nf_conn_help *help = nfct_help(ct);struct nf_conntrack_helper *helper;//如果该连接有helper模块,先调用该helper模块的销毁函数if (help) {rcu_read_lock();helper = rcu_dereference(help->helper);if (helper && helper->destroy)helper->destroy(ct);rcu_read_unlock();}spin_lock_bh(&nf_conntrack_lock);/* Inside lock so preempt is disabled on module removal path.* Otherwise we can get spurious warnings. */NF_CT_STAT_INC(delete_list);//将连接跟踪信息块、可能存在的期望连接一并从系统中删除clean_from_lists(ct);spin_unlock_bh(&nf_conntrack_lock);//递减连接信息跟踪块的引用计数,当引用计数变为0后会被其nf_ct_put(ct);
}

3.2.1 连接删除 clean_from_llists()

具体的删除由 clean_from_llists() 实现:

static void clean_from_lists(struct nf_conn *ct)
{//删除了tuple也就从全局的hash表中删除了连接跟踪信息块hlist_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode);hlist_del_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnode);//销毁所有的期望连接nf_ct_remove_expectations(ct);
}

3.3 定时器的更新nf_ct_refresh_acct()

当然,当收到数据包后,也应该更新该定时器,防止其超时,这是通过调用nf_ct_refresh_acct()刷新的。这个刷新动作的调用由L4协议的paket()回调负责完成,这同时也表示将超时时间的决定权给了L4协议,如下代码所示以udp_packet() 为例。

/* Returns verdict for packet, and may modify conntracktype */
static int udp_packet(struct nf_conn *ct,const struct sk_buff *skb,unsigned int dataoff,enum ip_conntrack_info ctinfo,u_int8_t pf,unsigned int hooknum)
{/* If we've seen traffic both ways, this is some kind of UDPstream.  Extend timeout. */if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {...} elsenf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout);return NF_ACCEPT;
}/* Refresh conntrack for this many jiffies and do accounting */
static inline void nf_ct_refresh_acct(struct nf_conn *ct,enum ip_conntrack_info ctinfo, const struct sk_buff *skb,unsigned long extra_jiffies)
{__nf_ct_refresh_acct(ct, ctinfo, skb, extra_jiffies, 1);
}
/* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */
void __nf_ct_refresh_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo,const struct sk_buff *skb, unsigned long extra_jiffies, int do_acct)
{int event = 0;NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);NF_CT_ASSERT(skb);spin_lock_bh(&nf_conntrack_lock);//设定了该标记的连接的超时值将无法被更新if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status))goto acct;/* If not in hash table, timer will not be active yet */if (!nf_ct_is_confirmed(ct)) {//连接跟踪信息块还没有被确认时,该定时器就还没有被激活,此时//重新设定超时时间,认为是重新更新超时时间戳ct->timeout.expires = extra_jiffies;event = IPCT_REFRESH;} else {//传入超时值是当前时间的相对值unsigned long newtime = jiffies + extra_jiffies;//只有当新的超时值至少超过当前超时值1s时才重新更新定时器(避免频繁更新)if ((newtime - ct->timeout.expires >= HZ) && del_timer(&ct->timeout)) {ct->timeout.expires = newtime;add_timer(&ct->timeout);event = IPCT_REFRESH;}}
acct:
#ifdef CONFIG_NF_CT_ACCT//做数据统计if (do_acct) {ct->counters[CTINFO2DIR(ctinfo)].packets++;ct->counters[CTINFO2DIR(ctinfo)].bytes +=skb->len - skb_network_offset(skb);if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000)|| (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000))event |= IPCT_COUNTER_FILLING;}
#endifspin_unlock_bh(&nf_conntrack_lock);/* must be unlocked when calling event cache */if (event)nf_conntrack_event_cache(event, skb);
}
EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct);

 

更多推荐

linux内核协议栈 netfilter 之连接跟踪子系统核心实现nf

本文发布于:2024-02-06 08:15:38,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1747547.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:子系统   内核   核心   协议   linux

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!