1. 程式人生 > >linux網路協議棧(四)鏈路層 (5)vlan處理

linux網路協議棧(四)鏈路層 (5)vlan處理

4.6、VLAN處理:

4.6.1、vlan原理

對於帶vlan的乙太網報文,其乙太網型別為0x8100,所以鏈路層中對於帶vlan報文的處理就是對於乙太網型別為0x8100的報文的處理;

vlan,用於在鏈路層劃分廣播域,實現資料在鏈路層分流,在二層交換機中,vlan實際行使其功能是體現在uni埠上,通過在埠上配置能通過哪些vlan,實現不同的埠可以通行不同的資料流,比如二層交換機的埠有的是access埠,即配置為只能通過一個vlan,有的是trunk埠,即配置為可通過多個vlan(很多二層網路裝置可能還有其他的叫法,如raisecom裝置根據CTC標準埠可配置為透傳模式、翻譯模式、trunk模式、tag模式等,但本質含義相似,都是在配置能通過哪些vlan),二層交換機的vlan處理如下圖:


在上圖中,發出報文帶有vlan10、20、30的PC分屬不同的廣播域,即比如發出報文帶vlan20的PC如果連在交換機的紅色埠上,那麼報文會被交換機直接過濾掉,因為紅色埠不能通過vlan20的報文,所以如果發出報文帶vlan20的某藍色PC發出廣播包,那麼所有其他的藍色PC都能收到這個廣播包,而其他PC無法收到,這就是劃分廣播域。

linux的vlan處理與之相似,每個介面相當於二層交換機的每個物理埠,同樣由使用者給不同埠配置其可通過的不同的vlan,當帶有某vlan的報文進入某介面時,通過檢測該介面是否存在該vlan的vlan子介面決定是否可通行此報文,可以理解為,linux對vlan的處理就是通過vlan子介面的方式,實現二層交換機埠vlan的功能。

4.6.2、linux的vlan處理:

linux的vlan處理原始碼在程式碼樹net/8021q/目錄下,核心檔案是vlan.c和vlan_dev.c;

linux的vlan處理和網橋處理很相似,在接收方向上,鏈路層收到帶vlan報文後先進入vlan模組處理,然後在找到對應的vlan子介面後,更新報文輸入介面為vlan子介面並剝除vlan,然後把該報文打回鏈路層重新處理,上層協議棧可見的是該報文從vlan子介面接收;在傳送方向上,上層協議棧把報文由vlan子介面傳送,繼而再通過其原始接收的宿主介面傳送出去,以帶vlan10的報文為例,下圖串聯了帶vlan報文的接收和傳送:


vlan模組在核心的初始化由函式vlan_proto_init完成,它包括如下內容:

1、  在核心中註冊乙太網型別值為0x8100的ptype(處理函式為vlan_skb_recv)

2、  註冊linux對於vlan的ioctl介面,典型如vconfig使用它

3、  初始化linux的vlan值集合功能,它用於記錄宿主裝置的vlan子介面的功能

4、  註冊linux的vlan相關的proc介面,即/proc/net/vlan;

5、  註冊linux對於vlan相關的routenetlink介面;

6、  註冊linux的vlan相關的核心通知鏈;

我們已經知道linux的vlan處理就是根據二層交換機vlan原理實現的,其本質就是介面的vlan子介面的實現,下面就通過vconfig工具建立vlan子介面的過程描述:

vconfig工具在核心中首先呼叫vlan_ioctl_handler函式,對於建立vlan子介面的操作,呼叫函式register_vlan_device,引數是使用者輸入的宿主介面和vlan值,如宿主介面是eth0、vlan值是10,那麼就是說在介面eth0中加入一個vlan子介面eth0.10,意思就是說介面eth0允許帶vlan10的報文通過;

vlan型介面的私貨是結構體vlan_dev_info,它裡邊最重要的欄位就是記錄了宿主介面和vlan值,此外和網橋型介面一樣,vlan型介面在核心中也有專用的ops,在vlan_dev.c檔案中定義了全域性變數vlan_netdev_ops,它規定了vlan型介面的ops,比較需要注意的就是它的傳送方法vlan_dev_hard_start_xmit,其他方法和普通介面區別不大;

建立的vlan子介面繼承了其宿主介面的MAC地址、MTU,最終由函式register_vlan_dev把該vlan子介面註冊進核心,並且同時在宿主介面中記錄該vlan子介面,

這裡注意下核心通過結構體vlan_group描述每個宿主介面都有哪些vlan子介面,在vlan模組中通過內部函式__vlan_find_group查詢定位,外部函式__find_vlan_dev供查詢某介面是否存在某vlan值的vlan子介面,這就是vlan報文處理函式vlan_skb_recv一上來就要判斷的,判斷該宿主介面是否存在該vlan值的子介面,即是否允許帶該vlan的報文通過;

如果不允許通過則就此丟棄該報文,否則說明可以通過,先將報文的輸入介面(skb->dev重置為vlan子介面),再將vlan標籤剝除(vlan_check_reorder_header),再打回鏈路層重新處理(netif_rx),這樣再處理時上層協議棧認為該報文由vlan子介面進入的;

所以對於該報文的回覆報文,上層協議棧也會把它從該vlan子介面傳送,這將呼叫vlan型介面的傳送函式vlan_dev_hard_start_xmit,它將根據vlan子介面找到其宿主介面,更新報文的出介面(skb->dev)為宿主介面,最終呼叫dev_queue_xmit把報文從宿主介面傳送出去。

事實上linux只實現了二層交換機的最簡單的vlan功能,主要是CPU並不直接做二層轉發,二層轉發是由硬體完成的,但是二層的很多qos功能由vlan實現,帶不同vlan的報文走不同的業務通道,比如vlan10在二層轉發中所走通道比其他vlan更快,那麼重要報文會加上vlan10的標籤,也許這樣的報文需要上CPU處理,所以linux需要能夠識別vlan。