当前位置: 首页 > 科技观察

浅谈Linux内核无线子系统_0

时间:2023-03-20 14:56:54 科技观察

Linux内核是如何实现无线网络接口的?数据包是如何发送和接收的?今天就和LinuxStory的小编一起来一探究竟吧!当我第一次开始使用Linux无线网络时,我迷失在浩瀚的底层代码中,寻找介绍材料来回答像上面提到的那些高级问题。在追踪和探索源代码一段时间后,我写了这篇总结,希望读者对Linux无线网络的工作原理有一个有益的概述。1大图概述在我们开始探索Linux无线的具体细节之前,让我们先了解一下Linux无线子系统的整体结构。图1给出了Linux无线子系统各个模块之间的抽象关系。图1中的虚线是Linux无线网络结构示意图,它展示了内核空间的情况。用户空间的程序运行在顶层,硬件相关的设备在底层。以太网设备在左边,WiFi设备在右边。如图所示,WiFi设备有两种类型,具体类型取决于IEEE802.11标准的MLME是如何实现的。如果直接通过硬件实现,那么该设备就是硬MAC(fullMAC)设备;如果是通过软件实现的,那么这个设备就是一个软MAC(softMAC)设备。现阶段,大多数无线设备都是通过软件实现的软MAC设备。通常我们把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高级回调函数。2模块之间的接口从图1中可以看出,模块之间的界限非常清晰,模块之间是透明不可见的。模块一般不会相互影响。比如我们修改WiFi设备驱动(比如打补丁,增加新的WiFi驱动等),这些改动不会影响mac80211模块,所以我们根本不需要改动mac80211的代码。再比如,增加一个新的网络协议,理论上不需要修改socket层和设备无关层的代码。总的来说,内核通过一系列的函数指针来实现层与层之间的相互透明。下面代码展示了rtl73usb无线网卡驱动与mac80211的连接。左边是mac80211为wifi驱动模块实现的ieee80211_ops结构形式的回调接口。回调函数的具体内容由驱动层实现。显然,不同的设备对相应的驱动程序有不同的实现。结构体ieee80211_ops负责将不同设备驱动实现的回调函数与mac80211提供的API映射进行绑定。在插入并注册驱动模块时,这些回调函数被注册到mac80211中(通过ieee80211_alloc_hw实现),然后mac80211绑定到相应的回调函数,不知道具体名称和实现细节。完整定义的ieee80211_ops结构包含很多成员,但并不是所有成员都必须由驱动层实现。一般来说,实现的前七个成员函数就足够了。但是,要正确实现其他功能,需要实现某些相关的成员函数,如上例所示。3数据路径和管理路径如图1所示,主要有两条路径:数据路径和管理路径。数据路径对应IEEE802.11数据帧,管理路径对应控制帧。在IEEE802.11控制帧中,大部分用于ACK等对时间要求严格的操作,一般直接由硬件实现。一个例外可能是PS-Poll帧(用于节电控制),它也由mac80211实现。数据和管理路径在mac80211中单独实现。4数据包是如何发送的?接下来,我们重点关注数据发送过程。首先,数据包来自用户空间的应用程序,应用程序首先创建一个套接字,然后绑定一个接口(例如,以太网接口,WiFi接口)。接下来将数据写入socket缓冲区,最后发送缓冲区中的数据。在创建套接字时,我们需要指定要使用的协议族,它会在内核中工作。刚才这些发生在图1的数据应用模块中,最后应用程序陷入系统调用,然后在内核空间进行接下来的工作。数据传输首先经过socket层,这个过程中最重要的数据结构之一就是sk_buff,一般称为skb。skb结构的成员包含缓冲区的地址和数据的长度。它还为内核中不同层对数据的操作提供了良好的支持;它实现了很多接口,比如不同网络层头部的插入和移除。这个结构在整个数据发送/接收过程中使用。让我们跳过网络协议模块。网络协议我就不多说了,因为一旦涉及到网络协议,简直就是说的太多了。协议不是我们在这里的主要关注点。但是我们需要知道的是,用于数据传输的协议在创建socket的时候就绑定了指定的协议,然后相关的协议就会负责相关层的数据传输。接下来,数据从网络层落到设备无关层。该层透明连接各种硬件设备(如以太网设备、WiFi设备等)。设备无关层的一个重要结构是:net_device。让我们回头看看图1,再看看下面的代码来解释内核是如何与以太网设备驱动程序通信的。具体的接口是通过net_device_ops结构体实现的,对应net_device的很多操作。下面是net_device_ops结构体的一些成员:发送数据包时,调用dev_queue_xmit时传入skb。经过跟踪具体的调用关系,最终是这样调用的: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等)。一些信息将被使用),然后驱动程序必须将数据转换为底层硬件可以理解的形式。一旦发送描述符到位,驱动程序也会对帧数据进行调整(如调整字节对齐等),然后将数据帧放入发送队列,最后将待发送帧的描述符发送给硬件。由于我们以基于rt73usb的USBWiFi适配器为例,因此数据帧肯定是通过USB接口发送到无线设备的。然后将数据与其他信息一起插入PHY标头,并通过空中发送最终数据包。驱动程序还需要向mac80211发送反馈和发送状态,通常状态信息存储在structieee80211_tx_info中。经过一系列的ieee80211_tx_status调用,或者一些变体函数反馈给上层。话虽如此,数据包的发送暂时告一段落。5说说管理路径理论上我们可以像数据路径一样,通过用户空间的套接字发送控制帧。但是有很多开发完善的用户级管理工具可以完成这种工作。特别是wpa_supplicant和host_apd。wpa_supplicant在clientSTA模式下控制无线网络的连接,如扫描寻网、鉴权、关联等。而host_apd可以做AP。说白了,前者是用来连接热点的,后者是用来发射热点的。这些用户空间工具通过netlink套接字与内核通信。内核中相关的回调接口是cfg80211中的nl80211。Userland工具通过netlink提供的库(例如NL80211_CMD_TRIGGER_SCAN)向内核发送命令。在内核中,nl80211接收应用层下发的命令。以下代码显示了相应的绑定。以触??发扫描为例,cfg80211向mac80211的扫描请求是通过mac80211在cfg80211中注册的回调函数实现的。在mac80211中,ieee80211_scan会具体实现扫描发现网络的具体细节。6数据包是如何接收的?我们逆向看一下数据接收的过程。现在我们不再比较数据路径和管理路径的区别。相信读者也能理解。当空中的无线设备捕获到数据包时,硬件会向内核发送中断(大多数PCI接口设备都是这样做的),或者使用轮询机制来判断是否有数据到来(例如,使用USB接口)。在前者中,中断会触发中断处理程序的执行,在后者中,将调用特定的接收函数。一般设备驱动层的回调函数不会对接收数据包做太多操作,只是做数据校验,为mac80211填充接收描述符,然后将数据包推送给mac80211,mac80211会做后面的工作(直接或间接将数据包放入接收队列)。数据进入mac80211后,会调用ieee80211_rx或其他变种接收函数。这里数据路径和管理路径也会分开。如果接收到的帧是数据,则将其转换为802.3数据帧(通过__ieee80211_data_to8023),然后通过netif_receive_skb将数据帧传递到网络堆栈。在协议栈中,每一层网络协议都会对数据进行分析,识别协议头。如果接收到控制帧,数据将由ieee80211_sta_rx_queued_mgmt处理。一些控制帧终止于mac80211层,其他的将通过cfg80211发送到用户空间的hypervisor。例如身份认证控制帧由cfg80211_rx_mlme_mgmt处理,然后通过nl80211_send_rx_auth发送给用户空间下的wpa_supplicant;相应的关联响应控制帧由cfg80211_rx_assoc_resp处理,由nl80211_send_rx_assoc发送到用户空间。7综上所述,一个通用的WiFi驱动包括以下三部分:配置、发送回调、接收回调。再次以USBWiFi适配器为例,当内核检测到设备插入时,调用probe函数。注册配置的ieee80211_ops时可能会发生这种情况。首先ieee80211_alloc_hw分配一个ieee80211_hw结构体,代表对应的WiFi设备。此外,还会分配以下数据结构:wiphy结构:主要用于描述WiFi硬件参数(如MAC地址、接口模式及组合、支持的波特率等一些硬件功能)。ieee80211_local结构:这是一个设备驱动层可见的结构,被mac80211大量使用。ieee80211_ops的映射绑定将链接到ieee80211_local。前者是后者的成员。在ieee80211_hw中,ieee80211_local可以通过container_of或hw_to_local等专用API获取。设备驱动使用的ieee80211_hw中的私有结构体void*priv。注意:硬件设备的注册是通过ieee80211_register_hw完成的,前提是mac80211模块已经提前插入注册,就像STA模式一样,wpa_supplicant控制设备必须连接到热点才能进行通信。***希望这个总结能让相关人员在探索源代码时有一个整体的感觉。