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