Skip to content

Latest commit

 

History

History
2849 lines (2302 loc) · 99.1 KB

File metadata and controls

2849 lines (2302 loc) · 99.1 KB

Linux 内核 mac80211/cfg80211 无线网络子系统深度分析

分析基于 Linux 内核源码,主要文件:

  • net/mac80211/ieee80211_i.h
  • net/mac80211/tx.c
  • net/mac80211/rx.c
  • net/mac80211/scan.c
  • net/mac80211/rate.h
  • net/mac80211/rc80211_minstrel_ht.h
  • net/mac80211/sta_info.h
  • net/wireless/nl80211.c
  • net/wireless/core.h

1. 整体架构概览

Linux 无线网络子系统分为三个主要层次:用户空间通过 nl80211 Netlink 接口与内核的 cfg80211 层通信;cfg80211 是通用无线配置框架(上层);mac80211 是 IEEE 802.11 协议的软件实现层(中间层);最底层是具体的硬件驱动。

+------------------------------------------------------------------+
|                        用户空间                                   |
|  wpa_supplicant   iw   hostapd   NetworkManager                  |
+------------------------------------------------------------------+
           |  NETLINK (Generic Netlink / nl80211 协议族)
+------------------------------------------------------------------+
|                     nl80211 接口层                                |
|   net/wireless/nl80211.c                                         |
|   static const struct genl_ops nl80211_ops[]          (L18297)  |
|   static const struct genl_small_ops nl80211_small_ops[] (L18309)|
+------------------------------------------------------------------+
           |  cfg80211_ops 回调
+------------------------------------------------------------------+
|                   cfg80211 通用配置层                              |
|   net/wireless/core.h                                            |
|   struct cfg80211_registered_device  (cfg80211_rdev)  (L31)     |
|   struct cfg80211_internal_bss                        (L186)     |
|   struct wiphy (无线物理设备抽象)                                  |
+------------------------------------------------------------------+
           |  ieee80211_ops 回调 (mac80211_config_ops)
+------------------------------------------------------------------+
|                   mac80211 中间协议层                              |
|   net/mac80211/ieee80211_i.h                                     |
|   struct ieee80211_local    (全局状态)               (L1409)     |
|   struct ieee80211_sub_if_data  (虚拟接口 sdata)     (L1119)     |
|   struct txq_info / ieee80211_txq  (发送队列)         (L957)     |
+------------------------------------------------------------------+
           |  ieee80211_ops (驱动回调集合)
+------------------------------------------------------------------+
|                       硬件驱动层                                   |
|   ath9k / iwlwifi / mt76 / rtw88 等驱动                          |
|   struct ieee80211_hw  (驱动可见的硬件抽象)                        |
+------------------------------------------------------------------+
           |  物理层 PHY (射频/基带)
+------------------------------------------------------------------+
|                       无线硬件                                    |
+------------------------------------------------------------------+

这种分层设计的好处在于:驱动开发者只需实现 ieee80211_ops 中的硬件相关操作(如 TX/RX DMA、信道切换等),而复杂的 802.11 协议状态机(关联、认证、分片、加密、速率控制等)由 mac80211 统一实现。


2. cfg80211 层核心结构

2.1 cfg80211_registered_device

net/wireless/core.h 第 31 行定义了 cfg80211 中最核心的设备结构:

struct cfg80211_registered_device {
    const struct cfg80211_ops *ops;    // 操作回调集合(驱动/mac80211实现)
    struct list_head list;             // 全局 rdev 链表节点

    // rfkill 射频开关支持
    struct rfkill_ops rfkill_ops;
    struct work_struct rfkill_block;

    // 监管域(regdomain)管理
    char country_ie_alpha2[2];
    const struct ieee80211_regdomain *requested_regd;
    enum environment_cap env;

    int wiphy_idx;                     // wiphy 索引(系统唯一)
    int devlist_generation, wdev_id;   // 接口列表版本号

    // BSS 数据库(扫描结果缓存)
    spinlock_t bss_lock;
    struct list_head bss_list;
    struct rb_root bss_tree;           // 按 BSSID+频率 索引的红黑树
    u32 bss_generation;
    struct cfg80211_scan_request_int *scan_req;  // 当前扫描请求

    // 扫描完成工作项
    struct wiphy_work scan_done_wk;

    /* 必须最后,wiphy_priv() 依赖偏移量计算 */
    struct wiphy wiphy __aligned(NETDEV_ALIGN);   // L126
};

wiphy_to_rdev() 函数(L130)使用 container_ofwiphy 指针反向获取 cfg80211_registered_device

static inline struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)
{
    BUG_ON(!wiphy);
    return container_of(wiphy, struct cfg80211_registered_device, wiphy);
}

2.2 cfg80211_internal_bss

BSS(基本服务集)是扫描结果的基本单元,net/wireless/core.h 第 186 行定义:

struct cfg80211_internal_bss {
    struct list_head list;          // 链接到 bss_list
    struct list_head hidden_list;   // 隐藏 SSID 相关链表
    struct rb_node rbn;             // 红黑树节点(用于快速查找)
    unsigned long ts;               // 时间戳
    unsigned long refcount;         // 引用计数
    atomic_t hold;                  // 防止提前释放

    u64 parent_tsf;                 // 父 BSS 的 TSF 时间戳
    // ...
};

BSS 数据库以红黑树组织(bss_tree),支持按频率和 BSSID 快速查找,同时用 bss_list 链表支持遍历。


3. mac80211 核心数据结构

3.1 ieee80211_local(全局状态)

net/mac80211/ieee80211_i.h 第 1409 行定义了 mac80211 层最重要的结构,它代表一个物理无线设备的完整状态:

struct ieee80211_local {
    /* 驱动可见部分,必须在首位 */
    struct ieee80211_hw hw;                        // L1413

    /* AQL(Airtime Queue Limits)公平调度 */
    struct fq fq;                                  // Fair Queue
    struct codel_vars *cvars;                      // CoDel 变量
    struct codel_params cparams;                   // CoDel 参数

    /* 每 AC 的 TXQ 调度锁和活跃队列列表 */
    spinlock_t active_txq_lock[IEEE80211_NUM_ACS]; // L1420
    struct list_head active_txqs[IEEE80211_NUM_ACS];
    u16 schedule_round[IEEE80211_NUM_ACS];

    /* AQL 全局统计 */
    u16 airtime_flags;
    u32 aql_txq_limit_low[IEEE80211_NUM_ACS];      // L1428
    u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
    atomic_t aql_total_pending_airtime;

    const struct ieee80211_ops *ops;               // 驱动操作集

    /* 扫描状态 */
    unsigned long scanning;                        // L1562,scan flags 位图
    struct wiphy_delayed_work scan_work;           // L1581
    struct ieee80211_sub_if_data __rcu *scan_sdata; // 发起扫描的接口

    /* STA 数据库(哈希表 + 链表) */
    struct list_head sta_list;                     // L1535
    struct rhltable sta_hash;                      // L1536
    struct rhltable link_sta_hash;                 // L1537(MLO 链路 STA)

    /* 速率控制算法 */
    struct rate_control_ref *rate_ctrl;            // L1550

    /* 电源管理 */
    struct ieee80211_sub_if_data *ps_sdata;        // L1640,当前 PS 接口
    struct wiphy_work dynamic_ps_enable_work;      // L1641
    struct wiphy_work dynamic_ps_disable_work;     // L1642
    struct timer_list dynamic_ps_timer;            // L1643
    int dynamic_ps_forced_timeout;                 // L1651
    // ...
};

3.2 ieee80211_sub_if_data(虚拟接口)

每个 wlan0wlan0-1 等接口对应一个 ieee80211_sub_if_data(简称 sdata),定义于 ieee80211_i.h 第 1119 行:

struct ieee80211_sub_if_data {
    struct list_head list;          // 链接到 local->interfaces
    struct wireless_dev wdev;       // cfg80211 无线设备(必须在前)

    struct net_device *dev;         // Linux 网络设备
    struct ieee80211_local *local;  // 指向物理设备状态

    /* 加密 */
    struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS];  // L1149
    struct ieee80211_key __rcu *default_unicast_key;

    /* 控制端口(EAPOL 路径) */
    __be16 control_port_protocol;          // L1154,通常为 0x888E (EAPOL)
    bool control_port_no_encrypt;          // L1155
    bool control_port_over_nl80211;        // L1157,EAPOL 走 nl80211 上报

    /* QoS 映射 */
    struct mac80211_qos_map __rcu *qos_map; // L1160

    /* 工作队列项和帧队列 */
    struct wiphy_work work;                // L1162
    struct sk_buff_head skb_queue;         // L1163(RX 帧暂存队列)
    struct sk_buff_head status_queue;      // L1164(TX 状态帧队列)

    /* 接口类型特定状态 */
    union {
        struct ieee80211_if_ap     ap;     // AP 模式
        struct ieee80211_if_vlan   vlan;   // VLAN 模式
        struct ieee80211_if_managed mgd;   // STA managed 模式
        struct ieee80211_if_ibss   ibss;   // IBSS/Ad-Hoc 模式
        struct ieee80211_if_mesh   mesh;   // Mesh 模式
        struct ieee80211_if_ocb    ocb;    // OCB 模式(V2X)
        struct ieee80211_if_mntr   mntr;   // monitor 模式
        struct ieee80211_if_nan    nan;    // NAN 模式
    } u;                                   // L1186~1195

    /* MLO(多链路操作)支持 */
    struct ieee80211_link_data deflink;    // L1197,默认(单链路)link
    struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; // L1198

    /* 必须在最后:driver 通过 ieee80211_vif 访问 sdata */
    struct ieee80211_vif vif;              // L1218
};

vif 反向获取 sdata 的方法(L1222):

static inline struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
{
    return container_of(p, struct ieee80211_sub_if_data, vif);
}

3.3 ieee80211_if_managed(STA 模式状态)

STA 关联状态机的核心,定义于 ieee80211_i.h 第 521 行:

struct ieee80211_if_managed {
    struct timer_list timer;                  // 重传/超时定时器
    struct timer_list conn_mon_timer;         // 连接监控定时器(NULL帧探测)
    struct timer_list bcn_mon_timer;          // Beacon 监控定时器

    /* 各种工作项 */
    struct wiphy_work monitor_work;           // 连接状态监控
    struct wiphy_work beacon_connection_loss_work; // Beacon 丢失处理

    /* 关联状态 */
    unsigned long beacon_timeout;
    unsigned long probe_timeout;
    u8 connection_loss:1,
       driver_disconnect:1,
       reconnect:1,
       associated:1;                         // L533~536

    /* 认证/关联数据(进行中) */
    struct ieee80211_mgd_auth_data *auth_data;  // L538
    struct ieee80211_mgd_assoc_data *assoc_data; // L539

    bool powersave;                          // L543,用户请求省电
    bool broken_ap;                          // L544,问题 AP,关闭 PS

    /* U-APSD 配置 */
    unsigned int uapsd_queues;               // L565,已启用 U-APSD 的 AC
    unsigned int uapsd_max_sp_len;           // L572,最大 SP 长度

    /* RSSI 阈值监控 */
    int rssi_min_thold, rssi_max_thold;      // L581

    /* 能力协商缓存 */
    struct ieee80211_ht_cap ht_capa;         // L583
    struct ieee80211_ht_cap ht_capa_mask;    // L584
    struct ieee80211_vht_cap vht_capa;       // L585
    // ...
};

3.4 txq_info 与 ieee80211_txq(发送队列)

每个 STA 的每个 TID,以及每个虚拟接口,都有一个 TXQ(发送队列),定义于 ieee80211_i.h 第 957 行:

struct txq_info {
    struct fq_tin tin;             // Fair Queue TIN(流量整形入口)
    struct codel_vars def_cvars;   // 默认流的 CoDel 变量
    struct codel_stats cstats;     // CoDel 统计

    u16 schedule_round;            // 调度轮次(防止无限循环)
    struct list_head schedule_order; // 链接到 local->active_txqs

    struct sk_buff_head frags;     // 解聚合后的分片暂存

    unsigned long flags;           // TXQ 标志(停止/AMPDU/AMSDU 等)

    /* 驱动可见部分必须在最后 */
    struct ieee80211_txq txq;      // L970,暴露给驱动的结构
};

ieee80211_txqieee80211_hw.h 中面向驱动的公开结构,包含 vifstatidac 等字段,驱动通过 ieee80211_tx_dequeue() 从中取帧。

TXQ 初始化路径(tx.c L1505):

void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
                        struct sta_info *sta,
                        struct txq_info *txqi, int tid)
{
    fq_tin_init(&txqi->tin);
    codel_vars_init(&txqi->def_cvars);
    codel_stats_init(&txqi->cstats);
    __skb_queue_head_init(&txqi->frags);
    INIT_LIST_HEAD(&txqi->schedule_order);

    txqi->txq.vif = &sdata->vif;
    // 根据 tid 计算 AC(Access Category)
    if (tid == IEEE80211_NUM_TIDS) {
        txqi->txq.ac = IEEE80211_AC_VO;  // 管理帧 MMPDU TXQ
    } else {
        txqi->txq.ac = ieee80211_ac_from_tid(tid);
    }
    // ...
}

3.5 sta_info(站点信息)

sta_info.h 中定义的 sta_info 是 mac80211 内部对一个无线站点(对端)的完整描述,其中包含大量重要子结构:

站点状态标志sta_info.h L78):

enum ieee80211_sta_info_flags {
    WLAN_STA_AUTH,          // 已认证
    WLAN_STA_ASSOC,         // 已关联
    WLAN_STA_PS_STA,        // 处于省电模式
    WLAN_STA_AUTHORIZED,    // 已授权(802.1X 通过)
    WLAN_STA_MFP,           // 管理帧保护
    WLAN_STA_BLOCK_BA,      // 禁止 BA 会话(挂起/恢复期间)
    WLAN_STA_PS_DRIVER,     // 驱动要求保持 PS 状态
    WLAN_STA_PSPOLL,        // 站点在 PS 时发送了 PS-Poll
    WLAN_STA_UAPSD,         // U-APSD 相关
    WLAN_STA_RATE_CONTROL,  // 速率控制已初始化
    WLAN_STA_USES_ENCRYPTION, // 需要加密
    // ...
};

AMPDU 聚合 MLME 状态sta_info.h L281):

struct sta_ampdu_mlme {
    /* RX 方向 */
    struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; // 每 TID 的 RX 重排序缓冲
    unsigned long agg_session_valid[...]; // 有效 BA 会话位图

    /* TX 方向 */
    struct wiphy_work work;              // BA 协商工作项
    struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS]; // 每 TID 的 TX AMPDU 会话
    u8 addba_req_num[IEEE80211_NUM_TIDS]; // 每 TID 的 ADDBA 发送次数
    u8 dialog_token_allocator;           // 对话令牌分配器
};

Airtime 公平性信息sta_info.h L138):

struct airtime_info {
    u64 rx_airtime;           // 已用 RX 空口时间(微秒)
    u64 tx_airtime;           // 已用 TX 空口时间
    unsigned long last_active; // 最后活跃时间(jiffies)
    s32 deficit;              // 公平调度赤字
    atomic_t aql_tx_pending;  // 待发送帧估计空口时间
    u32 aql_limit_low;        // AQL 低水位
    u32 aql_limit_high;       // AQL 高水位
};

4. nl80211 与 cfg80211 的 Netlink 接口

4.1 Generic Netlink 协议族

nl80211 使用 Linux 的 Generic Netlink(GENL)框架,注册了名为 nl80211 的协议族。net/wireless/nl80211.c 第 40 行:

static struct genl_family nl80211_fam;

4.2 多播组

nl80211 注册了多个多播组,用于内核向用户空间推送事件(nl80211.c L43~63):

enum nl80211_multicast_groups {
    NL80211_MCGRP_CONFIG,      // 配置变更(接口、密钥等)
    NL80211_MCGRP_SCAN,        // 扫描结果通知
    NL80211_MCGRP_REGULATORY,  // 监管域变更
    NL80211_MCGRP_MLME,        // MLME 事件(关联/解关联/漫游等)
    NL80211_MCGRP_VENDOR,      // 厂商自定义事件
    NL80211_MCGRP_NAN,         // NAN(邻居感知网络)事件
    NL80211_MCGRP_TESTMODE     // 测试模式
};

4.3 命令分发

所有 nl80211 命令通过 nl80211_ops[](L18297)和 nl80211_small_ops[](L18309)两张表分发。nl80211_small_ops 是大型命令表,部分典型命令:

NL80211_CMD_SET_WIPHY       -> nl80211_set_wiphy       设置 wiphy 参数
NL80211_CMD_GET_INTERFACE   -> nl80211_get_interface   获取接口信息
NL80211_CMD_NEW_INTERFACE   -> nl80211_new_interface   创建新接口
NL80211_CMD_NEW_KEY         -> nl80211_new_key         安装加密密钥
NL80211_CMD_START_AP        -> nl80211_start_ap        启动 AP
NL80211_CMD_TRIGGER_SCAN    -> nl80211_trigger_scan    触发扫描
NL80211_CMD_CONNECT         -> nl80211_connect         发起连接
NL80211_CMD_ASSOCIATE       -> nl80211_associate       发起关联
NL80211_CMD_AUTHENTICATE    -> nl80211_authenticate    发起认证

4.4 从 wdev 到 rdev 的解析

用户空间通过 NL80211_ATTR_IFINDEXNL80211_ATTR_WDEV 指定操作目标,__cfg80211_rdev_from_attrs()(nl80211.c L140)负责解析:

// nl80211.c L180~199
if (attrs[NL80211_ATTR_IFINDEX]) {
    int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
    netdev = __dev_get_by_index(netns, ifindex);
    if (netdev) {
        if (netdev->ieee80211_ptr)
            tmp = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
        // ...
    }
}

每个无线网络设备的 net_device->ieee80211_ptr 指向 struct wireless_dev,后者的 wiphy 字段指向物理设备,从而联系到 cfg80211_registered_device


5. 扫描流程

5.1 扫描类型

类型 说明
硬件扫描 (HW scan) 驱动实现 hw_scan 回调,硬件自主完成
软件扫描 (SW scan) mac80211 控制信道切换,发送 Probe Request
被动扫描 (Passive) 只监听 Beacon,不发 Probe Request
主动扫描 (Active) 发送 Probe Request,等待 Probe Response

扫描延时常量(scan.c L28~30):

#define IEEE80211_PROBE_DELAY         (HZ / 33)   // ~30ms,发送 Probe 前等待
#define IEEE80211_CHANNEL_TIME        (HZ / 33)   // ~30ms,主动扫描单信道时间
#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9)   // ~111ms,被动扫描单信道时间

5.2 扫描状态机

ieee80211_scan_work(scan.c L1095)是核心扫描工作函数,在 local->scan_work delayed work 中执行。扫描状态机通过 local->next_scan_state 驱动(ieee80211_i.h L1398):

enum mac80211_scan_state {
    SCAN_DECISION,     // 决策:继续扫描还是返回工作信道
    SCAN_SET_CHANNEL,  // 切换到待扫描信道
    SCAN_SEND_PROBE,   // 发送 Probe Request(主动扫描)
    SCAN_SUSPEND,      // 暂停扫描,回到工作信道发数据
    SCAN_RESUME,       // 恢复扫描
    SCAN_ABORT,        // 中止扫描
};

扫描状态机流程:

 nl80211 TRIGGER_SCAN 命令
        |
        v
 __ieee80211_start_scan()  (scan.c L731)
        |
        +-- HW scan: drv_hw_scan() -> 驱动自主完成
        |       |
        |       v  ieee80211_scan_completed() (L540)
        |
        +-- SW scan: ieee80211_start_sw_scan() (L557)
                |
                v
        ieee80211_scan_work() (L1095) [delayed work]
                |
                v
        +---> SCAN_DECISION
        |       |
        |       +-- 还有信道? -> SCAN_SET_CHANNEL
        |       |       |
        |       |       v  切换信道、等待
        |       |
        |       +-- no IR/radar? -> 被动扫描
        |       |       监听 Beacon -> SCAN_BEACON_WAIT
        |       |       收到 Beacon -> SCAN_BEACON_DONE -> SCAN_SEND_PROBE
        |       |
        |       v  主动扫描
        |   SCAN_SEND_PROBE
        |       |
        |       v  ieee80211_send_scan_probe_req() (L660)
        |       |  发送 Probe Request(所有 SSID)
        |       |
        +<-- SCAN_DECISION (等待 Probe Response)
                |
        所有信道完成
                |
                v
        __ieee80211_scan_completed() (L448)
                |
        cfg80211_scan_done() -> nl80211 通知用户空间

5.3 被动扫描升级到主动扫描

scan.c L302~310 展示了这一关键逻辑:

if (test_and_clear_bit(SCAN_BEACON_WAIT, &local->scanning)) {
    /*
     * we were passive scanning because of radar/no-IR, but
     * the beacon/proberesp rx gives us an opportunity to upgrade
     * to active scan
     */
    set_bit(SCAN_BEACON_DONE, &local->scanning);
    wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0);
}

在雷达频段(DFS)或禁止主动发射(no-IR)信道上,mac80211 先以被动模式等待 Beacon,一旦收到 Beacon(证明该信道已有 AP 使用),立即升级为主动扫描模式。

5.4 BSS 信息更新

ieee80211_scan_rx() 函数(scan.c L277)处理收到的 Beacon 和 Probe Response:

void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
{
    // 仅处理 Probe Response 和 Beacon
    if (!ieee80211_is_probe_resp(mgmt->frame_control) &&
        !ieee80211_is_beacon(mgmt->frame_control) && ...)
        return;

    // 验证 DA(Probe Response 需匹配本机地址)
    // ...

    // 更新 BSS 数据库
    bss = ieee80211_bss_info_update(local, rx_status, mgmt, skb->len, channel);
    // ...
}

ieee80211_bss_info_update()(scan.c L167)调用 cfg80211_inform_bss_frame_data(),将 BSS 信息上报给 cfg80211 层缓存。


6. 关联流程与 EAPOL 四次握手

6.1 802.11 关联状态机

   [未关联]
      |
      | NL80211_CMD_AUTHENTICATE / nl80211_authenticate
      v
   [认证中]
      |  发送 Auth Request (Open System 或 SAE)
      |  收到 Auth Response
      v
   [已认证]
      |
      | NL80211_CMD_ASSOCIATE / nl80211_associate
      v
   [关联中]
      |  发送 Association Request (含 RSN IE、HT Caps 等)
      |  收到 Association Response
      v
   [已关联] (ieee80211_if_managed.associated = 1)
      |
      | WPA2/3: 四次握手 (EAPOL)
      v
   [已授权] (WLAN_STA_AUTHORIZED 标志置位)
      |
      | 正常数据传输
      v
   [连接正常]

6.2 EAPOL 与内核的关系

EAPOL(EAP Over LAN,EtherType 0x888E)是 WPA/WPA2/WPA3 四次握手的承载协议,在内核中有特殊处理路径:

控制端口(Control Port)机制:

ieee80211_sub_if_data 中的字段(ieee80211_i.h L1154~1157):

__be16 control_port_protocol;      // 通常 = htons(ETH_P_PAE) = 0x888E
bool control_port_no_encrypt;      // EAPOL 帧是否加密
bool control_port_no_preauth;      // 是否跳过预认证
bool control_port_over_nl80211;    // EAPOL 是否通过 nl80211 上报

TX 路径中的 EAPOL 处理:

EAPOL 帧在关联完成之前必须能够发出(否则四次握手无法进行),ieee80211_tx_h_check_assoc()(tx.c L270)专门处理这种情况:在软件扫描期间只允许 Probe Request 和 null function 帧,但控制端口帧(EAPOL)有例外路径。

RX 路径中的 EAPOL 处理:

收到 EAPOL 帧时,mac80211 的 RX 路径会检查 control_port_over_nl80211 标志:

  • 若设置:通过 nl80211 的 NL80211_CMD_CONTROL_PORT_FRAME 上报给用户空间(wpa_supplicant)
  • 若未设置:作为普通 802.3 帧递交给网络栈,wpa_supplicant 通过 PF_PACKET socket 接收

四次握手(以 WPA2-PSK 为例):

STA (wpa_supplicant)               AP (hostapd)
        |                               |
        |  <--- EAPOL-Key (Message 1)  |  ANonce
        |                               |
        |  PTK = PRF(PMK, ANonce,      |
        |            SNonce, MACs)      |
        |                               |
        |  EAPOL-Key (Message 2) --->  |  SNonce + MIC
        |                               |
        |  <--- EAPOL-Key (Message 3)  |  GTK(加密) + MIC
        |                               |
        |  EAPOL-Key (Message 4) --->  |  确认
        |                               |
        |  [安装 PTK/GTK 密钥]          |
        |  NL80211_CMD_NEW_KEY          |
        |  内核: ieee80211_key_alloc()  |

四次握手完成后,wpa_supplicant 调用 nl80211 的 NL80211_CMD_NEW_KEY 将协商好的 PTK(Pairwise Transient Key)和 GTK(Group Temporal Key)安装到内核,此后数据帧由内核硬件加密引擎处理。


7. TX 发送路径

7.1 TX 路径总览

 应用层数据 (TCP/UDP)
        |
        v (网络栈)
 ieee80211_subif_start_xmit()   [net_device->ndo_start_xmit]
        |
        v
 ieee80211_xmit()               (tx.c L2023)
        |
        v
 ieee80211_tx()                 (tx.c L1934)
        |
        +-- ieee80211_tx_prepare()       // 初始化 tx_data 结构
        |
        +-- invoke_tx_handlers_early()   // 早期处理器链(加密、PS 缓冲等)
        |   |
        |   +-- ieee80211_tx_h_dynamic_ps()    (L193)  动态省电
        |   +-- ieee80211_tx_h_check_assoc()   (L270)  关联状态检查
        |   +-- ieee80211_tx_h_multicast_ps_buf() (L381) 组播 PS 缓冲
        |   +-- ieee80211_tx_h_encrypt()              加密处理
        |
        +-- ieee80211_queue_skb()        // 入队到 TXQ(软件队列)
        |   |
        |   v  ieee80211_txq_enqueue() (L1442)
        |      fq_tin_enqueue() -> CoDel/FQ 队列
        |
        +-- invoke_tx_handlers_late()    // 晚期处理器链
        |
        +-- __ieee80211_tx()             (L1746)
            |
            v
        drv_tx()                         // 调用驱动的 .tx 回调

7.2 TXQ 软件队列与入队

ieee80211_txq_enqueue()(tx.c L1442)将帧放入 Fair Queue:

static void ieee80211_txq_enqueue(struct ieee80211_local *local,
                                  struct txq_info *txqi,
                                  struct sk_buff *skb)
{
    struct fq *fq = &local->fq;
    struct fq_tin *tin = &txqi->tin;
    u32 flow_idx;

    ieee80211_set_skb_enqueue_time(skb);  // 记录入队时间(CoDel 使用)

    spin_lock_bh(&fq->lock);
    /* 管理帧不走 CoDel,直接入 frags 队列 */
    if (unlikely(txqi->txq.tid == IEEE80211_NUM_TIDS)) {
        __skb_queue_tail(&txqi->frags, skb);
    } else {
        flow_idx = fq_flow_idx(fq, skb);  // 按五元组哈希计算流索引
        fq_tin_enqueue(fq, tin, flow_idx, skb, fq_skb_free_func);
    }
    spin_unlock_bh(&fq->lock);
}

7.3 CoDel 出队

出队通过 fq_tin_dequeue_func()(tx.c L1400)实现 CoDel(Controlled Delay)算法:

static struct sk_buff *fq_tin_dequeue_func(struct fq *fq,
                                           struct fq_tin *tin,
                                           struct fq_flow *flow)
{
    // ...
    return codel_dequeue(txqi,
                         &flow->backlog,
                         cparams,
                         cvars,
                         cstats,
                         codel_skb_len_func,
                         codel_skb_time_func,
                         codel_drop_func,      // 超时丢包
                         codel_dequeue_func);
}

CoDel 通过测量帧在队列中的驻留时间(sojourn time)来判断拥塞,目标是将排队延迟控制在 5ms 以内。

7.4 Airtime Fairness(AQL)

AQL(Airtime Queue Limits)是 mac80211 实现 Airtime Fairness 的机制。ieee80211_local 中(ieee80211_i.h L1427~1432):

u16 airtime_flags;                              // 开关标志
u32 aql_txq_limit_low[IEEE80211_NUM_ACS];       // 每 AC 低水位限制
u32 aql_txq_limit_high[IEEE80211_NUM_ACS];      // 每 AC 高水位限制
u32 aql_threshold;                              // 总体阈值
atomic_t aql_total_pending_airtime;             // 全局待发空口时间
atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS]; // 每 AC 待发空口时间

airtime_info 结构(sta_info.h L138)跟踪每个站点的空口时间使用情况:

struct airtime_info {
    u64 rx_airtime;            // 已消耗 RX 空口时间
    u64 tx_airtime;            // 已消耗 TX 空口时间
    unsigned long last_active; // 最后活跃时间
    s32 deficit;               // 公平调度赤字(负值表示欠账)
    atomic_t aql_tx_pending;   // 当前在 HW 队列中的估计空口时间
};

调度活跃性检查(ieee80211_i.h L105~112):

static inline bool
ieee80211_sta_keep_active(struct sta_info *sta, u8 ac)
{
    /* 如果站点在过去 100ms 内有活动,保持其在 active list */
    return time_before_eq(jiffies, sta->airtime[ac].last_active + HZ / 10);
}

7.5 动态省电对 TX 的影响

ieee80211_tx_h_dynamic_ps()(tx.c L193)在每次 TX 时检查省电状态:

static ieee80211_tx_result debug_noinline
ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
{
    // U-APSD VO 队列:不唤醒省电(由触发帧机制处理)
    if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) &&
        (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) &&
        skb_get_queue_mapping(tx->skb) == IEEE80211_AC_VO)
        return TX_CONTINUE;

    if (local->hw.conf.flags & IEEE80211_CONF_PS) {
        // 停止所有队列(等待硬件退出省电)
        ieee80211_stop_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_PS,
                                        false);
        // 触发动态 PS 禁用工作项
        wiphy_work_queue(local->hw.wiphy,
                         &local->dynamic_ps_disable_work);
    }
    // 重置动态 PS 定时器
    mod_timer(&local->dynamic_ps_timer, jiffies +
              msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
    return TX_CONTINUE;
}

8. RX 接收路径

8.1 RX 路径总览

 硬件中断 (DMA 完成)
        |
        v
 驱动调用 ieee80211_rx_list() / ieee80211_rx_napi()  (rx.c L5435/L5598)
        |
        v
 ieee80211_rx_list()
        |  验证: band/rate、suspend状态、started状态
        |
        v
 ieee80211_rx_monitor()    (L846)   // 复制给 monitor 接口
        |
        v
 ieee80211_prepare_rx_handoff()
        |
        v
 ieee80211_rx_reorder_ampdu()  (L1445)  // AMPDU 重排序
        |
        v
 ieee80211_rx_handlers()        (L4195)  // RX 处理器链
        |
        +-- ieee80211_rx_h_check_dup()      去重检查
        +-- ieee80211_rx_h_decrypt()        解密
        +-- ieee80211_rx_h_check_more_data() PS 相关
        +-- ieee80211_rx_h_sta_process()    STA 信息更新
        +-- ieee80211_rx_h_defragment()     分片重组
        +-- ieee80211_rx_h_michael_mic_verify() TKIP MIC 校验
        +-- ieee80211_rx_h_amsdu()          A-MSDU 解聚合
        +-- ieee80211_rx_h_data()           数据帧递交
        +-- ieee80211_rx_h_ctrl()           控制帧处理
        +-- ieee80211_rx_h_mgmt()           管理帧处理
        |
        v
 netif_receive_skb()  /  cfg80211 上报  /  nl80211 事件

8.2 RX 数据结构

ieee80211_rx_data(ieee80211_i.h L221)贯穿 RX 处理链的上下文:

struct ieee80211_rx_data {
    struct list_head *list;     // 输出帧链表
    struct sk_buff *skb;        // 当前处理帧
    struct ieee80211_local *local;
    struct ieee80211_sub_if_data *sdata; // 接收接口
    struct ieee80211_link_data *link;    // MLO 链路
    struct sta_info *sta;                // 发送站点
    struct link_sta_info *link_sta;      // MLO 链路 STA
    struct ieee80211_key *key;           // 使用的密钥

    unsigned int flags;         // IEEE80211_RX_AMSDU 等

    int seqno_idx;    // 序列号数组索引(0..16,16=non-QoS)
    int security_idx; // 安全 PN 数组索引

    int link_id;      // MLO 链路 ID(-1 表示无效)

    union {
        struct {
            u32 iv32;
            u16 iv16;
        } tkip;
        struct {
            u8 pn[IEEE80211_CCMP_PN_LEN];
        } ccm_gcm;
    };                // 解密相关(TKIP/CCMP 重放保护)
};

8.3 AMPDU 聚合帧重排序

ieee80211_rx_reorder_ampdu()(rx.c L1445)处理 A-MPDU 接收侧的乱序帧:

static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
                                       struct sk_buff_head *frames)
{
    struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
    u8 tid = ieee80211_get_tid(hdr);

    // 获取该 TID 的 RX BA 会话
    tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
    if (!tid_agg_rx) {
        // 无 BA 会话,收到 BlockAck 策略帧则发 DelBA
        if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
            !test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
            !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
            ieee80211_send_delba(...);
        goto dont_reorder;
    }

    // 刷新会话超时定时器
    if (tid_agg_rx->timeout)
        tid_agg_rx->last_rx = jiffies;

    // 分片帧不入重排序缓冲
    if (sc & IEEE80211_SCTL_FRAG) {
        ieee80211_queue_skb_to_iface(...);
        return;
    }

    // 进入重排序缓冲区
    if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb, frames))
        return;

dont_reorder:
    __skb_queue_tail(frames, skb);
}

重排序缓冲区 tid_ampdu_rx 结构(sta_info.h L238):

struct tid_ampdu_rx {
    struct sk_buff_head *reorder_buf;    // 重排序缓冲数组(按序列号索引)
    unsigned long *reorder_time;         // 每槽入队时间(用于超时释放)
    u16 head_seq_num;                    // 当前窗口头部序列号
    u16 stored_mpdu_num;                 // 缓冲中的 MPDU 数量
    u16 ssn;                            // Starting Sequence Number
    u16 buf_size;                        // 重排序窗口大小(通常 64)
    u16 timeout;                         // 超时值(TU)
    struct timer_list reorder_timer;     // 超时释放定时器
    // ...
};

AMPDU 重排序工作原理:

接收到 AMPDU 中的 MPDU(可能乱序到达):

序列号窗口: [head_seq_num, head_seq_num + buf_size)

[64][65][__][67][68][__][70]...
 ^                              <- head_seq_num(等待 SN=66 到来)

若 SN=66 到来: 窗口向前推进,释放 64、65、66、67、68
若超时: reorder_timer 触发,强制释放窗口头部积压的帧

8.4 Monitor 模式帧处理

ieee80211_rx_monitor()(rx.c L846)将原始帧(含 radiotap 头)复制给所有 monitor 接口:

static struct sk_buff *
ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                     struct ieee80211_rate *rate)
{
    // 遍历所有 monitor 接口
    list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
        // 检查信道匹配
        chandef = &sdata->vif.bss_conf.chanreq.oper;
        if (chandef->chan &&
            chandef->chan->center_freq != status->freq)
            continue;
        // 克隆并投递给 monitor 接口
        skb = skb_clone(monskb, GFP_ATOMIC);
        skb->dev = prev_sdata->dev;
        netif_receive_skb(skb);
    }
    // 返回清理后的 skb(去掉 radiotap/FCS)
    return ieee80211_clean_skb(origskb, present_fcs_len, rtap_space);
}

9. 速率控制算法 minstrel_ht

9.1 算法概述

minstrel_ht 是 mac80211 默认的自适应速率控制算法(Adaptive Rate Control,ARC),由 Felix Fietkau 开发。它通过统计每个速率的成功/失败次数,计算期望吞吐量,动态选择最优发送速率。

相关文件:net/mac80211/rc80211_minstrel_ht.hrc80211_minstrel_ht.c

9.2 速率分组

minstrel_ht 将所有可用速率组织成"分组"(groups),每个分组对应一种 MCS/流数/带宽/GI 组合(rc80211_minstrel_ht.h L43~58):

#define MINSTREL_MAX_STREAMS         4   // 最多 4 空间流
#define MINSTREL_HT_STREAM_GROUPS    4   // HT: BW(20/40) x SGI(on/off)
#define MINSTREL_VHT_STREAM_GROUPS   6   // VHT: BW(20/40/80) x SGI(on/off)

#define MINSTREL_HT_GROUPS_NB   (4 * 4)  // 16 个 HT 分组
#define MINSTREL_VHT_GROUPS_NB  (4 * 6)  // 24 个 VHT 分组
#define MINSTREL_LEGACY_GROUPS_NB 2       // CCK + OFDM 遗留分组
#define MINSTREL_GROUPS_NB      (16 + 24 + 2)  // 共 42 个速率分组

每个分组最多 10 个速率(MCS_GROUP_RATES = 10,对应 MCS0~MCS9)。

9.3 核心数据结构

每个速率的统计rc80211_minstrel_ht.h L110):

struct minstrel_rate_stats {
    u16 attempts, last_attempts;  // 当前/上次采样周期尝试次数
    u16 success, last_success;    // 当前/上次采样周期成功次数
    u32 att_hist, succ_hist;      // 历史总计数

    u16 prob_avg;                 // 成功率移动平均(EWMA)
    u16 prob_avg_1;               // 辅助 EWMA

    u8 retry_count;               // 最大重传次数
    u8 retry_count_rtscts;        // 使用 RTS/CTS 时的重传次数
};

每站点的 minstrel_ht 状态rc80211_minstrel_ht.h L154):

struct minstrel_ht_sta {
    struct ieee80211_sta *sta;

    /* AMPDU 长度统计(用于计算聚合效率) */
    unsigned int ampdu_len;
    unsigned int ampdu_packets;
    unsigned int avg_ampdu_len;    // EWMA 平均 AMPDU 长度

    /* 全局最优速率 */
    u16 max_tp_rate[MAX_THR_RATES]; // 最高吞吐量速率(前 4 个)
    u16 max_prob_rate;              // 最高成功率速率(备用路径)

    unsigned long last_stats_update; // 上次统计更新时间

    /* 帧开销(微秒)*/
    unsigned int overhead;          // 普通帧开销
    unsigned int overhead_rtscts;   // RTS/CTS 帧开销

    u16 sample_rate;               // 当前采样速率
    unsigned long sample_time;     // 采样时间

    /* 支持的 MCS 位图 */
    u16 supported[MINSTREL_GROUPS_NB]; // L192,每分组支持的速率位图

    /* 每分组统计数据 */
    struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB]; // L195
};

9.4 速率选择策略

minstrel_ht 维护两类最优速率:

  1. max_tp_rate(最高吞吐量):期望吞吐量 = 成功率 x 速率 x AMPDU 平均长度最高的速率,用于正常数据发送。

  2. max_prob_rate(最高成功率):作为低成功率环境的保底选择,保证基本可达性。

期望吞吐量计算(rc80211_minstrel_ht.c 中的 minstrel_ht_get_tp_avg(),对应头文件 L199):

int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
                           int prob_avg);

计算公式近似为:

tp_avg = prob_avg * bitrate * avg_ampdu_len / overhead

9.5 采样机制

EWMA 参数(rc80211_minstrel_ht.h L20~21):

#define EWMA_LEVEL  96   // EWMA 权重系数(分母 EWMA_DIV=128)
#define EWMA_DIV   128

采样周期(L73):

#define MINSTREL_SAMPLE_RATES     5    // 每次采样的速率数
#define MINSTREL_SAMPLE_INTERVAL  (HZ / 50)  // 20ms 采样间隔

采样类型(L129~134):

enum minstrel_sample_type {
    MINSTREL_SAMPLE_TYPE_INC,   // 递增采样(相邻速率)
    MINSTREL_SAMPLE_TYPE_JUMP,  // 跳跃采样(随机高速率)
    MINSTREL_SAMPLE_TYPE_SLOW,  // 慢速采样(低成功率速率验证)
    __MINSTREL_SAMPLE_TYPE_MAX
};

9.6 速率控制接口

速率控制算法通过 rate_control_ops 接口与 mac80211 交互(rate.h L25~37):

void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
                           struct sta_info *sta,
                           struct ieee80211_tx_rate_control *txrc);

void rate_control_tx_status(struct ieee80211_local *local,
                            struct ieee80211_tx_status *st);

void rate_control_rate_init(struct link_sta_info *link_sta);
void rate_control_rate_update(struct ieee80211_local *local,
                              struct ieee80211_supported_band *sband,
                              struct link_sta_info *link_sta,
                              u32 changed);

minstrel 注册(rate.h L98~108):

#ifdef CONFIG_MAC80211_RC_MINSTREL
int rc80211_minstrel_init(void);
void rc80211_minstrel_exit(void);
#endif

10. 电源管理

10.1 三种省电机制

机制 说明 适用场景
PS-Poll STA 发 PS-Poll 取回单帧 传统省电
U-APSD 触发帧触发 SP(服务周期),批量取回 WMM 语音/视频
Dynamic SMPS 动态调整 MIMO 接收链数 减少 RF 功耗

10.2 PS-Poll 机制

当 STA 进入省电模式(设置 frame control 中的 Power Management 位),AP 会缓冲发往该 STA 的帧。AP 在 Beacon 的 TIM(Traffic Indication Map)中标记哪些 AID 有缓冲帧。STA 收到 Beacon 后若发现自己的 AID 在 TIM 中,则发送 PS-Poll 控制帧取回缓冲数据。

AP 侧缓冲管理(ieee80211_i.h L315~326):

struct ps_data {
    /* TIM 位图(按 AID 索引) */
    u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]
            __aligned(__alignof__(unsigned long));

    struct sk_buff_head bc_buf;      // 组播/广播缓冲队列
    atomic_t num_sta_ps;             // 处于 PS 的 STA 数量
    int dtim_count;                  // DTIM 计数器
    bool dtim_bc_mc;                 // DTIM 期间投递组播
};

缓冲帧限制常量(ieee80211_i.h L46~52):

/* AP 模式下组播/广播最大缓冲帧数 */
#define AP_MAX_BC_BUFFER    128

/* 所有 STA 总缓冲帧数上限(每帧约 2KB,512帧 = 约 1MB) */
#define TOTAL_MAX_TX_BUFFER 512

10.3 U-APSD(非计划节电服务交付)

U-APSD 通过"触发帧"机制让 STA 控制服务周期:STA 发送一个 QoS Data 帧(作为触发帧),AP 收到后启动服务周期,将所有缓冲帧批量发送给 STA。

U-APSD 配置(ieee80211_i.h L78):

#define IEEE80211_DEFAULT_UAPSD_QUEUES  0  // 默认不启用 U-APSD

注释(L60~77)解释了为何默认关闭 U-APSD:某些 AP(如 Netgear R7000)会静默重分类数据帧到不同 AC,导致非 U-APSD AC 的帧被 AP 缓冲,STA 永远收不到(如 ARP 响应)。

U-APSD 最大 SP 长度(ieee80211_i.h L80~81):

#define IEEE80211_DEFAULT_MAX_SP_LEN  \
    IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL  // 每次服务周期不限制帧数

10.4 动态省电(Dynamic PS)

动态省电在无数据发送时自动进入省电模式,有数据时立即唤醒。

控制流程(tx.c L193 的 ieee80211_tx_h_dynamic_ps()):

有 TX 帧到来
    |
    +-- 检查驱动是否支持 SUPPORTS_PS
    +-- 检查是否在扫描(扫描期间不省电)
    +-- 若硬件已在 PS 状态 (IEEE80211_CONF_PS)
    |       停止所有 HW 队列
    |       提交 dynamic_ps_disable_work(向驱动发 conf 关闭 PS)
    |
    +-- 重置动态 PS 定时器(dynamic_ps_timeout ms 后超时)
    |
    动态 PS 定时器超时
        |
        v
    dynamic_ps_enable_work
        |
        v
    向驱动提交 IEEE80211_CONF_PS 标志(进入省电)

动态 PS 相关字段(ieee80211_local,ieee80211_i.h L1641~1651):

struct wiphy_work dynamic_ps_enable_work;   // 启用 PS 工作项
struct wiphy_work dynamic_ps_disable_work;  // 禁用 PS 工作项
struct timer_list dynamic_ps_timer;         // 定时器
int dynamic_ps_forced_timeout;              // 用户通过 WEXT 强制设置的超时(ms)

10.5 动态 SMPS(Spatial Multiplexing Power Save)

SMPS 通过减少活跃接收天线数来节省功耗。动态 SMPS 根据流量动态切换。

SMPS 模式管理(ieee80211_link_data_managed,ieee80211_i.h L1001~1003):

enum ieee80211_smps_mode req_smps,        // 请求的 SMPS 模式
                         driver_smps_mode; // 实际驱动中的 SMPS 模式

SMPS 重配工作项(L1025~1027):

struct wiphy_work request_smps_work;   // 用户发起 SMPS 变更
struct wiphy_work recalc_smps;         // 重新计算 SMPS(硬件重配)

状态类型定义(ieee80211_i.h L1097):

enum ieee80211_smps_mode smps_mode;    // link_data 中当前生效的 SMPS 模式

11. 连接模式与带宽枚举

mac80211 区分不同的连接模式以支持从 Legacy 到最新的 IEEE 802.11be(EHT/UHR)(ieee80211_i.h L390~407):

enum ieee80211_conn_mode {
    IEEE80211_CONN_MODE_S1G,     // IEEE 802.11ah(低功耗 IoT)
    IEEE80211_CONN_MODE_LEGACY,  // 802.11a/b/g
    IEEE80211_CONN_MODE_HT,      // 802.11n(HT)
    IEEE80211_CONN_MODE_VHT,     // 802.11ac(VHT)
    IEEE80211_CONN_MODE_HE,      // 802.11ax(HE/Wi-Fi 6)
    IEEE80211_CONN_MODE_EHT,     // 802.11be(EHT/Wi-Fi 7)
    IEEE80211_CONN_MODE_UHR,     // Ultra High Reliability
};

enum ieee80211_conn_bw_limit {
    IEEE80211_CONN_BW_LIMIT_20,  // 20 MHz
    IEEE80211_CONN_BW_LIMIT_40,  // 40 MHz
    IEEE80211_CONN_BW_LIMIT_80,  // 80 MHz
    IEEE80211_CONN_BW_LIMIT_160, // 160 MHz(也包括 80+80)
    IEEE80211_CONN_BW_LIMIT_320, // 320 MHz(EHT 新增)
};

12. 队列停止原因

MAC 层帧发送队列的停止是多路由的,由 queue_stop_reason 枚举管理(ieee80211_i.h L1325~1339):

enum queue_stop_reason {
    IEEE80211_QUEUE_STOP_REASON_DRIVER,        // 驱动请求停止
    IEEE80211_QUEUE_STOP_REASON_PS,            // 省电模式
    IEEE80211_QUEUE_STOP_REASON_CSA,           // 信道切换通告
    IEEE80211_QUEUE_STOP_REASON_AGGREGATION,   // AMPDU 聚合建立
    IEEE80211_QUEUE_STOP_REASON_SUSPEND,       // 系统挂起
    IEEE80211_QUEUE_STOP_REASON_SKB_ADD,       // 内部帧入队
    IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,    // 离信道操作(扫描等)
    IEEE80211_QUEUE_STOP_REASON_FLUSH,         // 队列刷新
    IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN, // TDLS 拆链
    IEEE80211_QUEUE_STOP_REASON_RESERVE_TID,   // TID 预留
    IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE, // 接口类型变更
};

对应的 queue_stop_reasons 字段(ieee80211_local,ieee80211_i.h L1442~1443):

unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; // 每队列停止原因位图
int q_stop_reasons[IEEE80211_MAX_QUEUES][IEEE80211_QUEUE_STOP_REASONS]; // 引用计数

多个停止原因可同时存在(以位图记录),只有当所有停止原因都清除后,队列才能恢复发送。


13. MLO(多链路操作)支持

802.11be(Wi-Fi 7)引入了多链路操作(MLO),允许设备同时在多条链路(如 2.4GHz + 5GHz + 6GHz)上并发收发。mac80211 对此有专门支持:

多链路接口结构ieee80211_sub_if_data,ieee80211_i.h L1197~1198):

struct ieee80211_link_data deflink;  // 默认链路(单链路设备用此)
struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; // MLO 链路数组

链路数据结构ieee80211_link_data,ieee80211_i.h L1066):

struct ieee80211_link_data {
    struct ieee80211_sub_if_data *sdata;
    unsigned int link_id;              // 链路 ID

    /* 每链路加密密钥 */
    struct ieee80211_key __rcu *gtk[...];

    /* 每链路信道管理 */
    struct ieee80211_chanctx *reserved_chanctx;
    struct ieee80211_chan_req reserved;

    /* 每链路 SMPS 状态 */
    enum ieee80211_smps_mode smps_mode;

    /* 每链路功率 */
    int user_power_level;              // dBm
    int ap_power_level;                // dBm

    /* DFS 定时器 */
    struct wiphy_hrtimer_work dfs_cac_timer_work;

    /* 每链路 AC 参数 */
    struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];

    struct ieee80211_bss_conf *conf;   // 链路的 BSS 配置
    // ...
};

扫描时对 MLO 的处理(scan.c L206~227):

if (ieee80211_vif_is_mld(&scan_sdata->vif)) {
    if (rx_status->link_valid) {
        s8 link_id = rx_status->link_id;
        link_conf = rcu_dereference(scan_sdata->vif.link_conf[link_id]);
    }
} else {
    link_conf = &scan_sdata->vif.bss_conf;
}

14. 关键数据流汇总

14.1 完整 TX 数据流

用户进程 write(socket)
    |
    v [内核网络栈]
ip_output() / udp_sendmsg()
    |
    v
dev_queue_xmit()
    |
    v [TC 流量控制,如有]
ieee80211_subif_start_xmit()   [net_device->ndo_start_xmit]
    |
    v
ieee80211_xmit()               [tx.c:2023, 添加 802.11 头]
    |
    v
ieee80211_tx()                 [tx.c:1934]
    |
    +-- TX handlers (early):
    |     - 动态 PS 检查
    |     - 关联状态验证
    |     - 组播 PS 缓冲
    |     - 速率选择 (rate_control_get_rate)
    |
    +-- ieee80211_queue_skb()  [入 TXQ]
    |     |
    |     v
    |   ieee80211_txq_enqueue() [tx.c:1442]
    |     |
    |     v
    |   fq_tin_enqueue()        [Fair Queue / CoDel]
    |
    +-- TX handlers (late):
    |     - 加密 (ieee80211_tx_h_encrypt)
    |     - 帧头完成
    |
    +-- __ieee80211_tx()       [tx.c:1746]
          |
          v
        drv_tx()               [调用驱动 .tx 回调]
          |
          v
        [硬件 DMA 发送]
          |
          v [发送完成中断]
        drv_tx_status()        [速率控制反馈]
          |
          v
        rate_control_tx_status() [minstrel 更新统计]

14.2 完整 RX 数据流

[硬件 DMA 完成中断]
    |
    v [驱动填写 ieee80211_rx_status]
ieee80211_rx_list()            [rx.c:5435]
    |
    +-- 验证 band/rate
    +-- 挂起/重配 检查
    |
    v
ieee80211_rx_monitor()         [rx.c:846, 分发给 monitor]
    |
    v
ieee80211_rx_reorder_ampdu()   [rx.c:1445, AMPDU 重排序]
    |
    v
ieee80211_rx_handlers()        [rx.c:4195]
    |
    +-- 去重检查 (seqno)
    +-- 解密 (AES-CCMP/GCMP/TKIP)
    +-- PN 重放保护
    +-- STA 信息更新 (RSSI/信号统计)
    +-- 分片重组
    +-- A-MSDU 解聚合
    |
    +-- 数据帧:
    |     ieee80211_rx_h_data() -> netif_receive_skb()
    |
    +-- 管理帧:
    |     ieee80211_rx_h_mgmt()
    |       |
    |       +-- Beacon: 更新 BSS/DTIM/TIM
    |       +-- Auth/Assoc/Disassoc: MLME 状态机
    |       +-- Action: Block Ack / TDLS / Spectrum Mgmt
    |
    +-- 控制帧:
          PS-Poll -> 释放缓冲帧

15. 调试接口

mac80211 和 cfg80211 提供了丰富的 debugfs 接口,挂载于 /sys/kernel/debug/ieee80211/

/sys/kernel/debug/ieee80211/
├── phy0/
│   ├── rc/
│   │   ├── name              # 速率控制算法名称
│   │   └── fixed_rate_idx    # 强制固定速率(调试用)
│   ├── stations/
│   │   └── <mac>/
│   │       ├── rc_stats      # minstrel 速率统计
│   │       ├── ampdu_stats   # AMPDU 聚合统计
│   │       └── airtime       # Airtime 使用情况
│   ├── queues                # 发送队列状态
│   ├── ps                    # 省电模式状态
│   └── scan                  # 当前扫描状态

速率控制 debugfs 注册(rate.h L67~86):

static inline void rate_control_add_debugfs(struct ieee80211_local *local)
{
    debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
    local->debugfs.rcdir = debugfsdir;
    debugfs_create_file("name", 0400, debugfsdir,
                        local->rate_ctrl, &rcname_ops);
    local->rate_ctrl->ops->add_debugfs(&local->hw, local->rate_ctrl->priv,
                                       debugfsdir);
}

16. 关键 API 快速索引

API 文件与行号 用途
ieee80211_rx_list() rx.c:5435 驱动提交接收帧(批量)
ieee80211_rx_napi() rx.c:5598 驱动提交接收帧(NAPI)
ieee80211_rx_irqsafe() rx.c:5628 中断上下文提交接收帧
ieee80211_scan_completed() scan.c:540 驱动通告 HW 扫描完成
ieee80211_scan_work() scan.c:1095 软件扫描工作函数
ieee80211_txq_init() tx.c:1505 初始化 TXQ
ieee80211_txq_enqueue() tx.c:1442 帧入队到 TXQ/FQ
ieee80211_tx() tx.c:1934 TX 主路径
rate_control_get_rate() rate.h:25 选择发送速率
rate_control_tx_status() rate.h:29 更新发送结果统计
cfg80211_scan_done() core.h 通知用户空间扫描完成
ieee80211_sta_update_pending_airtime() sta_info.h:148 更新 AQL 空口时间
ieee80211_rx_reorder_ampdu() rx.c:1445 AMPDU 重排序入口
ieee80211_scan_rx() scan.c:277 处理扫描收到的 Beacon/Probe Response
ieee80211_bss_info_update() scan.c:167 更新 BSS 数据库

17. 与其他子系统的交互

mac80211/cfg80211
    |
    +-- Regulatory(监管域)
    |     net/wireless/reg.c
    |     管理信道允许功率、DFS 规则
    |
    +-- rfkill(射频开关)
    |     net/rfkill/
    |     软/硬件射频开关集成(cfg80211_rdev.rfkill_ops)
    |
    +-- PM(电源管理)
    |     WoWLAN(Wake-on-Wireless-LAN)支持
    |     drivers/net/wireless/ 驱动层
    |
    +-- Netfilter/nftables
    |     无线帧进入网络栈后走标准 netfilter 路径
    |
    +-- TC(流量控制)
    |     ieee80211_subif_start_xmit 之前经过 TC egress
    |
    +-- genetlink / netlink
    |     nl80211 基于 Generic Netlink (GENL) 框架
    |
    +-- crypto 子系统
    |     AES/CCMP/GCMP 加解密通过 kernel crypto API
    |
    +-- LED 子系统
          TX/RX/assoc LED 触发(ieee80211_i.h L1591~1595)
          tx_led / rx_led / assoc_led / radio_led

18. A-MPDU 聚合机制深度分析

18.1 TX 聚合会话建立全流程

A-MPDU(Aggregated MAC Protocol Data Unit)是 IEEE 802.11n 引入的帧聚合机制,将多个 MPDU 合并在一个 PPDU 中发送,显著提升吞吐量(尤其是短帧场景)。

TX 聚合会话的建立由 ieee80211_start_tx_ba_session()agg-tx.c L600)发起:

int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
                                  u16 timeout)
{
    // 前置条件检查
    if (!pubsta->valid_links &&
        !pubsta->deflink.ht_cap.ht_supported &&
        !pubsta->deflink.vht_cap.vht_supported &&
        !pubsta->deflink.he_cap.has_he &&
        !pubsta->deflink.eht_cap.has_eht &&
        !pubsta->deflink.s1g_cap.s1g)
        return -EINVAL;   // 对端不支持任何高吞吐率标准

    if (!ieee80211_hw_check(&local->hw, AMPDU_AGGREGATION) ||
        ieee80211_hw_check(&local->hw, TX_AMPDU_SETUP_IN_HW))
        return -EINVAL;

    // 限速:超过最大重试次数后拒绝建立
    if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES)
        return -EBUSY;
    // ...
}

完整握手流程:

发起方 (Initiator / STA)           接收方 (Recipient / AP)
        |                                   |
        |  ieee80211_start_tx_ba_session()  |
        |  drv_ampdu_action()               |
        |  IEEE80211_AMPDU_TX_START         |
        |                                   |
        |  --- ADDBA Request Frame -------> |  action_code = WLAN_ACTION_ADDBA_REQ
        |      dialog_token                 |  agg_size, timeout, start_seq_num
        |      capab: AMSDU|POLICY|TID|BUF |
        |                                   |
        |  [addba_resp_timer 启动,超时则停] |
        |                                   |
        |  <-- ADDBA Response Frame ------- |  capab, status (0=Accept)
        |                                   |
        |  ieee80211_start_tx_ba_cb()       |
        |  drv_ampdu_action()               |
        |  IEEE80211_AMPDU_TX_OPERATIONAL   |
        |                                   |
        |  [正式开始 A-MPDU 聚合发送]       |
        |  HT_AGG_STATE_OPERATIONAL 置位    |
        |                                   |
        |  定期发送 BlockAck Request (BAR)  |
        |  来同步接收方窗口                  |

18.2 TX 聚合队列管理

在建立聚合会话期间,对应 TID 的队列会被停止(防止乱序),由 ieee80211_agg_stop_txq()ieee80211_agg_start_txq() 管理(agg-tx.c L181~225):

// 停止 TXQ:设置 IEEE80211_TXQ_STOP 标志
static void ieee80211_agg_stop_txq(struct sta_info *sta, int tid)
{
    struct ieee80211_txq *txq = sta->sta.txq[tid];
    struct txq_info *txqi = to_txq_info(txq);

    spin_lock_bh(&fq->lock);
    set_bit(IEEE80211_TXQ_STOP, &txqi->flags);
    spin_unlock_bh(&fq->lock);
}

// 启动 TXQ:清除停止标志,若 enable=true 则额外设置 AMPDU 标志
static void ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable)
{
    if (enable)
        set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
    else
        clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);

    clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
    schedule_and_wake_txq(sta->sdata->local, txqi);
}

18.3 BlockAck Request 帧构建

当需要重置接收方序列号窗口时,发送 BAR(Block Ack Request)帧(agg-tx.c L103~131):

void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
{
    struct sk_buff *skb;
    struct ieee80211_bar *bar;
    u16 bar_control = 0;

    bar = skb_put_zero(skb, sizeof(*bar));
    bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
                                     IEEE80211_STYPE_BACK_REQ);
    bar_control |= IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;
    bar_control |= IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA;
    bar_control |= (u16)(tid << IEEE80211_BAR_CTRL_TID_INFO_SHIFT);
    bar->control = cpu_to_le16(bar_control);
    bar->start_seq_num = cpu_to_le16(ssn);

    // BAR 帧不加密,需要 TX 状态反馈
    IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
                                    IEEE80211_TX_CTL_REQ_TX_STATUS;
}

18.4 RX 聚合会话管理

RX 侧的聚合由对端发起 ADDBA Request 触发,在 __ieee80211_stop_rx_ba_session()agg-rx.c L58~115)中处理会话拆除:

void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                    u16 initiator, u16 reason, bool tx)
{
    // 通知驱动停止 RX BA 会话
    params.action = IEEE80211_AMPDU_RX_STOP;
    if (drv_ampdu_action(local, sta->sdata, &params))
        sdata_info(...);

    // 如果是本地发起的停止,向对端发 DelBA
    if (initiator == WLAN_BACK_RECIPIENT && tx)
        ieee80211_send_delba(sta->sdata, sta->sta.addr,
                             tid, WLAN_BACK_RECIPIENT, reason);

    // 停止重排序定时器
    timer_delete_sync(&tid_rx->session_timer);
    spin_lock_bh(&tid_rx->reorder_lock);
    tid_rx->removed = true;
    spin_unlock_bh(&tid_rx->reorder_lock);
    timer_delete_sync(&tid_rx->reorder_timer);

    // RCU 延迟释放
    call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
}

18.5 A-MSDU 聚合

A-MSDU(Aggregated MAC Service Data Unit)是另一种聚合方式,将多个 MSDU 封装在一个 MPDU 中,减少帧头开销。与 A-MPDU 不同,A-MSDU 在 MPDU 层面,不需要 BlockAck 协商。

A-MSDU 大小限制由 HE 6GHz 能力控制(he.c L42~56):

switch (le16_get_bits(he_6ghz_capa->capa,
                      IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
    link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
    break;
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
    link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
    break;
default:
    link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
}

A-MPDU 与 A-MSDU 对比:

A-MSDU 结构(单个 MPDU 内):
+----802.11 Header----+----MSDU1----+----MSDU2----+----MSDU3----+
|  QoS Data Frame     | LLC/SNAP+data | LLC/SNAP+data | data   |
+---------------------+-------------+-------------+-----------+
                         ^                                 ^
                    A-MSDU 子帧1                     A-MSDU 子帧N

A-MPDU 结构(单个 PPDU 内):
+--MPDU1--+--MPDU2--+--MPDU3--+--MPDU4--+
| MPDU1   | MPDU2   | MPDU3   | MPDU4   |   <- 每个 MPDU 独立有 802.11 头
+DELIMITER+DELIMITER+DELIMITER+DELIMITER+   <- 4字节分隔符(EOF/CRC/padding)
  A-MPDU Delimiter (4字节): EOF(1bit)|RSVD(1bit)|MPDU Length(12bit)|CRC(8bit)|EOF(8bit)

对比:
  A-MSDU: 减少 802.11 帧头开销,但一个错误导致整个 MPDU 丢弃
  A-MPDU: 每个 MPDU 独立 CRC,部分失败可选择性重传(需 BlockAck)
  最优方案: A-MPDU of A-MSDU(在每个 MPDU 内再嵌套 A-MSDU)

19. 加密子系统深度分析

19.1 加密算法总览

mac80211 支持以下加密套件,定义于 include/linux/ieee80211.h

套件 标识符 用途 密钥长度
WEP-40 WLAN_CIPHER_SUITE_WEP40 已废弃 40-bit
WEP-104 WLAN_CIPHER_SUITE_WEP104 已废弃 104-bit
TKIP WLAN_CIPHER_SUITE_TKIP WPA 128-bit
CCMP-128 WLAN_CIPHER_SUITE_CCMP WPA2/3 128-bit
CCMP-256 WLAN_CIPHER_SUITE_CCMP_256 WPA3 256-bit
GCMP-128 WLAN_CIPHER_SUITE_GCMP WPA3 6GHz 128-bit
GCMP-256 WLAN_CIPHER_SUITE_GCMP_256 WPA3 6GHz 256-bit
AES-CMAC WLAN_CIPHER_SUITE_AES_CMAC 管理帧保护 BIP 128-bit
BIP-GMAC-128 WLAN_CIPHER_SUITE_BIP_GMAC_128 管理帧保护 128-bit
BIP-GMAC-256 WLAN_CIPHER_SUITE_BIP_GMAC_256 管理帧保护 256-bit

19.2 密钥管理架构

密钥在 mac80211 中以 ieee80211_key 结构表示,包含硬件加速的判断逻辑(key.c L120~226)。

密钥的生命周期:

wpa_supplicant 四次握手完成
        |
        v  NL80211_CMD_NEW_KEY
nl80211_new_key()
        |
        v
cfg80211_ops->add_key()
        |
        v
ieee80211_add_key()  [cfg.c]
        |
        v
ieee80211_key_alloc()   // 分配 ieee80211_key 结构
        |
        v
ieee80211_key_enable_hw_accel()  [key.c:120]
        |
        +-- drv_set_key(SET_KEY) -> 驱动尝试硬件加速
        |
        +-- 若硬件不支持 -> 软件加密路径
        |   key->flags 不含 KEY_FLAG_UPLOADED_TO_HARDWARE
        |
        v
RCU 安装到 sdata->keys[] 或 sta->ptk[]
        |
        v
[数据传输阶段]  TX: ieee80211_tx_h_encrypt() / RX: ieee80211_rx_h_decrypt()
        |
        v
NL80211_CMD_DEL_KEY
        |
        v
ieee80211_key_disable_hw_accel()  [key.c:228]
        |
        v  drv_set_key(DISABLE_KEY)
        kfree_rcu(key)

19.3 硬件加速与软件降级

ieee80211_key_enable_hw_accel()key.c L120)实现了硬件/软件加密的仲裁:

static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
{
    ret = drv_set_key(key->local, SET_KEY, sdata,
                      sta ? &sta->sta : NULL, &key->conf);

    if (!ret) {
        key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
        // 硬件处理加密,无需软件 tailroom
        if (!(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC | ...)))
            decrease_tailroom_need_count(sdata, 1);
        return 0;
    }

 out_unsupported:
    switch (key->conf.cipher) {
    case WLAN_CIPHER_SUITE_CCMP:
    case WLAN_CIPHER_SUITE_GCMP:
    // ...
        // 支持软件加密的算法:降级到软件
        if (ieee80211_hw_check(&key->local->hw, SW_CRYPTO_CONTROL))
            return -EINVAL;  // 驱动不允许降级
        return 0;  // 允许软件加密
    default:
        return -EINVAL;  // 不支持该算法
    }
}

tailroom 计数(key.c L75~107)用于管理 SKB 尾部空间的分配:当有软件加密密钥时,TX 路径在分配 SKB 时需要额外预留 MIC/ICV 空间。

19.4 CCMP(AES-CCM)实现

CCMP 是 WPA2 的核心加密算法,使用 AES 计数模式(CTR)加密 + CBC-MAC 认证(CCM 模式)。

加密流程wpa.c L379~450 的 ccmp_encrypt_skb()):

CCMP 帧格式:

802.11 头      CCMP 头(8B)     加密载荷         MIC(8B)
[FC|Dur|Addr] [PN0~5|KeyID] [Encrypted MSDU] [MIC]
               ^IV/PN字段^    ^AES-CTR加密^   ^CBC-MAC^

CCMP Header 格式(wpa.c L402~412 的 ccmp_pn2hdr()):

static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id)
{
    hdr[0] = pn[5];          // PN0
    hdr[1] = pn[4];          // PN1
    hdr[2] = 0;              // Reserved
    hdr[3] = 0x20 | (key_id << 6);  // ExtIV=1, KeyID
    hdr[4] = pn[3];          // PN2
    hdr[5] = pn[2];          // PN3
    hdr[6] = pn[1];          // PN4
    hdr[7] = pn[0];          // PN5
}

AAD 构建wpa.c L315~377): CCMP 的 AAD(Additional Authentication Data)包含 802.11 帧头的部分字段,用于防止帧头被篡改:

static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad, bool spp_amsdu)
{
    // AAD = FC(masked) | A1 | A2 | A3 | SC(seq masked) | [A4] | [QoS]
    mask_fc = hdr->frame_control;
    mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY |    // 屏蔽重传位
                            IEEE80211_FCTL_PM |
                            IEEE80211_FCTL_MOREDATA);
    mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);  // 设置 Protected

    // 序列号控制:保留分片号,清除序列号
    aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f;  // 只保留 Frag#
    aad[23] = 0;
    // ...
}

PN(Packet Number)防重放保护:每个密钥维护一个单调递增的 48-bit PN,接收方检查 PN 是否大于上次收到的 PN(ieee80211_rx_data.ccm_gcm.pn)。

19.5 TKIP 实现

TKIP(Temporal Key Integrity Protocol)是 WPA 的过渡加密方案,在 WEP 基础上添加了:

  1. Michael MIC(消息完整性码)
  2. IV 序列化(防重放)
  3. 每包密钥混合(TKIP Key Mixing)

TX MIC 添加wpa.c L30~91 的 ieee80211_tx_h_michael_mic_add()):

ieee80211_tx_result
ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
{
    key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];
    michael_mic(key, hdr, data, data_len, mic);  // 计算 8B MIC
    // 追加到帧尾
    mic = skb_put(skb, MICHAEL_MIC_LEN);
}

RX MIC 校验wpa.c L94~192 的 ieee80211_rx_h_michael_mic_verify()): 若 MIC 验证失败,调用 cfg80211_michael_mic_failure() 通知用户空间,触发 WPA 对策机制(60s 内再次失败则断开连接)。

TKIP 加密wpa.c L194~245 的 tkip_encrypt_skb()):

static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
{
    // 递增 PN(48-bit,分为 iv32:iv16 两部分)
    pn = atomic64_inc_return(&key->conf.tx_pn);
    pos = ieee80211_tkip_add_iv(pos, &key->conf, pn);

    // 若是软件加密,追加 ICV(4字节 CRC32)
    skb_put(skb, IEEE80211_TKIP_ICV_LEN);
    return ieee80211_tkip_encrypt_data(&tx->local->wep_tx_ctx,
                                       key, skb, pos, len);
}

19.6 Extended Key ID(EKI)重新密钥

WPA3 支持 Extended Key ID,允许在不中断流量的情况下更换 PTK,避免重新关联。key.c L290~320 中的 ieee80211_pairwise_rekey() 处理密钥轮换:

static void ieee80211_pairwise_rekey(struct ieee80211_key *old,
                                     struct ieee80211_key *new)
{
    if (new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) {
        // Extended Key ID 重新密钥
        if (sta->ptk_idx != INVALID_PTK_KEYIDX &&
            !ieee80211_hw_check(&local->hw, AMPDU_KEYBORDER_SUPPORT)) {
            // 不支持 AMPDU Key Border 的驱动必须停止所有聚合会话
            // 避免 A-MPDU 中混用不同 KeyID 的帧
            set_sta_flag(sta, WLAN_STA_BLOCK_BA);
            for (i = 0; i < IEEE80211_NUM_TIDS; i++)
                __ieee80211_stop_tx_ba_session(sta, i,
                                               AGG_STOP_LOCAL_REQUEST);
        }
    }
}

20. 信道管理与 DFS(动态频率选择)

20.1 信道上下文(Channel Context)

mac80211 通过 ieee80211_chanctx 结构管理信道使用,支持多接口复用同一信道:

信道上下文分配chan.c L830~904):

ieee80211_new_chanctx()
    |
    v  ieee80211_alloc_chanctx()   分配并初始化 chanctx 结构
    |     ctx->conf.def = chanreq->oper  // 设置信道定义
    |     ctx->conf.radar_enabled = false
    |     ctx->radar_detected = false
    |
    v  ieee80211_add_chanctx()
    |     drv_add_chanctx(local, ctx)   // 通知驱动
    |
    v  list_add_rcu(&ctx->list, &local->chanctx_list)

信道上下文迭代器(chan.c L109~133):

// 遍历所有已分配(assigned)到某 chanctx 的接口+链路
#define for_each_chanctx_user_assigned(local, ctx, iter)
// 遍历所有预留(reserved)的
#define for_each_chanctx_user_reserved(local, ctx, iter)
// 遍历全部
#define for_each_chanctx_user_all(local, ctx, iter)

ieee80211_chanreq_compatible()chan.c L222~240)检查两个信道请求是否兼容(可共用一个 chanctx),基于 cfg80211_chandef_compatible() 的物理兼容性(40MHz 与 20MHz 子信道兼容等)。

20.2 DFS:动态频率选择

DFS 是 5GHz 信道上的强制要求(IEEE 802.11h),用于检测雷达信号并避免干扰航空雷达。

DFS 状态机:

信道请求使用 5GHz DFS 信道(如 U-NII-2)
        |
        v
CAC(Channel Availability Check)开始
        |  ieee80211_link_data.dfs_cac_timer_work 启动
        |  cfg80211_cac_event(NL80211_RADAR_CAC_STARTED)
        |
        v  [CAC 期间只接收,不发送任何帧]
        |  CAC 持续时间(通常 60 秒)
        |
        +-- 超时(无雷达)-> 信道可用
        |   cfg80211_cac_event(NL80211_RADAR_CAC_FINISHED)
        |   正常使用该信道
        |
        +-- 检测到雷达 -> 立即停止使用
            ieee80211_radar_detected()  [util.c:3664]
                |
                v  标记所有使用该信道的 chanctx
                ieee80211_radar_mark_chan_ctx_iterator()
                |
                v  调度雷达处理工作
                wiphy_work_queue(&local->radar_detected_work)
                |
                v  ieee80211_dfs_radar_detected_work()  [util.c:3620]
                |     取消所有 CAC
                |     cfg80211_radar_event() -> 通知用户空间
                |
                v  wpa_supplicant/hostapd 切换到其他信道

雷达检测接口util.c L3664~3676):

void ieee80211_radar_detected(struct ieee80211_hw *hw,
                               struct cfg80211_chan_def *chandef)
{
    trace_api_radar_detected(local);

    // 原子地标记所有使用该信道的 chanctx
    ieee80211_iter_chan_contexts_atomic(hw,
        ieee80211_radar_mark_chan_ctx_iterator, chandef);

    // 调度工作队列处理(不能在中断上下文中直接处理)
    wiphy_work_queue(hw->wiphy, &local->radar_detected_work);
}
EXPORT_SYMBOL(ieee80211_radar_detected);

CAC 取消util.c L3581~3618):

void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
                              struct ieee80211_chanctx *ctx)
{
    list_for_each_entry(sdata, &local->interfaces, list) {
        for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
            // 取消 CAC 定时器
            wiphy_hrtimer_work_cancel(local->hw.wiphy,
                                      &link->dfs_cac_timer_work);
            if (!sdata->wdev.links[link_id].cac_started)
                continue;
            // 释放信道,通知 CAC 被中止
            ieee80211_link_release_channel(link);
            cfg80211_cac_event(sdata->dev, &chandef,
                               NL80211_RADAR_CAC_ABORTED,
                               GFP_KERNEL, link_id);
        }
    }
}

20.3 信道切换通告(CSA)

CSA(Channel Switch Announcement)允许 AP 提前通知所有关联的 STA 即将进行的信道切换,减少数据丢失。

队列停止原因中包含 IEEE80211_QUEUE_STOP_REASON_CSA,在 CSA 期间停止发送数据帧,等待所有 STA 完成切换。

信道切换参数在 ieee80211_bss_conf 中(ieee80211_i.h L690~700 附近):

bool csa_active;                // CSA 进行中
bool color_change_active;       // BSS 颜色变更进行中(HE)
u8 csa_count_offs;              // Beacon 中 CSA Count 字段偏移

21. Wi-Fi 6(802.11ax/HE)特性深度分析

21.1 HE 关键特性概览

802.11ax(High Efficiency,HE)相较于 802.11ac 主要带来以下增强:

802.11ax (Wi-Fi 6) 核心技术:
+----------------------------------------------------------+
| OFDMA (多用户正交频分多址)                                 |
|   DL: AP 同时向多个 STA 分配不同 RU 发送                  |
|   UL: 多个 STA 同时向 AP 发送(需要触发帧协调)            |
+----------------------------------------------------------+
| MU-MIMO (多用户 MIMO)                                     |
|   DL MU-MIMO: AP 同时向最多 8 个 STA 发送                 |
|   UL MU-MIMO: 多 STA 同时上传(802.11ax 新增)            |
+----------------------------------------------------------+
| BSS Coloring (BSS 着色)                                   |
|   为每个 BSS 分配 1-63 的颜色值                            |
|   STA 可快速判断帧是否来自本 BSS,减少不必要的退避          |
+----------------------------------------------------------+
| TWT (Target Wake Time,目标唤醒时间)                      |
|   STA 与 AP 协商精确唤醒时间,大幅节省 IoT 设备电量        |
+----------------------------------------------------------+
| 1024-QAM (额外 25% 吞吐量提升)                            |
| 更高效的 LDPC 编码                                        |
+----------------------------------------------------------+
| 空间复用(Spatial Reuse)                                  |
|   BSS 内外颜色区分,允许在检测到弱信号 OBSS 时仍发送       |
+----------------------------------------------------------+

21.2 OFDMA 资源单元(RU)分配

OFDMA 将信道分割为若干个资源单元(RU),AP 通过触发帧(Trigger Frame)协调多用户同时传输。mac80211 中的 OFDMA 相关标志(debugfs_sta.c L762):

PFLAG(MAC, 3, OFDMA_RA, "OFDMA-RA");  // 对端支持 OFDMA 随机访问

EHT(802.11be)进一步扩展了 OFDMA 能力(debugfs_sta.c L1164~1173):

PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_80MHZ,  "NON-OFDMA-UL-MU-MIMO-80MHZ");
PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_160MHZ, "NON-OFDMA-UL-MU-MIMO-160MHZ");
PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_320MHZ, "NON-OFDMA-UL-MU-MIMO-320MHZ");
PFLAG(PHY, 8, RX_1024QAM_WIDER_BW_DL_OFDMA, "RX-1024QAM-WIDER-BW-DL-OFDMA");
PFLAG(PHY, 8, RX_4096QAM_WIDER_BW_DL_OFDMA, "RX-4096QAM-WIDER-BW-DL-OFDMA");

mlme.c 中针对更宽带宽 OFDMA 的特殊处理(mlme.c L1024~1039):

// 驱动可声明 IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW 来忽略
// 仅用于更宽带宽 OFDMA 目的的信道(不作为工作信道对待)
if (sdata->vif.driver_flags & IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW)
    return false;

21.3 BSS Coloring(BSS 着色)

BSS Coloring 是 802.11ax 引入的空间复用机制,通过在 HE Operation IE 中携带颜色值(1~63),使得 STA 可在物理层快速识别帧的归属 BSS,从而实现更激进的空间复用。

颜色冲突检测rx.c L3452~3494):

static void
ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
{
    // 1. 驱动未使能颜色特性 -> 跳过
    if (!wiphy_ext_feature_isset(rx->local->hw.wiphy,
                                 NL80211_EXT_FEATURE_BSS_COLOR))
        return;

    // 2. 硬件自行检测 -> 跳过软件检测
    if (ieee80211_hw_check(&rx->local->hw, DETECTS_COLOR_COLLISION))
        return;

    // 3. CSA/颜色变更进行中 -> 跳过
    if (bss_conf->csa_active || bss_conf->color_change_active ||
        !bss_conf->he_bss_color.enabled)
        return;

    // 4. 解析收到的 Beacon 的 HE Operation IE
    ie = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ...);
    if (ie) {
        he_oper = (void *)(ie->data + 1);
        color = le32_get_bits(he_oper->he_oper_params,
                              IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
        // 5. 颜色与本 BSS 相同 -> 报告碰撞
        if (color == bss_conf->he_bss_color.color)
            ieee80211_obss_color_collision_notify(&rx->sdata->vif,
                                                  BIT_ULL(color),
                                                  bss_conf->link_id);
    }
}

ieee80211_obss_color_collision_notify() 最终通知 cfg80211,再通过 nl80211 向 hostapd 上报,由 hostapd 决定是否进行颜色变更(Color Change Announcement)。

21.4 HE MCS 能力协商

HE 引入了新的 MCS 表示方式,支持 MCS011(含 1024-QAM),并区分不同空间流数。MCS 交集计算(he.c L67115 的 ieee80211_he_mcs_intersection()):

static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx,
                                          __le16 *he_own_tx, __le16 *he_peer_tx)
{
    // 对每个 NSS(最多 8 流)取 own 和 peer 能力的交集
    for (i = 0; i < 8; i++) {
        own_rx = (le16_to_cpu(*he_own_rx) >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
        peer_rx = (le16_to_cpu(*he_peer_rx) >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
        // 取两者中较低的 MCS 支持等级
        // IEEE80211_HE_MCS_NOT_SUPPORTED = 3 (最高,表示不支持)
        // 0 = MCS 0-7, 1 = MCS 0-9, 2 = MCS 0-11
    }
}

HE 6GHz 频段能力(he.c L13~57)处理 6GHz 频段特有的 SMPS 模式和最大 MPDU 长度设置。

21.5 TWT(目标唤醒时间)

TWT 允许 STA 与 AP 协商精确的唤醒/睡眠时间表,特别适合 IoT 设备(如智能传感器)。

TWT 在 mac80211 中通过 ieee80211_hw 标志和 nl80211 命令支持,驱动需声明支持后方可使用。


22. 速率控制算法 minstrel_ht 的完整速率组计算

22.1 HT 分组宏定义

rc80211_minstrel_ht.c L19~50 定义了每个速率分组的发送时间计算宏:

/* 平均 AMPDU 大小:16 个 MPDU */
#define AVG_AMPDU_SIZE    16
/* 平均包大小:1200 字节 */
#define AVG_PKT_SIZE      1200

/* 平均数据包的比特数(含 AMPDU) */
#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)

/* 给定每符号比特数时,需要的符号数 */
#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))

/* OFDM 符号时间:SGI = 3.6us,LGI = 4us */
#define MCS_SYMBOL_TIME(sgi, syms)                    \
    (sgi ?                                             \
      ((syms) * 18000 + 4000) / 5 :  /* 3.6us/sym */ \
      ((syms) * 1000) << 2            /* 4us/sym */   \
    )

/* 每包平均发送持续时间(纳秒) */
#define MCS_DURATION(streams, sgi, bps) \
    (MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE)

22.2 HT 分组索引计算

/* HT 分组排序:HT40 > SGI > #streams */
#define GROUP_IDX(_streams, _sgi, _ht40)  \
    MINSTREL_HT_GROUP_0 +                 \
    MINSTREL_MAX_STREAMS * 2 * _ht40 +    \  // 0 或 8(HT40偏移)
    MINSTREL_MAX_STREAMS * _sgi +          \  // 0 或 4(SGI偏移)
    _streams - 1                              // 0~3(1~4流)

HT 分组布局(16个组):

GROUP_IDX(1,0,0)=0  : 1流, LGI, HT20  MCS0-7
GROUP_IDX(2,0,0)=1  : 2流, LGI, HT20  MCS8-15
GROUP_IDX(3,0,0)=2  : 3流, LGI, HT20  MCS16-23
GROUP_IDX(4,0,0)=3  : 4流, LGI, HT20  MCS24-31
GROUP_IDX(1,1,0)=4  : 1流, SGI, HT20  MCS0-7
GROUP_IDX(2,1,0)=5  : 2流, SGI, HT20  ...
...
GROUP_IDX(1,0,1)=8  : 1流, LGI, HT40
...
GROUP_IDX(4,1,1)=15 : 4流, SGI, HT40  最高速率

22.3 最优速率选择算法

minstrel_ht_set_best_tp_rates()rc80211_minstrel_ht.c L548~568)维护前 4 名最高吞吐量速率:

遍历所有速率分组的所有速率:
    计算 cur_tp_avg = minstrel_ht_get_tp_avg(mi, group, idx, prob_avg)
    |
    v  按 tp_avg 降序插入 mi->max_tp_rate[0..3] 排序列表
    |  (保留前 MAX_THR_RATES=4 名)

同时维护:
    mi->max_prob_rate: 成功率 > 75% 中吞吐量最高的速率
    mg->max_group_prob_rate: 每分组内成功率最高的速率

分组内最优成功率速率选择(rc80211_minstrel_ht.c L573~630 的 minstrel_ht_set_best_prob_rate()):

  • 若成功率 > 75%:选吞吐量最高的
  • 否则:选成功率最高的(更保守)

23. ieee80211_hw 硬件抽象与 ieee80211_ops 驱动接口

23.1 ieee80211_hw 结构

ieee80211_hw 是 mac80211 暴露给驱动的硬件抽象,定义于 include/net/mac80211.h

struct ieee80211_hw {
    struct ieee80211_conf conf;      // 当前硬件配置(信道、功率、PS 等)
    struct wiphy *wiphy;             // 指向 wiphy

    const char *rate_control_algorithm; // 覆盖默认速率控制算法

    void *priv;                      // 驱动私有数据指针

    unsigned long flags[...];        // ieee80211_hw_flags 位图
    unsigned long extra_flags[...];  // 扩展标志

    /* TX/RX 队列配置 */
    u8 queues;                       // 驱动硬件队列数(通常 4 对应 4 个 AC)
    u16 max_rates;                   // 每帧最多速率重试数
    u8 max_rate_tries;               // 每速率最多重试次数
    u8 max_rx_aggregation_subframes; // RX AMPDU 最大子帧数
    u8 max_tx_aggregation_subframes; // TX AMPDU 最大子帧数

    /* SKB 空间需求 */
    unsigned int extra_tx_headroom;  // 驱动需要的额外帧头空间
    unsigned int extra_beacon_tailroom; // Beacon 额外尾部空间

    /* 网络设备统计 */
    int vif_data_size;               // 驱动 ieee80211_vif 私有数据大小
    int sta_data_size;               // 驱动 ieee80211_sta 私有数据大小
    int chanctx_data_size;           // 驱动信道上下文私有数据大小

    netdev_features_t netdev_features; // 网络设备特性(TSO/GSO 等)

    u8 uapsd_queues;                 // 支持 U-APSD 的队列掩码
    u8 uapsd_max_sp_len;             // 最大 SP 长度

    u8 n_cipher_schemes;             // 自定义加密方案数量
    // ...
};

重要硬件标志(ieee80211_hw_flagsinclude/net/mac80211.h):

IEEE80211_HW_HAS_RATE_CONTROL       驱动实现速率控制(不走 minstrel)
IEEE80211_HW_RX_INCLUDES_FCS        RX 帧包含 FCS(驱动不去掉)
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING  主机缓冲 PS 组播帧
IEEE80211_HW_AMPDU_AGGREGATION      驱动/硬件支持 AMPDU 聚合
IEEE80211_HW_SUPPORTS_PS            硬件支持省电模式
IEEE80211_HW_SUPPORTS_DYNAMIC_PS   硬件支持动态省电
IEEE80211_HW_MFP_CAPABLE           硬件支持管理帧保护(802.11w)
IEEE80211_HW_REPORTS_TX_ACK_STATUS TX ACK 状态上报
IEEE80211_HW_SUPPORTS_REORDERING_BUFFER  硬件自行处理 AMPDU 重排序
IEEE80211_HW_WANT_MONITOR_VIF      驱动需要专用 monitor vif
IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT  AMPDU 跨密钥边界支持(EKI)
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW   AMPDU 建立由硬件处理

23.2 ieee80211_ops 回调集合

ieee80211_ops 是驱动向 mac80211 注册的操作表,分为以下几类:

基本 TX/RX:

struct ieee80211_ops {
    // TX:驱动发送帧(软件队列模型用 wake_tx_queue 替代)
    void (*tx)(struct ieee80211_hw *hw,
               struct ieee80211_tx_control *control,
               struct sk_buff *skb);

    // 唤醒 TXQ 调度(新式接口,替代 tx)
    void (*wake_tx_queue)(struct ieee80211_hw *hw,
                          struct ieee80211_txq *txq);
    // ...
};

硬件配置:

    int (*start)(struct ieee80211_hw *hw);  // 开启硬件
    void (*stop)(struct ieee80211_hw *hw);  // 停止硬件
    int (*config)(struct ieee80211_hw *hw, u32 changed);  // 配置变更
    void (*bss_info_changed)(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct ieee80211_bss_conf *info,
                             u64 changed);  // BSS 配置变更

接口管理:

    int (*add_interface)(struct ieee80211_hw *hw,
                         struct ieee80211_vif *vif);
    void (*remove_interface)(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif);
    int (*change_interface)(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            enum nl80211_iftype new_type, bool p2p);

STA 管理:

    int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta);
    int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                      struct ieee80211_sta *sta);
    void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       enum sta_notify_cmd cmd,
                       struct ieee80211_sta *sta);

加密密钥:

    int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                   struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta,
                   struct ieee80211_key_conf *key);

扫描:

    int (*hw_scan)(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif,
                   struct ieee80211_scan_request *req);
    void (*cancel_hw_scan)(struct ieee80211_hw *hw,
                           struct ieee80211_vif *vif);

AMPDU 聚合:

    int (*ampdu_action)(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
                        struct ieee80211_ampdu_params *params);
    // params->action 可以是:
    // IEEE80211_AMPDU_RX_START/STOP
    // IEEE80211_AMPDU_TX_START/STOP/OPERATIONAL

信道上下文(多信道支持):

    int (*add_chanctx)(struct ieee80211_hw *hw,
                       struct ieee80211_chanctx_conf *ctx);
    void (*remove_chanctx)(struct ieee80211_hw *hw,
                           struct ieee80211_chanctx_conf *ctx);
    void (*change_chanctx)(struct ieee80211_hw *hw,
                           struct ieee80211_chanctx_conf *ctx,
                           u32 changed);
    int (*assign_vif_chanctx)(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
                              struct ieee80211_bss_conf *link_conf,
                              struct ieee80211_chanctx_conf *ctx);

23.3 驱动发现和注册流程

驱动模块加载
    |
    v  驱动调用 ieee80211_alloc_hw(sizeof_priv, &ops)
    |    分配 ieee80211_hw + ieee80211_local 结构
    |    hw->priv 指向驱动私有数据
    |
    v  驱动填充 hw->queues, hw->flags, hw->wiphy->bands[] 等
    |
    v  ieee80211_register_hw(hw)
    |    注册 wiphy(cfg80211 层)
    |    注册 net_device(每个接口)
    |    初始化速率控制(rate_control_alloc)
    |
    v  驱动就绪,等待 ops->start() 调用
    |
    v  用户空间通过 nl80211 请求关联
    |    ops->start() -> 硬件上电
    |    ops->add_interface() -> 创建 VIF
    |    ops->config() -> 配置信道/功率
    |    ...

24. 节能模式(PS)深度机制

24.1 TIM 与 DTIM 完整机制

TIM(Traffic Indication Map)的作用:

TIM 是 Beacon 帧中的信息元素(IE),AP 用其告知处于省电模式的 STA 哪些 AID 有缓冲帧待取:

Beacon 帧结构(省电相关部分):
+--Fixed Params--+--SSID IE--+--Rates IE--+--TIM IE--+--HT IE--+...
                               ^
                    TIM IE 格式:
                    +--DTIM Count--+--DTIM Period--+--Bitmap Ctrl--+--Virtual Bitmap--+
                    |  0~(period-1)|   1~255       |  BC/MC flag + |  AID 位图        |
                    |              |               |  Bitmap Offset|  (最多 251B)     |

DTIM(Delivery Traffic Indication Map):

每隔 DTIM period 个 Beacon 周期,AP 发送 DTIM Beacon,此时发送缓冲的组播/广播帧。TIM IE 的 Bitmap Control 字节的最低位(bit0)表示是否有组播/广播缓冲帧。

内核侧 TIM 更新流程:

当有帧被缓冲给处于 PS 的 STA 时:

  1. sta->ps_tx_buf[ac] 队列缓冲帧
  2. ieee80211_set_tim(sta, true) 在 TIM 位图中设置对应 AID
  3. 下次 Beacon 发出时,驱动从 ps_data.tim 位图读取并填充 TIM IE

24.2 DTIM 组播/广播帧递交

组播/广播帧在 DTIM Beacon 之后立即发送。ps_data.dtim_bc_mc 字段控制此行为(ieee80211_i.h L315~326):

DTIM Beacon 发出
    |
    v  驱动调用 ieee80211_get_buffered_bc()
    |    取出 ps_data.bc_buf 中的组播帧
    |
    v  逐帧发送(More Data 位置1,最后一帧置0)
    |
    v  处于 PS 的 STA 在 DTIM 期唤醒
       收到帧后可重新进入省电

24.3 WMM-PS(Wi-Fi 多媒体省电)

WMM 为不同 AC 提供差异化 PS 处理,触发 AC 的 QoS 帧同时作为 service period 触发器。

WMM AC 与 U-APSD 的对应关系:

AC_BK (Background)    <-> UAPSD Queue 0  (低优先级后台流量)
AC_BE (Best Effort)   <-> UAPSD Queue 1  (标准流量)
AC_VI (Video)         <-> UAPSD Queue 2  (视频流量)
AC_VO (Voice)         <-> UAPSD Queue 3  (语音流量,最高优先级)

IEEE80211_DEFAULT_UAPSD_QUEUES = 0 默认全部禁用,驱动可通过 hw->uapsd_queues 覆盖。


25. 关键数据流汇总(完整版)

25.1 完整 TX 数据流

用户进程 write(socket)
    |
    v [内核网络栈]
ip_output() / udp_sendmsg()
    |
    v
dev_queue_xmit()
    |
    v [TC 流量控制,如有]
ieee80211_subif_start_xmit()   [net_device->ndo_start_xmit]
    |
    v
ieee80211_xmit()               [tx.c:2023, 添加 802.11 头]
    |
    v
ieee80211_tx()                 [tx.c:1934]
    |
    +-- TX handlers (early):
    |     - 动态 PS 检查
    |     - 关联状态验证
    |     - 组播 PS 缓冲
    |     - 速率选择 (rate_control_get_rate)
    |
    +-- ieee80211_queue_skb()  [入 TXQ]
    |     |
    |     v
    |   ieee80211_txq_enqueue() [tx.c:1442]
    |     |
    |     v
    |   fq_tin_enqueue()        [Fair Queue / CoDel]
    |
    +-- TX handlers (late):
    |     - 加密 (ieee80211_tx_h_encrypt)
    |     - 帧头完成
    |
    +-- __ieee80211_tx()       [tx.c:1746]
          |
          v
        drv_tx()               [调用驱动 .tx 回调]
          |
          v
        [硬件 DMA 发送]
          |
          v [发送完成中断]
        drv_tx_status()        [速率控制反馈]
          |
          v
        rate_control_tx_status() [minstrel 更新统计]

25.2 完整 RX 数据流

[硬件 DMA 完成中断]
    |
    v [驱动填写 ieee80211_rx_status]
ieee80211_rx_list()            [rx.c:5435]
    |
    +-- 验证 band/rate
    +-- 挂起/重配 检查
    |
    v
ieee80211_rx_monitor()         [rx.c:846, 分发给 monitor]
    |
    v
ieee80211_rx_reorder_ampdu()   [rx.c:1445, AMPDU 重排序]
    |
    v
ieee80211_rx_handlers()        [rx.c:4195]
    |
    +-- 去重检查 (seqno)
    +-- 解密 (AES-CCMP/GCMP/TKIP)
    +-- PN 重放保护
    +-- STA 信息更新 (RSSI/信号统计)
    +-- 分片重组
    +-- A-MSDU 解聚合
    |
    +-- 数据帧:
    |     ieee80211_rx_h_data() -> netif_receive_skb()
    |
    +-- 管理帧:
    |     ieee80211_rx_h_mgmt()
    |       |
    |       +-- Beacon: 更新 BSS/DTIM/TIM
    |       +-- Auth/Assoc/Disassoc: MLME 状态机
    |       +-- Action: Block Ack / TDLS / Spectrum Mgmt
    |       +-- HE Beacon: BSS Coloring 冲突检测
    |
    +-- 控制帧:
          PS-Poll -> 释放缓冲帧

26. 调试接口(完整版)

26.1 debugfs 目录结构

/sys/kernel/debug/ieee80211/
├── phy0/
│   ├── rc/
│   │   ├── name              # 速率控制算法名称(如 "minstrel_ht")
│   │   └── fixed_rate_idx    # 强制固定速率(-1 表示自动)
│   ├── stations/
│   │   └── aa:bb:cc:dd:ee:ff/
│   │       ├── rc_stats          # minstrel_ht 速率统计表
│   │       ├── rc_stats_csv      # CSV 格式速率统计
│   │       ├── ampdu_stats       # AMPDU 聚合会话统计
│   │       ├── airtime           # Airtime 使用情况(TX/RX 微秒)
│   │       ├── aql               # AQL 待发空口时间
│   │       ├── flags             # STA 标志位(ASSOC/AUTH/PS 等)
│   │       ├── ht_capa           # HT 能力
│   │       ├── vht_capa          # VHT 能力
│   │       └── he_capa           # HE 能力
│   ├── queues                # 每队列停止原因和帧计数
│   ├── ps                    # 省电模式状态(动态 PS 超时等)
│   ├── scan                  # 当前扫描状态
│   ├── chanctx/              # 信道上下文列表
│   │   └── <n>/
│   │       ├── frequency     # 中心频率
│   │       ├── width         # 信道带宽
│   │       └── users         # 使用此 chanctx 的接口数
│   └── hwflags               # 硬件标志位

26.2 minstrel_ht debugfs 输出解读

rc_stats 文件的典型输出格式(来自 rc80211_minstrel_ht_debugfs.c):

type     rate     throughput  ewma prob  retry  last_attempts  last_success
HT20/LGI MCS0      5.8        98.9%       2      1234          1219
HT20/LGI MCS1      11.4       99.0%       2       856           848
...
HT20/SGI MCS7      68.2       91.3%       4       322           294    *T
HT40/SGI MCS7      130.1      88.2%       4       189           167
...
OFDM     54.0Mbps  38.7       96.5%       3        56            54
CCK      11.0Mbps   8.8       99.9%       6        12            12   *P

字段说明:

  • *T:当前最高吞吐量速率(max_tp_rate[0]
  • *P:当前最高成功率速率(max_prob_rate
  • ewma prob:EWMA 成功率(minstrel_rate_stats.prob_avg

26.3 常用调试命令

# 查看所有无线接口的速率控制算法
cat /sys/kernel/debug/ieee80211/phy0/rc/name

# 强制使用 MCS7 速率(调试用,-1 为自动)
echo 7 > /sys/kernel/debug/ieee80211/phy0/rc/fixed_rate_idx

# 查看关联 STA 的 AMPDU 统计
cat /sys/kernel/debug/ieee80211/phy0/stations/aa:bb:cc:dd:ee:ff/ampdu_stats

# 查看队列状态
cat /sys/kernel/debug/ieee80211/phy0/queues

# 通过 iw 查看 minstrel 统计(封装了 debugfs)
iw dev wlan0 station dump

27. 与其他子系统的交互(完整版)

27.1 与 cfg80211 监管域子系统

监管域(Regulatory Domain)控制每个国家/地区的信道使用限制,包括最大发射功率、DFS 要求、no-IR 信道等。

net/wireless/reg.c                        监管域核心实现
    |
    +-- cfg80211_get_chan_state()          查询信道状态
    |     返回: NL80211_CHAN_NO_HT / HT20 / HT40 / ...
    |
    +-- regulatory_hint()                 驱动或用户上报监管域
    |     触发监管域重新计算
    |
    +-- nl80211_set_reg()                 用户空间直接设置监管域
    |
    +-- ieee80211_check_combinations()    检查信道组合是否符合规定
          由 chan.c 中的信道管理调用

27.2 与 rfkill 子系统

rfkill_block 工作项 (cfg80211_registered_device.rfkill_ops)
    |
    +-- 软 rfkill (写 /sys/class/rfkill/rfkillX/soft)
    |     cfg80211_rfkill_set_block()
    |     -> ieee80211_rfkill_poll() -> drv_rfkill_poll()
    |
    +-- 硬 rfkill (物理开关)
          wiphy_rfkill_stop_all()
          -> 停止所有接口、断开所有关联

27.3 与 crypto 子系统

加密算法通过内核 crypto API 实现:

// wpa.c 中 CCMP 加密调用 kernel crypto API
// aes_ccm.h 封装:
void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm,
                                u8 *b_0, u8 *aad, u8 *data, size_t data_len,
                                u8 *mic, size_t mic_len);

// 密钥安装时分配 tfm(转换对象):
// key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data, key_len, mic_len)

GCMP(AES-GCM 模式)通过 aes_gcm.h 类似实现,相比 CCMP 计算效率更高,是 WPA3 6GHz 的标准加密。


28. 关键 API 快速索引(完整版)

API 文件与行号 用途
ieee80211_rx_list() rx.c:5435 驱动提交接收帧(批量)
ieee80211_rx_napi() rx.c:5598 驱动提交接收帧(NAPI)
ieee80211_rx_irqsafe() rx.c:5628 中断上下文提交接收帧
ieee80211_scan_completed() scan.c:540 驱动通告 HW 扫描完成
ieee80211_scan_work() scan.c:1095 软件扫描工作函数
ieee80211_txq_init() tx.c:1505 初始化 TXQ
ieee80211_txq_enqueue() tx.c:1442 帧入队到 TXQ/FQ
ieee80211_tx() tx.c:1934 TX 主路径
rate_control_get_rate() rate.h:25 选择发送速率
rate_control_tx_status() rate.h:29 更新发送结果统计
rate_control_rate_init() rate.c:31 初始化站点速率控制
cfg80211_scan_done() core.h 通知用户空间扫描完成
ieee80211_sta_update_pending_airtime() sta_info.h:148 更新 AQL 空口时间
ieee80211_rx_reorder_ampdu() rx.c:1445 AMPDU 重排序入口
ieee80211_scan_rx() scan.c:277 处理扫描收到的 Beacon/Probe Response
ieee80211_bss_info_update() scan.c:167 更新 BSS 数据库
ieee80211_start_tx_ba_session() agg-tx.c:600 发起 TX AMPDU 会话
ieee80211_stop_tx_ba_session() agg-tx.c 停止 TX AMPDU 会话
ieee80211_send_bar() agg-tx.c:103 发送 BlockAck Request
__ieee80211_stop_rx_ba_session() agg-rx.c:58 停止 RX AMPDU 会话
ieee80211_key_enable_hw_accel() key.c:120 尝试硬件加速密钥
ieee80211_pairwise_rekey() key.c:290 PTK 密钥轮换处理
ieee80211_tx_h_michael_mic_add() wpa.c:30 TKIP TX MIC 添加
ieee80211_rx_h_michael_mic_verify() wpa.c:94 TKIP RX MIC 校验
ieee80211_crypto_tkip_encrypt() wpa.c:249 TKIP 加密
ieee80211_radar_detected() util.c:3664 驱动报告雷达检测
ieee80211_dfs_cac_cancel() util.c:3581 取消 CAC
ieee80211_dfs_radar_detected_work() util.c:3620 雷达检测工作函数
ieee80211_rx_check_bss_color_collision() rx.c:3452 BSS 颜色碰撞检测
ieee80211_he_mcs_intersection() he.c:67 HE MCS 能力交集
ieee80211_new_chanctx() chan.c:879 创建新信道上下文
ieee80211_chanreq_compatible() chan.c:222 信道请求兼容性检查
ieee80211_agg_stop_txq() agg-tx.c:181 停止 TXQ(聚合建立期)
ieee80211_agg_start_txq() agg-tx.c:202 启动 TXQ(聚合完成后)

29. 小结

Linux mac80211/cfg80211 无线子系统是一个高度模块化的三层架构:

cfg80211 提供设备抽象和统一的用户接口(通过 nl80211 Netlink),维护 BSS 数据库(红黑树 + 链表)、监管域等全局状态,通过 cfg80211_ops 回调向上层框架导出能力。核心结构 cfg80211_registered_device 嵌入 struct wiphy 用于访问和反向查找。

mac80211 实现 802.11 协议的绝大部分逻辑:

  • 扫描状态机(被动/主动扫描升级,scan.c 状态机)
  • 关联/认证 MLME(ieee80211_if_managed 结构)
  • TX 路径(FQ-CoDel 软件队列 + AQL airtime fairness)
  • RX 路径(AMPDU 重排序窗口 + 解密 + 分片重组)
  • A-MPDU/A-MSDU 聚合(agg-tx.c/agg-rx.c ADDBA/DELBA 握手 + 重排序缓冲)
  • 速率控制(minstrel_ht EWMA 自适应,42 个速率分组,HT/VHT/Legacy)
  • 加密(CCMP/GCMP AES 认证加密 + TKIP Michael MIC + 硬件加速仲裁)
  • 电源管理(Dynamic PS + U-APSD + TIM/DTIM + 动态 SMPS)
  • 信道管理(chanctx 信道上下文 + DFS CAC + 雷达检测工作流)
  • Wi-Fi 6/7(HE 能力协商 + BSS Coloring + OFDMA + MLO 多链路操作)

驱动层 仅需实现硬件相关操作(DMA、PHY 配置、信道切换),通过 ieee80211_ops 接口与 mac80211 对接,通过 ieee80211_hw 标志声明硬件能力(AMPDU/PS/MFP/硬件加密等),这使 Linux 能以统一的质量支持数十种无线芯片(Intel iwlwifi、Qualcomm ath9k/ath10k/ath11k、MediaTek mt76、Realtek rtw88/rtw89 等),同时持续演进支持最新 Wi-Fi 7(802.11be)的 MLO 多链路操作和 EHT(Extremely High Throughput)特性。


由 Claude Code 分析生成