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); }
更多內容請看內核技術 系統管理專題,或