Android wpa_supplicant源碼分析--bss掃描結果

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)
獲取支持速率的長度
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章