Android wpa_supplicant源码分析--bss扫描结果

本文深入探讨Wi-Fi扫描的工作原理和技术实现细节,包括被动和主动两种扫描方式、扫描结果的存储与处理流程、bss列表的维护及扫描结果的获取方法等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 扫描方式

手机扫描结果的获取有两种方式:被动和主动
1,AP隔固定时间会发送Beacon帧,Beacon帧中有AP的SSID BSSID等基本信息,手机接收到Beacon帧就认为搜索到该AP创建的网络
2,手机主动发出probe request帧,AP接收到probe request帧后会发送probe response帧,手机接收到response帧后,就认为扫描到该网络。
在手机wlan界面中,点击刷新既采用的第二种方式(当然扫描结果中也会包含部分方式1中扫描到的网络)

2 扫描结果在wap_s中的存储

为了提高处理效率,驱动在接收到相关扫描结果时,是不会对帧中的信息进行解析的,80211管理帧的构成请查阅相关资料。帧传递到wpa_s时会经过强制类型转换成struct wpa_scan_res,帧头会转换成结构体总的元素,帧的载荷会作为ie(information element信息元素)存储在紧跟结构体后面的内存中,wpa_s中有专门方法获取ie域中的相关信息。

存储驱动上报的扫描结果的结构体

struct wpa_scan_res {
    unsigned int flags;         //BSS/IBSS标记
    u8 bssid[ETH_ALEN];
    int freq;
    u16 beacon_int;             //beacon间隔
    u16 caps;                   
    int qual;
    int noise;
    int level;
    u64 tsf;                    //时间戳
                                //时间,单位ms,用于计算update time,
    unsigned int age;           //struct wpa_scan_results中存储的featch time减去age就是update time
    size_t ie_len;
    size_t beacon_ie_len;       
//紧跟的IE(information element)数据长度,例如ssid,p2p一类的
    //数据组成为type(4 u8),element长度(u8),element内容
    //在每个res结构体指针后(res+1)通过比对每个element的type类型获取
};

一个扫描结果被认为是一个bss网络,经初步解析后会转变为wpa_bss的结构体,该结构体的信息包含了一些ie中的信息,因此包含的信息更加的的详细。wpa_bss会存储在struct wpa_supplicant结构体的链表bss和bss_id中。

存储一个bss的结构体

struct wpa_bss {
    struct dl_list list;               //bssid的链表
    struct dl_list list_id;            //内部id的链表
    unsigned int id;                   //id
    unsigned int scan_miss_count;      //扫描结果中不包含该bss的次数
    unsigned int last_update_idx;      //上次扫描更新(不懂)
    unsigned int flags;                //标记BSS/IBSS (WPA_BSS_*)
    u8 bssid[ETH_ALEN];                //bssid,mac地址
    u8 hessid[ETH_ALEN];               //16进制ssid?
    u8 ssid[32];                       
    size_t ssid_len;                   //网络名,最长32个字符
    int freq;                          //网络信道
    u16 beacon_int;                    //beacon间隔,AP设置,一般为100ms
    /** Capability information field in host byte order */
    u16 caps;                          //Capability,AP设置
    int qual;                          //信号质量,怎么确定?
    int noise;                         //背景噪声
    int level;                         //信号强度 db
    u64 tsf;                           //Timestamp of last Beacon/Probe Response frame */
    struct os_reltime last_update;     //上次由Beacon or Probe Response RX更新的时间
    struct wpa_bss_anqp *anqp;         //ANQP与安全相关
    size_t ie_len;                     //Probe Response中IE域的长度
    size_t beacon_ie_len;              //Beacon IE field
};

3 bss列表初始化

每隔10秒都会刷新一下struct wpa_supplicant中的bss列表

int wpa_bss_init(struct wpa_supplicant *wpa_s)
{
    //更新对应连的链表wpa_s->bss   wpa_s->bss_id
    //使用eloop_register_timeout注册超时函数,时间为10秒
    wpa_bss_timeout{
        //通过对比当前的时间与wpa_s bss链表中每个bss存储的时间
        //删除掉超过bss_expiration_age(默认值180)秒未更新(未扫描到)的bss
        wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
        //使用eloop_register_timeout注册超时函数wpa_bss_timeout,时间为10秒
        //这样每隔10秒都会调用wpa_bss_flush_by_age更新时间
    }
}

4 wpa_s->bss链表更新

接收到扫描结果时:更新bss按如下顺序调用

wpa_bss_update_start(wpa_s);
//每次只能更新一次扫描结果
wpa_bss_update_scan_res(wpa_s, scan_res->res[i], &scan_res->fetch_time);
wpa_bss_update_end(wpa_s, info, new_scan);

在end函数中会对wpas中的bss链表做一次更新,
如果当前bss正在使用 || 包含在扫描结果中,则不作处理
如果当前bss的更新次数(last_update_idx)< 整个bss链表的更新次数(last_update_idx),说明是首次没有扫描到,那么计数器+1
如果没有扫描的次数超过了 wpa_s->conf->bss_expiration_scan_count,该值一般为2,那么会从链表中删除该bss

void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, struct os_reltime *fetch_time)
{
    //获取ie中存储的ssid,如果ssid长度大于32,直接return
    u8* ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
    //获取p2p信息,如果ssid前缀为“DIRECT-”, 是P2P listen discovery results,直接return。
    p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);

    //查询wpa_s中的bss列表是否有该bss,需要bssid和ssid全部一致
    bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
    //无,直接添加
    bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
    //添加过程
    {
        //分配内存
        bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
        //将ssid信息复制到bss中
        os_memcpy(bss->ssid, ssid, ssid_len);
        //将res后面的ie空间和beacon ie拷贝到bss后的内存
        os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
        //从ie中获取hessid
        wpa_bss_set_hessid(bss);
        //将bss添加到wpa_s对应链表中中
        dl_list_add_tail(&wpa_s->bss, &bss->list);
        dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
        //向上发送bss添加的event
        wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
    }
    //有,更新信息,主要更新信号值和后面的ie域
    bss = wpa_bss_update(wpa_s, bss, res, fetch_time);

    //将bss添加到wpa_s->last_scan_res中
}

5 获取扫描结果bss

wpa_s定义了多种方式获取bss链表中的元素,通过bssid匹配,ssid匹配,获取id等等(id的含义等再详细看看代码,目前还不清楚)

struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid)
通过bssid获取bssid结构体

struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s, const u8 *bssid)
获取最后一次更新bss。
    获取钱会与wpa_s->bssid_filter比对,如果该bss包含在filter中,则返回NULL,该filter通过上层的SET命令设置。

struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, const u8 *dev_addr)
通过P2P Device Addr 获取bss,P2P Device Addr存储处在bss指针后的ie域中。

struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
通过identifier获取bss

struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s, unsigned int idf, unsigned int idl)
给定id的最小值idf和最大值idl,获取这个范围的bss

当FMKS获取扫描结果时,就是获取的bss(wpa_s->bss)链表的元素。通常使用的命令有
‘SCAN_RESULTS’获取所有的扫描结果和“BSS RANGE=5- MASK=0x29d87”获取一部分id(>5)范围的扫描结果。

6 获取帧信息ie域

const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
通过ie的类型获取对应的ie

struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, u32 vendor_type)
通过type获取ie

const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
每一个vendor ie都有对应的type,通过每个type获取对应的ie

const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss, u32 vendor_type)
根据type从beacon ie域中获取信息

struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, u32 vendor_type)
获取beacon中的ie

7 其他方法

刷新:wpa_bss_flush(struct wpa_supplicant *wpa_s)
        除了wpa_s正在使用(连接,关联)的bss,删除掉wpa_s->bss所有的bss元素

清除bss数据:wpa_bss_deinit(struct wpa_supplicant *wpa_s)
            去掉超时函数wpa_bss_timeout
            清除不在使用的bss  wpa_bss_flush。
            获取速率
int wpa_bss_get_max_rate(const struct wpa_bss *bss)
获取maximum legacy TX rate,存储在ie域中

int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
获取支持速率的长度
<think>嗯,用户问的是这个命令执行后出现的问题。命令是wpa_supplicant -D8733bs -iwlan0 -c /etc/wpa_supplicant.conf -B,然后显示驱动8733bs不支持。我需要先分析这个命令的各个部分,然后找出为什么驱动不被支持。 首先,分解命令:-D指定驱动,这里是8733bs;-i是接口wlan0;-c是配置文件;-B是后台运行。错误信息指出驱动不支持,说明可能这个驱动名称不正确,或者系统内核没有对应的驱动模块。 接下来,用户可能需要连接Wi-Fi,但遇到了驱动问题。可能的情况包括驱动未安装、模块名称不同,或者该驱动不被wpa_supplicant支持。需要检查可用驱动,用wpa_supplicant -h看支持的驱动列表,或者检查内核模块是否存在。 另外,用户可能使用的是特定硬件,比如某些Realtek芯片,可能需要安装额外的驱动。例如,8733bs可能对应的是realtek的模块,正确的驱动名称可能是rtl8723bs或者其他变体。这时候需要用户确认硬件型号,查找正确的驱动名称,并安装相应的模块。 还有可能用户输入了错误的驱动名称,比如大小写问题或者拼写错误。需要检查是否正确输入了驱动名称,或者尝试其他可能的名称变体。 解决步骤可能包括:检查可用驱动,安装正确驱动模块,更新系统,或者使用通用的驱动选项如wext。另外,查看dmesg或系统日志可以帮助确认驱动加载时的错误信息。 总结下来,用户需要逐步排查驱动问题,确认硬件型号,安装正确的驱动,并确保wpa_supplicant支持该驱动。如果驱动确实不支持,可能需要寻找替代方案或更新软件版本。</think>让我们逐步分析并解决你遇到的问题: # 命令分解 `wpa_supplicant -D8733bs -iwlan0 -c /etc/wpa_supplicant.conf -B` 包含以下参数: 1. `-D8733bs`:指定无线网卡驱动类型 2. `-iwlan0`:指定网络接口名称 3. `-c /etc/wpa_supplicant.conf`:指定配置文件路径 4. `-B`:后台运行模式 # 错误诊断 `Unsupported driver '8733bs'` 表明: 1. 系统内核未加载 `8733bs` 驱动模块 2. wpa_supplicant 未编译支持该驱动 3. 驱动名称输入错误(常见于Realtek芯片) # 解决方案 分步排查建议: ## 第一步:确认可用驱动 执行命令查看支持的驱动列表: ```bash wpa_supplicant -h | grep drivers ``` 典型输出包含:`nl80211, wext` 等通用驱动 ## 第二步:检查内核模块 对于常见Realtek 8723/8821系列芯片: ```bash lsmod | grep -i rtl ``` 预期看到类似 `rtl8723bs` 的模块 ## 第三步:尝试替代驱动 ### 方案1:使用通用驱动 ```bash sudo wpa_supplicant -Dnl80211 -iwlan0 -c /etc/wpa_supplicant.conf -B ``` 或 ```bash sudo wpa_supplicant -Dwext -iwlan0 -c /etc/wpa_supplicant.conf -B ``` ### 方案2:安装专用驱动(Ubuntu/Debian示例) ```bash sudo apt update sudo apt install firmware-realtek sudo modprobe -r 8733bs # 先尝试卸载旧模块 sudo modprobe rtl8723bs # 加载正确模块 ``` ## 第四步:验证硬件识别 ```bash dmesg | grep -i wlan ``` 正常应显示类似: `rtl8723bs: Firmware rtlwifi/rtl8723bs_nic.bin available` # 补充说明 1. 如果使用树莓派等嵌入式设备,可能需要: ```bash sudo apt install raspberrypi-kernel-headers ``` 2. 某些旧设备需要禁用NetworkManager: ```bash sudo systemctl stop NetworkManager ``` 建议先尝试使用`nl80211`驱动,这是现代Linux系统首选的通用驱动接口。如果仍然遇到问题,可以通过`dmesg -w`命令实时查看内核日志中的驱动加载情况。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值