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)
獲取支持速率的長度