wpa_supplicant 扫描代码分析
WPA周期性扫描
上电后,弱sta未连接任何AP,且当wpa状态DISCONNECTED的状态, 并且保存了未被disable的AP, 则WPAS会发起周期扫描 。
// wpa状态
enum wpa_states {
WPA_DISCONNECTED,
WPA_INTERFACE_DISABLED,
WPA_INACTIVE,
WPA_SCANNING,
WPA_AUTHENTICATING,
WPA_ASSOCIATING,
WPA_ASSOCIATED,
WPA_4WAY_HANDSHAKE,
WPA_GROUP_HANDSHAKE,
WPA_COMPLETED
};
过程如下:
int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
{
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
wpa_s->prev_scan_wildcard = 0;
if (wpa_supplicant_enabled_networks(wpa_s)) {
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
interface_count = 0;
}
#ifndef ANDROID
if (!wpa_s->p2p_mgmt &&
wpa_supplicant_delayed_sched_scan(wpa_s,
interface_count % 3,
100000))
wpa_supplicant_req_scan(wpa_s, interface_count % 3,
100000);// 在这个地方,设置0.1s 后启动扫描
#endif /* ANDROID */
interface_count++;
} else
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
return 0;
}
对应日志如下:

此后驱动层上报扫描结果到应用层,处理流程如下:
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
case EVENT_SCAN_RESULTS:
if (wpa_supplicant_event_scan_results(wpa_s, data))
break; /* interface may have been removed */
}
static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0);
}
static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
int own_request, int update_only)
{
return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
}
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request)
{
int timeout_sec = wpa_s->scan_interval; // wpa_supplicant_alloc函数初始化为5s
if (wpa_supplicant_req_sched_scan(wpa_s)){
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec); // 这个地方传进来的是 wpa_s->scan_interval 默认5s
log_d("wpas_select_network_from_last_scan\n");
}
wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_NETWORK_NOT_FOUND);
}
static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
int timeout_sec, int timeout_usec)
{
wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
}
日志如下:

上面提到的wpa_s->scan_interval初始值地方如下:
// 初始化扫描间隔的地方
static struct wpa_supplicant *
wpa_supplicant_alloc(struct wpa_supplicant *parent)
{
struct wpa_supplicant *wpa_s;
wpa_s = os_zalloc(sizeof(*wpa_s));
wpa_s->scan_req = INITIAL_SCAN_REQ; // 默认扫描类型为INITIAL_SCAN_REQ
wpa_s->scan_interval = 5; // 这里设置的默认扫描时间
wpa_s->new_connection = 1;
wpa_s->sched_scanning = 0;
//······
return wpa_s;
}
这个参数可以通过下面的命令修改
wpa_cli -p /var/run/wpa_supplicant-ath2 -i ath2 scan_interval 5
# 注意:ath2 要换成自己的无线网卡
# scan_interval 5: 说明是在没有连接ap的时候,wpa自动扫描时间间隔为5 s
总结上面的流程如下:

空口抓包分析如下:

到最后发起扫描的函数为void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec),后面会重点分析。
手动发起扫描
执行扫描命令
wpa_cli -p /var/run/wpa_supplicant-ath2 -i ath2 scan
TYPE=ONLY
freq=5600 #参数个数如: freq=2412-2432,2462,5000-6000
passive=1
use_id=1
only_new=1 #Request driver to clear scan cache due to manual only_new=1 scan
scan_id=
bssid=88:ff:ff:12:40:01
wildcard_ssid=1
ssid YW ssid 5957 #解释0x59 对应字符Y 0x57 对应字符W 所以就是请求 ssid 为 YW 的ap 如果请求多个格式如下 ssid YW ssid 5957 ssid 5856 中间都是空格隔开的,这个参数在发出probe帧的时候是指定ssid的
#注意 上面的参数 按照当前顺序写 否则 有可能不生效
扫描过程分析
// 收到命令后,会在下面的地方进行解析
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
if (os_strcmp(buf, "SCAN") == 0) {
wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
}
}
static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
char *reply, int reply_size, int *reply_len)
{
// 解析scan参数
if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
scan_only = 1;
if (!wpa_s->sched_scanning && !wpa_s->scanning &&
((wpa_s->wpa_state <= WPA_SCANNING) ||
(wpa_s->wpa_state == WPA_COMPLETED)))
{
// 不是sched_scanning且没有正在scanning 且
// 状态小于WPA_SCANNING或者已经WPA_COMPLETED 的时候
// 解释:sched_scanning:如果wifi driver支持sched scan的话,
// WPAS可以使用sched scan来进行定时扫描,
// sched scan 的扫描间隔存储在 struct wpa_supplicant.sched_scan_interval 中
//
//
wpa_supplicant_req_scan(wpa_s, 0, 0);
}else if (wpa_s->sched_scanning){
// 正在sched_scanning扫描,先取消sched_scanning,然后在重新扫描
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
}else{
// 否则返回结果 FAIL-BUSY
wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
}
}
综上:需要解析wpa_supplicant_req_scan函数
wpa_supplicant_req_scan函数解析
函数原型
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { int res; //······ res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); //······ wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",sec, usec); // 通过eloop机制,注册wpa_supplicant_scan 函数,时间到会自动调用wpa_supplicant_scan函数 eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); }
wpa_supplicant_scan
wpa_supplicant_scan函数分析
分析前先了解下wpa的扫描类型
enum scan_req_type {
/**
* NORMAL_SCAN_REQ - Normal scan request
*
* This is used for scans initiated by wpa_supplicant to find an
* AP for a connection.
*/
NORMAL_SCAN_REQ, // 解释:wpa程序自己发起的扫描请求
/**
* INITIAL_SCAN_REQ - Initial scan request
*
* This is used for the first scan on an interface to force at
* least one scan to be run even if the configuration does not
* include any enabled networks.
*/
INITIAL_SCAN_REQ,// 解释:
/**
* MANUAL_SCAN_REQ - Manual scan request
*
* This is used for scans where the user request a scan or
* a specific wpa_supplicant operation (e.g., WPS) requires scan
* to be run.
*/
MANUAL_SCAN_REQ // 解释:用户通过wpa_cli 命令发起的扫描请求
} scan_req, last_scan_req;
static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_ssid *ssid;
int ret, p2p_in_prog;
struct wpabuf *extra_ie = NULL;
struct wpa_driver_scan_params params;
struct wpa_driver_scan_params *scan_params;
size_t max_ssids;
int connect_without_scan = 0;
wpa_s->ignore_post_flush_scan_res = 0;
// 如果接口禁能,直接返回:很好理解 比如ath0 没有up起来
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
return;
}
//当前wifi为断开状态,并且是wpa_s自主发起的扫描,则直接设置wpa_s状态为断开,不再扫描,
//直接返回
if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
}
//如果已经在扫描,则延后1秒再发起扫描
if (wpa_s->scanning) {
/*
* If we are already in scanning state, we shall reschedule the
* the incoming scan request.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
wpa_supplicant_req_scan(wpa_s, 1, 0);
return;
}
//conf文件中没有使能可用的网络,并且是wpa_s自主发起的扫描,则直接返回,
//并设置状 WPA_INACTIVE
if (!wpa_supplicant_enabled_networks(wpa_s) &&
wpa_s->scan_req == NORMAL_SCAN_REQ) {
wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
return;
}
// wpa_s->conf->ap_scan 默认1
// 如果采用有线认证 这里不支持 下面这个不用管
if (wpa_s->conf->ap_scan != 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
"overriding ap_scan configuration");
wpa_s->conf->ap_scan = 0;
wpas_notify_ap_scan_changed(wpa_s);
}
//1---wpas负责扫描和选择网络 默认1
//0、2---驱动负责扫描和连接 int ap_scan;
if (wpa_s->conf->ap_scan == 0) {
wpa_supplicant_gen_assoc_event(wpa_s);
return;
}
ssid = NULL;
// 扫描模式不是MANUAL模式,即wpa_s 自己发起的扫描
// 且 wpa_s->connect_without_scan 不为空的时候,p2p和mesh模式下不为空
// 这里不关心
//
if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
wpa_s->connect_without_scan) {
connect_without_scan = 1;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == wpa_s->connect_without_scan)
break;
}
}
// 下面也是p2p先不考虑
p2p_in_prog = wpas_p2p_in_progress(wpa_s);
if (p2p_in_prog && p2p_in_prog != 2 &&
(!ssid ||
(ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
wpa_supplicant_req_scan(wpa_s, 5, 0);
return;
}
/*
* Don't cancel the scan based on ongoing PNO; defer it. Some scans are
* used for changing modes inside wpa_supplicant (roaming,
* auto-reconnect, etc). Discarding the scan might hurt these processes.
* The normal use case for PNO is to suspend the host immediately after
* starting PNO, so the periodic 100 ms attempts to run the scan do not
* normally happen in practice multiple times, i.e., this is simply
* restarting scanning once the host is woken up and PNO stopped.
*/
// 下面是安卓那种pno 计划扫描模式,这里也不考虑
if (wpa_s->pno || wpa_s->pno_sched_pending) {
wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress");
wpa_supplicant_req_scan(wpa_s, 0, 100000);
return;
}
//wpa_s->conf->ap_scan=1 这里走else分支
if (wpa_s->conf->ap_scan == 2)
max_ssids = 1;
else {
// 对max_ssids 进行限幅
max_ssids = wpa_s->max_scan_ssids;
if (max_ssids > WPAS_MAX_SCAN_SSIDS)
max_ssids = WPAS_MAX_SCAN_SSIDS;
}
// 保存上次的扫描模式
wpa_s->last_scan_req = wpa_s->scan_req;
// 把当前扫描模式切换为 NORMAL_SCAN_REQ
wpa_s->scan_req = NORMAL_SCAN_REQ;
//如果该项不为空,则直接关联,跳过扫描步骤,
// 从上面的结果分析connect_without_scan=0,在sta模式中没有这一项,p2p中可能会用到,这里
// 也不考虑
if (connect_without_scan) {
wpa_s->connect_without_scan = NULL;
if (ssid) {
wpa_printf(MSG_DEBUG, "Start a pre-selected network "
"without scan step");
wpa_supplicant_associate(wpa_s, NULL, ssid);
return;
}
}
//params参数中保存扫描用到的参数
os_memset(¶ms, 0, sizeof(params));
// 保存上次的wpa_s->wpa_state
wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
//下面修改状态,为 WPA_SCANNING
if (wpa_s->wpa_state == WPA_DISCONNECTED ||
wpa_s->wpa_state == WPA_INACTIVE)
wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
/*
* If autoscan has set its own scanning parameters
*/
//如果设置了autosan, 直接扫描 这里为空
if (wpa_s->autoscan_params != NULL) {
scan_params = wpa_s->autoscan_params;
goto scan;
}
//这里设置特殊的ssid 扫描的,
// 如果扫描参数为 wpa_cli scan ssid 5957 解释0x59 对应字符Y 0x57 对应字符W
// 所以就是请求 ssid 为 YW 的ap
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_set_ssids_from_scan_req(wpa_s, ¶ms, max_ssids)) {
wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
goto ssid_list_set;
}
/* Find the starting point from which to continue scanning */
//找到最开始的ssid
ssid = wpa_s->conf->ssid;
if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
while (ssid) {
if (ssid == wpa_s->prev_scan_ssid) {
ssid = ssid->next;
break;
}
ssid = ssid->next;
}
}
if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
#ifdef CONFIG_AP
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
wpa_s->conf->ap_scan == 2) {
wpa_s->connect_without_scan = NULL;
wpa_s->prev_scan_wildcard = 0;
wpa_supplicant_assoc_try(wpa_s, ssid);
return;
} else if (wpa_s->conf->ap_scan == 2) {
/*
* User-initiated scan request in ap_scan == 2; scan with
* wildcard SSID.
*/
ssid = NULL;
} else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
/*
* Perform single-channel single-SSID scan for
* reassociate-to-same-BSS operation.
*/
/* Setup SSID */
// 由于wpa_s->conf->ap_scan=1
// wpa_s->reattach不为空 说明wpa_cli reattach 重新关联同一个bss,
// wpa_s->current_ssid不为空 说明当前连接过ap
ssid = wpa_s->current_ssid;
wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
ssid->ssid, ssid->ssid_len);
params.ssids[0].ssid = ssid->ssid;
params.ssids[0].ssid_len = ssid->ssid_len;
params.num_ssids = 1;
/*
* Allocate memory for frequency array, allocate one extra
* slot for the zero-terminator.
*/
params.freqs = os_malloc(sizeof(int) * 2);
if (params.freqs) {
params.freqs[0] = wpa_s->assoc_freq;
params.freqs[1] = 0;
}
/*
* Reset the reattach flag so that we fall back to full scan if
* this scan fails.
*/
wpa_s->reattach = 0;
} else {
// 由于wpa_s->conf->ap_scan=1
// wpa_s->reattach 为空
// wpa_s->current_ssid 空 说明当前没有连接过ap
// 即没有连接过ap 走这个分支
struct wpa_ssid *start = ssid, *tssid;
int freqs_set = 0;
if (ssid == NULL && max_ssids > 1)
ssid = wpa_s->conf->ssid;
while (ssid) {
/*
* 有一些AP被设置为hidden ssid,即它不响应wildcard ssid扫描的Probe Request
* 同时,自己发送的Beacon帧也不携带ssid信息
* 这样,只有知道ssid的STA才能和这些AP连接上,其安全性略有提高
* scan_ssid 就是用来判断此无线网络是否需要指明ssid
* 这里默认为0
*/
if (!wpas_network_disabled(wpa_s, ssid) &&
ssid->scan_ssid) {
wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
ssid->ssid, ssid->ssid_len);
params.ssids[params.num_ssids].ssid =
ssid->ssid;
params.ssids[params.num_ssids].ssid_len =
ssid->ssid_len;
params.num_ssids++;
if (params.num_ssids + 1 >= max_ssids)
break;
}
if (!wpas_network_disabled(wpa_s, ssid)) {
/*
* Also add the SSID of the OWE BSS, to allow
* discovery of transition mode APs more
* quickly.
*/
// OWE相关 这里不做解释
wpa_add_owe_scan_ssid(wpa_s, ¶ms, ssid,
max_ssids);
}
ssid = ssid->next;
if (ssid == start)
break;
if (ssid == NULL && max_ssids > 1 &&
start != wpa_s->conf->ssid)
ssid = wpa_s->conf->ssid;
}
if (wpa_s->scan_id_count &&
wpa_s->last_scan_req == MANUAL_SCAN_REQ)
wpa_set_scan_ssids(wpa_s, ¶ms, max_ssids);
/*
处理扫描时的频率选择
如果已经知道目标无线网络的工作信道,可以直接设定频率参数以优化扫描过程
否则,无线网卡将尝试在各个信道上搜索无线网络
*/
for (tssid = wpa_s->conf->ssid;
wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
tssid = tssid->next) {
if (wpas_network_disabled(wpa_s, tssid))
continue;
if (((params.freqs || !freqs_set) &&
tssid->scan_freq) &&
int_array_len(params.freqs) < 100) {
int_array_concat(¶ms.freqs,
tssid->scan_freq);
} else {
os_free(params.freqs);
params.freqs = NULL;
}
freqs_set = 1;
}
//对所有频率参数进行升序排序
int_array_sort_unique(params.freqs);
}
if (ssid && max_ssids == 1) {
/*
* If the driver is limited to 1 SSID at a time interleave
* wildcard SSID scans with specific SSID scans to avoid
* waiting a long time for a wildcard scan.
*/
if (!wpa_s->prev_scan_wildcard) {
params.ssids[0].ssid = NULL;
params.ssids[0].ssid_len = 0;
wpa_s->prev_scan_wildcard = 1;
wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
"wildcard SSID (Interleave with specific)");
} else {
wpa_s->prev_scan_ssid = ssid;
wpa_s->prev_scan_wildcard = 0;
wpa_dbg(wpa_s, MSG_DEBUG,
"Starting AP scan for specific SSID: %s",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
}
} else if (ssid) {
/* max_ssids > 1 */
wpa_s->prev_scan_ssid = ssid;
wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
"the scan request");
params.num_ssids++;
} else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_scan_passive && params.num_ssids == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
} else if (wpa_s->conf->passive_scan) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Use passive scan based on configuration");
} else {
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
params.num_ssids++;
wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
"SSID");
}
ssid_list_set:
//wps和p2p会用到指定扫描的频率 //对频率参数进行修改,和P2P以及WPS有关
wpa_supplicant_optimize_freqs(wpa_s, ¶ms);
//是否携带附件的IE信息,主要用在WPS等情况
extra_ie = wpa_supplicant_extra_ies(wpa_s);
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_scan_only_new) {
wpa_printf(MSG_DEBUG,
"Request driver to clear scan cache due to manual only_new=1 scan");
params.only_new_results = 1;
}
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
wpa_s->manual_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
params.freqs = wpa_s->manual_scan_freqs;
wpa_s->manual_scan_freqs = NULL;
}
if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Limit select_network scan to specified channels");
params.freqs = wpa_s->select_network_scan_freqs;
wpa_s->select_network_scan_freqs = NULL;
}
if (params.freqs == NULL && wpa_s->next_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
"generated frequency list");
params.freqs = wpa_s->next_scan_freqs;
} else
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
wpa_setband_scan_freqs(wpa_s, ¶ms);
/* See if user specified frequencies. If so, scan only those. */
if (wpa_s->conf->freq_list && !params.freqs) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Optimize scan based on conf->freq_list");
int_array_concat(¶ms.freqs, wpa_s->conf->freq_list);
}
/* Use current associated channel? */
if (wpa_s->conf->scan_cur_freq && !params.freqs) {
unsigned int num = wpa_s->num_multichan_concurrent;
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
num = get_shared_radio_freqs(wpa_s, params.freqs, num);
if (num > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
"current operating channels since "
"scan_cur_freq is enabled");
} else {
os_free(params.freqs);
params.freqs = NULL;
}
}
}
#ifdef CONFIG_MBO
if (wpa_s->enable_oce & OCE_STA)
params.oce_scan = 1;
#endif /* CONFIG_MBO */
//如果conf中定义了filter_bssid使能,那么扫描结果中只包含conf中已有的ssid
/*
scan请求可以设置一个过滤条件
扫描完毕后,driver wrapper会过滤掉那些不符合条件的无线网络
filter_ssid用来保存那些不能被过滤的无线网络ssid
即扫描到的无线网络不在filter_ssids中,它将被过滤掉
*/
params.filter_ssids = wpa_supplicant_build_filter_ssids(
wpa_s->conf, ¶ms.num_filter_ssids);
if (extra_ie) {
params.extra_ies = wpabuf_head(extra_ie);
params.extra_ies_len = wpabuf_len(extra_ie);
}
#ifdef CONFIG_P2P
if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
(wpa_s->show_group_started && wpa_s->go_params)) {
/*
* The interface may not yet be in P2P mode, so we have to
* explicitly request P2P probe to disable CCK rates.
*/
params.p2p_probe = 1;
}
#endif /* CONFIG_P2P */
if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
wpa_s->wpa_state <= WPA_SCANNING)
wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_scan);
if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
struct wpa_bss *bss;
params.bssid = wpa_s->next_scan_bssid;
bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid);
if (!wpa_s->next_scan_bssid_wildcard_ssid &&
bss && bss->ssid_len && params.num_ssids == 1 &&
params.ssids[0].ssid_len == 0) {
params.ssids[0].ssid = bss->ssid;
params.ssids[0].ssid_len = bss->ssid_len;
wpa_dbg(wpa_s, MSG_DEBUG,
"Scan a previously specified BSSID " MACSTR
" and SSID %s",
MAC2STR(params.bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len));
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"Scan a previously specified BSSID " MACSTR,
MAC2STR(params.bssid));
}
}
scan_params = ¶ms;
scan:
#ifdef CONFIG_P2P
/*
* If the driver does not support multi-channel concurrency and a
* virtual interface that shares the same radio with the wpa_s interface
* is operating there may not be need to scan other channels apart from
* the current operating channel on the other virtual interface. Filter
* out other channels in case we are trying to find a connection for a
* station interface when we are not configured to prefer station
* connection and a concurrent operation is already in process.
*/
if (wpa_s->scan_for_connection &&
wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
!scan_params->freqs && !params.freqs &&
wpas_is_p2p_prioritized(wpa_s) &&
wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
non_p2p_network_enabled(wpa_s)) {
unsigned int num = wpa_s->num_multichan_concurrent;
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
num = get_shared_radio_freqs(wpa_s, params.freqs, num);
if (num > 0 && num == wpa_s->num_multichan_concurrent) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
} else {
os_free(params.freqs);
params.freqs = NULL;
}
}
}
#endif /* CONFIG_P2P */
//向driver wrapper发起scan请求,调用driver wrapper的scan2函数
// 下面重点分析该函数
ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
!wpa_s->manual_scan_freqs) {
/* Restore manual_scan_freqs for the next attempt */
wpa_s->manual_scan_freqs = params.freqs;
params.freqs = NULL;
}
wpabuf_free(extra_ie);
os_free(params.freqs);
os_free(params.filter_ssids);
os_free(params.mac_addr);
if (ret) {
wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
wpa_supplicant_set_state(wpa_s,
wpa_s->scan_prev_wpa_state);
/* Restore scan_req since we will try to scan again */
wpa_s->scan_req = wpa_s->last_scan_req;
wpa_supplicant_req_scan(wpa_s, 1, 0);
} else {
wpa_s->scan_for_connection = 0;
#ifdef CONFIG_INTERWORKING
wpa_s->interworking_fast_assoc_tried = 0;
#endif /* CONFIG_INTERWORKING */
wpa_s->next_scan_bssid_wildcard_ssid = 0;
if (params.bssid)
os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
}
}
接下来分析wpa_supplicant_trigger_scan函数
wpa_supplicant_trigger_scan
wpa_supplicant_trigger_scan函数分析int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { struct wpa_driver_scan_params *ctx; if (wpa_s->scan_work) { wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending"); return -1; } ctx = wpa_scan_clone_params(params); if (!ctx || //把wpas_trigger_scan_cb 添加到工作队列 radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) { wpa_scan_free_params(ctx); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); return -1; } return 0; }
wpas_trigger_scan_cb函数分析
static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; struct wpa_driver_scan_params *params = work->ctx; int ret; if (deinit) { if (!work->started) { wpa_scan_free_params(params); return; } wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); wpa_s->scan_work = NULL; return; } if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) && wpa_s->wpa_state <= WPA_SCANNING) wpa_setup_mac_addr_rand_params(params, wpa_s->mac_addr_scan); if (wpas_update_random_addr_disassoc(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to assign random MAC address for a scan"); wpa_scan_free_params(params); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); radio_work_done(work); return; } wpa_supplicant_notify_scanning(wpa_s, 1); if (wpa_s->clear_driver_scan_cache) { wpa_printf(MSG_DEBUG, "Request driver to clear scan cache due to local BSS flush"); params->only_new_results = 1; } // 调用驱动层,实现扫描 下面重点分析该函数 ret = wpa_drv_scan(wpa_s, params); /* * Store the obtained vendor scan cookie (if any) in wpa_s context. * The current design is to allow only one scan request on each * interface, hence having this scan cookie stored in wpa_s context is * fine for now. * * Revisit this logic if concurrent scan operations per interface * is supported. */ if (ret == 0) wpa_s->curr_scan_cookie = params->scan_cookie; wpa_scan_free_params(params); work->ctx = NULL; if (ret) { int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ && !wpa_s->beacon_rep_data.token; if (wpa_s->disconnected) retry = 0; wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); if (wpa_s->wpa_state == WPA_SCANNING) wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s", ret, retry ? " retry=1" : ""); radio_work_done(work); if (retry) { /* Restore scan_req since we will try to scan again */ wpa_s->scan_req = wpa_s->last_scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); } else if (wpa_s->scan_res_handler) { /* Clear the scan_res_handler */ wpa_s->scan_res_handler = NULL; } if (wpa_s->beacon_rep_data.token) wpas_rrm_refuse_request(wpa_s); return; } os_get_reltime(&wpa_s->scan_trigger_time); wpa_s->scan_runs++; wpa_s->normal_scans++; wpa_s->own_scan_requested = 1; wpa_s->clear_driver_scan_cache = 0; wpa_s->scan_work = work; }
下面分析wpa_drv_scan函数
wpa_drv_scan
wpa_drv_scan函数分析
static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER) return -EBUSY; #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->driver->scan2) return wpa_s->driver->scan2(wpa_s->drv_priv, params); return -1; }
这里的wpa_s->driver->scan2 对应的为driver_nl80211_scan2函数
const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", .get_bssid = wpa_driver_nl80211_get_bssid, .get_ssid = wpa_driver_nl80211_get_ssid, .set_key = driver_nl80211_set_key, .scan2 = driver_nl80211_scan2, // 这里初始化 };
下面需要driver_nl80211_scan2函数分析
driver_nl80211_scan2
driver_nl80211_scan2
static int driver_nl80211_scan2(void *priv, struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; return wpa_driver_nl80211_scan(bss, params); }
wpa_driver_nl80211_scan
wpa_driver_nl80211_scan函数分析,发送NL80211_CMD_TRIGGER_SCAN的消息到内核
int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params) { //i802_bss是driver wrapper的上下文信息 //获取wpa_driver_nl80211_data对象 struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1, timeout; struct nl_msg *msg = NULL; wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; if (TEST_FAIL()) return -1; // 生成nl消息 msg 命令为 NL80211_CMD_TRIGGER_SCAN 非常重要 msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params); if (!msg) return -1; if (params->p2p_probe) { struct nlattr *rates; wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); if (rates == NULL) goto fail; /* * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates * by masking out everything else apart from the OFDM rates 6, * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz * rates are left enabled. */ if (nla_put(msg, NL80211_BAND_2GHZ, 8, "\x0c\x12\x18\x24\x30\x48\x60\x6c")) goto fail; nla_nest_end(msg, rates); if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) goto fail; } if (params->bssid) { wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: " MACSTR, MAC2STR(params->bssid)); if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) goto fail; } // 通过发送nl消息,扫描返回的event 接收后在处理 ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); if (drv->hostapd && is_ap_interface(drv->nlmode)) { enum nl80211_iftype old_mode = drv->nlmode; /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. */ if (wpa_driver_nl80211_set_mode( bss, NL80211_IFTYPE_STATION)) goto fail; if (wpa_driver_nl80211_scan(bss, params)) { wpa_driver_nl80211_set_mode(bss, old_mode); goto fail; } /* Restore AP mode when processing scan results */ drv->ap_scan_as_station = old_mode; ret = 0; } else goto fail; } drv->scan_state = SCAN_REQUESTED; /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ timeout = 10; /* 一般情况下,driver扫描完成后需要通知WPAS一个scan complte事件 如果驱动不通知,WPAS会自己去查询driver以获取扫描到的无线网络信息 WPAS通过scan_complete_events变量来判断driver是否会通知此事件的 */ // 如果驱动不支持上报扫描结果的event 那么注册超时10秒,如果支持,注册超时30秒, // 利用wpa_driver_nl80211_scan_timeout读取扫描结果 if (drv->scan_complete_events) { /* * The driver seems to deliver events to notify when scan is * complete, so use longer timeout to avoid race conditions * with scanning and following association request. */ timeout = 30; } wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " "seconds", ret, timeout); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, drv, drv->ctx); //最终会调用wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);接收扫描结果 drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN; fail: nlmsg_free(msg); return ret; }

发送NL80211_CMD_TRIGGER_SCAN消息后就到了驱动层
qca/src/linux-4.4/net/wireless/nl80211.c 文件下的NL80211_CMD_TRIGGER_SCAN对应的函数nl80211_trigger_scan
static const struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_TRIGGER_SCAN, .doit = nl80211_trigger_scan, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, }
下面就属于驱动层内容了
关于扫描耗时计算
wpa_s中关于扫描统计了两个耗时
下发驱动扫描命令到驱动执行扫描的时间
//正式运行radio work时间 radio_start_next_work() os_get_reltime(&now); work->cb(work, 0); wpas_trigger_scan_cb() os_get_reltime(&wpa_s->scan_trigger_time); //下发驱动扫描后,接收到driver EVENT_SCAN_STARTED(这个event就是为了输出log,没有什么实际的操作) //计算开始扫描时间 os_get_reltime(&wpa_s->scan_start_time); //输出socket调用时间 os_reltime_sub(&wpa_s->scan_start_time, &wpa_s->scan_trigger_time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", diff.sec, diff.usec);驱动执行扫描的耗时
//计算dirver发送上EVENT_SCAN_STARTED 和 EVENT_SCAN_RESULTS 耗时 os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",diff.sec, diff.usec);
关于扫描结果处理
前面介绍了扫描请求中,driver_nl80211发送了 NL80211_CMD_TRIGGER_SCAN 命令以通知它开始扫描周围的无线网络,当驱动层完成此任务后,它将向wpa_driver_nl80211_init_nl_global函数中注册的三个netlink组播之一的scan组播地址发送结果
现在来看do_process_drv_enevt中和扫描结果相关的代码
static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int external_scan_event = 0;
struct nlattr *frame = tb[NL80211_ATTR_FRAME];
//ap_scan_as_station和hostapd有关
if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
(cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
cmd == NL80211_CMD_SCAN_ABORTED)) {
wpa_driver_nl80211_set_mode(drv->first_bss,
drv->ap_scan_as_station);
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
}
switch (cmd) {
//dirver wrapper发送NL80211_CMD_TRIGGER_SCAN命令之后,wlan driver也会回复同名的一个消息
case NL80211_CMD_TRIGGER_SCAN:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
drv->scan_state = SCAN_STARTED;
if (drv->scan_for_auth) {
/*
* Cannot indicate EVENT_SCAN_STARTED here since we skip
* EVENT_SCAN_RESULTS in scan_for_auth case and the
* upper layer implementation could get confused about
* scanning state.
*/
wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
break;
}
wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
break;
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New scan results available");
if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
drv->scan_state = SCAN_COMPLETED;
//收到来自driver的scan回复
drv->scan_complete_events = 1;
if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
//取消超时任务
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
drv, drv->ctx);
drv->last_scan_cmd = 0;
} else {
external_scan_event = 1;
}
//用来处理和发送与scan相关的driver event
send_scan_event(drv, 0, tb, external_scan_event);
break;
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
break;
}
}
send_scan_event 将解析来自 wlan driver 的扫描完毕事件,然后再向WPAS发送driver event
下面看下send_scan_event 函数
static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
struct nlattr *tb[], int external_scan)
{
//代表driver event的数据结构
//是一个联合体,包含assoc_info、auth_info、scan_info等内容
union wpa_event_data event;
struct nlattr *nl;
int rem;
//扫描信息,包含频率数组以及wpa_driver_scan_ssid等数组
//scan_info不是扫描结果
struct scan_info *info;
#define MAX_REPORT_FREQS 50
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
/*
scan_for_auth变量和wlan driver有关,它描述了如下的应用场景
当WPAS发起认证(authentication)操作时,它将向driver发送 NL80211_CMD_AUTHENTICATE 命令
driver可能因为某种原因(超时等),其内部存储的目标BSS(代表目标无线网络)信息失效
这时,它将返回 ENOENT 给WPAS
正常情况下,WPAS应该重新扫描
但为了加快这个流程,WPAS可以单独扫描目标无线网络(因为WPAS还是保存了目标无线网络的信息,例如频道等),然后再发起认证操作
从笔者角度来看,该场景对应的问题是wlan driver没有目标BSS信息,而WPAS有目标BSS信息
WPAS和wlan driver是两个不同模块,二者的信息并不能总是保持一致
既然WPAS有目标BSS信息,那么可以通过更加快捷的方法让wlan driver也得到这个信息(通过在指定频道到扫描目标BSS)
从而加快整个流程(从WPAS角度来看,用户加入无线网络的流程已经到了Authentication阶段
此时再退回到重新扫描的阶段实在有些麻烦
*/
if (!external_scan && drv->scan_for_auth) {
drv->scan_for_auth = 0;
wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
"cfg80211 BSS entry");
wpa_driver_nl80211_authenticate_retry(drv);
return;
}
os_memset(&event, 0, sizeof(event));
info = &event.scan_info;
info->aborted = aborted;
info->external_scan = external_scan;
info->nl_scan_event = 1;
//遍历NL80211_ATTR_SCAN_SSIDS属性
if (tb[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
struct wpa_driver_scan_ssid *s =
&info->ssids[info->num_ssids];
s->ssid = nla_data(nl);
s->ssid_len = nla_len(nl);
wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'",
wpa_ssid_txt(s->ssid, s->ssid_len));
info->num_ssids++;
if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
break;
}
}
//获取netlink消息中的频率信息
if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
char msg[300], *pos, *end;
int res;
pos = msg;
end = pos + sizeof(msg);
*pos = '\0';
nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
{
freqs[num_freqs] = nla_get_u32(nl);
res = os_snprintf(pos, end - pos, " %d",
freqs[num_freqs]);
if (!os_snprintf_error(end - pos, res))
pos += res;
num_freqs++;
if (num_freqs == MAX_REPORT_FREQS - 1)
break;
}
info->freqs = freqs;
info->num_freqs = num_freqs;
wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
msg);
}
if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] &&
tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) {
info->scan_start_tsf =
nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]);
os_memcpy(info->scan_start_tsf_bssid,
nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]),
ETH_ALEN);
}
//wpa_supplicant_event被driver event模块用来发送driver事件给WPAS
wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
}
scan_info定义于联合体wpa_event_data中,包含了本次扫描请求扫描了哪些SSID、对应的频率等
scan_info不是扫描后得到的结果,而是代表驱动处理scan请求时的一些处理信息,对于那些不支持通知scan complete事件的driver而言,scan_info就没法获得。
下面分析wpa_supplicant_event函数
wpa_supplicant_event(wpa_supplicant *wpa_s, EVENT_SCAN_RESULTS, NULL);
{
case EVENT_SCAN_RESULTS:
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_s->scan_res_handler = NULL;
wpa_s->own_scan_running = 0;
wpa_s->radio->external_scan_running = 0;
wpa_s->last_scan_req = NORMAL_SCAN_REQ;
break;
}
if (!(data && data->scan_info.external_scan) &&
os_reltime_initialized(&wpa_s->scan_start_time)) {
struct os_reltime now, diff;
os_get_reltime(&now);
//计算dirver发送上EVENT_SCAN_STARTED 和 EVENT_SCAN_RESULTS 耗时
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
wpa_s->scan_start_time.sec = 0;
wpa_s->scan_start_time.usec = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
diff.sec, diff.usec);
}
//获取扫描结果, 如果返回值为0,则成功获取到扫描结果
if (wpa_supplicant_event_scan_results(wpa_s, data))
break; /* interface may have been removed */
if (!(data && data->scan_info.external_scan))
wpa_s->own_scan_running = 0;
if (data && data->scan_info.nl_scan_event)
wpa_s->radio->external_scan_running = 0;
radio_work_check_next(wpa_s);
break;
}
下面分析wpa_supplicant_event_scan_results
//获取扫描结果, 如果返回值为0,则成功获取到扫描结果
static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
struct wpa_supplicant *ifs;
int res;
res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0);
if (res == 2) {
/*
* Interface may have been removed, so must not dereference
* wpa_s after this.
*/
return 1;
}
if (res < 0) {
/*
* If no scan results could be fetched, then no need to
* notify those interfaces that did not actually request
* this scan. Similarly, if scan results started a new operation on this
* interface, do not notify other interfaces to avoid concurrent
* operations during a connection attempt.
*/
return 0;
}
/*
* Check other interfaces to see if they share the same radio. If
* so, they get updated with this same scan info.
*/
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
if (ifs != wpa_s) {
wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
"sibling", ifs->ifname);
res = _wpa_supplicant_event_scan_results(ifs, data, 0,
res > 0);
if (res < 0)
return 0;
}
}
return 0;
}
/*
* Return a negative value if no scan results could be fetched or if scan
* results should not be shared with other virtual interfaces.
* Return 0 if scan results were fetched and may be shared with other
* interfaces.
* Return 1 if scan results may be shared with other virtual interfaces but may
* not trigger any operations.
* Return 2 if the interface was removed and cannot be used.
*/
static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
int own_request, int update_only)
{
//从驱动获取扫描结果,
struct wpa_scan_results *scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1);
{
//从驱动中获取结果
scan_res = wpa_drv_get_scan_results2(wpa_s);
wpa_driver_nl80211_get_scan_results(void *priv);
{
//生成msg 并从驱动中获取,bss_info_handler 为处理结果的函数
nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
}
//检查wpa_s->bssid_filter,如果不为空则进行filter
filter_scan_res(wpa_s, scan_res);
//进行一次排序
qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), compar);
//for 更新每一个扫描结果,存储到wpa_s bbs链表和 bssid中
wpa_bss_update_scan_res(wpa_s, scan_res->res[i], &scan_res->fetch_time);
}
// 扫描结果为空的情况下处理情况
if (scan_res == NULL) {
if (wpa_s->conf->ap_scan == 2 || ap ||
wpa_s->scan_res_handler == scan_only_handler)
return -1;
if (!own_request)
return -1;
if (data && data->scan_info.external_scan)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
"scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
ret = -1;
goto scan_work_done;
}
#ifndef CONFIG_NO_RANDOM_POOL
/*
把扫描结果得到的无线网络频率、信号强度等信息取出来,然后写到crpto模块
这样可以增加随机数生成的随机性
*/
num = scan_res->num;
if (num > 10)
num = 10;
for (i = 0; i < num; i++) {
u8 buf[5];
struct wpa_scan_res *res = scan_res->res[i];
buf[0] = res->bssid[5];
buf[1] = res->qual & 0xff;
buf[2] = res->noise & 0xff;
buf[3] = res->level & 0xff;
buf[4] = res->tsf & 0xff;
random_add_randomness(buf, sizeof(buf));
}
#endif /* CONFIG_NO_RANDOM_POOL */
if (update_only) {
ret = 1;
goto scan_work_done;
}
// 这里当使用TYPE=ONLY的时候会进这个里面
if (own_request && wpa_s->scan_res_handler &&
!(data && data->scan_info.external_scan)) {
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
scan_res_handler = wpa_s->scan_res_handler;
wpa_s->scan_res_handler = NULL;
scan_res_handler(wpa_s, scan_res);
ret = 1;
goto scan_work_done;
}
wpas_notify_scan_results(wpa_s);
wpas_notify_scan_done(wpa_s, 1);
if (data && data->scan_info.external_scan) {
wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
wpa_scan_results_free(scan_res);
return 0;
}
if (wnm_scan_process(wpa_s, 1) > 0)
goto scan_work_done;
if (sme_proc_obss_scan(wpa_s, scan_res) > 0)
goto scan_work_done;
if (own_request && data &&
wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0)
goto scan_work_done;
if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
goto scan_work_done;
if (autoscan_notify_scan(wpa_s, scan_res))
goto scan_work_done;
if (wpa_s->disconnected) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
goto scan_work_done;
}
if (!wpas_driver_bss_selection(wpa_s) &&
bgscan_notify_scan(wpa_s, scan_res) == 1)
goto scan_work_done;
wpas_wps_update_ap_info(wpa_s, scan_res);
if (wpa_s->wpa_state >= WPA_AUTHENTICATING &&
wpa_s->wpa_state < WPA_COMPLETED)
goto scan_work_done;
wpa_scan_results_free(scan_res);
if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
}
os_free(wpa_s->last_scan_freqs);
wpa_s->last_scan_freqs = NULL;
wpa_s->num_last_scan_freqs = 0;
if (own_request && data &&
data->scan_info.freqs && data->scan_info.num_freqs) {
wpa_s->last_scan_freqs = os_malloc(sizeof(int) *
data->scan_info.num_freqs);
if (wpa_s->last_scan_freqs) {
os_memcpy(wpa_s->last_scan_freqs,
data->scan_info.freqs,
sizeof(int) * data->scan_info.num_freqs);
wpa_s->num_last_scan_freqs = data->scan_info.num_freqs;
}
}
//从扫描结果中选择一个合适的无线网络
//下面返回目标无线网络对应的wpa_bss对象
wpas_select_network_from_last_scan(wpa_s, 1, own_request);
{
//从扫描结果中选取一个bss网络
selected = wpa_supplicant_pick_network(wpa_s, &ssid(null));
//判断是否需要漫游,这里不太明白,没看到匹配加密方式的,
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
{
//根据当前连接的bss网络信号强度和选择的网络信号强度的差值判断是否需要漫游
//这是wpa_s自主决定的漫游,有三个地方可以决定漫游 framework wpa_s driver
current_bss->level < -85 : min_diff = 1;
current_bss->level < -80 : min_diff = 2;
current_bss->level < -75 : min_diff = 3;
current_bss->level < -70 : min_diff = 4;
current_bss->level > -70 : min_diff = 5;
}
}
scan_work_done:
wpa_scan_results_free(scan_res);
if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
}
return ret;
}
struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
struct scan_info *info, int new_scan)
{
struct wpa_scan_results *scan_res;
size_t i;
int (*compar)(const void *, const void *) = wpa_scan_result_compar;
//调用driver interface的wpa_drv_get_scan_results2函数
//对nl80211来说,其真实函数是 wpa_driver_nl80211_get_scan_results
//该函数将向wlan driver发送 NL80211_CMD_GET_SCAN命令以获取扫描结果
scan_res = wpa_drv_get_scan_results2(wpa_s);
if (scan_res == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
return NULL;
}
if (scan_res->fetch_time.sec == 0) {
/*
* Make sure we have a valid timestamp if the driver wrapper
* does not set this.
*/
os_get_reltime(&scan_res->fetch_time);
}
filter_scan_res(wpa_s, scan_res);
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *scan_res_item = scan_res->res[i];
scan_snr(scan_res_item);
scan_est_throughput(wpa_s, scan_res_item);
}
//对扫描结果进行排序
//排序函数是 wpa_scan_result_compar
//它将根据信噪比(SNR)进行排序
//除了SNR,wpa_scan_result_compar 还有别的比较条件
if (scan_res->res) {
qsort(scan_res->res, scan_res->num,
sizeof(struct wpa_scan_res *), compar);
}
dump_scan_res(scan_res);
if (wpa_s->ignore_post_flush_scan_res) {
/* FLUSH command aborted an ongoing scan and these are the
* results from the aborted scan. Do not process the results to
* maintain flushed state. */
wpa_dbg(wpa_s, MSG_DEBUG,
"Do not update BSS table based on pending post-FLUSH scan results");
wpa_s->ignore_post_flush_scan_res = 0;
return scan_res;
}
//更新WPAS保存的那些bss信息(每一个无线网络由wpa_bss表示)
wpa_bss_update_start(wpa_s);
for (i = 0; i < scan_res->num; i++)
wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
&scan_res->fetch_time);
wpa_bss_update_end(wpa_s, info, new_scan);
return scan_res;
}
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request)
{
struct wpa_bss *selected;
struct wpa_ssid *ssid = NULL;
int time_to_reenable = wpas_reenabled_network_time(wpa_s);
if (time_to_reenable > 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Postpone network selection by %d seconds since all networks are disabled",
time_to_reenable);
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
eloop_register_timeout(time_to_reenable, 0,
wpas_network_reenabled, wpa_s, NULL);
return 0;
}
if (wpa_s->p2p_mgmt)
return 0; /* no normal connection on p2p_mgmt interface */
wpa_s->owe_transition_search = 0;
//从扫描结果中选择一个合适的无线网络
//selected是一个wpa_bss对象
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
if (selected) {
int skip;
/*
判断是否需要漫游,漫游表示需要切换无线网络
如果之前没有和AP关联,那么此处需要漫游
wpa_supplicant_need_to_roam中海包括对同一个ESS中如何选择更合适的BSS有一些简单的判断
主要是根据信号强度来选择
*/
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
if (skip) { //不需要切换
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
return 0;
}
wpa_s->suitable_network++;
if (ssid != wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
//关联至目标无线网络
if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
return -1;
}
if (new_scan)
//预认证处理,与Fast Transition有关
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
/*
* Do not allow other virtual radios to trigger operations based
* on these scan results since we do not want them to start
* other associations at the same time.
*/
return 1;
} else {
//......//没有匹配的无线网络的处理
}
return 0;
}

todo
扫描参数解析
执行扫描命令如下
wpa_cli -p /var/run/wpa_supplicant-ath2 -i ath2 scan
TYPE=ONLY
only_new=1 # Request driver to clear scan cache due to manual only_new=1 scan
freq=5600 # 2412-2432,2462,5000-6000
passive=1
use_id=1
only_new=1 #Request driver to clear scan cache due to manual only_new=1 scan
scan_id=
bssid=88:ff:ff:12:40:01
wildcard_ssid=1
ssid YW ssid 5957 #解释0x59 对应字符Y 0x57 对应字符W 所以就是请求 ssid 为 YW 的ap 如果请求多个格式如下 ssid YW ssid 5957 ssid 5856 中间都是空格隔开的,这个参数在发出probe帧的时候是指定ssid的
TYPE=ONLY参数wpa接收到
scan TYPE=ONLY命令后,在下面的函数中会把wpa_s->scan_res_handler = scan_res_handler赋值,代码如下:static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { if (params) { if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) scan_only = 1; } if (scan_only) scan_res_handler = scan_only_handler; wpa_s->scan_res_handler = scan_res_handler; }
在扫描结束后,处理扫描结果
static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0); } static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data, int own_request, int update_only) { // 更新扫描结果到wpa_s 数据结构体中 scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : // 会进入下面的代码 own_request==1 wpa_s->scan_res_handler不为空 if (own_request && wpa_s->scan_res_handler && !(data && data->scan_info.external_scan)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); scan_res_handler = wpa_s->scan_res_handler; wpa_s->scan_res_handler = NULL; //这里调用 scan_res_handler 函数 即scan_only_handler函数 scan_res_handler(wpa_s, scan_res); ret = 1; goto scan_work_done; //这里直接走到最后面,不会执行下面的wpas_select_network_from_last_scan //也即不会执行里面的自主roam } // 这里面会自主漫游---代码片段1 return wpas_select_network_from_last_scan(wpa_s, 1, own_request); scan_work_done: wpa_scan_results_free(scan_res); if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); } return ret; }
下面分析
scan_only_handler函数:该函数目前没啥作用/** * scan_only_handler - Reports scan results */ void scan_only_handler(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received"); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", wpa_s->manual_scan_id); wpa_s->manual_scan_use_id = 0; } else { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); } wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); if (wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); } if (wpa_s->wpa_state == WPA_SCANNING) wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); }
从上面的分析可知,
scan TYPE=ONLY扫描的情况下,得到扫描结果后会跳过_wpa_supplicant_event_scan_results函数中的代码片段1的自主漫游,也就是不进行自主漫游。only_new=1在
wpa_supplicant_scan函数中,会有下面的代码片段:ssid_list_set: wpa_supplicant_optimize_freqs(wpa_s, ¶ms); extra_ie = wpa_supplicant_extra_ies(wpa_s); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_only_new) { wpa_printf(MSG_DEBUG, "Request driver to clear scan cache due to manual only_new=1 scan"); params.only_new_results = 1; }
这里只是设置了扫描的参数
params.only_new_results = 1;,生效的地方在驱动程序中。freq=5600设置扫描频率,格式如下:
freq=5600 # 2412-2432,2462,5000-6000
在下面的函数中解析,存放在
manual_scan_freqs数组中static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { pos = os_strstr(params, "freq="); if (pos) { manual_scan_freqs = freq_range_to_channel_list(wpa_s, pos + 5); if (manual_scan_freqs == NULL) { *reply_len = -1; goto done; } } // 最终存放在wpa_s中 wpa_s->manual_scan_freqs = manual_scan_freqs; }
然后在
wpa_supplicant_scan扫描函数中ssid_list_set: if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL && wpa_s->manual_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels"); // 设置到扫描参数中 params.freqs = wpa_s->manual_scan_freqs; wpa_s->manual_scan_freqs = NULL; }
passive=1不主动发probe 帧,被动监听ap的100ms一个的becon帧的扫描方式
static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { pos = os_strstr(params, "passive="); if (pos) manual_scan_passive = !!atoi(pos + 8); wpa_s->manual_scan_passive = manual_scan_passive; } static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_passive && params.num_ssids == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request"); } else if (wpa_s->conf->passive_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on configuration"); } }
其实在配置文件中同样可以开启该功能

use_id=1暂时看不出来什么作用
only_new=1在
static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { pos = os_strstr(params, "only_new=1"); if (pos) manual_scan_only_new = 1; wpa_s->manual_scan_only_new = manual_scan_only_new; } static struct nl_msg * nl80211_scan_common(struct i802_bss *bss, u8 cmd, struct wpa_driver_scan_params *params) { if (params->only_new_results) { wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH"); scan_flags |= NL80211_SCAN_FLAG_FLUSH; } }
在驱动层会看到下面的判断
int wlan_cfg80211_scan(struct wlan_objmgr_vdev *vdev, struct cfg80211_scan_request *request, struct scan_params *params) { if (request->flags & NL80211_SCAN_FLAG_FLUSH) ucfg_scan_flush_results(pdev, NULL); } QDF_STATUS ucfg_scan_flush_results(struct wlan_objmgr_pdev *pdev, struct scan_filter *filter) { return scm_flush_results(pdev, filter); } QDF_STATUS scm_flush_results(struct wlan_objmgr_pdev *pdev, struct scan_filter *filter) { struct wlan_objmgr_psoc *psoc; struct scan_dbs *scan_db; QDF_STATUS status = QDF_STATUS_SUCCESS; psoc = wlan_pdev_get_psoc(pdev); scan_db = wlan_pdev_get_scan_db(psoc, pdev); scm_flush_scan_entries(psoc, scan_db, filter); return status; } //关键函数遍历扫描结果 进行刷新老化 static void scm_flush_scan_entries(struct wlan_objmgr_psoc *psoc, struct scan_dbs *scan_db, struct scan_filter *filter) { int i; struct scan_cache_node *cur_node; struct scan_cache_node *next_node = NULL; for (i = 0 ; i < SCAN_HASH_SIZE; i++) { cur_node = scm_get_next_node(scan_db, &scan_db->scan_hash_tbl[i], NULL); while (cur_node) { scm_scan_apply_filter_flush_entry(psoc, scan_db, cur_node, filter); next_node = scm_get_next_node(scan_db, &scan_db->scan_hash_tbl[i], cur_node); cur_node = next_node; } } }
scan_id=5,2格式如下:
#用逗号隔开,以空格结尾 scan_id=5,2
代码分析
static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { //存放在scan_id数组中 pos = os_strstr(params, "scan_id="); if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count, scan_id) < 0) { *reply_len = -1; goto done; } // 存放在wpa_s->scan_id数组中 wpa_s->scan_id_count = scan_id_count; os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); }
static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { if (wpa_s->scan_id_count && wpa_s->last_scan_req == MANUAL_SCAN_REQ) wpa_set_scan_ssids(wpa_s, ¶ms, max_ssids); { unsigned int i; struct wpa_ssid *ssid; /* * For devices with max_ssids greater than 1, leave the last slot empty * for adding the wildcard scan entry. */ max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids; // 根据wpa_s->scan_id[i]的network id 找配置文件中的 ssid for (i = 0; i < wpa_s->scan_id_count; i++) { ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]); if (!ssid) continue; if (ssid->scan_ssid) wpa_add_scan_ssid(wpa_s, params, max_ssids, ssid->ssid, ssid->ssid_len); /* * Also add the SSID of the OWE BSS, to allow discovery of * transition mode APs more quickly. */ wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids); } wpa_s->scan_id_count = 0; } }
总结
scan_id后面跟的就是网络id号bssid=88:ff:ff:12:40:01和wildcard_ssid=1static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { pos = os_strstr(params, "bssid="); if (pos) { u8 bssid[ETH_ALEN]; pos += 6; if (hwaddr_aton(pos, bssid)) { wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos); *reply_len = -1; goto done; } // 解析参数放到wpa_s->next_scan_bssid中 os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN); wpa_s->next_scan_bssid_wildcard_ssid = os_strstr(params, "wildcard_ssid=1") != NULL; } }
在下面的函数中使用
static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) { struct wpa_bss *bss; // 赋值给扫描参数 params.bssid = wpa_s->next_scan_bssid; bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid); if (!wpa_s->next_scan_bssid_wildcard_ssid && bss && bss->ssid_len && params.num_ssids == 1 && params.ssids[0].ssid_len == 0) { params.ssids[0].ssid = bss->ssid; params.ssids[0].ssid_len = bss->ssid_len; wpa_dbg(wpa_s, MSG_DEBUG, "Scan a previously specified BSSID " MACSTR " and SSID %s", MAC2STR(params.bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len)); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Scan a previously specified BSSID " MACSTR, MAC2STR(params.bssid)); } } }
ssid 5957 ssid 595757格式如下
# 用空格隔开,多个的话也是空格 16进制 比如0x59 assii Y 0x57为W 故而,这里意思是YW的 ssid扫描 ssid 5957 ssid 595757
static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { while (pos && *pos != '\0') { if (os_strncmp(pos, "ssid ", 5) == 0) { char *end; pos += 5; end = pos; while (*end) { if (*end == '\0' || *end == ' ') break; end++; } ns = os_realloc_array( ssid, ssid_count + 1, sizeof(struct wpa_ssid_value)); if (ns == NULL) { *reply_len = -1; goto done; } ssid = ns; if ((end - pos) & 0x01 || end - pos > 2 * SSID_MAX_LEN || hexstr2bin(pos, ssid[ssid_count].ssid, (end - pos) / 2) < 0) { wpa_printf(MSG_DEBUG, "Invalid SSID value '%s'", pos); *reply_len = -1; goto done; } // 解析参数存放到ssid数组中 ssid[ssid_count].ssid_len = (end - pos) / 2; wpa_hexdump_ascii(MSG_DEBUG, "scan SSID", ssid[ssid_count].ssid, ssid[ssid_count].ssid_len); ssid_count++; pos = end; } pos = os_strchr(pos, ' '); if (pos) pos++; } // 最终存放到wpa_s->ssids_from_scan_req中 wpa_s->num_ssids_from_scan_req = ssid_count; os_free(wpa_s->ssids_from_scan_req); if (ssid_count) { wpa_s->ssids_from_scan_req = ssid; ssid = NULL; } else { wpa_s->ssids_from_scan_req = NULL; } }
使用地方
static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_set_ssids_from_scan_req(wpa_s, ¶ms, max_ssids)) { wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command"); goto ssid_list_set; } } //在下面的函数中设置到扫描参数中params->ssids数组中 wpa_set_ssids_from_scan_req { for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) { params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid; params->ssids[i].ssid_len = wpa_s->ssids_from_scan_req[i].ssid_len; wpa_hexdump_ascii(MSG_DEBUG, "specific SSID", params->ssids[i].ssid, params->ssids[i].ssid_len); } params->num_ssids = wpa_s->num_ssids_from_scan_req; wpa_s->num_ssids_from_scan_req = 0; }
todo