Chinaunix首页 | 论坛 | 博客
  • 博客访问: 283117
  • 博文数量: 72
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 276
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-28 23:52
文章分类

全部博文(72)

文章存档

2017年(20)

2014年(52)

分类: LINUX

2014-10-16 16:56:19

内核中采用了面向对象的设计思想,把一些共有的操作函数和属性提取出来包含到一个结构体内,和面向对象的类是一个概念。

 

1L3处理conntrack的函数

数据结构定义,这里介绍几个关键字段

struct nf_conntrack_l3proto {

    /* L3 Protocol Family number. ex) PF_INET */

    u_int16_t l3proto;

 

    /* Protocol name */

    const char *name;

 

    /*从报文的L3头部提取出conntrack 的元组信息*/

    bool (*pkt_to_tuple)(const struct sk_buff *skb, unsigned int nhoff,

                                 struct nf_conntrack_tuple *tuple);

 

    /*conntrack 正反向连接的元组中L3信息的转换函数*/

    bool (*invert_tuple)(struct nf_conntrack_tuple *inverse,

                                 const struct nf_conntrack_tuple *orig);

 

    /*获取L4协议类型的函数*/

    int (*get_l4proto)(const struct sk_buff *skb, unsigned int nhoff,

                               unsigned int *dataoff, u_int8_t *protonum);

};

 

每个网络层的协议要是实现conntrack功能,需要自己实现上面定义的函数,并初始化一个struct nf_conntrack_l3proto实例,并注册到Netfilter管理的数组中。

 

Netfilter定义了一个全局的数组,来存放各个L3协议注册的nf_conntrack_l3proto实例。

struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX] __read_mostly;

 

注册:

int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto)

{

    int ret = 0;
    。。。。。。//省略


    mutex_lock(&nf_ct_proto_mutex);
    /*根据协议类型为数组下标,在全局数组中查看是否重复注册*/


    old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],lockdep_is_held(&nf_ct_proto_mutex));


    if (old != &nf_conntrack_l3proto_generic) {

        ret = -EBUSY;

        goto out_unlock;

    }

。。。。。。
    /*注册到全局数组里*/


    rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);

 

out_unlock:

    mutex_unlock(&nf_ct_proto_mutex);

    return ret; 

}

EXPORT_SYMBOL_GPL(nf_ct_l3proto_register);

 

具体使用大家可以查看IPv4注册实例的代码,这里不介绍了。

 

 

 

2L4conntrack的处理函数

struct  nf_conntrack_l4proto {

    /* L3 Protocol number. */

    u_int16_t l3proto;

    /* L4 Protocol number. */

    u_int8_t l4proto;

 

    /* 从报文的L4头部提取出conntrack 的元组信息 */

    bool (*pkt_to_tuple)(const struct sk_buff *skb, unsigned int dataoff,

                                 struct nf_conntrack_tuple *tuple);

 
    /*conntrack 正反向连接的元组中L3信息的转换函数*/


    bool (*invert_tuple)(struct nf_conntrack_tuple *inverse,

                                 const struct nf_conntrack_tuple *orig);

 

    /* Returns verdict for packet, or -1 for invalid. */
    
/*各个协议对报文的额外处理函数*/


    int (*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,

                         unsigned int *timeouts);

 

    /* Called when a new connection for this protocol found;

     * returns TRUE if it's OK.  If so, packet() called next. 
     *判断是否根据该报文新建一个conntrack连接*/


    bool (*new)(struct nf_conn *ct, const struct sk_buff *skb,

                        unsigned int dataoff, unsigned int *timeouts);

 
    /*检查报文数据的正确性*/


    int (*error)(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,

                     unsigned int dataoff, enum ip_conntrack_info *ctinfo,

                     u_int8_t pf, unsigned int hooknum);

。。。。。。

    /* Protocol name */

    const char *name;

};

每个传输层的协议要是实现conntrack功能,需要自己实现上面定义的函数,并初始化一个struct nf_conntrack_l4proto实例,并注册到Netfilter管理的数组中。

 

Netfilter定义了一个全局的数组,来存放各个L4协议注册的nf_conntrack_l4proto实例。

 

static struct nf_conntrack_l4proto __rcu **nf_ct_protos[PF_MAX] __read_mostly;

 

注册

int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto)

{

    int ret = 0;

。。。。。。

    mutex_lock(&nf_ct_proto_mutex);

    if (!nf_ct_protos[l4proto->l3proto]) 
    {

        /* l3proto may be loaded latter. */

        struct nf_conntrack_l4proto __rcu **proto_array;

        int i;
        /*对应L3下还没有创建L4所用的内存,申请一块*/


        proto_array = kmalloc(MAX_NF_CT_PROTO *sizeof(struct nf_conntrack_l4proto *),GFP_KERNEL);


        if (proto_array == NULL) {

            ret = -ENOMEM;

            goto out_unlock;

        }

 

        for (i = 0; i < MAX_NF_CT_PROTO; i++)

            RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);

 

        /* Before making proto_array visible to lockless readers,

         * we must make sure its content is committed to memory.

         */

        smp_wmb();

 

        nf_ct_protos[l4proto->l3proto] = proto_array;

    }
    else if (rcu_dereference_protected(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],


                                    lockdep_is_held(&nf_ct_proto_mutex)) != &nf_conntrack_l4proto_generic)
    {


        ret = -EBUSY;

        goto out_unlock;

    }

 

    l4proto->nla_size = 0;

    if (l4proto->nlattr_size)

        l4proto->nla_size += l4proto->nlattr_size();

    if (l4proto->nlattr_tuple_size)

        l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();

/*注册到Netfilter*/

    rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],l4proto);


out_unlock:

    mutex_unlock(&nf_ct_proto_mutex);

    return ret;

}

 

具体使用大家可以查看IPv4udptcp注册实例的代码,这里不介绍了。

具体位置

net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c

 

static int __init nf_conntrack_l3proto_ipv4_init(void)

{

    int ret = 0;

    ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4);

    ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4);

    ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp);

    ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);

}

(未完待续)


阅读(1544) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~