歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核實踐 - 如何添加網絡協議[三]:實現

Linux內核實踐 - 如何添加網絡協議[三]:實現

日期:2017/3/3 16:37:51   编辑:Linux內核

內核版本:2.6.34

接上篇《添加網絡協議》。

為了用戶方便查看brcm設備的工作狀態,使用proc文件系統是很好的方 式。一個網絡協議模塊可以注冊到網絡空間中register_pernet_subsys(),這個函數會為子空間分配一個id號,通過id可以在網 絡空間中找到分配給該子空間的內存:init_net->gen->ptr[id - 1]。而我們正是利用這塊內存去存儲proc中的相關信息 :struct brcm_net,它記錄了brcm設備在proc文件系統中的位置。

struct brcm_net {     
    /* /proc/net/brcm */ 
    struct proc_dir_entry *proc_brcm_dir;     
    /* /proc/net/brcm/config */ 
    struct proc_dir_entry *proc_brcm_conf;     
};

在加載brcm模塊時會注冊子空間,brcm_init_net創建在proc中的相關項,並記錄路徑在brcm_net中; brcm_exit_net刪除在proc中的相關項;brcm_net_id記錄分配給子空間的id,這樣通過init_net->gen->ptr[brcm_net_id - 1]就可以操作brcm_net了。

err = register_pernet_subsys(&brcm_net_ops);     
static struct pernet_operations brcm_net_ops = {     
    .init = brcm_init_net,     
    .exit = brcm_exit_net,     
    .id = &brcm_net_id,     
    .size = sizeof(struct brcm_net),     
};

注意到在brcm_init_net和brcm_exit_net中添加和刪除的僅僅是/proc/net/brcm目錄和config文件,而在使用中 brcm設備是可以動態創建的,因此這部分代碼應該發生在添加和刪除brcm設備時,而不是在brcm模塊注冊和刪除時。最簡單的是 直接添加在register方法或unregister方法中,但內核提供了更好的機制:事件,將對proc的操作分離出來,因為proc的操作實 際上屬於附加的操作而不是必須的操作。下面就來看event機制。

前面幾篇已經有描述過event機制,這裡的事件都是關於設 備的事件,使用的是register_netdevice_notifier()來,注冊notifier到netdev_chain鏈表上。在加載brcm模塊時注冊 notifier,brcm_notifier_block包含了brcm設備對所關心的事件作出的反應。

err = register_netdevice_notifier

(&brcm_notifier_block);     
static struct notifier_block brcm_notifier_block __read_mostly = {     
    .notifier_call = brcm_device_event,     
};

設備對哪些事件會感興趣,首先,brcm設備對注冊和注銷是感興趣的,要操作proc文件系統;其次,對於發往brcm 下層設備的事件,要考慮這些事件造成的連帶影響(比如eth1被down掉,則其上的brcm設備也應該被down掉),一般是下層設備的 事件對其上的所有brcm設備都進行相應操作。

所以在brcm_device_event有兩類進入的設備dev會進行操作,一類是brcm設備 ,它僅僅是操作proc文件系統。判斷是否為brcm設備,是的話則由__brcm_device_event() 處理。

if (is_brcm_dev(dev))     
    __brcm_device_event(dev, event);     
         
static void __brcm_device_event(struct net_device *dev, unsigned long event)     
{     
    switch (event) {     
    case NETDEV_CHANGENAME:     
        brcm_proc_rem_dev(dev);     
        if (brcm_proc_add_dev(dev) < 0)     
            pr_warning("BRCM: failed to change proc name for %s\n",     
                    dev->name);     
        break;     
    case NETDEV_REGISTER:
        if (brcm_proc_add_dev(dev) < 0)     
            pr_warning("BRCM: failed to add proc entry for %s\n",     
                    dev->name);     
        break;     
    case NETDEV_UNREGISTER:     
        brcm_proc_rem_dev(dev);     
        break;     
    }     
}

如何是brcm的下層設備,如根據brcm_group_hash中的映射關系,對下層設備相關的所有brcm設備進行操作:

switch (event) {     
case NETDEV_CHANGE:     
    /* Propagate real device state to vlan devices */ 
    for (i = 0; i < BRCM_GROUP_ARRAY_LEN; i++) {     
        brcmdev = brcm_group_get_device(grp, i);
        if (!brcmdev)
            continue;
         
        netif_stacked_transfer_operstate(dev, brcmdev);
    }
    break;
         
case NETDEV_CHANGEADDR:     
    /* Adjust unicast filters on underlying device */ 
    for (i = 0; i < BRCM_GROUP_ARRAY_LEN; i++) {     
        brcmdev = brcm_group_get_device(grp, i);     
        if (!brcmdev)     
            continue;     
         
        flgs = brcmdev->flags;     
        if (!(flgs & IFF_UP))     
            continue;     
         
        brcm_sync_address(dev, brcmdev);     
    }     
    break;     
         
case NETDEV_CHANGEMTU:     
    for (i = 0; i < BRCM_GROUP_ARRAY_LEN; i++) {     
        brcmdev = brcm_group_get_device(grp, i);     
        if (!brcmdev)     
            continue;     
         
        if (brcmdev->mtu <= dev->mtu)     
            continue;     
         
        dev_set_mtu(brcmdev, dev->mtu);     
    }     
    break;     
         
case NETDEV_DOWN:     
    /* Put all VLANs for this dev in the down state too.  */ 
    for (i = 0; i < BRCM_GROUP_ARRAY_LEN; i++) {     
        brcmdev = brcm_group_get_device(grp, i);     
        if (!brcmdev)     
            continue;     
         
        flgs = brcmdev->flags;     
        if (!(flgs & IFF_UP))     
            continue;     
         
        brcm = brcm_dev_info(brcmdev);     
        dev_change_flags(brcmdev, flgs & ~IFF_UP);     
        netif_stacked_transfer_operstate(dev, brcmdev);     
    }     
    break;     
         
case NETDEV_UP:     
    /* Put all VLANs for this dev in the up state too.  */ 
    for (i = 0; i < BRCM_GROUP_ARRAY_LEN; i++) {     
        brcmdev = brcm_group_get_device(grp, i);     
        if (!brcmdev)     
            continue;     
         
        flgs = brcmdev->flags;     
        if (flgs & IFF_UP)     
            continue;     
         
        brcm = brcm_dev_info(brcmdev);     
        dev_change_flags(brcmdev, flgs | IFF_UP);     
        netif_stacked_transfer_operstate(dev, brcmdev);     
    }     
    break;     
         
case NETDEV_UNREGISTER:     
    /* Delete all BRCMs for this dev. */ 
    grp->killall = 1;     
         
    for (i = 0; i < BRCM_GROUP_ARRAY_LEN; i++) {     
        brcmdev = brcm_group_get_device(grp, i);     
        if (!brcmdev)     
            continue;     
         
        /* unregistration of last brcm destroys group, abort    
         * afterwards */ 
        if (grp->nr_ports == 1)
            i = BRCM_GROUP_ARRAY_LEN;
         
        unregister_brcm_dev(brcmdev, &list);
    }     
    unregister_netdevice_many(&list);
    break;     
}

到這裡,協議的添加就大致完成了,當然還包括一些頭文件的修改,宏變量的添加等就不一一詳述,具體可見最後的附件。

為了編譯進內核,還需要修改以下文件:

$(linux)/net/Kconfig $(linux)/net/Makefile

最後,在make menuconfig選擇添加brcm協議

Networking Support -> Networking options

同時,需要一個簡單 的用戶空間工具來配置我們的brcm設備,就像vconfig用來配置vlan設備一樣;編寫的簡單的bconfig工具,命令格式:

"Usage: add [interface-name] [brcm_port]\n"

" rem [dev-name]";

內核編譯完成後就該進行測試了,如果開啟了內核調 試信息,啟動內核就看到以下信息:

然後啟用網卡,可以查看到添加了brcm設備後的狀態:

可以使用原生套接字自己打上brcm頭後發送報文讓協議棧接收,或者用wireshark等捕獲協議棧發出的報文,下圖即是捕獲到 的報文:

這是主機發出的arp報文,可以看到,在源mac後接的不是vlan報頭,而是我們添加的brcm報文,協議號是8744。

查看proc 中信息:

附:patch補丁 && 重要的源文件 && bconfig工具源碼

http://download.csdn.net/source/3548117

Copyright © Linux教程網 All Rights Reserved