转自:http://blog.csdn.net/dianhuiren/article/details/6920380
- 在 数据包接收过程的那篇笔记中可以知道,在数据包的处理函数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; -
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 -
-
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, -
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和后面这种套节字的方法是在链路层截获数据包.