Linux Wireless之WIFI扫描
Linux Wireless之WIFI扫描
Linux 下的 Wi-Fi 扫描流程横跨用户层、内核通用层与驱动层,核心路径是 应用 → nl80211 → cfg80211 → mac80211/驱动 → 硬件。
下面从最新的内核源码(7.1.0-rc6)来看看整个过程。
一、整体架构总览
WiFi 扫描在 Linux 中分为以下几层,从上到下依次为:
┌─────────────────────────────────────────────────────────────────┐
│ 用户空间 (Userspace) │
│ wpa_supplicant / iw / NetworkManager │
│ 通过 Netlink Socket 发送命令 │
└───────────────────────────┬─────────────────────────────────────┘
│ NL80211_CMD_TRIGGER_SCAN
│ NL80211_CMD_GET_SCAN
│ NL80211_CMD_START_SCHED_SCAN
▼
┌─────────────────────────────────────────────────────────────────┐
│ nl80211 (Netlink 802.11 接口层) │
│ net/wireless/nl80211.c │
│ 解析用户空间命令 → 构造扫描请求 → 调用 cfg80211 │
└───────────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ cfg80211 (无线配置子系统) │
│ net/wireless/scan.c, core.c │
│ 扫描请求管理 / BSS 数据库 / 扫描结果过期 / 6GHz 拆分扫描 │
└───────────────────────────┬─────────────────────────────────────┘
│ rdev->ops->scan()
│ (或 mac80211: ieee80211_scan)
▼
┌─────────────────────────────────────────────────────────────────┐
│ mac80211 (软 MAC 层) │
│ net/mac80211/scan.c, cfg.c │
│ 软件扫描状态机 / 硬件扫描调度 / 扫描结果收集 │
└───────────────────────────┬─────────────────────────────────────┘
│ drv_hw_scan() / drv_sw_scan_start()
▼
┌─────────────────────────────────────────────────────────────────┐
│ 硬件驱动 (Hardware Driver) │
│ iwlwifi / ath11k / mt76 / rtw89 / ... │
│ 控制射频硬件发送 Probe Request / 接收 Beacon/Probe Response │
└─────────────────────────────────────────────────────────────────┘
二、核心数据结构
2.1 扫描请求结构
┌──────────────────────────────────────────────────────────────┐
│ struct cfg80211_scan_request │
│ (include/net/cfg80211.h:2895) │
├──────────────────────────────────────────────────────────────┤
│ ssids[] / n_ssids -- 要扫描的 SSID 列表 │
│ channels[] / n_channels -- 要扫描的信道列表 │
│ ie / ie_len -- 附加的 IE 信息 │
│ flags -- 扫描标志 (随机化等) │
│ mac_addr / mac_addr_mask -- MAC 地址随机化 │
│ bssid -- 指定 BSSID 扫描 │
│ duration -- 信道驻留时间 │
│ rates[NL80211_NUM_BANDS] -- 每个频段的速率掩码 │
│ wdev / wiphy -- 关联的无线设备 │
│ scan_6ghz -- 是否包含 6GHz 信道 │
│ scan_6ghz_params[] -- 6GHz 扫描参数 │
│ no_cck -- 不使用 CCK 速率 │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ struct cfg80211_sched_scan_request │
│ (include/net/cfg80211.h:3025) │
├──────────────────────────────────────────────────────────────┤
│ reqid -- 请求 ID │
│ ssids[] / n_ssids -- 匹配的 SSID │
│ channels[] / n_channels -- 扫描信道 │
│ match_sets[] / n_match_sets-- RSSI 匹配集 │
│ scan_plans[] / n_scan_plans-- 扫描计划 (间隔+迭代次数) │
│ flags -- 标志 │
│ mac_addr / mac_addr_mask -- MAC 随机化 │
│ min_rssi_thold -- 最小 RSSI 阈值 │
│ delay -- 延迟报告 │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ struct cfg80211_bss │
│ (include/net/cfg80211.h:3174) │
├──────────────────────────────────────────────────────────────┤
│ bssid[ETH_ALEN] -- BSS 标识 │
│ channel -- 信道 │
│ ies / beacon_ies / proberesp_ies -- IE 数据 │
│ signal -- 信号强度 (mBm) │
│ beacon_interval -- Beacon 间隔 │
│ capability -- 能力字段 │
│ ts_boottime -- 引导时间戳 │
│ hidden_beacon_bss -- 隐藏 SSID 关联 │
│ transmitted_bss -- MBSSID 传输 BSS │
│ nontrans_list -- 非传输 BSS 列表 │
└──────────────────────────────────────────────────────────────┘
2.2 cfg80211 内部扫描状态
┌──────────────────────────────────────────────────────────────┐
│ struct cfg80211_registered_device (net/wireless/core.h) │
├──────────────────────────────────────────────────────────────┤
│ scan_req -- 当前活跃的扫描请求 │
│ int_scan_req -- 内部拆分扫描请求 (6GHz) │
│ scan_msg -- 延迟的扫描完成消息 │
│ scan_done_wk -- 扫描完成工作队列项 │
│ sched_scan_req_list -- 活跃的定时扫描请求链表 │
│ sched_scan_res_wk -- 定时扫描结果工作队列项 │
└──────────────────────────────────────────────────────────────┘
三、完整扫描流程
3.1 普通扫描 (Trigger Scan) 完整流程
用户空间 (wpa_supplicant / iw)
│
│ ① 发送 NL80211_CMD_TRIGGER_SCAN Netlink 消息
│ 携带: 频率列表、SSID列表、IE、扫描标志、MAC随机化参数
▼
┌───────────────────────────────────────────────────────────────┐
│ nl80211_trigger_scan() [nl80211.c:10805] │
│ ─────────────────────────────────────────────────────────── │
│ ② 解析 Netlink 属性: │
│ - NL80211_ATTR_SCAN_FREQUENCIES (信道列表) │
│ - NL80211_ATTR_SCAN_SSIDS (SSID 列表) │
│ - NL80211_ATTR_IE (附加 IE) │
│ - NL80211_ATTR_SCAN_FLAGS (扫描标志) │
│ - NL80211_ATTR_MAC / NL80211_ATTR_MAC_MASK (MAC随机化) │
│ ③ 分配 cfg80211_scan_request_int 结构 │
│ ④ 填充 channels[], ssids[], ie 数据 │
│ ⑤ rdev->scan_req = request │
└───────────────────────────┬───────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ cfg80211_scan() [scan.c:1069] │
│ ─────────────────────────────────────────────────────────── │
│ ⑥ 检查 WIPHY_FLAG_SPLIT_SCAN_6GHZ 标志: │
│ ├─ 未设置: 直接调用 rdev_scan(rdev, request) │
│ └─ 已设置 (6GHz 拆分扫描): │
│ ├─ 统计非 6GHz 信道数量 │
│ ├─ 如果只有 6GHz 信道: cfg80211_scan_6ghz() │
│ └─ 否则: 先扫描非 6GHz 信道, 完成后再扫描 6GHz │
└───────────────────────────┬───────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ rdev_scan() [rdev-ops.h:458] │
│ ─────────────────────────────────────────────────────────── │
│ ⑦ 调用 rdev->ops->scan(rdev->wiphy, &request->req) │
│ 这是驱动注册的扫描回调函数 │
│ 对于 mac80211 驱动: ieee80211_scan() │
└───────────────────────────┬───────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ ⑧ 同时发送 NL80211_CMD_TRIGGER_SCAN 多播通知 │
│ nl80211_send_scan_start() [nl80211.c:20200] │
│ → 多播到 NL80211_MCGRP_SCAN 组 │
└───────────────────────────────────────────────────────────────┘
3.2 mac80211 层扫描处理
ieee80211_scan() [cfg.c:3289]
│
│ ⑨ 验证接口类型 (STATION/ADHOC/MESH/P2P_CLIENT/P2P_DEVICE)
│ 检查 AP 模式下是否允许扫描
▼
ieee80211_request_scan() [scan.c:1196]
│
▼
__ieee80211_start_scan() [scan.c:731]
│
│ ⑩ 决定扫描方式:
│
├─── 硬件扫描 (hw_scan) ───────────────────────────────────┐
│ │ │
│ │ 分配 hw_scan_req 结构 │
│ │ 设置 SCAN_HW_SCANNING 标志 │
│ │ 调用 ieee80211_prep_hw_scan() 准备信道列表和 IE │
│ │ 调用 drv_hw_scan() → ops->hw_scan() │
│ │ 驱动接管扫描, 完成后调用 ieee80211_scan_completed() │
│ │ │
│ └─────────────────────────────────────────────────────┘
│
├─── 单信道在网扫描 (on-channel) ──────────────────────────┐
│ │ │
│ │ 设置 SCAN_ONCHANNEL_SCANNING 标志 │
│ │ 调用 drv_sw_scan_start() 通知驱动 │
│ │ 发送 Probe Request 或等待 Beacon │
│ │ │
│ └─────────────────────────────────────────────────────┘
│
└─── 软件扫描 (sw_scan) ──────────────────────────────────┐
│ │
│ 设置 SCAN_SW_SCANNING 标志 │
│ 调用 ieee80211_start_sw_scan(): │
│ - drv_sw_scan_start() 通知驱动 │
│ - 停止 off-channel VIF │
│ - 发送 NullFunc (PS bit) │
│ - 配置过滤器 │
│ - 启动 scan_work 状态机 │
│ │
└─────────────────────────────────────────────────────┘
3.3 软件扫描状态机
这是 mac80211 中最核心的扫描驱动逻辑:
ieee80211_scan_work() [scan.c:1095]
│
▼
┌─────────────────┐
│ SCAN_DECISION │ ◄──────────────────────┐
│ [scan.c:923] │ │
└────────┬────────┘ │
│ │
┌──────────────┼──────────────┐ │
│ │ │ │
▼ ▼ ▼ │
所有信道完成 需要返回运营信道 继续扫描下一信道 │
│ │ │ │
▼ ▼ ▼ │
扫描完成 ┌──────────┐ ┌───────────────┐ │
│SCAN_ │ │ SCAN_SET_ │ │
│SUSPEND │ │ CHANNEL │ │
│[.1062] │ │ [scan.c:990] │ │
└────┬─────┘ └───────┬───────┘ │
│ │ │
▼ ├──────┐ │
返回运营信道 │ │ │
(200ms) │ ▼ │
│ 被动信道 主动信道 │
│ (无IR) (有IR) │
▼ │ │ │
┌──────────┐ │ ▼ │
│SCAN_ │ │ ┌───────────────┐ │
│RESUME │ │ │ SCAN_SEND_ │ │
│[.1077] │ │ │ PROBE │ │
└────┬─────┘ │ │ [scan.c:689] │ │
│ │ └───────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ 等待被动 发送 Probe │
│ 驻留时间 Request │
│ │ │ │
└──────────────┴──────────┴────────────┘
│
▼
┌──────────────┐
│ 扫描完成 │
│ __ieee80211_ │
│ scan_completed│
└──────────────┘
各状态详细说明:
| 状态 | 函数 | 作用 |
|---|---|---|
SCAN_DECISION |
ieee80211_scan_state_decision() |
决定下一步: 完成/暂停/切信道/发Probe |
SCAN_SET_CHANNEL |
ieee80211_scan_state_set_channel() |
切换到目标信道, 设置驻留时间 |
SCAN_SEND_PROBE |
ieee80211_scan_state_send_probe() |
为每个 SSID 发送 Probe Request |
SCAN_SUSPEND |
ieee80211_scan_state_suspend() |
临时返回运营信道处理缓存帧 (200ms) |
SCAN_RESUME |
ieee80211_scan_state_resume() |
恢复扫描, 停止 VIF |
SCAN_ABORT |
- | 扫描被中止 |
3.4 扫描结果上报流程
┌───────────────────────────────────────────────────────────────┐
│ 硬件接收到 Beacon / Probe Response 帧 │
└───────────────────────────┬───────────────────────────────────┘
│
▼
ieee80211_scan_rx() [scan.c:277]
│
│ 过滤: 只处理 Probe Response / Beacon / S1G Beacon
│ 验证信道匹配
│ 检查 Probe Response 地址是否匹配扫描请求
▼
ieee80211_bss_info_update() [scan.c:168]
│
│ 填充 cfg80211_inform_bss 结构:
│ - signal (信号强度)
│ - channel (信道)
│ - boottime_ns (引导时间)
│ - parent_tsf / parent_bssid
▼
cfg80211_inform_bss_frame_data() [scan.c:3246]
│
│ 解析管理帧: BSSID, TSF, Capability, Beacon Interval, IEs
│ 判断帧类型 (Beacon / Probe Response)
▼
cfg80211_inform_bss_data() [scan.c:2253]
│
│ 验证信号强度、信道、6GHz 功率类型
│ 创建 cfg80211_bss_ies 结构
▼
__cfg80211_bss_update()
│
│ 查找已有的 BSS 条目:
│ - 找到: 更新 IE 和信号强度
│ - 未找到: 插入新的 BSS 到哈希表和链表
│
│ 处理 Multi-BSSID (MBSSID) 非传输 BSS
▼
返回 cfg80211_bss 引用
3.5 扫描完成流程
┌───────────────────────────────────────────────────────────────┐
│ 硬件扫描完成 / 软件扫描遍历完所有信道 │
└───────────────────────────┬───────────────────────────────────┘
│
▼
ieee80211_scan_completed() [scan.c:540]
│ (EXPORTED, 驱动调用)
│
│ 设置 SCAN_COMPLETED 标志 (如果中止还设置 SCAN_ABORTED)
│ 排入 scan_work 工作队列
▼
ieee80211_scan_work() 检测到完成 [scan.c:1122]
│
▼
__ieee80211_scan_completed() [scan.c:448]
│
│ ① 多频段硬件扫描: 启动下一频段扫描 (如果需要)
│ ② 释放 hw_scan_req
│ ③ 恢复功率等级和过滤器配置
│ ④ 通知 MLME: ieee80211_mlme_notify_scan_completed()
│ → 重启 STA 定时器
│ ⑤ 通知 IBSS
│ ⑥ 重新排队延迟的工作
│ ⑦ 启动下一个 ROC (Remain On Channel)
│
▼
cfg80211_scan_done() [scan.c:1183]
│ (EXPORTED, mac80211 调用)
│
│ 存储 scan_info (aborted, tsf, bssid)
│ 排入 scan_done_wk 工作队列
▼
__cfg80211_scan_done() [scan.c:1178]
│
▼
___cfg80211_scan_done() [scan.c:1107]
│
│ ① 检查是否需要 6GHz 后续扫描
│ ② 构建 Netlink 消息:
│ - 正常完成: NL80211_CMD_NEW_SCAN_RESULTS
│ - 被中止: NL80211_CMD_SCAN_ABORTED
│ ③ 处理 NL80211_SCAN_FLAG_FLUSH (清除过期 BSS)
│ ④ 发送多播到 NL80211_MCGRP_SCAN
▼
用户空间收到扫描结果通知
3.6 扫描结果查询流程
用户空间 (wpa_supplicant / iw scan)
│
│ NL80211_CMD_GET_SCAN (dumpit)
▼
nl80211_dump_scan() [nl80211.c:12092]
│
│ 首次调用: cfg80211_bss_expire() 过期旧 BSS
│
│ 遍历 rdev->bss_list:
│ └── nl80211_send_bss() [nl80211.c:11933]
│ │
│ │ 构建 NL80211_CMD_NEW_SCAN_RESULTS 消息
│ │ 包含 NL80211_ATTR_BSS 属性:
│ │ - BSSID, TSF, IEs (Probe Response + Beacon)
│ │ - Beacon Interval, Capability
│ │ - Frequency, Signal (mBm)
│ │ - Seen ms ago (上次看到时间)
│ │ - Parent TSF, Parent BSSID
│ │ - Chain Signal 等
│ ▼
│ 返回给用户空间
│
│ 分页: 多次 dump 调用返回所有 BSS 条目
▼
用户空间获取完整扫描结果
3.7 定时扫描 (Scheduled Scan) 流程
用户空间
│
│ NL80211_CMD_START_SCHED_SCAN
▼
┌───────────────────────────────────────────────────────────────┐
│ nl80211_start_sched_scan() [nl80211.c:11497] │
│ ─────────────────────────────────────────────────────────── │
│ 验证支持 (max_sched_scan_reqs, ops->sched_scan_start) │
│ cfg80211_sched_scan_req_possible() 检查资源限制 │
│ 解析: match_sets, scan_plans, channels, ssids │
└───────────────────────────┬───────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ rdev_sched_scan_start() [rdev-ops.h:888] │
│ → rdev->ops->sched_scan_start() │
└───────────────────────────┬───────────────────────────────────┘
│
┌─────────────────┴─────────────────┐
▼ ▼
mac80211 驱动 直接 cfg80211 驱动
│
▼
ieee80211_sched_scan_start() [cfg.c:3351]
│
▼
__ieee80211_request_sched_scan_start() [scan.c:1331]
│
│ 构建扫描 IE、速率掩码
│ 调用 drv_sched_scan_start() → ops->sched_scan_start()
│ 存储 sched_scan_req 和 sched_scan_sdata
▼
│
├─ cfg80211_add_sched_scan_req() 加入链表
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 驱动定期执行扫描, 发现匹配网络时上报结果 │
└───────────────────────────┬───────────────────────────────────┘
│
▼
ieee80211_sched_scan_results() [scan.c:1431]
│ (EXPORTED, 驱动调用)
▼
cfg80211_sched_scan_results() [scan.c:1302]
│
│ 设置 report_results = true
│ 排入 sched_scan_res_wk 工作队列
▼
cfg80211_sched_scan_results_wk() [scan.c:1276]
│
│ 遍历 sched_scan_req_list 中 report_results=true 的请求
│ 发送 NL80211_CMD_SCHED_SCAN_RESULTS 多播
▼
用户空间收到定时扫描结果
停止定时扫描:
NL80211_CMD_STOP_SCHED_SCAN
→ nl80211_stop_sched_scan() [nl80211.c:11550]
→ cfg80211_stop_sched_scan_req() [scan.c:1340]
→ rdev_sched_scan_stop()
→ 发送 NL80211_CMD_SCHED_SCAN_STOPPED
3.8 6GHz 拆分扫描流程
┌───────────────────────────────────────────────────────────────┐
│ 6GHz 频段扫描采用 "拆分扫描" 策略 │
│ (WIPHY_FLAG_SPLIT_SCAN_6GHZ) │
│ │
│ 原因: 6GHz 信道数量多, 与 2.4/5GHz 合并扫描耗时过长 │
└───────────────────────────┬───────────────────────────────────┘
│
▼
第一阶段: 扫描 2.4GHz + 5GHz 信道
│
│ cfg80211_scan() 中拆分出非 6GHz 信道
│ rdev_scan() 执行扫描
│
▼
扫描完成回调 ___cfg80211_scan_done()
│
│ 检测到还有 6GHz 信道未扫描
▼
cfg80211_scan_6ghz() [scan.c:841]
│
│ 从 RNR (Reduced Neighbor Report) 元素中获取:
│ - 协同 AP 的信道信息
│ - Short SSID 匹配
│ 构建 6GHz 专用扫描请求:
│ - PSC (Preferred Scanning Channels) 信道
│ - 协同 AP 关联的信道
│ - unsolicited probe 信道
│
│ rdev_scan() 执行 6GHz 扫描
▼
第二阶段完成, 发送最终扫描结果
四、关键函数索引表
4.1 nl80211 层 (net/wireless/nl80211.c)
| 行号 | 函数 | 功能 |
|---|---|---|
| 10805 | nl80211_trigger_scan() |
解析用户空间扫描请求, 启动扫描 |
| 11054 | nl80211_abort_scan() |
中止正在进行的扫描 |
| 11497 | nl80211_start_sched_scan() |
启动定时扫描 |
| 11550 | nl80211_stop_sched_scan() |
停止定时扫描 |
| 12092 | nl80211_dump_scan() |
导出扫描结果到用户空间 |
| 11933 | nl80211_send_bss() |
发送单个 BSS 条目 |
| 20200 | nl80211_send_scan_start() |
多播扫描开始通知 |
| 20219 | nl80211_build_scan_msg() |
构建扫描完成/中止消息 |
| 20239 | nl80211_send_scan_msg() |
多播扫描完成通知 |
4.2 cfg80211 层 (net/wireless/scan.c)
| 行号 | 函数 | 功能 |
|---|---|---|
| 1069 | cfg80211_scan() |
扫描入口, 处理 6GHz 拆分 |
| 1107 | ___cfg80211_scan_done() |
扫描完成核心处理 |
| 1183 | cfg80211_scan_done() |
导出函数, 驱动调用通知扫描完成 |
| 841 | cfg80211_scan_6ghz() |
6GHz 拆分扫描处理 |
| 1302 | cfg80211_sched_scan_results() |
导出函数, 定时扫描结果通知 |
| 1340 | cfg80211_stop_sched_scan_req() |
停止定时扫描请求 |
| 2253 | cfg80211_inform_single_bss_data() |
BSS 信息录入核心函数 |
| 3246 | cfg80211_inform_bss_frame_data() |
导出函数, 报告收到的 Beacon/Probe Response |
| 468 | __cfg80211_bss_expire() |
过期旧的 BSS 条目 |
4.3 mac80211 层 (net/mac80211/scan.c)
| 行号 | 函数 | 功能 |
|---|---|---|
| 731 | __ieee80211_start_scan() |
扫描启动核心: 决定 hw/sw/onchannel |
| 1095 | ieee80211_scan_work() |
软件扫描状态机主循环 |
| 923 | ieee80211_scan_state_decision() |
扫描决策状态 |
| 990 | ieee80211_scan_state_set_channel() |
设置信道状态 |
| 689 | ieee80211_scan_state_send_probe() |
发送 Probe Request 状态 |
| 448 | __ieee80211_scan_completed() |
扫描完成处理 |
| 540 | ieee80211_scan_completed() |
导出函数, 硬件扫描完成回调 |
| 277 | ieee80211_scan_rx() |
接收 Beacon/Probe Response |
| 63 | ieee80211_inform_bss() |
BSS 信息通知回调 |
| 1331 | __ieee80211_request_sched_scan_start() |
定时扫描启动 |
4.4 驱动接口
| 文件 | 回调 | 功能 |
|---|---|---|
include/net/cfg80211.h:5203 |
.scan |
cfg80211 驱动扫描回调 |
include/net/cfg80211.h:5205 |
.abort_scan |
cfg80211 驱动中止扫描回调 |
include/net/cfg80211.h:5306 |
.sched_scan_start |
cfg80211 定时扫描启动 |
include/net/cfg80211.h:5309 |
.sched_scan_stop |
cfg80211 定时扫描停止 |
include/net/mac80211.h:4749 |
.hw_scan |
mac80211 硬件扫描回调 |
include/net/mac80211.h:4751 |
.cancel_hw_scan |
mac80211 取消硬件扫描 |
include/net/mac80211.h:4753 |
.sched_scan_start |
mac80211 定时扫描启动 |
include/net/mac80211.h:4757 |
.sched_scan_stop |
mac80211 定时扫描停止 |
五、Netlink 命令与多播组
┌─────────────────────────────────────────────────────────────┐
│ Netlink 命令 │
├──────────────────────────┬──────────────────────────────────┤
│ NL80211_CMD_TRIGGER_SCAN (1469) 触发扫描 │
│ NL80211_CMD_GET_SCAN (1470) 获取扫描结果 │
│ NL80211_CMD_NEW_SCAN_RESULTS(1471) 扫描结果通知 │
│ NL80211_CMD_SCAN_ABORTED (1472) 扫描中止通知 │
│ NL80211_CMD_ABORT_SCAN (1604) 中止扫描命令 │
│ NL80211_CMD_START_SCHED_SCAN(1537) 启动定时扫描 │
│ NL80211_CMD_STOP_SCHED_SCAN (1538) 停止定时扫描 │
│ NL80211_CMD_SCHED_SCAN_RESULTS (1539) 定时扫描结果 │
│ NL80211_CMD_SCHED_SCAN_STOPPED (1540) 定时扫描停止 │
├──────────────────────────┴──────────────────────────────────┤
│ 多播组 │
├─────────────────────────────────────────────────────────────┤
│ NL80211_MCGRP_SCAN ("scan") 所有扫描事件发送到此组 │
└─────────────────────────────────────────────────────────────┘
六、扫描标志 (Scan Flags)
┌──────────────────────────────────────────────────────────────┐
│ NL80211_SCAN_FLAG_LOW_PRIORITY 低优先级扫描 │
│ NL80211_SCAN_FLAG_FLUSH 刷新旧结果 │
│ NL80211_SCAN_FLAG_AP AP 模式扫描 │
│ NL80211_SCAN_FLAG_RANDOM_ADDR 使用随机 MAC 地址 │
│ NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME 最大信道驻留时间 │
│ NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP 接受广播探测响应 │
│ NL80211_SCAN_FLAG_OOC_PROBE_OUI OOC 探测 OUI │
│ NL80211_SCAN_FLAG_MIN_PREQ_CONTENT 最小探测请求内容 │
└──────────────────────────────────────────────────────────────┘
七、关键时序图
7.1 完整扫描时序 (从触发到结果上报)
时间轴 ──────────────────────────────────────────────────────────►
用户空间 │──NL80211_CMD_TRIGGER_SCAN──►│ │
│ │ │
nl80211 │ nl80211_trigger_scan() │ │
│ cfg80211_scan() │ │
│ rdev_scan() │ │
│ nl80211_send_scan_start() │ │
│ │ │
mac80211 │ ieee80211_scan() │ │
│ __ieee80211_start_scan() │ │
│ │ │
│ ┌─ SCAN_SET_CHANNEL ──┐ │ │
│ │ 切换到信道 1 │ │ │
│ │ 等待驻留时间 │ │ │
│ │ SCAN_SEND_PROBE │ │ │
│ │ 发送 Probe Request │ │ │
│ │ 等待响应 │ │ │
│ │ SCAN_DECISION │ │ │
│ └─────────────────────┘ │ │
│ ... 重复所有信道 ... │ │
│ │ │
驱动 │ ── 收到 Beacon/Probe Resp ──►│ │
│ ieee80211_scan_rx() │ │
│ cfg80211_inform_bss_*() │ │
│ │ │
│ 扫描完成 │ │
│ ieee80211_scan_completed() │ │
│ cfg80211_scan_done() │ │
│ │ │
nl80211 │ nl80211_build_scan_msg() │ │
│ ──NL80211_CMD_NEW_SCAN_RESULTS──►│ │
│ │ │
用户空间 │ ◄── 收到扫描结果通知 ────── │ │
│ NL80211_CMD_GET_SCAN │ │
│ ◄── 获取完整 BSS 列表 ───── │ │
八、源文件索引
| 文件路径 | 层级 | 职责 |
|---|---|---|
include/uapi/linux/nl80211.h |
UAPI | Netlink 命令和属性定义 |
include/net/cfg80211.h |
cfg80211 | 数据结构、驱动操作、导出 API |
include/net/mac80211.h |
mac80211 | ieee80211_ops 定义 |
net/wireless/nl80211.c |
nl80211 | Netlink 命令处理器 |
net/wireless/scan.c |
cfg80211 | 扫描核心: 请求管理、BSS 数据库、扫描完成 |
net/wireless/core.c |
cfg80211 | 初始化、清理、定时扫描停止 |
net/wireless/core.h |
cfg80211 | 内部结构定义 |
net/wireless/rdev-ops.h |
cfg80211 | 驱动调用包装器 |
net/mac80211/scan.c |
mac80211 | 扫描状态机、hw/sw 扫描、BSS 通知 |
net/mac80211/cfg.c |
mac80211 | cfg80211 操作注册 |
net/mac80211/mlme.c |
mac80211 | MLME 扫描完成通知 |
net/mac80211/driver-ops.h |
mac80211 | 驱动调用包装器 |
九、总结
Linux 内核 WiFi 扫描流程的核心设计特点:
- 分层架构: nl80211 → cfg80211 → mac80211 → 驱动, 每层职责清晰
- 异步模型: 扫描请求异步执行, 通过工作队列和 Netlink 多播通知结果
- 双模扫描: 支持硬件扫描 (hw_scan, 驱动实现) 和软件扫描 (sw_scan, mac80211 状态机)
- 6GHz 拆分: 6GHz 信道单独扫描, 利用 RNR 和 PSC 优化扫描效率
- BSS 数据库: cfg80211 维护统一的 BSS 哈希表, 支持过期清理和 MBSSID
- 定时扫描: 支持基于扫描计划 (interval + iterations) 的后台扫描
- MAC 随机化: 支持扫描时使用随机 MAC 地址保护隐私
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 DD'Notes!
评论






