接下來看net_bridge以及相關的數據結構:
- struct net_bridge
- {
- ///自旋鎖
- spinlock_t lock;
- ///網橋所有端口的鏈表,其中每個元素都是一個net_bridge_port結構。
- struct list_head port_list;
- ///加到這個網橋的物理設備
- struct net_device *dev;
- ///這個鎖是用來保護下面的那個hash鏈表。
- spinlock_t hash_lock;
- ///保存forwarding database的一個hash鏈表(這個也就是地址學習的東東,所以通過hash能 快速定位),這裏每個元素都是一個net_bridge_fsb_entry結構
- struct hlist_head hash[BR_HASH_SIZE];
- ///這個結構沒有被使用
- struct list_head age_list;
- unsigned long feature_mask;
- #ifdef CONFIG_BRIDGE_NETFILTER
- struct rtable fake_rtable;
- #endif
- unsigned long flags;
- #define BR_SET_MAC_ADDR 0x00000001
- ///stp相關的一些東西
- bridge_id designated_root;
- bridge_id bridge_id;
- u32 root_path_cost;
- unsigned long max_age;
- unsigned long hello_time;
- unsigned long forward_delay;
- unsigned long bridge_max_age;
- unsigned long ageing_time;
- unsigned long bridge_hello_time;
- unsigned long bridge_forward_delay;
- u8 group_addr[ETH_ALEN];
- u16 root_port;
- ///當前使用的協議。
- enum {
- BR_NO_STP, /* no spanning tree */
- BR_KERNEL_STP, /* old STP in kernel */
- BR_USER_STP, /* new RSTP in userspace */
- } stp_enabled;
- unsigned char topology_change;
- unsigned char topology_change_detected;
- ///stp要用的一些定時器列表。
- struct timer_list hello_timer;
- struct timer_list tcn_timer;
- struct timer_list topology_change_timer;
- struct timer_list gc_timer;
- struct kobject *ifobj;
- };
- struct net_bridge_port
- {
- ///從屬於的網橋設備
- struct net_bridge *br;
- ///表示鏈接到這個端口的物理設備
- struct net_device *dev;
- struct list_head list;
- ///stp相關的一些參數。
- u8 priority;
- u8 state;
- u16 port_no;
- unsigned char topology_change_ack;
- unsigned char config_pending;
- port_id port_id;
- port_id designated_port;
- bridge_id designated_root;
- bridge_id designated_bridge;
- u32 path_cost;
- u32 designated_cost;
- ///端口定時器,也就是stp控制超時的一些定時器列表.(詳細的需要去看stp的協議).
- struct timer_list forward_delay_timer;
- struct timer_list hold_timer;
- struct timer_list message_age_timer;
- struct kobject kobj;
- struct rcu_head rcu;
- };
- struct net_bridge_fdb_entry
- {
- struct hlist_node hlist;
- ///橋的端口(最主要的兩個域就是這個域和下面的mac地址域)
- struct net_bridge_port *dst;
- ///當使用RCU策略,纔用到
- struct rcu_head rcu;
- ///引用計數
- atomic_t use_count;
- unsigned long ageing_timer;
- ///mac地址。
- mac_addr addr;
- unsigned char is_local;
- unsigned char is_static;
- };
通過下面的圖能更好的理解這個結構:
接下來簡要的介紹一下網橋的初始化。
網橋的初始化和一般網絡設備的初始化很相似,只不過由於它是虛擬設備,因此這裏還有一點不同。
首先來看內核的網絡模塊的初始化br_init,也就是初始化上面介紹的數據結構:
- static int __init br_init(void)
- {
- int err;
- ///stp的註冊。
- err = stp_proto_register(&br_stp_proto);
- if (err < 0) {
- printk(KERN_ERR "bridge: can't register sap for STP\n");
- return err;
- }
- ///forwarding database的初始化
- err = br_fdb_init();
- if (err)
- goto err_out;
- ///網橋的netfilter鉤子函數的初始化。
- err = br_netfilter_init();
- if (err)
- goto err_out1;
- ///註冊到netdevice的通知鏈上
- err = register_netdevice_notifier(&br_device_notifier);
- if (err)
- goto err_out2;
- err = br_netlink_init();
- if (err)
- goto err_out3;
- ///安裝網絡設備的do_ioctl函數,也就是提供給用戶空間ioctl接口。
- brioctl_set(br_ioctl_deviceless_stub);
- br_handle_frame_hook = br_handle_frame;
- br_fdb_get_hook = br_fdb_get;
- br_fdb_put_hook = br_fdb_put;
- return 0;
- .........................................
- return err;
- }
我們新建一個網橋,使用br_add_bridge,在這個函數中,主要是調用new_bridge_dev函數,下面我們主要就來看這個函數:
- static struct net_device *new_bridge_dev(const char *name)
- {
- struct net_bridge *br;
- struct net_device *dev;
- ///這裏看到setup回調函數,是br_dev_setup(也就是網橋設備專用的)。setup函數的用途,可以看我以前寫的網絡設備初始化的blog。
- dev = alloc_netdev(sizeof(struct net_bridge), name,
- br_dev_setup);
- if (!dev)
- return NULL;
- ///得到priv數據。
- br = netdev_priv(dev);
- ///接下來初始化br數據結構。
- br->dev = dev;
- spin_lock_init(&br->lock);
- INIT_LIST_HEAD(&br->port_list);
- spin_lock_init(&br->hash_lock);
- ///網橋優先級 32768(也就是默認是0x8000)
- br->bridge_id.prio[0] = 0x80;
- br->bridge_id.prio[1] = 0x00;
- memcpy(br->group_addr, br_group_address, ETH_ALEN);
- br->feature_mask = dev->features;
- br->stp_enabled = BR_NO_STP;
- br->designated_root = br->bridge_id;
- br->root_path_cost = 0;
- br->root_port = 0;
- br->bridge_max_age = br->max_age = 20 * HZ;
- br->bridge_hello_time = br->hello_time = 2 * HZ;
- br->bridge_forward_delay = br->forward_delay = 15 * HZ;
- br->topology_change = 0;
- br->topology_change_detected = 0;
- br->ageing_time = 300 * HZ;
- ///初始化網橋設備的netfilter相關域。
- br_netfilter_rtable_init(br);
- INIT_LIST_HEAD(&br->age_list);
- br_stp_timer_init(br);
- return dev;
- }
加一個新端口到一個網橋使用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。我們來看這個函數的實現:
{
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,而此時由於設備已經替換爲虛擬的網橋設備,因此就會直接將包發往下層正確的協議處理。