淺談Linux內(nèi)核無線子系統(tǒng)
Linux 內(nèi)核是如何實現(xiàn)無線網(wǎng)絡(luò)接口呢?數(shù)據(jù)包是通過怎樣的方式被發(fā)送和接收呢?今天跟著 LinuxStory 小編一起來探索一番吧!
剛開始工作接觸 Linux 無線網(wǎng)絡(luò)時,我曾迷失在浩瀚的基礎(chǔ)代碼中,尋找具有介紹性的材料來回答如上面提到的那些高層次的問題。跟蹤探索了一段時間的源代碼后,我寫下了這篇總結(jié),希望在 Linux 無線網(wǎng)絡(luò)的工作原理上,讀者能從這篇文章獲得一個具有幫助性的概覽。
1 全局概覽
在開始探索 Linux 無線具體細節(jié)之前,讓我們先來把握一下 Linux 無線子系統(tǒng)整體結(jié)構(gòu)。如圖1,展示了 Linux 無線子系統(tǒng)各個模塊之間的抽象關(guān)系。
圖一 Linux 無線網(wǎng)絡(luò)結(jié)構(gòu)示意圖
圖示中的虛線內(nèi)展示的是內(nèi)核空間的情況。用戶空間的程序運行在最上層,而硬件相關(guān)的設(shè)備則在最下面。圖示中左邊為以太網(wǎng)設(shè)備,右邊為 WiFi 設(shè)備。
正如圖中看到的一樣,存在著兩種 WiFi 設(shè)備,具體是哪一類要看 IEEE802.11 標準的 MLME 如何實現(xiàn)。
如果直接通過硬件實現(xiàn),那么設(shè)備就是硬 MAC (full MAC)設(shè)備;如果通過軟件的方式實現(xiàn),那么設(shè)備就是軟 MAC (soft MAC)設(shè)備。現(xiàn)階段大部分無線設(shè)備都是軟件實現(xiàn)的軟 MAC 設(shè)備。
通常我們把 Linux 內(nèi)核無線子系統(tǒng)看成兩大塊: cfg80211 和 mac80211 ,它們連通內(nèi)核其他模塊和用戶空間的應用程序。
特別指出, cfg80211 在內(nèi)核空間提供配置管理服務,內(nèi)核與應用層通過 nl80211 實現(xiàn)配置管理接口。需要記住的是,
硬 MAC 設(shè)備和軟 MAC 設(shè)備都需要 cfg80211 才能工作。而 mac80211 只是一個驅(qū)動 API ,它只支持軟件實現(xiàn)的軟 MAC 設(shè)備。
接下來,我們主要關(guān)注軟 MAC 設(shè)備。
Linux 內(nèi)核無線子系統(tǒng)統(tǒng)一各種 WiFi 設(shè)備,并處理 OSI 模型中***層的 MAC 、 PHY 兩層。
若進一步劃分, MAC 層可以分為 MAC 高層和 MAC 底層。前者負責管理 MAC 層無線網(wǎng)絡(luò)的探測發(fā)現(xiàn)、身份認證、關(guān)聯(lián)等;
后者實現(xiàn) MAC 層如 ACK 等緊急操作。大部分情況下,硬件(如無線適配器)處理大部分的 PHY 層以及 MAC 底層操作。Linux 子系統(tǒng)實現(xiàn)大部分的 MAC 高層回調(diào)函數(shù)。
2 模塊間接口
從圖一中我們可以看出,各個模塊之間分界線很清晰,并且模塊間相互透明不可見。模塊之間一般不會相互影響。
舉個例子,我們在 WiFi 設(shè)備驅(qū)動做修改(如,打補丁、添加新的 WiFi 驅(qū)動等),這些變更并不會影響到 mac80211 模塊,
所以我們根本不用改動 mac80211 的代碼。再如,添加一個新的網(wǎng)絡(luò)協(xié)議理論上是不用修改套接字層以及設(shè)備無關(guān)層代碼。一般情況下,內(nèi)核通過一系列的函數(shù)指針實現(xiàn)各層之間相互透明。
如下代碼展示 rtl73usb 無線網(wǎng)卡驅(qū)動與 mac80211 的聯(lián)系。
左側(cè)是 mac80211 為 WiFi 驅(qū)動模塊實現(xiàn)的 ieee80211_ops 結(jié)構(gòu)體形式的回調(diào)接口,回調(diào)函數(shù)的具體內(nèi)容由驅(qū)動層實現(xiàn)。
顯然,不同設(shè)備相應驅(qū)動的實現(xiàn)不同。結(jié)構(gòu)體 ieee80211_ops 負責將不同設(shè)備驅(qū)動實現(xiàn)的回調(diào)函數(shù)與 mac80211 提供的 API 映射綁定起來。
當驅(qū)動模塊插入注冊時,這些回調(diào)函數(shù)就被注冊到 mac80211 里面(通過 ieee80211_alloc_hw 實現(xiàn)),接著 mac80211 就綁定了相應的回調(diào)函數(shù),根本不用知道具體的名字,以及實現(xiàn)細節(jié)等。
完整定義的 ieee80211_ops 結(jié)構(gòu)包含很多成員,但不是所有都必須要驅(qū)動層實現(xiàn)。一般而言,實現(xiàn)的前七個成員函數(shù)就足夠了。但是,要想正確實現(xiàn)其他功能,某些相關(guān)的成員函數(shù)就需要被實現(xiàn),就像上面的例子一樣。
3 數(shù)據(jù)路徑與管理路徑
圖一所示中,存在兩條主要路徑:數(shù)據(jù)路徑和管理路徑。數(shù)據(jù)路徑對應 IEEE802.11 數(shù)據(jù)幀,而管理路徑對應著控制幀。
在 IEEE802.11 的控制幀中,大部分用于如 ACK 這類時間緊急的操作,并且一般直接由硬件實現(xiàn)。一個例外可能就是 PS-Poll 幀(用于 Power Save 控制),它也可以由 mac80211 實現(xiàn)。
數(shù)據(jù)和管理路徑在 mac80211 里面是分開實現(xiàn)的。
4 數(shù)據(jù)包是如何被發(fā)送?
接下來,我們集中探討下數(shù)據(jù)的發(fā)送過程。
首先,數(shù)據(jù)包起源于用戶空間的應用程序,應用程序首先創(chuàng)建一個套接字,然后綁定一個接口(如,以太網(wǎng)接口、 WiFi 接口)。
接下來將數(shù)據(jù)寫入到套接字緩沖區(qū),***再將緩沖區(qū)的數(shù)據(jù)發(fā)送出去。在套接字創(chuàng)建時,我們需要指明將要使用的協(xié)議族,這將在內(nèi)核中起作用。
剛才這些發(fā)生在圖一中的 Data Application 模塊中,最終應用程序陷入系統(tǒng)調(diào)用,隨后在內(nèi)核空間進行接下來的工作。
數(shù)據(jù)的傳輸首先經(jīng)過套接字層,這個過程中一個最重要的數(shù)據(jù)結(jié)構(gòu)就是 sk_buff ,一般稱為 skb 。一個 skb 結(jié)構(gòu)中的成員包含著緩沖區(qū)的地址以及數(shù)據(jù)長度。
它還為內(nèi)核中不同層對數(shù)據(jù)的操縱提供了良好的支持;實現(xiàn)了眾多的接口,如,不同網(wǎng)絡(luò)層首部的插入與去除等。整個數(shù)據(jù)的發(fā)送/接收過程均會用到這個結(jié)構(gòu)。
我們跳過網(wǎng)絡(luò)協(xié)議模塊,對于網(wǎng)絡(luò)協(xié)議我沒有太多想說的,因為一旦涉及網(wǎng)絡(luò)協(xié)議,簡直說不盡道不完。在這里協(xié)議并不是我們主要關(guān)心的。
不過我們需要知道的是,數(shù)據(jù)傳輸使用的協(xié)議在套接字創(chuàng)建的時候就與指定的協(xié)議綁定了,然后相關(guān)的協(xié)議便會負責相關(guān)層的數(shù)據(jù)傳輸。
接下來,數(shù)據(jù)由網(wǎng)絡(luò)層落到了設(shè)備無關(guān)層。這一層透明的連接著各種各樣的硬件設(shè)備(如以太網(wǎng)設(shè)備、 WiFi 設(shè)備等)。
設(shè)備無關(guān)層一個重要的結(jié)構(gòu)是: net_device 。我們回去看圖一,再看接下來的代碼就能解釋內(nèi)核是如何與以太網(wǎng)設(shè)備驅(qū)動通信的。
具體接口通過 net_device_ops 結(jié)構(gòu)實現(xiàn),該結(jié)構(gòu)對應了 net_device 的很多操作。
如下是 net_device_ops 結(jié)構(gòu)的部分成員:
發(fā)包的時候, skb 在調(diào)用 dev_queue_xmit 時被傳入。在跟蹤具體調(diào)用關(guān)系后,最終是這樣調(diào)用的: ops->ndo_start_xmit(skb, dev) 。
注意,剛才的這個函數(shù)需要注冊才能生效。
對于 WiFi 設(shè)備而言,通常我們使用 mac80211 (代替了相應的設(shè)備驅(qū)動),那是因為 mac80211 已經(jīng)幫我們注冊了。
從 net/mac80211/iface.c 可以看到:
因此 mac80211 也就可以看作是一個 net_device ,當一個數(shù)據(jù)包通過 WiFi 傳輸時,相關(guān)的傳輸函數(shù) ieee80211_subif_start_xmit 將被調(diào)用。
我們進入 mac80211 內(nèi)部 ieee80211_subif_start_xmit 實現(xiàn)可以看到這樣一個調(diào)用子序列:ieee80211_xmit => ieee80211_tx => ieee80211_tx_frags => drv_tx
目前我們處在 mac80211 和 WiFi 驅(qū)動的邊界, drv_tx 僅僅調(diào)用了一個在 WiFi 驅(qū)動層實現(xiàn)的并已注冊的回調(diào)函數(shù)。
到這里, mac80211 就結(jié)束了,并且設(shè)備驅(qū)動相關(guān)也暫時告一段落了。
正如之前提到的一樣,通過 mac80211 中的 local->ops->tx ,注冊到設(shè)備驅(qū)動中的回調(diào)函數(shù)將會被調(diào)用。盡管每個驅(qū)動對相應回調(diào)函數(shù)的實現(xiàn)不盡相同。
下面利用之前模塊間接口的例子。結(jié)構(gòu)體成員 tx 對應的函數(shù) rt2x00max_tx 首先需要填充準備發(fā)送描述符(一般包含幀長度、ACK 策略、 RTS/CTS、重傳時限、分片標志以及 MCS 等)
部分信息由 mac80211 傳下來(結(jié)構(gòu)體 ieee80211_tx_info 中就有一些信息將會被使用到),然后驅(qū)動程序還要將數(shù)據(jù)轉(zhuǎn)換成底層硬件可識別的形式。
一旦發(fā)送描述符就位,驅(qū)動程序還會調(diào)整幀數(shù)據(jù)(如,調(diào)整字節(jié)對齊等),然后將數(shù)據(jù)幀放入發(fā)送隊列,***將要發(fā)送的幀的描述符發(fā)給硬件。
由于我們以一個基于 rt73usb 的 USB WiFi 適配器為例,所以數(shù)據(jù)幀***是通過 USB 接口發(fā)送給無線設(shè)備。
然后數(shù)據(jù)將被插入 PHY 首部以及其他信息,***數(shù)據(jù)包被發(fā)送到了空中。驅(qū)動同時也需要反饋發(fā)送狀態(tài)給 mac80211 , 通常狀態(tài)信息存放在 struct ieee80211_tx_info 中。
經(jīng)過 ieee80211_tx_status 一系列的調(diào)用,或者某些變種函數(shù)反饋給上層。
說到這里,關(guān)于數(shù)據(jù)包的發(fā)送也暫時告一段落了。
5 談談管理路徑
理論上,我們可以像數(shù)據(jù)路徑一樣在用戶空間下通過套接字發(fā)送控制幀。但是目前有很多開發(fā)得十分完善的用戶層管理工具能完成這樣的工作。
特別是 wpa_supplicant 和 host_apd 。wpa_supplicant 控制客戶端 STA 模式下無線網(wǎng)絡(luò)的連接,如掃描發(fā)現(xiàn)網(wǎng)絡(luò)、身份認證、關(guān)聯(lián)等。
而 host_apd 可以做 AP 。說白了前者就是用來連接熱點,后者用來發(fā)射熱點。這些用戶層工具通過 netlink 套接字與內(nèi)核通信。
內(nèi)核中相關(guān)的回調(diào)接口是 cfg80211 中的 nl80211 。用戶層的工具通過 netlink 提供的庫(如, NL80211_CMD_TRIGGER_SCAN )將命令發(fā)送到內(nèi)核。
在內(nèi)核中,由 nl80211 接收應用層發(fā)出的命令。如下代碼展示了對應綁定情況。
以 triggering scan 為例,掃描請求從 cfg80211 到 mac80211 是通過 mac80211 在 cfg80211 中注冊的回調(diào)函數(shù)來實現(xiàn)的。
在 mac80211 中, ieee80211_scan 將會具體去實現(xiàn)掃描發(fā)現(xiàn)網(wǎng)絡(luò)的具體細節(jié)。
6 數(shù)據(jù)包又是如何被接收?
我們接下來反過來看看數(shù)據(jù)接收的過程,現(xiàn)在我們不再比較數(shù)據(jù)路徑與管理路徑的不同了。相信讀者同樣能明白。
當一個數(shù)據(jù)包在空中被無線設(shè)備捕捉到后,硬件將會向內(nèi)核發(fā)出一個中斷(大部分 PCI 接口的設(shè)備這樣做),或則通過輪詢機制判斷是否有數(shù)據(jù)到來(如,使用了 USB 接口)。
前者,中斷將會引發(fā)中斷處理程序的執(zhí)行,后者促使特定的接收函數(shù)將被調(diào)用。
一般設(shè)備驅(qū)動層的回調(diào)函數(shù)不會做太多關(guān)于接收數(shù)據(jù)包的操作,僅僅做數(shù)據(jù)校驗,為 mac80211 填充接收描述符,然后把數(shù)據(jù)包推給 mac80211 , 由 mac80211 來做之后的工作(直接或間接將數(shù)據(jù)包放入接收隊列)。
數(shù)據(jù)進入 mac80211 后,將會調(diào)用 ieee80211_rx 或者其他變種接收函數(shù)。在這里數(shù)據(jù)路徑和管理路徑也將分開進行。
如果收到的幀是數(shù)據(jù),它將被轉(zhuǎn)換成 802.3 數(shù)據(jù)幀(通過 __ieee80211_data_to8023 實現(xiàn)),然后該數(shù)據(jù)幀將通過 netif_receive_skb 交付到網(wǎng)絡(luò)協(xié)議棧。在協(xié)議棧中,各層網(wǎng)絡(luò)協(xié)議將會對數(shù)據(jù)進行解析,識別協(xié)議首部。
如果接收到的是控制幀,數(shù)據(jù)將會由 ieee80211_sta_rx_queued_mgmt 處理。部分控制幀在 mac80211 層就終止,另外一些將會通過 cfg80211 發(fā)送到用戶空間下的管理程序。
例如,身份認證控制幀被 cfg80211_rx_mlme_mgmt 處理,然后通過 nl80211_send_rx_auth 發(fā)送到用戶空間下的 wpa_supplicant ; 相應的關(guān)聯(lián)響應控制幀被 cfg80211_rx_assoc_resp 處理,并由 nl80211_send_rx_assoc 發(fā)送到用戶空間。
7 總結(jié)一下
一般 WiFi 驅(qū)動包含如下三個部分:配置、發(fā)送回調(diào)、接收回調(diào)。再以 USB WiFi 適配器為例,當內(nèi)核探測到設(shè)備被插入時,會調(diào)用 probe 函數(shù)。這可能發(fā)生在注冊配置好的 ieee80211_ops 時。
首先, ieee80211_alloc_hw 分配一個 ieee80211_hw 結(jié)構(gòu)體,代表著相應 WiFi 設(shè)備。另外,如下的數(shù)據(jù)結(jié)構(gòu)也會被分配:
wiphy 結(jié)構(gòu):主要用來描述 WiFi 硬件參數(shù)(如, MAC 地址、接口模式與組合、支持的波特率以及其他一些硬件功能)。
ieee80211_local 結(jié)構(gòu):這是一個設(shè)備驅(qū)動層可見的結(jié)構(gòu),并且被 mac80211 大量使用。ieee80211_ops 的映射綁定將鏈接到 ieee80211_local 中。 前者作為后者的一個成員。在 ieee80211_hw 中可以通過 container_of 或者 hw_to_local 這個專用 API 得到 ieee80211_local 。
設(shè)備驅(qū)動使用到的在 ieee80211_hw 中的私有結(jié)構(gòu) void *priv 。
注意:硬件設(shè)備的注冊由 ieee80211_register_hw 完成,前提是事先已經(jīng)插入注冊了 mac80211 模塊,好比在 STA 模式中,要先用 wpa_supplicant 控制設(shè)備連接上了某個熱點才能進行通信一樣。
***希望這篇總結(jié)能讓相關(guān)人員在探索源代碼時具有整體把握。




























