Netfilter之连接跟踪实现机制初步分析[转2]
2010-01-25 18:30
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) {
if (!nf_ct_get_tuple(skb, (unsigned int)(skb->nh.raw - skb->data), dataoff, l3num, protonum, &tuple, l3proto, l4proto)) { DEBUGP("resolve_normal_ct: Can't get tuple\n"); return NULL; }
/* look for tuple match */ h = nf_conntrack_find_get(&tuple, NULL); if (!h) { h = init_conntrack(&tuple, l3proto, l4proto, skb, dataoff); if (!h) return NULL; if (IS_ERR(h)) return (void *)h; } ct = nf_ct_tuplehash_to_ctrack(h);
......
*ctinfo = IP_CT_NEW; } *set_reply = 0; } skb->nfct = &ct->ct_general; skb->nfctinfo = *ctinfo; return ct; }
4.3. ip_conntrack_help() 函数ip_conntrack_help()被挂载在钩子点NF_IP_LOCAL_IN和NF_IP_POST_ROUTING,其首先根据传来的 sk_buff结构查找连接跟踪记录,如果该包所属连接有辅助模块helper,且包符合一定的状态要求,则调用相应辅助模块的函数help()处理数据 包。
static unsigned int ip_conntrack_help(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct ip_conntrack *ct; enum ip_conntrack_info ctinfo;
/* This is where we call the helper: as the packet goes out. */ ct = ip_conntrack_get(*pskb, &ctinfo); if (ct && ct->helper && ctinfo != IP_CT_RELATED + IP_CT_IS_REPLY) { unsigned int ret; ret = ct->helper->help(pskb, ct, ctinfo); if (ret != NF_ACCEPT) return ret; } return NF_ACCEPT; }
|
4.4. ip_confirm() 函数ip_confirm()被挂载在钩子点NF_IP_LOCAL_IN和NF_IP_POST_ROUTING,其对数据 包再次进行连接跟踪记录确认,并将新建的连接跟踪记录加到表中。考虑到包可能被过滤掉,之前新建的连接跟踪记录实际上并未真正加到连接跟踪表中,而在最后 由函数ip_confirm()确认后真正添加,实际对传来的sk_buff进行确认的函数是__ip_conntrack_confirm()。在该函数中首先调用函数ip_conntrack_get()查找相应的连接跟踪记录,如果数据包不是IP_CT_DIR_ORIGINAL方向的包,则直接ACCEPT,否则接着调用hash_conntrack()计算所找到的连接跟踪记录的ip_conntrack_tuple类型的hash值,且同时计算两个方向的值。然后根据这两个hash值分别查找连接跟踪记录的hash表,如果找到了,则返回NF_DROP,如果未找到,则调用函数__ip_conntrack_hash_insert()将两个方向的连接跟踪记录加到hash表中。
static unsigned int ip_confirm(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { /* We've seen it coming out the other side: confirm it */ return ip_conntrack_confirm(pskb); }
|
static inline int ip_conntrack_confirm(struct sk_buff **pskb) { struct ip_conntrack *ct = (struct ip_conntrack *)(*pskb)->nfct; int ret = NF_ACCEPT;
if (ct) { if (!is_confirmed(ct) && !is_dying(ct)) ret = __ip_conntrack_confirm(pskb); ip_ct_deliver_cached_events(ct); } return ret; }
|
int __ip_conntrack_confirm(struct sk_buff **pskb) {
ct = ip_conntrack_get(*pskb, &ctinfo);
if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) return NF_ACCEPT;
hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
/* No external references means noone else could have confirmed us. */ IP_NF_ASSERT(!is_confirmed(ct)); DEBUGP("Confirming conntrack %p\n", ct);
write_lock_bh(&ip_conntrack_lock);
list_for_each_entry(h, &ip_conntrack_hash[hash], list) if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, &h->tuple)) goto out; list_for_each_entry(h, &ip_conntrack_hash[repl_hash], list) if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, &h->tuple)) goto out;
/* Remove from unconfirmed list */ list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
__ip_conntrack_hash_insert(ct, hash, repl_hash);
ct->timeout.expires += jiffies; add_timer(&ct->timeout); atomic_inc(&ct->ct_general.use); set_bit(IPS_CONFIRMED_BIT, &ct->status); CONNTRACK_STAT_INC(insert); write_unlock_bh(&ip_conntrack_lock); if (ct->helper) ip_conntrack_event_cache(IPCT_HELPER, *pskb);
......
out: CONNTRACK_STAT_INC(insert_failed); write_unlock_bh(&ip_conntrack_lock); return NF_DROP; }
4.5.ip_conntrack_local() 函数ip_conntrack_local()被挂载在 钩子点NF_IP_LOCAL_OUT,该函数会调用ip_conntrack_in(),函数源码如下:
static unsigned int ip_conntrack_local(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { if (net_ratelimit()) printk("ipt_hook: happy cracking.\n"); return NF_ACCEPT; } return ip_conntrack_in(hooknum, pskb, in, out, okfn); }
|
5.数据包转发的连接跟踪流程 下面以数据包转发为例描述连接跟踪的 流程,其中的函数及结构体为前几节所介绍的一部分,图中主要想体现数据包sk_buff在连接跟踪流程中的相应改变,连接跟踪记录与连接跟踪表的关系,何时查找和修改连接跟踪表,辅助模块以及传输协议如何在连接跟踪 中使用等。所有的函数说明以及结构体在之前都有描述。发往本机以及本机发出的数据包的连接跟踪流程在此不再做分析。
图5-1 数据包转发的连接跟踪
6.总结 以上只是简要分析了Netfilter架构中连接跟踪功能的实现机 制,其中很多细节被忽略,如数据包的状态,连接跟踪记录的状态,具体传输协议的连接跟踪,主要目的是想要对整个实现框架有所认识。另外,对于较复杂的活动 协议,期望连接与主连接之间的关联等并未做分析,希望在以后有时间再做分析。鉴于自身水平有限,且可参考资料较少,因此以上的分析不能保证所描述的内容完 全正确。
PS:对于其中存在的错误问 题,希望各位能指正,以便将此分析尽量完善,另外,对于我尚未分析完成的部分,也希望大家能够提出意见,不甚感谢。
|
|
|
|