分享

Netfilter之连接跟踪实现机制初步分析

 mrjbydd 2010-09-19
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_INNF_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:对于其中存在的错误问 题,希望各位能指正,以便将此分析尽量完善,另外,对于我尚未分析完成的部分,也希望大家能够提出意见,不甚感谢。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多