1. 程式人生 > >結構體 net_device常用成員分析

結構體 net_device常用成員分析

   結構體net_device代表了一個網路裝置介面,它是我們理解網路裝置驅動程式的關鍵。這裡,我們選擇其中的一些重要成員,一一作詳細分析,並結合乙太網裝置,看看Linux核心是如何為乙太網裝置提供結構體中某些成員的預設值的。
    在Linux核心原始碼中是這樣為這個結構體作註釋的:實際上,這個結構體是一個很大的錯誤,它把I/O資料和更高層的資料混合在一起,而且它幾乎必須知道INET模組中的每個資料結構。
    毫無疑問,這是一個巨型結構體。但我們為編寫網路裝置驅動程式,只需要瞭解其中的一部分,下面選擇其中的一些作分析,並給出乙太網裝置的預設值。

unsigned short  flags;
void (*set_multicast_list)(struct net_device *dev);

    這是一個介面標誌,包含了很多值的位掩碼。在乙太網的預設初始化函式中,該標誌被設定為:IFF_BROADCAST|IFF_MULTICAST,表示乙太網卡是可廣播的,並且是能夠進行組播發送的。另外,該標誌介面還有一些只讀標誌,如IFF_UP,當介面被啟用並可以開始傳輸資料包時,核心設定該標誌。而IFF_PROMISC被設定或清除時,會呼叫set_multicast_list函式通知板卡上的硬體過濾器。

unsigned short hard_header_len;
unsigned short type;
    hard_header_len是硬體頭的長度,在乙太網裝置的初始化函式中,該成員被賦為ETH_HLEN,即乙太網頭的長度,該值為14,下面是乙太網頭的定義:

struct ethhdr {
    unsigned char   h_dest[ETH_ALEN];   /* destination eth addr */
    unsigned char   h_source[ETH_ALEN]; /* source ether addr    */
    unsigned short  h_proto;        /* packet type ID field */
} __attribute__((packed));
    ETH_ALEN被定義為6,即乙太網MAC地址的長度。h_proto儲存type的值。type是介面的硬體型別,乙太網裝置的初始化函式中將其賦值為ARPHRD_ETHER,即10Mb乙太網。


int (*hard_header) (struct sk_buff *skb, struct net_device *dev,
                unsigned short type, void *daddr,void *saddr,
                unsigned len);
    該函式在資料被傳輸之前被呼叫,它根據先前檢索到的源和目標地址建立硬體頭。乙太網裝置的預設函式是eth_header:
int eth_header(struct sk_buff *skb, struct net_device *dev,
                unsigned short type, void *daddr, 
                void *saddr, unsigned len)
{
    struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);

    /*
     *  Set the protocol type. For a packet of 
     *  type ETH_P_802_3 we put the length
     *  in here instead. It is up to the 802.2 
     *  layer to carry protocol information.
     */
    if(type!=ETH_P_802_3)
        eth->h_proto = htons(type);
    else
        eth->h_proto = htons(len);

    //Set the source hardware address.
    if(!saddr)
        saddr = dev->dev_addr;
    memcpy(eth->h_source,saddr,dev->addr_len);
    //Anyway, the loopback-device should never 
    //use this function...
    if (dev->flags & (IFF_LOOPBACK|IFF_NOARP)){
        memset(eth->h_dest, 0, dev->addr_len);
        return ETH_HLEN;
    }

    if(daddr){
        memcpy(eth->h_dest,daddr,dev->addr_len);
        return ETH_HLEN;
    }
    return -ETH_HLEN;
}
    它根據傳入的引數建立乙太網頭,如果傳入的源地址為空,則使用裝置的地址作為源地址。與之相關的還有一個int (*rebuild_header)(struct sk_buff *skb)函式,在乙太網裝置中,它是用於在ARP地址解析完成以後,重新建立乙太網頭,主要是更新目標MAC地址,因為在前一次建立時,由於沒有經過 ARP協議,有可能目標MAC地址是錯誤的。
int (*set_mac_address)(struct net_device *dev, void *addr);
    改變網絡卡的mac地址。實際上,許多硬體裝置介面根本不支援這種功能,就算支援,也需要硬體廠商提供的工具修改EPROM中的硬體地址。一般我們在某一作業系統下所謂的修改MAC地址,只是修改了作業系統在安裝網絡卡時從網絡卡EPROM中讀出來的值,如果系統被重灌,則MAC地址又恢復為原來的值。乙太網裝置的預設函式是eth_mac_addr,它只是簡單地修改了dev->dev_addr的值。

int (*hard_header_cache)(struct neighbour *neigh,struct hh_cache *hh);
void (*header_cache_update)(struct hh_cache *hh,struct net_device *dev,
                            unsigned char *  haddr);
int (*hard_header_parse)(struct sk_buff *skb,unsigned char *haddr);
    這三個函式在乙太網裝置介面中都有預設的值,hard_header_cache把硬體地址快取到struct hh_cache中;header_cache_update在地址發生變化中,更新hh_cache結構中的目標地址;而 hard_header_parse則從skb中的資料包中獲得源地址,並將其複製到位於haddr的緩衝區。

int (*change_mtu)(struct net_device *dev, int new_mtu);
    下面是乙太網裝置介面的change_mtu的實現:
    static int eth_change_mtu(struct net_device *dev, int new_mtu)
    {
        if ((new_mtu < 68) || (new_mtu > 1500))
            return -EINVAL;
        dev->mtu = new_mtu;
        return 0;
    }

    在net_device中,還有很多網路驅動程式必須涉及的重要成員,隨著我們的8139too網絡卡驅動程式的進一步深入,在涉及時再作詳細分析。