1.序言
以太头中不仅6字节目的MAC地址、6字节源MAC地址外linux内核添加函数,还有两字节的以太帧类型值,如IPv4为0x0800,ARP为0x0806等,网卡驱动收到以太帧后通过插口函数netif_receive_skb()(netif_rx实际最后也是调用netif_receive_skb)交到下层,而这个插口函数就完成对以太帧类型的分辨,交到不同的合同处理程序。假如想自己编撰某一以太类型帧的处理程序linux端口映射,须要自己添加相应的代码。以下为Linux内核2.6代码。
2.数据结构
每种合同都要定义一个packet_type结构linux内核添加函数,引导步入相关的合同数据处理函数,所有节点组成一个数组(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结构都是静态存在linux服务器搭建,初始化时只提供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);
}