当前位置: 首页 > java >正文

Linux Wlan 无线网络驱动开发-scan协议全流程详解

任务

  • 下面我们将用应用层下发 iwlist wlan1 scan 来进行扫描协议(管理帧)的学习

扫描协议标准(有点枯燥)

在这里插入图片描述
如下是翻译
在这里插入图片描述

实战内容分为三个内容流程

  1. 无线驱动注册相关流程
  2. iwlist工具调用扫描发送 probe req流程
  3. probe req发出后,接收到probe rsp后的列表返回
    在这里插入图片描述

三种类型包明确
在这里插入图片描述

扫描类型

802.11 定义了两种扫描方式

  • 主动扫描(Active Scanning)
    设备发送 Probe Request 帧(探测请求),附近的 AP(接入点)收到后回复 Probe Response(探测响应)
    在这里插入图片描述

  • 被动扫描(Passive Scanning)
    设备仅监听 AP 定期广播的 Beacon 帧(信标帧),不主动发送请求
    在这里插入图片描述

那我们现在只考虑主动扫描(Active Scanning)的情况

无线wifi的扫描在不同操作系统的分布

在 Linux 和 FreeBSD 操作系统中,Wi-Fi 扫描功能 的实现归属于不同的子系统,但都遵循 IEEE 802.11 协议

  • Linux 系统中的 Wi-Fi 扫描 归属组件:cfg80211 + mac80211
  • FreeBSD 系统中的 Wi-Fi 扫描 归属组件:net80211
    在这里插入图片描述

无线wifi的扫描也算无线协议的一部分吗?

  • 对,wifi扫描(Scanning)是 IEEE 802.11无线协议的核心功能之一,属于无线网络通信的标准流程。它的主要作用是帮助无线设备(如手机、笔记本电脑)发现周围的可用网络,并获取关键参数(如信道、信号强度、安全方式等),以便后续的连接或优化

基础概念

  • 驱动路径:linux-5.10.x/drivers/net/wireless
  • 驱动包含文件夹
    在这里插入图片描述
    core驱动核心实现,实现802.11帧构造/解析、加密算法、硬件抽象接口等等
    include头文件集合,包含公共API、硬件寄存器、协议定义等等
    os_dep操作系统适配层,包含Linux设备注册(register_netdev)、ioctl命令转换等等
    phl协议硬件抽象层(PHY & MAC High Layer)等等
    platform平台相关代码包含SoC集成、电源管理、外设控制:SDIO/USB接口等等
    profiling性能分析工具
    script构建与自动化脚本
调用链

在这里插入图片描述
前面已经有了基础的观念框架,下面我们就开始实战分析

协议分析(包含驱动注册和初始化流程,但是只关注wifi扫描协议相关流程)

驱动和数据包处理函数注册

  1. 初始化 PCIe 无线网卡驱动,注册必要的内核接口和资源
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 设备探测
    在这里插入图片描述
    在这里插入图片描述
  3. 包含硬件初始化和数据包处理
    在这里插入图片描述
    在这里插入图片描述
  4. 核心接收数据包处理函数 rtw_core_rx_process
    在这里插入图片描述
  5. 对管理帧进行处理
    在这里插入图片描述
    在这里插入图片描述
  6. 管理帧(Management Frame)分发器函数 mgt_dispatcher,主要用于处理接收到的 Wi-Fi 管理帧
    在这里插入图片描述

iwlist wlan1 scan 命令下发 ioctl 流程

  1. 为设备对象分配并注册 OS 层网络设备及相关结构
    在这里插入图片描述
  2. cfg80211 资源分配
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
  3. 默认使用驱动常规的cfg80211操作集
    在这里插入图片描述
  4. 注册的ioctl扫描函数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  5. 扫描操作回调函数集合,调用到驱动扫描
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  6. 构造并发送 Probe Request(探测请求帧)
    在这里插入图片描述
  7. 发送帧
    在这里插入图片描述

_issue_probereq 的核心作用是根据扫描需求构造符合 802.11 规范的 Probe Request 帧,并通过硬件发送,我们这里对扫描协议进行详细的解释

int _issue_probereq(_adapter *padapter, const NDIS_802_11_SSID *pssid, const u8 *da, u8 ch, bool append_wps, int wait_ack)//构造并发送 Probe Request 帧
{int ret = _FAIL;struct xmit_frame		*pmgntframe;// 管理帧发送结构体(存储帧数据和属性struct pkt_attrib		*pattrib;// 帧属性(长度、序列号、速率等)unsigned char			*pframe;// 帧数据缓冲区指针struct rtw_ieee80211_hdr	*pwlanhdr;// 802.11帧头结构体unsigned short		*fctrl;unsigned char			*mac;unsigned char			bssrate[NumRates];struct xmit_priv		*pxmitpriv = &(padapter->xmitpriv);struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);struct mlme_ext_priv	*pmlmeext = &(padapter->mlmeextpriv);int	bssrate_len = 0;u8	bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
#ifdef CONFIG_RTW_CFGVENDOR_RANDOM_MAC_OUIstruct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter);
#endif#ifdef CONFIG_PROBEREQ_ADD_HT_VHT_HEu8 *p;_adapter *pri_adapter = GET_PRIMARY_ADAPTER(padapter);struct mlme_ext_priv	*pri_pmlmeext = &(pri_adapter->mlmeextpriv);struct mlme_ext_info	*pri_pmlmeinfo = &(pri_pmlmeext->mlmext_info);WLAN_BSSID_EX *pri_cur_network = &(pri_pmlmeinfo->network);u8 *ie = pri_cur_network->IEs;uint ie_len = 0;
#endif// 检查射频是否被信道等待阻塞(如DFS信道检测中,需等待完成才能发送)if (rtw_rfctl_is_tx_blocked_by_ch_waiting(adapter_to_rfctl(padapter)))//检查射频状态----如果射频处于信道等待状态(如 DFS 信道检测),则直接退出goto exit;pmgntframe = alloc_mgtxmitframe(pxmitpriv);//分配管理帧内存----分配一个管理帧(mgntframe)缓冲区,用于构造 Probe Requestif (pmgntframe == NULL)goto exit;/* update attribute */pattrib = &pmgntframe->attrib;// 获取帧属性结构体(存储长度、序列号等)if (update_mgntframe_attrib(padapter, pattrib) != _SUCCESS) {// 更新管理帧属性(如设置发送速率、优先级等)rtw_free_xmitframe(&padapter->xmitpriv, pmgntframe);goto exit;}_rtw_memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);// 初始化帧缓冲区(帧头+数据区域)pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;// 定位帧数据起始位置(跳过硬件描述符区域)pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;// 802.11帧头指针// 确定源MAC地址(正常使用适配器MAC,特殊场景下使用随机MAC)
#ifdef CONFIG_RTW_CFGVENDOR_RANDOM_MAC_OUI// 如果启用随机MAC且处于未关联状态,使用PNO(主动扫描)的随机MACif ((pwdev_priv->pno_mac_addr[0] != 0xFF)&& (MLME_IS_STA(padapter))&& (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE) == _FALSE))mac = pwdev_priv->pno_mac_addr;else
#endifmac = adapter_mac_addr(padapter);// 默认使用适配器自身MACfctrl = &(pwlanhdr->frame_ctl);*(fctrl) = 0;/*11 管理帧通常使用 “3 地址格式”:addr1(DA,目的地址)、addr2(SA,源地址)、addr3(BSSID,基本服务集标识)*/// 设置目的地址(DA)和BSSID(A3)if (da) {/*	unicast probe request frame */_rtw_memcpy(pwlanhdr->addr1, da, ETH_ALEN);// DA = 目标AP的MAC_rtw_memcpy(pwlanhdr->addr3, da, ETH_ALEN);// BSSID = 目标AP的MAC} else {/*	broadcast probe request frame */_rtw_memcpy(pwlanhdr->addr1, bc_addr, ETH_ALEN);// DA = 广播地址_rtw_memcpy(pwlanhdr->addr3, bc_addr, ETH_ALEN);// BSSID = 广播地址}_rtw_memcpy(pwlanhdr->addr2, mac, ETH_ALEN);#ifdef CONFIG_RTW_CFGVENDOR_RANDOM_MAC_OUI// 设置序列号(避免重复帧,确保AP正确处理)if ((pwdev_priv->pno_mac_addr[0] != 0xFF)&& (MLME_IS_STA(padapter))&& (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE) == _FALSE)) {
#ifdef CONFIG_RTW_DEBUGRTW_DBG("%s pno_scan_seq_num: %d\n", __func__,pwdev_priv->pno_scan_seq_num);
#endifSetSeqNum(pwlanhdr, pwdev_priv->pno_scan_seq_num);pattrib->seqnum = pwdev_priv->pno_scan_seq_num;pattrib->qos_en = 1;pwdev_priv->pno_scan_seq_num++;// 序列号自增} else
#endif{SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);// 使用MLME层的管理帧序列号pmlmeext->mgnt_seq++;// 序列号自增}set_frame_sub_type(pframe, WIFI_PROBEREQ);//将帧类型设为 Probe Request(子类型 0x04)printk("---------将帧类型设为 Probe Request(子类型 0x04)[%d][%s]------\n",__LINE__,__FUNCTION__);pframe += sizeof(struct rtw_ieee80211_hdr_3addr);pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);if (pssid && !MLME_IS_MESH(padapter))//SSID IE(标识探测的网络名称),SSID IE 是核心 IE:携带目标网络名称(SSID)时,只有对应 AP 会回应;不携带时,所有 AP 都会回应pframe = rtw_set_ie(pframe, _SSID_IE_, pssid->SsidLength, pssid->Ssid, &(pattrib->pktlen)); // 携带指定SSID(如用户输入的网络名称),用于探测特定网络elsepframe = rtw_set_ie(pframe, _SSID_IE_, 0, NULL, &(pattrib->pktlen));// 不携带SSID(长度0),用于探测所有可用网络(AP会回应自身SSID)get_rate_set(padapter, bssrate, &bssrate_len);// 获取适配器支持的速率集/*AP 需要知道 STA 支持的速率,才能选择合适的速率回应(Probe Response)*/if (bssrate_len > 8) {// 支持速率超过8个时,分“支持速率IE”和“扩展支持速率IE”发送(802.11规范限制)pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_ , 8, bssrate, &(pattrib->pktlen));pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_ , (bssrate_len - 8), (bssrate + 8), &(pattrib->pktlen));} elsepframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_ , bssrate_len , bssrate, &(pattrib->pktlen));if (ch) // 若指定了信道(如当前扫描的信道),添加DS设置IE,_DSSET_IE_(DS Parameter Set IE)携带当前信道号,AP 可确认 STA 在正确信道上pframe = rtw_set_ie(pframe, _DSSET_IE_, 1, &ch, &pattrib->pktlen);#ifdef CONFIG_RTW_MESHif (MLME_IS_MESH(padapter)) {if (pssid)pframe = rtw_set_ie_mesh_id(pframe, &pattrib->pktlen, pssid->Ssid, pssid->SsidLength);elsepframe = rtw_set_ie_mesh_id(pframe, &pattrib->pktlen, NULL, 0);}
#endifif (append_wps) {/* add wps_ie for wps2.0 */if (pmlmepriv->wps_probe_req_ie_len > 0 && pmlmepriv->wps_probe_req_ie) {_rtw_memcpy(pframe, pmlmepriv->wps_probe_req_ie, pmlmepriv->wps_probe_req_ie_len);pframe += pmlmepriv->wps_probe_req_ie_len;pattrib->pktlen += pmlmepriv->wps_probe_req_ie_len;/* pmlmepriv->wps_probe_req_ie_len = 0 ; */ /* reset to zero */}}#ifdef CONFIG_PROBEREQ_ADD_HT_VHT_HE//条件编译/* Parsing HT CAP IE */// 从主适配器获取当前网络的IE信息,添加到探测帧中p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, EID_HTCapability, &ie_len, (pri_cur_network->IELength - _BEACON_IE_OFFSET_));if (p && ie_len > 0) {pframe = rtw_set_ie(pframe, _HT_CAPABILITY_IE_, ie_len, p+2, &(pattrib->pktlen));}/* Parsing HT INFO IE */// 从主适配器获取当前网络的IE信息,添加到探测帧中p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, EID_HTInfo, &ie_len, (pri_cur_network->IELength - _BEACON_IE_OFFSET_));if (p && ie_len > 0) {pframe = rtw_set_ie(pframe, _HT_ADD_INFO_IE_, ie_len, p+2, &(pattrib->pktlen));}/* Parsing VHT CAP IE */p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, EID_VHTCapability, &ie_len, (pri_cur_network->IELength - _BEACON_IE_OFFSET_));if (p && ie_len > 0) {pframe = rtw_set_ie(pframe, EID_VHTCapability, ie_len, p+2, &(pattrib->pktlen));}/* Parsing VHT OP IE */p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, EID_VHTOperation, &ie_len, (pri_cur_network->IELength - _BEACON_IE_OFFSET_));if (p && ie_len > 0) {pframe = rtw_set_ie(pframe, EID_VHTOperation, ie_len, p+2, &(pattrib->pktlen));}/* Parsing HE CAP&OP IE */p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _HE_CAPABILITY_IE_, &ie_len, (pri_cur_network->IELength - _BEACON_IE_OFFSET_));if (p && ie_len > 0) {pframe = rtw_set_ie(pframe, _HE_CAPABILITY_IE_, ie_len, p+2, &(pattrib->pktlen));p += (ie_len+2);p = rtw_get_ie(p, _HE_CAPABILITY_IE_, &ie_len, (pri_cur_network->IELength - _BEACON_IE_OFFSET_ - (ie_len + 2)));if (p && ie_len > 0) {pframe = rtw_set_ie(pframe, _HE_CAPABILITY_IE_, ie_len, p+2, &(pattrib->pktlen));}}
#endif#ifdef CONFIG_APPEND_VENDOR_IE_ENABLE// 添加厂商自定义IE(如驱动特定的扩展信息)pattrib->pktlen += rtw_build_vendor_ie(padapter , &pframe , WIFI_PROBEREQ_VENDOR_IE_BIT);
#endifpattrib->last_txcmdsz = pattrib->pktlen;if (wait_ack){ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe);}else {dump_mgntframe(padapter, pmgntframe);// 发送管理帧的核心函数,主要功能是将构造好的管理帧(如 Probe Request、Beacon、Association Request 等)提交到硬件队列并触发发送ret = _SUCCESS;}#ifdef CTC_WIFI_DIAGctcwifi_diag_log(padapter, pwlanhdr->addr1/*da*/, 1, "Probe Request", (unsigned char *)pwlanhdr, pattrib->pktlen);
#endifexit:return ret;
}

ioctl 下发probe req后接收 probe rsp包流程

  1. 在MLME STA模式下的处理函数表中,已经注册了对probe rsp的接收函数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 将扫描到的无线网络(BSS)信息封装为 “扫描事件(Survey Event)” 并向上层报告
    在这里插入图片描述
  3. 从信标帧(Beacon)和探测请求 / 响应帧(Probe request/response frames)中收集基本服务集(BSS)信息
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  4. 将网络信息添加到扫描结果列表
    在这里插入图片描述
    在这里插入图片描述
  5. 负责维护扫描到的网络列表并处理网络信息更新
    在这里插入图片描述
  6. 当新扫描到一个无线网络(target)时,检查该网络是否已存在于队列中;若存在则更新其信息(如信号强度、IE
    元素),若不存在则添加到队列

    在这里插入图片描述
  7. 更新网络信息(信号、IE、时间戳等)到队列
    在这里插入图片描述
  8. ioctl转化模块扫描队列结果,当用户执行 iwlist wlan1 scan 等命令时,该函数会将驱动内部维护的扫描网络队列(scanned_queue)中的信息转换为用户空间可识别的格式,最终展示为用户可见的 WiFi 列表
    在这里插入图片描述
    在这里插入图片描述
  9. 获取当前遍历的网络,将网络信息转换为用户空间格式(如iwlist可解析的字符串)
    在这里插入图片描述
  10. 结果显示
    在这里插入图片描述

希望这个实例demo能够抛砖引玉,让读者能够自行探索有趣的无线网络驱动,并加入其中的开发应用

纸上得来终觉浅,绝知此事要躬行!

在这里插入图片描述

http://www.xdnf.cn/news/16168.html

相关文章:

  • QT开发---字符编码与QString和QByteArray
  • 深度分析Java内存回收机制
  • 基于深度学习的图像分类:使用EfficientNet实现高效分类
  • RocketMQ搭建及测试(Windows环境)
  • 大模型处理私有数据的核心技术
  • 【News】同为科技亮相首届气象经济博览会
  • Django Models详解:数据库模型的核心
  • 第二十七章 W55MH32 Interrupt示例
  • go语言基础教程:【1】基础语法:变量
  • 爬虫基础概念
  • 数学基础弱能学好大数据技术吗?
  • Kubernetes 集群架构和Pod创建流程
  • tcp基础协议
  • 字节的机器人模型 GR-3
  • 高可用架构模式——如何应对接口级的故障
  • uni-app支付宝小程序样式穿透失效
  • C51:用DS18B20传感器读取温度
  • 如何将拥有的域名自定义链接到我的世界服务器(Minecraft服务器)
  • 【Rust线程】Rust高并发编程之线程原理解析与应用实战
  • 【unity游戏开发入门到精通——组件篇】unity的粒子系统力场 (Particle System Force Field)实现如旋风、吸引力、风吹效果等
  • 数据库垂直拆分和水平拆分
  • 【​I2S:芯片设计中的“音频桥梁”​】
  • Android Service 全解析:从基础原理到实战优化
  • Windows11 本地安装docker Desktop 部署dify 拉取镜像报错
  • 【DataWhale】快乐学习大模型 | 202507,Task06笔记
  • 游戏装备被盗,运营商赔不赔
  • Petalinux的常用指令
  • 【Linux | 网络】应用层(HTTPS)
  • Python 程序设计讲义(7):Python 的基本数据类型——整数类型
  • Linux 或者 Ubuntu 离线安装 ollama