網橋在內核的實現

我們知道netdevice有一個priv域,這個域用來保存設備的私有數據,當這個設備是一個網橋的時候,priv域也就指向一個struct net_bridge. 

接下來看net_bridge以及相關的數據結構: 
Java代碼  收藏代碼
  1. struct net_bridge  
  2. {  
  3. ///自旋鎖  
  4.     spinlock_t          lock;  
  5. ///網橋所有端口的鏈表,其中每個元素都是一個net_bridge_port結構。  
  6.     struct list_head        port_list;  
  7. ///加到這個網橋的物理設備  
  8.     struct net_device       *dev;  
  9. ///這個鎖是用來保護下面的那個hash鏈表。  
  10.     spinlock_t          hash_lock;  
  11. ///保存forwarding database的一個hash鏈表(這個也就是地址學習的東東,所以通過hash能 快速定位),這裏每個元素都是一個net_bridge_fsb_entry結構  
  12.     struct hlist_head       hash[BR_HASH_SIZE];  
  13. ///這個結構沒有被使用  
  14.     struct list_head        age_list;  
  15.     unsigned long           feature_mask;  
  16. #ifdef CONFIG_BRIDGE_NETFILTER  
  17.     struct rtable           fake_rtable;  
  18. #endif  
  19.     unsigned long           flags;  
  20. #define BR_SET_MAC_ADDR     0x00000001  
  21.   
  22. ///stp相關的一些東西  
  23.     bridge_id           designated_root;  
  24.     bridge_id           bridge_id;  
  25.     u32             root_path_cost;  
  26.     unsigned long           max_age;  
  27.     unsigned long           hello_time;  
  28.     unsigned long           forward_delay;  
  29.     unsigned long           bridge_max_age;  
  30.     unsigned long           ageing_time;  
  31.     unsigned long           bridge_hello_time;  
  32.     unsigned long           bridge_forward_delay;  
  33.   
  34.     u8              group_addr[ETH_ALEN];  
  35.     u16             root_port;  
  36. ///當前使用的協議。  
  37.     enum {  
  38.         BR_NO_STP,      /* no spanning tree */  
  39.         BR_KERNEL_STP,      /* old STP in kernel */  
  40.         BR_USER_STP,        /* new RSTP in userspace */  
  41.     } stp_enabled;  
  42.   
  43.     unsigned char           topology_change;  
  44.     unsigned char           topology_change_detected;  
  45.   
  46. ///stp要用的一些定時器列表。  
  47.     struct timer_list       hello_timer;  
  48.     struct timer_list       tcn_timer;  
  49.     struct timer_list       topology_change_timer;  
  50.     struct timer_list       gc_timer;  
  51.     struct kobject          *ifobj;  
  52. };  


Java代碼  收藏代碼
  1. struct net_bridge_port  
  2. {  
  3. ///從屬於的網橋設備  
  4.     struct net_bridge       *br;  
  5. ///表示鏈接到這個端口的物理設備  
  6.     struct net_device       *dev;  
  7.     struct list_head        list;  
  8. ///stp相關的一些參數。  
  9.     u8              priority;  
  10.     u8              state;  
  11.     u16             port_no;  
  12.     unsigned char           topology_change_ack;  
  13.     unsigned char           config_pending;  
  14.     port_id             port_id;  
  15.     port_id             designated_port;  
  16.     bridge_id           designated_root;  
  17.     bridge_id           designated_bridge;  
  18.     u32             path_cost;  
  19.     u32             designated_cost;  
  20. ///端口定時器,也就是stp控制超時的一些定時器列表.(詳細的需要去看stp的協議).  
  21.     struct timer_list       forward_delay_timer;  
  22.     struct timer_list       hold_timer;  
  23.     struct timer_list       message_age_timer;  
  24.     struct kobject          kobj;  
  25.     struct rcu_head         rcu;  
  26. };  


Java代碼  收藏代碼
  1. struct net_bridge_fdb_entry  
  2. {  
  3.     struct hlist_node       hlist;  
  4. ///橋的端口(最主要的兩個域就是這個域和下面的mac地址域)  
  5.     struct net_bridge_port      *dst;  
  6. ///當使用RCU策略,纔用到  
  7.     struct rcu_head         rcu;  
  8. ///引用計數  
  9.     atomic_t            use_count;  
  10.     unsigned long           ageing_timer;  
  11. ///mac地址。  
  12.     mac_addr            addr;  
  13.     unsigned char           is_local;  
  14.     unsigned char           is_static;  
  15. };  

通過下面的圖能更好的理解這個結構: 

 

接下來簡要的介紹一下網橋的初始化。 

網橋的初始化和一般網絡設備的初始化很相似,只不過由於它是虛擬設備,因此這裏還有一點不同。 

首先來看內核的網絡模塊的初始化br_init,也就是初始化上面介紹的數據結構: 

Java代碼  收藏代碼
  1. static int __init br_init(void)  
  2. {  
  3.     int err;  
  4. ///stp的註冊。  
  5.     err = stp_proto_register(&br_stp_proto);  
  6.     if (err < 0) {  
  7.         printk(KERN_ERR "bridge: can't register sap for STP\n");  
  8.         return err;  
  9.     }  
  10.   
  11. ///forwarding database的初始化  
  12.     err = br_fdb_init();  
  13.     if (err)  
  14.         goto err_out;  
  15. ///網橋的netfilter鉤子函數的初始化。  
  16.     err = br_netfilter_init();  
  17.     if (err)  
  18.         goto err_out1;  
  19. ///註冊到netdevice的通知鏈上  
  20.     err = register_netdevice_notifier(&br_device_notifier);  
  21.     if (err)  
  22.         goto err_out2;  
  23.   
  24.     err = br_netlink_init();  
  25.     if (err)  
  26.         goto err_out3;  
  27. ///安裝網絡設備的do_ioctl函數,也就是提供給用戶空間ioctl接口。  
  28.     brioctl_set(br_ioctl_deviceless_stub);  
  29.     br_handle_frame_hook = br_handle_frame;  
  30.   
  31.     br_fdb_get_hook = br_fdb_get;  
  32.     br_fdb_put_hook = br_fdb_put;  
  33.   
  34.     return 0;  
  35. .........................................  
  36.     return err;  
  37. }  


我們新建一個網橋,使用br_add_bridge,在這個函數中,主要是調用new_bridge_dev函數,下面我們主要就來看這個函數: 

Java代碼  收藏代碼
  1. static struct net_device *new_bridge_dev(const char *name)  
  2. {  
  3.     struct net_bridge *br;  
  4.     struct net_device *dev;  
  5.   
  6. ///這裏看到setup回調函數,是br_dev_setup(也就是網橋設備專用的)。setup函數的用途,可以看我以前寫的網絡設備初始化的blog。  
  7.     dev = alloc_netdev(sizeof(struct net_bridge), name,  
  8.                br_dev_setup);  
  9.   
  10.     if (!dev)  
  11.         return NULL;  
  12. ///得到priv數據。  
  13.     br = netdev_priv(dev);  
  14.   
  15. ///接下來初始化br數據結構。  
  16.     br->dev = dev;  
  17.   
  18.     spin_lock_init(&br->lock);  
  19.     INIT_LIST_HEAD(&br->port_list);  
  20.     spin_lock_init(&br->hash_lock);  
  21.   
  22. ///網橋優先級 32768(也就是默認是0x8000)  
  23.     br->bridge_id.prio[0] = 0x80;  
  24.     br->bridge_id.prio[1] = 0x00;  
  25.   
  26.     memcpy(br->group_addr, br_group_address, ETH_ALEN);  
  27.   
  28.     br->feature_mask = dev->features;  
  29.     br->stp_enabled = BR_NO_STP;  
  30.     br->designated_root = br->bridge_id;  
  31.     br->root_path_cost = 0;  
  32.     br->root_port = 0;  
  33.     br->bridge_max_age = br->max_age = 20 * HZ;  
  34.     br->bridge_hello_time = br->hello_time = 2 * HZ;  
  35.     br->bridge_forward_delay = br->forward_delay = 15 * HZ;  
  36.     br->topology_change = 0;  
  37.     br->topology_change_detected = 0;  
  38.     br->ageing_time = 300 * HZ;  
  39. ///初始化網橋設備的netfilter相關域。  
  40.     br_netfilter_rtable_init(br);  
  41.   
  42.     INIT_LIST_HEAD(&br->age_list);  
  43.   
  44.     br_stp_timer_init(br);  
  45.   
  46.     return dev;  
  47. }  


加一個新端口到一個網橋使用br_add_if方法。這裏就不詳細介紹這個方法了,不過這裏要注意,他會在sys文件系統下,生成一些相關的東西。要看sysfs的介紹,去看kernel的文檔。 

最後來看一下網橋的子系統在這個網絡子系統的位置: 




可以看到這裏有很多的hook在ip層,基本都是netfilter子系統的東西。 

這裏網橋的輸入幀的處理是通過br_handle_frame來處理的。而網橋的輸出幀是通過br_dev_xmit來處理的。 

當網絡幀通過NIC的設備驅動被接收了之後,skb->dev被實例化爲真實的設備,然後這個幀被放入網絡棧,然後當be_handle_frame_finish之後調用br_pass_frame_up。我們來看這個函數的實現: 

引用
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) 

struct net_device *indev, *brdev = br->dev; 

brdev->stats.rx_packets++; 
brdev->stats.rx_bytes += skb->len; 

indev = skb->dev; 
///這步將真實的物理設備替換爲虛擬的網橋設備。因此對3層來說就完全不知道物理設備的存在了。 
skb->dev = brdev; 
///調用netfiltel的相關hook。 
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, 
netif_receive_skb); 
}


當這個函數執行完畢後,會再次調用netif_receive_skb,他則會再次調用handle_bridge,而此時由於設備已經替換爲虛擬的網橋設備,因此就會直接將包發往下層正確的協議處理。 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章