快捷搜索:

Linux内核如何接收不同类型以太帧

1. 媒介

以太头中除了6字节目的MAC地址、6字节源MAC地址外,还有两字节的以太帧类型值,如IPv4为0x0800,ARP为0x0806等,网卡驱动收到以太帧后经由过程接口函数netif_receive_skb()(netif_rx实际着末也是调用netif_receive_skb)交到上层,而这个接口函数就完成对以太帧类型的区分,交到不合的协议处置惩罚法度榜样。假如想自己编写某一以太类型帧的处置惩罚法度榜样,必要自己添加响应的

以下Linux内核代码为2.6.17.11。

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.

", 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();

}

4. 实例

您可能还会对下面的文章感兴趣: