Linux下的虛擬Bridge實現

2021-09-02 20:58:30 字數 4171 閱讀 4185

linux下的bridge也是一種虛擬裝置,這多少和vlan有點相似,它依賴於乙個或多個從裝置。與vlan不同的是,它不是虛擬出和從裝置同一層次的映象裝置,而是虛擬出乙個高一層次的裝置,並把從裝置虛擬化為埠port,且同時處理各個從裝置的資料收發及**,再加上netfilter框架的一些東西,使得它的實現相比vlan複雜得多。

它是linux下虛擬出來bridge裝置,linux下可用brctl命令建立br裝置,如下

brctl addbr brname

然後新增port,並進行相應配置,就可以使用了

brctl addif brname eth0

brctl addif brname eth1

ifconfig brname ip up

ifconfig eth0 0.0.0.0 up

ifconfig eth1 0.0.0.0 up

可見br裝置是建立在從裝置之上的(這些從裝置可以是實際裝置,也可以是vlan裝置等),並且可以為br準備乙個ip(br裝置的mac位址是它所有從裝置中最小的mac位址),這樣該主機就可以通過這個br裝置與網路中的其它主機通訊了(詳見傳送功能框圖)。

另外它的從裝置被虛擬化為埠port,它們的ip及mac都不再可用,且它們被設定為接收任何包,最終由bridge裝置來決定資料報的去向:接收到本機、**、丟棄(詳見接收功能框圖)。

傳送功能接收功能

簡單的資料結構框圖如下所示。

和vlan一樣,bridge也被當成乙個module載入進核心,它的module_init()函式和vlan差不多,進行一些namespace的註冊,特殊的是它還註冊了乙個netfilter_ops,在核心全域性的hook函式表中增加了7個函式,其中5個的pf=bridge,另兩個的pf分別為inet、inet6,它們主要用於bridge中的netfilter操作(後面會細講)。

最後,也是我們這最關心的是,它註冊了乙個ioctl函式br_ioctl_deviceless_stub(),該ioctl函式和vlan的一樣,都會作為sock_ioctl()的特殊情況被呼叫。對映到應用層,它應該是對某個socket插口進行ioctl操作,詳見brctl原始碼。該ioctl函式中最主要的就是br_add_bridge(net,buf),用於建立bridge裝置,如下圖所示:

該函式呼叫netdev_alloc(),申請net_device(),並分配私有空間net_bridge()結構,指明初始化函式為br_dev_setup(),最後register_netdev()把該裝置組冊進核心,可見bridge裝置和一般的裝置差不多。

主要看其中br_dev_setup(),首先初始化裝置的type、flags為bridge,然後最關鍵的是設定其dev->netdev_ops = br_netdev_ops,即核心為bridge裝置準備好了一套通用的驅動函式,這個直接關係到bridge的工作方法,後面再細講。然後初始化私有空間net_bridge()結構,設定bridge的本地裝置及從裝置的list(當然這是還沒有從裝置加進來),然後設定了橋的group_address,即上一節所說的特殊的mac位址,最後還初始化了timer相關的。

這時bridge還不完整,還需新增port從裝置,由命令brctl addif brname portdev完成,但要注意,雖然還是brctl命令,但此時的操作物件是已經存在的bridge裝置,對映到核心中就是br_netdev_ops->ioctl()中的br_add_if()(它是br裝置的ioctl操作,和之前那個sock_ioctl的分支不是乙個層次上的)。至於怎麼從應用層直接操作底層的net_device裝置的,可以參見brctl原始碼,以後再看吧,先看看這裡的br_add_if(),如下圖:

首先判斷dev從裝置必須不是loopback,不是bridge,不是其他bridge的port,且要是ethernet裝置,才能繼續;然後根據br、dev選擇乙個index號,並分配乙個新的net_bridge_port結構,初始化之,並將它加入bridge的port_list中;最後br的一些物理引數,其mac位址為所有從裝置中mac最小的(由上一節知,從裝置被設定成全接收模式,其ip和mac都沒有了),且其mtu也為所有從裝置中最小的。

上面設定br的相關引數,下面還要設定從裝置,首先使dev->master=br_dev(實際上就是構成上一節資料結構中的索引關係);然後設定dev->prive_flags加上iff_bridge_port,這樣它就不能再作為其他br的從裝置了;最後也是最關鍵的,設定dev->rx_handler為br_handler_frame(),為資料接收作準備。

前面也講過了,linux下的bridge裝置,對下層而言是乙個橋裝置,進行資料的**(實際上對下也有接收能力,下一節講)。而對上層而言,它就像普通的ethernet裝置一樣,有自己的ip和mac位址,那麼上層當然可以把它加入路由系統,並利用它傳送資料啦,並且很容易想到,它的發射函式最終肯定是利用某個從裝置的驅動去完成實際的傳送的,這個和vlan是相通的。具體看**:

上層根據目的ip位址,路由選擇了該br_dev裝置傳送,並且由arp快取中得到了對應的目的mac,填寫在了skb中,然後啟動了傳送流程dev_queue_xmit(skb)。因為此時的skb->dev為br_dev,無queue,直接去呼叫br裝置的傳送函式,該函式就是br_netdev_ops中定義的br_dev_xmit(skb,br_dev)。

該函式首先根據目的mac位址,確定是廣播還是單播,這裡僅討論單播時,根據dmac在net_bridge的fdb_hash中找到相應的net_bridge_fdb_entry項,並索引到對應的埠net_bridge_port。最後利用該埠的從裝置來傳送資料,注意,這裡是直接呼叫dev->ops->ndo_start_xmit(skb,dev)的,一放面這裡的dev已經是從裝置了,另一方面,這裡沒有像vlan中那樣重定位skb->dev,並重啟傳送流程dev_queue_xmit(),是因為乙個從裝置只能作為乙個bridge的port,沒有其它身份,不存在競爭問題。

和vlan一樣,實際接收由硬體裝置完成,最終通過netif_receive_skb(skb)函式提交給上層,而在該函式中會處理vlan、bridge這類特殊裝置。與lvan的僅是把skb裝置重定位以實現對上層透明的要求不同,bridge接受過程複雜得多,因此專門註冊了乙個函式來處理,即前面提到的rx_handler(),它被註冊在port裝置的net_device結構中(這算是port裝置失去自身ip、mac的乙個補償吧j),如下圖所示。只有作為bridge的從裝置才會註冊rx_handler(),並在這裡執行,處理橋接,普通的裝置不會執行到這裡。

這裡兩種典型的返回值是rx_consumed、rx_pass,後者表示處理了一下回來,繼續之前的流程,實際上就是對應的接收功能框圖中的第一種情況;前者表示該skb已不再屬於這個從裝置了,而是被提交給了br裝置,所以本次netif_receive_skb()就不用管啦,直接goto out,這裡還要再分兩種情況,一是**的,這時br就真的充當了橋的角色,二是由br提交給上層的,這時br充當的是乙個乙太網裝置,如前面所述。

要處理這麼多情況,**需設計得很巧妙,這裡的rx_handler被設定為br_handle_frame(**pskb),看具體**:

通過對bridge和vlan的學習,了解了網路棧底層的工作方式,傳送這個主動過程相對簡單,而接收過程則相對複雜,用到bh模型,napi等。

vlan和bridge功能有所不同,但相似處很多,更重要的是:它們都對上層透明,所以不會牽扯到協議域的問題。

Linux 虛擬網路裝置詳解之 Bridge 網橋

linux 虛擬網路裝置詳解之 bridge 網橋 同 tap tun veth pair 一樣,bridge 也是一種虛擬網路裝置,所以具備虛擬網路裝置的所有特性,比如可以配置 ip mac 等。除此之外,bridge 還是乙個交換機,具有交換機所有的功能。對於普通的網路裝置,就像乙個管道,只有兩...

Linux 虛擬網路裝置詳解之 Bridge 網橋

前面幾篇文章介紹了 tap tun veth pair,今天這篇來看看 bridge。同 tap tun veth pair 一樣,bridge 也是一種虛擬網路裝置,所以具備虛擬網路裝置的所有特性,比如可以配置 ip mac 等。除此之外,bridge 還是乙個交換機,具有交換機所有的功能。對於普...

linux中的網橋bridge

網橋 橋接與網橋 乙太網是一種共享網路傳輸介質的技術,在這種技術下,如果一台計算機傳送資料的時候,在同一物理網路介質上的計算機都需要接收,在接收後分析目的mac位址,如果是屬於目的mac位址和自己的mac位址相同便進行封裝提供給網路層,如果目的mac位址不是自己的mac位址,那麼就丟棄資料報。橋接的...