1. 前言

以太头中除了6字节目的MAC地址、6字节源MAC地址外,更有两字节的以太帧类型值,如IPv4为0x0800,ARP为0x0806等,网卡驱动收到以太帧后通过接口函数netif_receive_skb()(netif_rx实际最后也是调用netif_receive_skb)交到上层,而这个接口函数就完成对以太帧类型的区分,交到不同的协议处理程式。假如想自己编写某一以太类型帧的处理程式,需要自己添加相应的代码。以下为Linux内核2.6代码。

2. 数据结构

每种协议都要定义一个packet_type结构,引导进入相关的协议数据处理函数,任何节点组成一个链表(HASH链表)。

/* include/linux/netdevice.h */

            struct packet_type {

            __be16 type; /* This is really htons(ether_type). */

            struct net_device *dev; /* NULL is wildcarded here */

            int (*func) (struct sk_buff *,

            struct net_device *,

            struct packet_type *,

            struct net_device *);

            void *af_packet_priv;

            struct list_head list;

            };

参数说明:

type:以太帧类型,16位。

dev:所附着的网卡设备,假如为NULL则匹配全部网卡。

func:协议入口接收处理函数。

af_packet_priv:协议私有数据。

list:链表扣。

一般各协议的packet_type结构都是静态存在,初始化时只提供type和func两个参数就能够了,每个协议在初始化时都要将此结构加入到系统类型链表中。

3. 处理函数

3.1 添加节点

/* net/core/dev.c */

            /**

            * dev_add_pack - add packet handler

            * @pt: packet type declaration

            *

            * Add a protocol handler to the networking stack. The passed &packet_type

            * is linked into kernel lists and may not be freed until it has been

            * removed from the kernel lists.

            *

            * This call does not sleep therefore it can not

            * guarantee all CPU's that are in middle of receiving packets

            * will see the new packet type (until the next received packet).

            */

            void dev_add_pack(struct packet_type *pt)

            {

            int hash;

            spin_lock_bh(&ptype_lock);

            // 假如类型是全部以太类型,则节点链接到ptype_all链

            if (pt->type == htons(ETH_P_ALL)) {

            netdev_nit  ;

            list_add_rcu(&pt->list, &ptype_all);

            } else {

            // 根据协议类型取个HASH,共15个HASH链表

            hash = ntohs(pt->type) & 15;

            // 将节点链接到HASH链表中,list_add_rcu是加了smp_wmb()保护的list_add链表操作

            list_add_rcu(&pt->list, &ptype_base[hash]);

            }

            spin_unlock_bh(&ptype_lock);

            }

3.2 删除节点

/**

            * __dev_remove_pack - remove packet handler

            * @pt: packet type declaration

            *

            * Remove a protocol handler that was previously added to the kernel

            * protocol handlers by dev_add_pack(). The passed &packet_type is removed

            * from the kernel lists and can be freed or reused once this function

            * returns.

            *

            * The packet type might still be in use by receivers

            * and must not be freed until after all the CPU's have gone

            * through a quiescent state.

            */

            void __dev_remove_pack(struct packet_type *pt)

            {

            struct list_head *head;

            struct packet_type *pt1;

            spin_lock_bh(&ptype_lock);

            // 根据协议类型找是在ptype_all表还是某一HASH链表中

            if (pt->type == htons(ETH_P_ALL)) {

            netdev_nit--;

            head = &ptype_all;

            } else

            head = &ptype_base[ntohs(pt->type) & 15];

            // 直接用地址比对进行查找,而不是类型,因为同一个类型也可能有多个节点

            list_for_each_entry(pt1, head, list) {

            if (pt == pt1) {

            list_del_rcu(&pt->list);

            goto out;

            }

            }

            printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);

            out:

            spin_unlock_bh(&ptype_lock);

            }

            /**

            * dev_remove_pack - remove packet handler

            * @pt: packet type declaration

            *

            * Remove a protocol handler that was previously added to the kernel

            * protocol handlers by dev_add_pack(). The passed &packet_type is removed

            * from the kernel lists and can be freed or reused once this function

            * returns.

            *

            * This call sleeps to guarantee that no CPU is looking at the packet

            * type after return.

            */

            // 只是__dev_remove_pack()的包裹函数

            void dev_remove_pack(struct packet_type *pt)

            {

            __dev_remove_pack(pt);

            synchronize_net();

            }

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!