上篇文章導入博客園的比較早,而這篇自己在寫的時候才發現內部復雜的很,以至於沒能按時完成,造成兩篇文章的間隔時間有點長!
話不多說,言歸正傳!
前面的文章介紹了橋接模式下的基礎理論知識,其實本節想結合LInux源代碼分析下橋接模式下的數據包的轉發流程,但是看了源碼才發現,這部分內容太多,非一篇文章可以描述的清楚的,所以決定本篇文章主要介紹下linux網絡相關的主要結構體,以及各個結構之間的關當一個網絡數據包來到host的物理網卡,由於此時網卡已經是混雜模式,所以此數據包的目的地不一定就是host本身。那么此時網卡的 設備控制器就會向host的APIC發送中斷信號。CPU收到中斷信號后,會自動進入處理該中斷的流程,調用IDT中網卡驅動注冊的中斷處理函數進行處理。
1 struct net_device{ 2 …… 3 unsigned long state; 4 5 …… 6 unsigned int flags; /* interface flags (a la BSD) */ 7 unsigned int priv_flags; /* Like 'flags' but invisible to userspace. 8 9 …… 10 11 #if IS_ENABLED(CONFIG_VLAN_8021Q) 12 struct vlan_info __rcu *vlan_info; /* VLAN info */ 13 #endif 14 15 …… 16 unsigned char *dev_addr 17 rx_handler_func_t __rcu *rx_handler; 18 void __rcu *rx_handler_data; 19 20 …… 21 22 } 23
net_device結構代表一個網絡設備,每一個物理網卡還有linux內部都有一個獨立的net_device結構與之對應。
state表示設備的狀態
flag表示設備的特性,而priv_flag則表示設備的私有特性,對用戶空間是不可見的。
dev_addr表示設備的mac地址
rx_handler代表一個鈎子函數,在網卡混雜模式開啟時,此函數會被初始化成一個轉發數據包的函數br_handle_frame
rx_hander_data 指向一個net_bridge_port結構,該端口是正是skb->dev在開啟網橋特性后的代表的端口結構。說起來由點繞,還是看下代碼
1 static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev) 2 { 3 struct net_bridge_port *port = 4 rcu_dereference_rtnl(dev->rx_handler_data); 5 6 return br_port_exists(dev) ? port : NULL; 7 }
該函數從一個net_device獲取橋接端口,可以看到正是通過其rx_handler_data指針。而dev是否存在這個端口即其結構里面是否設置了此指針就要看dev的私有特性了
1 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
1 struct sk_buffer{ 2 struct sk_buff *next; 3 struct sk_buff *prev; 4 5 …… 6 7 struct net_device *dev; 8 9 …… 1115 __u16 transport_header;//傳輸層頭部偏移 16 __u16 network_header; //IP頭部偏移 17 __u16 mac_header;//MAC地址偏移 18 /* These elements must be at the end, see alloc_skb() for details. */ 19 sk_buff_data_t tail; 20 sk_buff_data_t end; 21 unsigned char *head,//buffer header指針 22 *data; //數據指針 23 unsigned int truesize; 24 atomic_t users; 25 26 }
該結構是數據包逐層交付所必須的結構,其中Next和prev分別指向下一個和上一個buffer,dev表示這個buffer從哪個設備進入,data指向buffer的數據區,head指向buffer的最開始的頭部,
mac_header是以太網頭部到head指針的偏移,network_header是Ip數據包頭部到head指針的偏移,transport_header是傳送層頭部到head指針的偏移,tail指向數據部分的結束,end指向buffer的結束。truesize是buffer實際的大小,user記錄用戶數,主要表明是否共享。
1 struct net_bridge{ 2 struct list_head port_list;//所有端口組成的鏈表頭 3 struct net_device *dev; //對應的物理設備 4 5 …… 6 7 struct net_bridge_mdb_htable __rcu *mdb; 8 …… 9 }
這是linux內部的網橋對應的結構,port_list連接網橋所有的端口,dev指向網橋的設備結構體,mdb指向網橋的組播數據庫轉發表
1 struct net_bridge_port 2 { 3 struct net_bridge *br; //對應的網橋 4 struct net_device *dev; //端口對應的設備 5 struct list_head list; 6 …… 7 u8 state; 8 …… 9 unsigned long flags; 10 …… 11 struct hlist_head mglist; 12 …… 13 }
net_bridge_port結構對應於網橋的一個端口,state表明端口的狀態,flags表明端口本身的特性,dev指向它關聯的設備,br指向它attach的網橋,mglist連接所有port加入的組,flag記錄了端口的某些特性,state表明了端口的某一個狀態,如轉發、學習等。
1 struct net_bridge_fdb_entry 2 { 3 struct hlist_node hlist; 4 struct net_bridge_port *dst; 5 6 struct rcu_head rcu; 7 unsigned long updated; 8 unsigned long used; 9 mac_addr addr; 10 unsigned char is_local; 11 unsigned char is_static; 12 __u16 vlan_id; 13 };
這是網橋內部轉發表的表項,hlist表明表項作為一個節點存在於某張表中,這張表就是轉發表。dst指向目的端口,addr是表項的mac地址,isLocal表明是否是本地端口,本地端口我猜想是網橋的數據流入端口,即當目的mac是本地端口表明這是發往本地的數據包;isstatic表明是否是靜態地址,靜態地址不能自動更新。
1 struct net_bridge_mdb_htable 2 { 3 struct hlist_head *mhash; 4 struct rcu_head rcu; 5 struct net_bridge_mdb_htable *old; 6 u32 size; 7 u32 max; 8 u32 secret; 9 u32 ver; 10 };
該結構表示一個組播數據庫轉發表,連接了所有的組播數據庫轉發項.size表示表的大小,max表示最大容量。
1 struct net_port_vlans { 2 u16 port_idx; 3 u16 pvid; 4 union { 5 struct net_bridge_port *port; 6 struct net_bridge *br; 7 } parent; 8 struct rcu_head rcu; 9 unsigned long vlan_bitmap[BR_VLAN_BITMAP_LEN]; 10 unsigned long untagged_bitmap[BR_VLAN_BITMAP_LEN]; 11 u16 num_vlans; 12 };
struct net_bridge_mdb_entry { struct hlist_node hlist[2]; struct hlist_node mglist; struct net_bridge *br;//橋 struct net_bridge_port_group *ports;// struct rcu_head rcu; struct timer_list timer;//組播組數據庫項失效定時器,若超時,則會將該組播端口從組播組數據庫項的組播端口列表中刪除 struct timer_list query_timer;//查詢定時 __be32 addr;//組播組地址 u32 queries_sent; };
1 struct net_bridge_port_group { 2 struct net_bridge_port *port; 3 struct net_bridge_port_group __rcu *next; 4 struct hlist_node mglist; 5 struct rcu_head rcu; 6 struct timer_list timer; 7 struct br_ip addr; 8 unsigned char state; 9 };
這是用於組播的組結構,一個組綁定一個組播地址addr,next指向下一個組播組,port指向組的端口,timer是定時器,mgList用於連接一個端口加入的所有的group,表頭保存在port結構里
1 struct mac_addr 2 { 3 unsigned char addr[6]; 4 };
可以看到內核中MAC地址用6個字節表示