內核版本: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