【轉載】網橋原理
[原文 ](https://blog.csdn.net/qq_29422251/article/details/76849261)
本文的參考分析的原始碼版本是2.6.15,我是邊學習邊總結,學習的過程中得益於linux論壇(http://linux.chinaunix.net/bbs/)上大俠們總結分析的文件,他山之石可以攻玉,學習過程中我也會邊學邊總結,開源的發展在於共享,我也拋塊磚,望能引到玉!
由於自身水平有限,且相關的參考資料較少,因此其中的結論不能保證完全正確,如果在閱讀本文的過程中發現了問題歡迎及時與作者聯絡。也希望能有機會和大家多多交流學習心得!
2.1 橋接的概念
簡單來說,橋接就是把一臺機器上的若干個網路介面“連線”起來。其結果是,其中一個網口收到的報文會被複制給其他網口併發送出去。以使得網口之間的報文能夠互相轉發。
交換機就是這樣一個裝置,它有若干個網口,並且這些網口是橋接起來的。於是,與交換機相連的若干主機就能夠通過交換機的報文轉發而互相通訊。
如下圖:主機A傳送的報文被送到交換機S1的eth0口,由於eth0與eth1、eth2橋接在一起,故而報文被複制到eth1和eth2,並且傳送出 去,然後被主機B和交換機S2接收到。而S2又會將報文轉發給主機C、D。
交換機在報文轉發的過程中並不會篡改報文資料,只是做原樣複製。然而橋接卻並不是在物理層實現的,而是在資料鏈路層。交換機能夠理解資料鏈路層的報文,所 以實際上橋接卻又不是單純的報文轉發。
交換機會關心填寫在報文的資料鏈路層頭部中的Mac地址資訊(包括源地址和目的地址),以便了解每個Mac地址所代表的主機都在什麼位置(與本交換機的哪 個網口相連)。在報文轉發時,交換機就只需要向特定的網口轉發即可,從而避免不必要的網路互動。這個就是交換機的“地址學習”。但是如果交換機遇到一個自 己未學習到的地址,就不會知道這個報文應該從哪個網口轉發,則只好將報文轉發給所有網口(接收報文的那個網口除外)。
比如主機C向主機A傳送一個報文,報文來到了交換機S1的eth2網口上。假設S1剛剛啟動,還沒有學習到任何地址,則它會將報文轉發給eth0和 eth1。同時,S1會根據報文的源Mac地址,記錄下“主機C是通過eth2網口接入的”。於是當主機A向C傳送報文時,S1只需要將報文轉發到 eth2網口即可。而當主機D向C傳送報文時,假設交換機S2將報文轉發到了S1的eth2網口(實際上S2也多半會因為地址學習而不這麼做),則S1會 直接將報文丟棄而不做轉發(因為主機C就是從eth2接入的)。
然而,網路拓撲不可能是永不改變的。假設我們將主機B和主機C換個位置,當主機C發出報文時(不管發給誰),交換機S1的eth1口收到報文,於是交換機 S1會更新其學習到的地址,將原來的“主機C是通過eth2網口接入的”改為“主機C是通過eth1網口接入的”。
但是如果主機C一直不傳送報文呢?S1將一直認為“主機C是通過eth2網口接入的”,於是將其他主機發送給C的報文都從eth2轉發出去,結果報文就發 丟了。所以交換機的地址學習需要有超時策略。對於交換機S1來說,如果距離最後一次收到主機C的報文已經過去一定時間了(預設為5分鐘),則S1需要忘記 “主機C是通過eth2網口接入的”這件事情。這樣一來,發往主機C的報文又會被轉發到所有網口上去,而其中從eth1轉發出去的報文將被主機C收到。
linux核心支援網口的橋接(目前只支援乙太網介面)。但是與單純的交換機不同,交換機只是一個二層裝置,對於接收到的報文,要麼轉發、要麼丟棄。小型的交換機裡面只需要一塊交換晶片即可,並不需要CPU。而執行著linux核心的機器本身就是一臺主機,有可能就是網路報文的目的地。其收到的報文除了轉發和丟棄,還可能被送到網路協議棧的上層(網路層),從而被自己消化。
linux核心是通過一個虛擬的網橋裝置來實現橋接的。這個虛擬裝置可以繫結若干個乙太網介面裝置,從而將它們橋接起來。如下圖(摘自ULNI):
網橋裝置br0綁定了eth0和eth1。對於網路協議棧的上層來說,只看得到br0,因為橋接是在資料鏈路層實現的,上層不需要關心橋接的細節。於是協 議棧上層需要傳送的報文被送到br0,網橋裝置的處理程式碼再來判斷報文該被轉發到eth0或是eth1,或者兩者皆是;反過來,從eth0或從eth1接 收到的報文被提交給網橋的處理程式碼,在這裡會判斷報文該轉發、丟棄、或提交到協議棧上層。
而有時候eth0、eth1也可能會作為報文的源地址或目的地址,直接參與報文的傳送與接收(從而繞過網橋)。
2.3 網橋的功能
概括來說,網橋實現最重要的兩點:
1. MAC學習:學習MAC地址,起初,網橋是沒有任何地址與埠的對應關係的,它傳送資料,還是得想HUB一樣,但是每傳送一個數據,它都會關心資料包的來源MAC是從自己的哪個埠來的,由於學習,建立地址-埠的對照表(CAM表)。
2. 報文轉發:每傳送一個數據包,網橋都會提取其目的MAC地址,從自己的地址-埠對照表(CAM表)中查詢由哪個埠把資料包傳送出去。
在Linux裡面使用網橋非常簡單,僅需要做兩件事情就可以配置了。其一是在編譯核心裡把CONFIG_BRIDGE或CONDIG_BRIDGE_MODULE編譯選項開啟;其二是安裝brctl工具。第一步是使核心協議棧支援網橋,第二步是安裝使用者空間工具,通過一系列的ioctl呼叫來配置網橋。下面以一個相對簡單的例項來貫穿全文,以便分析程式碼。
Linux機器有4個網絡卡,分別是eth0~eth4,其中eth0用於連線外網,而eth1, eth2, eth3都連線到一臺PC機,用於配置網橋。只需要用下面的命令就可以完成網橋的配置:
Brctl addbr br0 (建立一個網橋br0, 同時在Linux核心裡面建立虛擬網絡卡br0)
Brctl addif br0 eth1
Brctl addif br0 eth2
Brctl addif br0 eth3 (分別為網橋br0新增介面eth1, eth2和eth3)
其中br0作為一個網橋,同時也是虛擬的網路裝置,它即可以用作網橋的管理埠,也可作為網橋所連線區域網的閘道器,具體情況視你的需求而定。要使用br0介面時,必需為它分配IP地址。為正常工作,PC1, PC2,PC3和br0的IP地址分配在同一個網段。
在核心,網橋是以模組的方式存在,註冊原始碼路徑:\net\brige\br.c:
4.1 初始化
|
4.2 新建網橋
前面說到通過brctl addbr br0命令建立網橋,此處使用者控制元件呼叫的brctl命令最終對應到核心中的br_ioctl_deviceless_stub處理函式:
|
在這裡,我們傳入的cmd為SIOCBRADDBR.轉入br_add_bridge(buf)中進行:
|
網橋是一個虛擬的裝置,它的註冊跟實際的物理網路設備註冊是一樣的。我們關心的是網橋對應的net_device結構是什麼樣的,繼續跟蹤進new_bridge_dev:
|
在br_dev_setup中還做了一些另外在函式指標初始化:
|
僅僅建立網橋,還是不夠的。實際應用中的網橋需要新增實際的埠(即物理介面),如例子中的eth1, eth2等。應用程式在使用ioctl來為網橋增加物理介面,對應核心函式br_dev_ioctl的程式碼和分析如下:
|
下面分析具體的新增刪除函式add_del_if:
|
對應的新增刪除函式分別為:br_add_if, br_del_if;
br_add_if:
|
br_del_if:
|
網橋最主要有三個資料結構:struct net_bridge,struct net_bridge_port,struct net_bridge_fdb_entry,他們之間的關係如下圖:
展開來如下圖:
說明:
1. 其中最左邊的net_device是一個代表網橋的虛擬裝置結構,它關聯了一個net_bridge結構,這是網橋裝置所特有的資料結構。
2. 在net_bridge結構中,port_list成員下掛一個連結串列,連結串列中的每一個節點(net_bridge_port結構)關聯到一個真實的網口裝置的net_device。網口裝置也通過其br_port指標做反向的關聯(那麼顯然,一個網口最多隻能同時被繫結到一個網橋)。
3. net_bridge結構中還維護了一個hash表,是用來處理地址學習的。當網橋準備轉發一個報文時,以報文的目的Mac地址為key,如果可以在 hash表中索引到一個net_bridge_fdb_entry結構,通過這個結構能找到一個網口裝置的net_device,於是報文就應該從這個網口轉發出去;否則,報文將從所有網口轉發。
各個結構體具體內容如下:
struct net_bridge
|
2. struct net_bridge_port
|
3. struct net_bridge_fdb_entry
|
這裡所說的網橋資料庫指的是CAM表,即struct net_bridge結構中的hash表,資料庫的維護對應的是對結構struct net_bridge_fdb_entry的操作;
眾所周知,網橋需要維護一個MAC地址-埠對映表,埠是指網橋自身提供的埠,而MAC地址是指與埠相連的另一端的MAC地址。當網橋收到一個報文時,先獲取它的源MAC,更新資料庫,然後讀取該報文的目標MAC地址,查詢該資料庫,如果找到,根據找到條目的埠進行轉發;否則會把資料包向除入口埠以外的所有埠轉發。
資料庫使用kmem_cache_create函式進行建立,使用kmem_cache_desctory進行銷燬。路徑:[/net/bridge/br_fdb.c]:
|
當網橋收到一個數據包時,它會獲取該資料的源MAC地址,然後對資料庫進行更新。如果該MAC地址不在數庫中,則創新一個數據項。如果存在,更新它的年齡。資料庫使用hash表的結構方式,便於高效查詢。下面是hash功能程式碼的分析:
路徑:[/net/bridge/br_fdb.c]
|
在更新函式裡面已為某一MAC找到了它所屬於的Hash連結串列,因此,建立函式只需要在該鏈上新增一個數據項即可。
|
查詢分兩種:一種是資料項更新時候的查詢,另一種是轉發報文時候查詢,兩者區別是轉發時查詢需要判斷MAC地址是否過期,即我們常說的MAC老化;更新時則不用判斷;
網橋更新一MAC地址時,不管該地址是否已經過期了,只需遍歷該MAC地址對應的Hash連結串列,然後更新年齡,此時它肯定不過期了。
網橋要轉發資料時,除了要找到該目標MAC的出口埠外,還要判斷該記錄是否過期了。
更新時查詢:
|
轉發時查詢:
|
比較一下,轉發時多了一個函式處理:has_expired, Has_expired函式來決定該資料項是否是過期的,程式碼如下:
|
橋建立時設定一個定時器,迴圈檢測,如果發現有過期的MAC,則清除對應的資料項,MAC地址過期清除由函式br_fdb_cleanup實現:
|
網橋處理包遵循以下幾條原則:
1. 在一個介面上接收的包不會再在那個介面上傳送這個資料包;
2. 每個接收到的資料包都要學習其源地址;
3. 如果資料包是多播或廣播包,則要在同一個網段中除了接收埠外的其他所有埠傳送這個資料包,如果上層協議棧對多播包感興趣,則需要把資料包提交給上層協議棧;
4. 如果資料包的目的MAC地址不能再CAM表中找到,則要在同一個網段中除了接收埠外的其他所有埠傳送這個資料包;
5. 如果能夠在CAM表中查詢到目的MAC地址,則在特定的埠上傳送這個資料包,如果傳送埠和接收埠是同一埠則不傳送;
網橋在整個網路子系統中處理可用下列簡圖說明:
網路資料包在軟終端處理時會進行網橋部分處理,大致的處理流程如下(處理函式呼叫鏈):
netif_recerve_skb函式主要做三件事情:
1. 如果有抓包程式(socket)需要skb,則將skb複製給他們;
2. 處理橋接,即如果開啟了網橋,進行網橋處理;
3. 將skb交給網路層;
|