Linux内核分析 - 网络[十七]:NetFilter之连接跟踪
TCP的过程 TCP涉及到三次握手才能建立连接,因此相对于UDP要更为复杂,下面以一个TCP建立连 接跟踪的例子来详细分析其过程。 场景:主机A与主机B,主机A向主机B发起TCP连接 站在B的角度,分析连接跟踪在 TCP三次握手中的过程。 1. 收到SYN报文 [pre_routing -> local_in] 勾子点PRE_ROUTEING [ipv4_conntrack_in] ipv4_conntrack_in() -> nf_conntrack_in() nf_ct_l3protos和nf_ct_protos分别存储注册其中的3层和4层协议的连 接跟踪操作,对ipv4而言,它们在__init_nf_conntrack_l3proto_ipv4_init()中被注册(包括tcp/udp/icmp/ipv4),其中ipv4是 在nf_ct_l3protos中的,其余是在nf_ct_protos中的。下面函数__nf_ct_l3proto_find()根据协议簇(AF_INET)找到ipv4(即 nf_conntrack_l3proto_ipv4)并赋给l3proto;下面函数__nf_ct_l4proto_find()根据协议号(TCP)找到tcp(即 nf_conntrack_l4proto_tcp4)并赋给l4proto。 l3proto = __nf_ct_l3proto_find(pf); ret = l3proto->get_l4proto(skb, skb_network_offset(skb), &dataoff, &protonum); ...... l4proto = __nf_ct_l4proto_find(pf, protonum); 然后调用resolve_normal_ct()返回对应的连接跟踪ct(由于是第一 次,它会创建ct),下面会详细分析这个函数。l4proto->packet()等价于tcp_packet(),作用是得到新的TCP状态,这里只要 知道ct->proto.tcp.state被设置为TCP_CONNTRACK_SYN_SENT,下面也会具体分析这个函数。 ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, l3proto, l4proto, &set_reply, &ctinfo); ...... ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum); ...... if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_REPLY, ct); resolve_normal_ct() 先调用nf_ct_get_tuple()从当前报文skb中 得到相应的tuple,然后调用nf_conntrack_find_get()来判断连接跟踪是否已存在,已记录连接的tuple都会存储在net- >ct.hash中。如果已存在,则直接返回;如果不存在,则调用init_conntrack()创建新的,最后设置相关的连接信息。 就 本例中收到SYN报文而言,是第一次收到报文,显然在hash表中是没有的,进而调用init_conntrack()创建新的连接跟踪,下面 会具体分析该函数;最后根据报文的方向及所处的状态,设置ctinfo和set_reply,此时方向是IP_CT_DIR_ORIGIN,ct- >status未置值,因此最终*ctinfo=IP_CT_NEW; *set_reply=0。ctinfo是很重要的,它表示连接跟踪所处的状态,如同TCP建 立连接,连接跟踪建立也要经历一系列的状态变更,skb->nfctinfo=*ctinfo记录了此时的状态(注意与TCP的状态相区别 ,两者没有必然联系)。 if (!nf_ct_get_tuple(skb, skb_network_offset(skb), dataoff, l3num, protonum, &tuple, l3proto, l4proto)) { pr_debug("resolve_normal_ct: Can't get tuplen"); return NULL; } h = nf_conntrack_find_get(net, zone, &tuple); if (!h) { h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto, skb, dataoff); …… } ct = nf_ct_tuplehash_to_ctrack(h); if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) { *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; *set_reply = 1; } else { if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { pr_debug("nf_conntrack_in: normal packet for %pn", ct); *ctinfo = IP_CT_ESTABLISHED; } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { pr_debug("nf_conntrack_in: related packet for %pn", ct); *ctinfo = IP_CT_RELATED; } else { pr_debug("nf_conntrack_in: new packet for %pn", ct); *ctinfo = IP_CT_NEW; } *set_reply = 0; } skb->nfct = &ct->ct_general; skb->nfctinfo = *ctinfo; 其中,连接的表示是用数据结构nf_conn,而存储tuple是用nf_conntrack_tuple_hash,两者的关系是: init_conntrack() 该函数创建一个连接跟踪,由触发的报文得到了tuple,然后调用nf_ct_invert_tuple()将其反转,得到反向的repl_tuple, nf_conntrack_alloc()为新的连接跟踪ct分配空间,并设置了 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = tuple; ct->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple; (编辑:应用网_丽江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |