网络协议的初始化dev_add_pack

在数据包接收过程的那篇笔记中可以知道,在数据包的处理函数netif_receive_skb中,会先看ptype_all中是否有注册的协议,如果有,则调用相应的处理函数,然后再到ptype_base中,找到合适的协议,将skb发送到相关协议的处理函数.比如ip协议(ip_rcv)或者arp(arp_rcv)等等.此篇笔记讲的是有关ptype_all和ptype_base的相关知识点.

ptype_base和ptype_all在内核中存储的情况如下图:



可以看到,ptype_base为一个hash表,而ptype_all为一个双向链表.每一个里面注册的协议都用一个struct packet_type表示.

struct packet_type 
{
    unsigned short        type;    /*协议类型*/
    struct net_device     *dev;   
    int            (*func) (struct sk_buff *, struct net_device *,
                     struct packet_type *);
    void            *data;    /* Private to the packet type        */
    struct packet_type    *next;
};
其中需要注意的是dev参数,此参数表明了协议只处理来自dev指向device的数据,当dev=NULL时,表示该协议处理来自所有device的数据.这样,当注册自己的协议时,就可以指定自己想要监听或者接收的device.
其中注册和注销协议的函数为:
dev_add_pack(...)和dev_remove_pack(...)
这两个函数很简单,分别如下:
void dev_add_pack(struct packet_type *pt)
{
    int hash;
    br_write_lock_bh(BR_NETPROTO_LOCK);
#ifdef CONFIG_NET_FASTROUTE
    /* Hack to detect packet socket */
    if ((pt->data) && ((int)(pt->data)!=1)) {
        netdev_fastroute_obstacles++;
        dev_clear_fastroute(pt->dev);
    }
#endif
    if (pt->type == htons(ETH_P_ALL)) {
        netdev_nit++;
        pt->next=ptype_all;
        ptype_all=pt;
    } else {
        hash=ntohs(pt->type)&15;
        pt->next = ptype_base[hash];
        ptype_base[hash] = pt;
    }
    br_write_unlock_bh(BR_NETPROTO_LOCK);
}
此函数判断协议类型,然后加到ptype_base或者ptype_all中.
void dev_remove_pack(struct packet_type *pt)
{
    struct packet_type **pt1;
    br_write_lock_bh(BR_NETPROTO_LOCK);
    if (pt->type == htons(ETH_P_ALL)) {
        netdev_nit--;
        pt1=&ptype_all;
    } else {
        pt1=&ptype_base[ntohs(pt->type)&15];
    }
    for (; (*pt1) != NULL; pt1 = &((*pt1)->next)) {
        if (pt == (*pt1)) {
            *pt1 = pt->next;
#ifdef CONFIG_NET_FASTROUTE
            if (pt->data)
                netdev_fastroute_obstacles--;
#endif
            br_write_unlock_bh(BR_NETPROTO_LOCK);
            return;
        }
    }
    br_write_unlock_bh(BR_NETPROTO_LOCK);
    printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
}
此函数也很简单,只是把协议从相关的链表中移除.
下面以ip协议为例子来看看相关的实现:
ip协议结构体的定义如下:
static struct packet_type ip_packet_type =
{
    __constant_htons(ETH_P_IP),
    NULL,    /* All devices */
    ip_rcv,
    (void*)1,
    NULL,
};
当ipv4协议栈初始化时,会调用ip_init.之后,所有协议类型为ETH_P_IP的包都会交由ip_rcv处理.代码如下:
void __init ip_init(void)
{
    dev_add_pack(&ip_packet_type);
    ip_rt_init();
    inet_initpeers();
#ifdef CONFIG_IP_MULTICAST
    proc_net_create("igmp", 0, ip_mc_procinfo);
#endif
}
这样在系统启动之后,ip协议便被注册到ptype_base链表中,相应的处理函数为ip_rcv.
arp协议和其他类型的协议(在ptype_base或者ptype_all中的)的执行过程同理.
本人初学网络,水平很菜,如有错误,希望看到的朋友们及时指出,不胜感激.
ps1:记得刚来实验室的时候,做的截包模块的第一种方法是用的netfilter,第二种方法主要就是用到的这块知识.现在总结起来,觉得还算简单,当初却用了很长时间,想想,真是难者不会,会者不难啊.今天看书的时候,好像又发现了另外一种方法可以实现我的要求,记录在ps2上.
ps2:在数据链路层截包的另一种方法:用PF_PACKET socket type.linux可以用此类型套节字直接从链路层截获或者注入数据.发送数据时,直接发送到dev_queue_xmit.而接收函数时,可以在数据包通过路由之前截获到.如tcpdump和Ethereal都是用到了此套接字.那么总结起来可以看出,截获数据包至少可以有三种方法实现,第一种的netfilter是在协议栈中截获数据包,而利用ptype_all或者ptype_base和后面这种套节字的方法是在链路层截获数据包.


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中,初始化方法__init__是一个特殊的方法,它用于初始化一个类的实例对象。当我们创建一个类的实例时,Python会自动调用__init__方法,并将实例对象作为self参数传递给__init__方法。 在__init__方法中,我们通常会对实例对象的属性进行初始化,以便在后续的方法中可以使用这些属性。例如,我们可以在__init__方法中为对象设置初始状态、初始化属性、创建对象的内部状态等。__init__方法通常不返回任何值。 下面是一个使用__init__方法的示例,我们定义了一个名为Person的类,并在__init__方法中为实例对象设置了name和age两个属性: ```python class Person: def __init__(self, name, age): self.name = name self.age = age def say_hi(self): print('Hello, my name is', self.name, 'and I am', self.age, 'years old.') p = Person('Tom', 25) p.say_hi() ``` 在上面的代码中,我们定义了一个Person类,并在__init__方法中为实例对象设置了name和age两个属性。在say_hi方法中,我们通过self.name和self.age访问了对象的name和age属性,并将它们打印出来。当我们创建一个名为Tom、年龄为25的Person实例时,Python会自动调用__init__方法,并将实例对象作为self参数传递给__init__方法,然后在__init__方法中为实例对象设置name和age属性。 需要注意的是,__init__方法是一个可选的方法,如果我们不定义该方法,则Python会使用默认的__init__方法。默认的__init__方法不会做任何事情,只是将实例对象创建出来。但是,在实际开发中,我们通常会定义__init__方法来初始化实例对象的属性,以便后续方法可以使用这些属性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值