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);
} |