diff options
Diffstat (limited to 'contrib/wpa/wpa_supplicant/scan.c')
-rw-r--r-- | contrib/wpa/wpa_supplicant/scan.c | 1092 |
1 files changed, 982 insertions, 110 deletions
diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index edc8c83..d2b671a 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - Scanning - * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -20,8 +14,10 @@ #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" -#include "mlme.h" #include "wps_supplicant.h" +#include "p2p_supplicant.h" +#include "p2p/p2p.h" +#include "hs20_supplicant.h" #include "notify.h" #include "bss.h" #include "scan.h" @@ -42,21 +38,21 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) wpas_notify_network_changed(wpa_s); } wpa_supplicant_initiate_eapol(wpa_s); - wpa_printf(MSG_DEBUG, "Already associated with a configured network - " - "generating associated event"); + wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured " + "network - generating associated event"); os_memset(&data, 0, sizeof(data)); wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); } #ifdef CONFIG_WPS -static int wpas_wps_in_use(struct wpa_config *conf, +static int wpas_wps_in_use(struct wpa_supplicant *wpa_s, enum wps_request_type *req_type) { struct wpa_ssid *ssid; int wps = 0; - for (ssid = conf->ssid; ssid; ssid = ssid->next) { + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) continue; @@ -69,20 +65,50 @@ static int wpas_wps_in_use(struct wpa_config *conf, return 2; } +#ifdef CONFIG_P2P + if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p && + !wpa_s->conf->p2p_disabled) { + wpa_s->wps->dev.p2p = 1; + if (!wps) { + wps = 1; + *req_type = WPS_REQ_ENROLLEE_INFO; + } + } +#endif /* CONFIG_P2P */ + return wps; } #endif /* CONFIG_WPS */ -int wpa_supplicant_enabled_networks(struct wpa_config *conf) +/** + * wpa_supplicant_enabled_networks - Check whether there are enabled networks + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 if no networks are enabled, >0 if networks are enabled + * + * This function is used to figure out whether any networks (or Interworking + * with enabled credentials and auto_interworking) are present in the current + * configuration. + */ +int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s) { - struct wpa_ssid *ssid = conf->ssid; + struct wpa_ssid *ssid = wpa_s->conf->ssid; + int count = 0, disabled = 0; while (ssid) { - if (!ssid->disabled) - return 1; + if (!wpas_network_disabled(wpa_s, ssid)) + count++; + else + disabled++; ssid = ssid->next; } - return 0; + if (wpa_s->conf->cred && wpa_s->conf->interworking && + wpa_s->conf->auto_interworking) + count++; + if (count == 0 && disabled > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled " + "networks)", disabled); + } + return count; } @@ -90,15 +116,15 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { while (ssid) { - if (!ssid->disabled) + if (!wpas_network_disabled(wpa_s, ssid)) break; ssid = ssid->next; } /* ap_scan=2 mode - try to associate with each SSID. */ if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached " - "end of scan list - go back to beginning"); + wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached " + "end of scan list - go back to beginning"); wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; wpa_supplicant_req_scan(wpa_s, 0, 0); return; @@ -131,7 +157,7 @@ static void int_array_concat(int **res, const int *a) reslen = int_array_len(*res); alen = int_array_len(a); - n = os_realloc(*res, (reslen + alen + 1) * sizeof(int)); + n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); if (n == NULL) { os_free(*res); *res = NULL; @@ -182,6 +208,12 @@ static void int_array_sort_unique(int *a) } +/** + * wpa_supplicant_trigger_scan - Request driver to start a scan + * @wpa_s: Pointer to wpa_supplicant data + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { @@ -189,16 +221,71 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, wpa_supplicant_notify_scanning(wpa_s, 1); - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - ret = ieee80211_sta_req_scan(wpa_s, params); - else - ret = wpa_drv_scan(wpa_s, params); - + ret = wpa_drv_scan(wpa_s, params); if (ret) { wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); - } else + } else { wpa_s->scan_runs++; + wpa_s->normal_scans++; + } + + return ret; +} + + +static void +wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan"); + + if (wpa_supplicant_req_sched_scan(wpa_s)) + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void +wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it"); + + wpa_s->sched_scan_timed_out = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); +} + + +static int +wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + int interval) +{ + int ret; + + wpa_supplicant_notify_scanning(wpa_s, 1); + ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000); + if (ret) + wpa_supplicant_notify_scanning(wpa_s, 0); + else + wpa_s->sched_scanning = 1; + + return ret; +} + + +static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) +{ + int ret; + + ret = wpa_drv_stop_sched_scan(wpa_s); + if (ret) { + wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!"); + /* TODO: what to do if stopping fails? */ + return -1; + } return ret; } @@ -237,36 +324,235 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) } -static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +static void wpa_supplicant_optimize_freqs( + struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { - struct wpa_supplicant *wpa_s = eloop_ctx; - struct wpa_ssid *ssid; - int scan_req = 0, ret; - struct wpabuf *wps_ie = NULL; +#ifdef CONFIG_P2P + if (params->freqs == NULL && wpa_s->p2p_in_provisioning && + wpa_s->go_params) { + /* Optimize provisioning state scan based on GO information */ + if (wpa_s->p2p_in_provisioning < 5 && + wpa_s->go_params->freq > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO " + "preferred frequency %d MHz", + wpa_s->go_params->freq); + params->freqs = os_zalloc(2 * sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->go_params->freq; + } else if (wpa_s->p2p_in_provisioning < 8 && + wpa_s->go_params->freq_list[0]) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common " + "channels"); + int_array_concat(¶ms->freqs, + wpa_s->go_params->freq_list); + if (params->freqs) + int_array_sort_unique(params->freqs); + } + wpa_s->p2p_in_provisioning++; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WPS + if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) { + /* + * Optimize post-provisioning scan based on channel used + * during provisioning. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz " + "that was used during provisioning", wpa_s->wps_freq); + params->freqs = os_zalloc(2 * sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->wps_freq; + wpa_s->after_wps--; + } + + if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq) + { + /* Optimize provisioning scan based on already known channel */ + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz", + wpa_s->wps_freq); + params->freqs = os_zalloc(2 * sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->wps_freq; + wpa_s->known_wps_freq = 0; /* only do this once */ + } +#endif /* CONFIG_WPS */ +} + + +#ifdef CONFIG_INTERWORKING +static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, + struct wpabuf *buf) +{ + if (wpa_s->conf->interworking == 0) + return; + + wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB); + wpabuf_put_u8(buf, 4); + wpabuf_put_u8(buf, 0x00); + wpabuf_put_u8(buf, 0x00); + wpabuf_put_u8(buf, 0x00); + wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */ + + wpabuf_put_u8(buf, WLAN_EID_INTERWORKING); + wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 : + 1 + ETH_ALEN); + wpabuf_put_u8(buf, wpa_s->conf->access_network_type); + /* No Venue Info */ + if (!is_zero_ether_addr(wpa_s->conf->hessid)) + wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN); +} +#endif /* CONFIG_INTERWORKING */ + + +static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *extra_ie = NULL; #ifdef CONFIG_WPS int wps = 0; enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ + +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->interworking && + wpabuf_resize(&extra_ie, 100) == 0) + wpas_add_interworking_elements(wpa_s, extra_ie); +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_WPS + wps = wpas_wps_in_use(wpa_s, &req_type); + + if (wps) { + struct wpabuf *wps_ie; + wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON : + DEV_PW_DEFAULT, + &wpa_s->wps->dev, + wpa_s->wps->uuid, req_type, + 0, NULL); + if (wps_ie) { + if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0) + wpabuf_put_buf(extra_ie, wps_ie); + wpabuf_free(wps_ie); + } + } + +#ifdef CONFIG_P2P + if (wps) { + size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + if (wpabuf_resize(&extra_ie, ielen) == 0) + wpas_p2p_scan_ie(wpa_s, extra_ie); + } +#endif /* CONFIG_P2P */ + +#endif /* CONFIG_WPS */ + + return extra_ie; +} + + +#ifdef CONFIG_P2P + +/* + * Check whether there are any enabled networks or credentials that could be + * used for a non-P2P connection. + */ +static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (wpas_network_disabled(wpa_s, ssid)) + continue; + if (!ssid->p2p_group) + return 1; + } + + if (wpa_s->conf->cred && wpa_s->conf->interworking && + wpa_s->conf->auto_interworking) + return 1; + + return 0; +} + + +/* + * Find the operating frequency of any other virtual interface that is using + * the same radio concurrently. + */ +static int shared_vif_oper_freq(struct wpa_supplicant *wpa_s) +{ + const char *rn, *rn2; + struct wpa_supplicant *ifs; + u8 bssid[ETH_ALEN]; + + if (!wpa_s->driver->get_radio_name) + return -1; + + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + if (rn == NULL || rn[0] == '\0') + return -1; + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s || !ifs->driver->get_radio_name) + continue; + + rn2 = ifs->driver->get_radio_name(ifs->drv_priv); + if (!rn2 || os_strcmp(rn, rn2) != 0) + continue; + + if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) + continue; + + if (ifs->current_ssid->mode == WPAS_MODE_AP || + ifs->current_ssid->mode == WPAS_MODE_P2P_GO) + return ifs->current_ssid->frequency; + if (wpa_drv_get_bssid(ifs, bssid) == 0) + return ifs->assoc_freq; + } + + return 0; +} + +#endif /* CONFIG_P2P */ + + +static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_ssid *ssid; + enum scan_req_type scan_req = NORMAL_SCAN_REQ; + int ret; + struct wpabuf *extra_ie = NULL; struct wpa_driver_scan_params params; + struct wpa_driver_scan_params *scan_params; size_t max_ssids; enum wpa_states prev_state; - if (wpa_s->disconnected && !wpa_s->scan_req) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); + return; + } + + 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; } - if (!wpa_supplicant_enabled_networks(wpa_s->conf) && - !wpa_s->scan_req) { - wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); + 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); +#ifdef CONFIG_P2P + wpa_s->sta_scan_pending = 0; +#endif /* CONFIG_P2P */ return; } if (wpa_s->conf->ap_scan != 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) { - wpa_printf(MSG_DEBUG, "Using wired authentication - " - "overriding ap_scan configuration"); + 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); } @@ -276,8 +562,24 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) return; } - if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || - wpa_s->conf->ap_scan == 2) +#ifdef CONFIG_P2P + if (wpas_p2p_in_progress(wpa_s)) { + if (wpa_s->sta_scan_pending && + wpas_p2p_in_progress(wpa_s) == 2 && + wpa_s->global->p2p_cb_on_scan_complete) { + wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station " + "mode scan during P2P search"); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan " + "while P2P operation is in progress"); + wpa_s->sta_scan_pending = 1; + wpa_supplicant_req_scan(wpa_s, 5, 0); + return; + } + } +#endif /* CONFIG_P2P */ + + if (wpa_s->conf->ap_scan == 2) max_ssids = 1; else { max_ssids = wpa_s->max_scan_ssids; @@ -285,12 +587,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) max_ssids = WPAS_MAX_SCAN_SSIDS; } -#ifdef CONFIG_WPS - wps = wpas_wps_in_use(wpa_s->conf, &req_type); -#endif /* CONFIG_WPS */ - scan_req = wpa_s->scan_req; - wpa_s->scan_req = 0; + wpa_s->scan_req = NORMAL_SCAN_REQ; os_memset(¶ms, 0, sizeof(params)); @@ -299,6 +597,40 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_s->wpa_state == WPA_INACTIVE) wpa_supplicant_set_state(wpa_s, WPA_SCANNING); + /* + * If autoscan has set its own scanning parameters + */ + if (wpa_s->autoscan_params != NULL) { + scan_params = wpa_s->autoscan_params; + goto scan; + } + + if (scan_req != MANUAL_SCAN_REQ && wpa_s->connect_without_scan) { + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->connect_without_scan) + break; + } + 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; + } + } + +#ifdef CONFIG_P2P + if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) && + wpa_s->go_params) { + wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during " + "P2P group formation"); + params.ssids[0].ssid = wpa_s->go_params->ssid; + params.ssids[0].ssid_len = wpa_s->go_params->ssid_len; + params.num_ssids = 1; + goto ssid_list_set; + } +#endif /* CONFIG_P2P */ + /* Find the starting point from which to continue scanning */ ssid = wpa_s->conf->ssid; if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { @@ -311,9 +643,9 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } } - if (scan_req != 2 && (wpa_s->conf->ap_scan == 2 || - wpa_s->connect_without_scan)) { - wpa_s->connect_without_scan = 0; + if (scan_req != MANUAL_SCAN_REQ && 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) { @@ -328,7 +660,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) if (ssid == NULL && max_ssids > 1) ssid = wpa_s->conf->ssid; while (ssid) { - if (!ssid->disabled && ssid->scan_ssid) { + 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 = @@ -348,7 +681,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) { - if (tssid->disabled) + if (wpas_network_disabled(wpa_s, tssid)) continue; if ((params.freqs || !freqs_set) && tssid->scan_freq) { int_array_concat(¶ms.freqs, @@ -362,58 +695,123 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) int_array_sort_unique(params.freqs); } - if (ssid) { - wpa_s->prev_scan_ssid = ssid; - if (max_ssids > 1) { - wpa_printf(MSG_DEBUG, "Include wildcard SSID in the " - "scan request"); - params.num_ssids++; + 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)); } - wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)"); + } 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 { wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; params.num_ssids++; - wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID"); + wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " + "SSID"); } +#ifdef CONFIG_P2P +ssid_list_set: +#endif /* CONFIG_P2P */ + + wpa_supplicant_optimize_freqs(wpa_s, ¶ms); + extra_ie = wpa_supplicant_extra_ies(wpa_s); + +#ifdef CONFIG_HS20 + if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 6) == 0) + wpas_hs20_add_indication(extra_ie); +#endif /* CONFIG_HS20 */ + + 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; -#ifdef CONFIG_WPS - if (params.freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) { + 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->show_group_started && wpa_s->go_params)) { /* - * Optimize post-provisioning scan based on channel used - * during provisioning. + * The interface may not yet be in P2P mode, so we have to + * explicitly request P2P probe to disable CCK rates. */ - wpa_printf(MSG_DEBUG, "WPS: Scan only frequency %u MHz that " - "was used during provisioning", wpa_s->wps_freq); - params.freqs = os_zalloc(2 * sizeof(int)); - if (params.freqs) - params.freqs[0] = wpa_s->wps_freq; - wpa_s->after_wps--; + params.p2p_probe = 1; } - - if (wps) { - wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, - wpa_s->wps->uuid, req_type); - if (wps_ie) { - params.extra_ies = wpabuf_head(wps_ie); - params.extra_ies_len = wpabuf_len(wps_ie); +#endif /* CONFIG_P2P */ + + 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 && scan_req == NORMAL_SCAN_REQ && + !scan_params->freqs && !params.freqs && + wpas_is_p2p_prioritized(wpa_s) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) && + wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && + non_p2p_network_enabled(wpa_s)) { + int freq = shared_vif_oper_freq(wpa_s); + if (freq > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only the current " + "operating channel (%d MHz) since driver does " + "not support multi-channel concurrency", freq); + params.freqs = os_zalloc(sizeof(int) * 2); + if (params.freqs) + params.freqs[0] = freq; + scan_params->freqs = params.freqs; } } -#endif /* CONFIG_WPS */ - - params.filter_ssids = wpa_supplicant_build_filter_ssids( - wpa_s->conf, ¶ms.num_filter_ssids); +#endif /* CONFIG_P2P */ - ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); + ret = wpa_supplicant_trigger_scan(wpa_s, scan_params); - wpabuf_free(wps_ie); + wpabuf_free(extra_ie); os_free(params.freqs); os_free(params.filter_ssids); if (ret) { - wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); + wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan"); if (prev_state != wpa_s->wpa_state) wpa_supplicant_set_state(wpa_s, prev_state); + /* Restore scan_req since we will try to scan again */ + wpa_s->scan_req = scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); + } else { + wpa_s->scan_for_connection = 0; } } @@ -440,18 +838,19 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) struct wpa_ssid *ssid = wpa_s->conf->ssid; while (ssid) { - if (!ssid->disabled && ssid->scan_ssid) + if (!wpas_network_disabled(wpa_s, ssid) && + ssid->scan_ssid) break; ssid = ssid->next; } if (ssid) { - wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " + wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " "ensure that specific SSID scans occur"); return; } } - wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", + wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", sec, usec); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); @@ -459,6 +858,251 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) /** + * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan + * @wpa_s: Pointer to wpa_supplicant data + * @sec: Number of seconds after which to scan + * @usec: Number of microseconds after which to scan + * Returns: 0 on success or -1 otherwise + * + * This function is used to schedule periodic scans for neighboring + * access points after the specified time. + */ +int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s, + int sec, int usec) +{ + if (!wpa_s->sched_scan_supported) + return -1; + + eloop_register_timeout(sec, usec, + wpa_supplicant_delayed_sched_scan_timeout, + wpa_s, NULL); + + return 0; +} + + +/** + * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 is sched_scan was started or -1 otherwise + * + * This function is used to schedule periodic scans for neighboring + * access points repeating the scan continuously. + */ +int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_scan_params params; + struct wpa_driver_scan_params *scan_params; + enum wpa_states prev_state; + struct wpa_ssid *ssid = NULL; + struct wpabuf *extra_ie = NULL; + int ret; + unsigned int max_sched_scan_ssids; + int wildcard = 0; + int need_ssids; + + if (!wpa_s->sched_scan_supported) + return -1; + + if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS) + max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; + else + max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; + if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload) + return -1; + + if (wpa_s->sched_scanning) { + wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning"); + return 0; + } + + need_ssids = 0; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) { + /* Use wildcard SSID to find this network */ + wildcard = 1; + } else if (!wpas_network_disabled(wpa_s, ssid) && + ssid->ssid_len) + need_ssids++; + +#ifdef CONFIG_WPS + if (!wpas_network_disabled(wpa_s, ssid) && + ssid->key_mgmt == WPA_KEY_MGMT_WPS) { + /* + * Normal scan is more reliable and faster for WPS + * operations and since these are for short periods of + * time, the benefit of trying to use sched_scan would + * be limited. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " + "sched_scan for WPS"); + return -1; + } +#endif /* CONFIG_WPS */ + } + if (wildcard) + need_ssids++; + + if (wpa_s->normal_scans < 3 && + (need_ssids <= wpa_s->max_scan_ssids || + wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) { + /* + * When normal scan can speed up operations, use that for the + * first operations before starting the sched_scan to allow + * user space sleep more. We do this only if the normal scan + * has functionality that is suitable for this or if the + * sched_scan does not have better support for multiple SSIDs. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " + "sched_scan for initial scans (normal_scans=%d)", + wpa_s->normal_scans); + return -1; + } + + os_memset(¶ms, 0, sizeof(params)); + + /* If we can't allocate space for the filters, we just don't filter */ + params.filter_ssids = os_zalloc(wpa_s->max_match_sets * + sizeof(struct wpa_driver_scan_filter)); + + prev_state = wpa_s->wpa_state; + if (wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_INACTIVE) + wpa_supplicant_set_state(wpa_s, WPA_SCANNING); + + if (wpa_s->autoscan_params != NULL) { + scan_params = wpa_s->autoscan_params; + goto scan; + } + + /* Find the starting point from which to continue scanning */ + ssid = wpa_s->conf->ssid; + if (wpa_s->prev_sched_ssid) { + while (ssid) { + if (ssid == wpa_s->prev_sched_ssid) { + ssid = ssid->next; + break; + } + ssid = ssid->next; + } + } + + if (!ssid || !wpa_s->prev_sched_ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); + + if (wpa_s->sched_scan_interval == 0) + wpa_s->sched_scan_interval = 10; + wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; + wpa_s->first_sched_scan = 1; + ssid = wpa_s->conf->ssid; + wpa_s->prev_sched_ssid = ssid; + } + + if (wildcard) { + wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan"); + params.num_ssids++; + } + + while (ssid) { + if (wpas_network_disabled(wpa_s, ssid)) + goto next; + + if (params.num_filter_ssids < wpa_s->max_match_sets && + params.filter_ssids && ssid->ssid && ssid->ssid_len) { + wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid, + ssid->ssid, ssid->ssid_len); + params.filter_ssids[params.num_filter_ssids].ssid_len = + ssid->ssid_len; + params.num_filter_ssids++; + } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len) + { + wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID " + "filter for sched_scan - drop filter"); + os_free(params.filter_ssids); + params.filter_ssids = NULL; + params.num_filter_ssids = 0; + } + + if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) { + if (params.num_ssids == max_sched_scan_ssids) + break; /* only room for broadcast SSID */ + wpa_dbg(wpa_s, MSG_DEBUG, + "add to active scan ssid: %s", + wpa_ssid_txt(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 >= max_sched_scan_ssids) { + wpa_s->prev_sched_ssid = ssid; + do { + ssid = ssid->next; + } while (ssid && + (wpas_network_disabled(wpa_s, ssid) || + !ssid->scan_ssid)); + break; + } + } + + next: + wpa_s->prev_sched_ssid = ssid; + ssid = ssid->next; + } + + if (params.num_filter_ssids == 0) { + os_free(params.filter_ssids); + params.filter_ssids = NULL; + } + + extra_ie = wpa_supplicant_extra_ies(wpa_s); + if (extra_ie) { + params.extra_ies = wpabuf_head(extra_ie); + params.extra_ies_len = wpabuf_len(extra_ie); + } + + scan_params = ¶ms; + +scan: + if (ssid || !wpa_s->first_sched_scan) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan: interval %d timeout %d", + wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan: interval %d (no timeout)", + wpa_s->sched_scan_interval); + } + + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, + wpa_s->sched_scan_interval); + wpabuf_free(extra_ie); + os_free(params.filter_ssids); + if (ret) { + wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan"); + if (prev_state != wpa_s->wpa_state) + wpa_supplicant_set_state(wpa_s, prev_state); + return ret; + } + + /* If we have more SSIDs to scan, add a timeout so we scan them too */ + if (ssid || !wpa_s->first_sched_scan) { + wpa_s->sched_scan_timed_out = 0; + eloop_register_timeout(wpa_s->sched_scan_timeout, 0, + wpa_supplicant_sched_scan_timeout, + wpa_s, NULL); + wpa_s->first_sched_scan = 0; + wpa_s->sched_scan_timeout /= 2; + wpa_s->sched_scan_interval *= 2; + } + + return 0; +} + + +/** * wpa_supplicant_cancel_scan - Cancel a scheduled scan request * @wpa_s: Pointer to wpa_supplicant data * @@ -467,11 +1111,38 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) */ void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) { - wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request"); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); } +/** + * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to stop a periodic scheduled scan. + */ +void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->sched_scanning) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan"); + eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL); + wpa_supplicant_stop_sched_scan(wpa_s); +} + + +/** + * wpa_supplicant_notify_scanning - Indicate possible scan state change + * @wpa_s: Pointer to wpa_supplicant data + * @scanning: Whether scanning is currently in progress + * + * This function is to generate scanning notifycations. It is called whenever + * there may have been a change in scanning (scan started, completed, stopped). + * wpas_notify_scanning() is called whenever the scanning state changed from the + * previously notified state. + */ void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning) { @@ -504,6 +1175,15 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res) } +/** + * wpa_scan_get_ie - Fetch a specified information element from a scan result + * @res: Scan result entry + * @ie: Information element identitifier (WLAN_EID_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + */ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { const u8 *end, *pos; @@ -523,6 +1203,15 @@ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) } +/** + * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + */ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type) { @@ -544,6 +1233,16 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, } +/** + * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element payload or %NULL if not found + * + * This function returns concatenated payload of possibly fragmented vendor + * specific information elements in the scan result. The caller is responsible + * for freeing the returned buffer. + */ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, u32 vendor_type) { @@ -575,15 +1274,28 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, } +/* + * Channels with a great SNR can operate at full rate. What is a great SNR? + * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general + * rule of thumb is that any SNR above 20 is good." This one + * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23 + * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a + * conservative value. + */ +#define GREAT_SNR 30 + /* Compare function for sorting scan results. Return >0 if @b is considered * better. */ static int wpa_scan_result_compar(const void *a, const void *b) { +#define IS_5GHZ(n) (n > 4000) +#define MIN(a,b) a < b ? a : b struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int wpa_a, wpa_b, maxrate_a, maxrate_b; + int snr_a, snr_b; /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || @@ -604,17 +1316,78 @@ static int wpa_scan_result_compar(const void *a, const void *b) (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; - /* best/max rate preferred if signal level close enough XXX */ - if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || + if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) && + !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) { + snr_a = MIN(wa->level - wa->noise, GREAT_SNR); + snr_b = MIN(wb->level - wb->noise, GREAT_SNR); + } else { + /* Not suitable information to calculate SNR, so use level */ + snr_a = wa->level; + snr_b = wb->level; + } + + /* best/max rate preferred if SNR close enough */ + if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { maxrate_a = wpa_scan_get_max_rate(wa); maxrate_b = wpa_scan_get_max_rate(wb); if (maxrate_a != maxrate_b) return maxrate_b - maxrate_a; + if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq)) + return IS_5GHZ(wa->freq) ? -1 : 1; } /* use freq for channel preference */ + /* all things being equal, use SNR; if SNRs are + * identical, use quality values since some drivers may only report + * that value and leave the signal level zero */ + if (snr_b == snr_a) + return wb->qual - wa->qual; + return snr_b - snr_a; +#undef MIN +#undef IS_5GHZ +} + + +#ifdef CONFIG_WPS +/* Compare function for sorting scan results when searching a WPS AP for + * provisioning. Return >0 if @b is considered better. */ +static int wpa_scan_result_wps_compar(const void *a, const void *b) +{ + struct wpa_scan_res **_wa = (void *) a; + struct wpa_scan_res **_wb = (void *) b; + struct wpa_scan_res *wa = *_wa; + struct wpa_scan_res *wb = *_wb; + int uses_wps_a, uses_wps_b; + struct wpabuf *wps_a, *wps_b; + int res; + + /* Optimization - check WPS IE existence before allocated memory and + * doing full reassembly. */ + uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL; + uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL; + if (uses_wps_a && !uses_wps_b) + return -1; + if (!uses_wps_a && uses_wps_b) + return 1; + + if (uses_wps_a && uses_wps_b) { + wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE); + wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE); + res = wps_ap_priority_compar(wps_a, wps_b); + wpabuf_free(wps_a); + wpabuf_free(wps_b); + if (res) + return res; + } + + /* + * Do not use current AP security policy as a sorting criteria during + * WPS provisioning step since the AP may get reconfigured at the + * completion of provisioning. + */ + /* all things being equal, use signal level; if signal levels are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ @@ -622,6 +1395,99 @@ static int wpa_scan_result_compar(const void *a, const void *b) return wb->qual - wa->qual; return wb->level - wa->level; } +#endif /* CONFIG_WPS */ + + +static void dump_scan_res(struct wpa_scan_results *scan_res) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + size_t i; + + if (scan_res->res == NULL || scan_res->num == 0) + return; + + wpa_printf(MSG_EXCESSIVE, "Sorted scan results"); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *r = scan_res->res[i]; + u8 *pos; + if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID)) + == WPA_SCAN_LEVEL_DBM) { + int snr = r->level - r->noise; + wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " + "noise=%d level=%d snr=%d%s flags=0x%x", + MAC2STR(r->bssid), r->freq, r->qual, + r->noise, r->level, snr, + snr >= GREAT_SNR ? "*" : "", r->flags); + } else { + wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " + "noise=%d level=%d flags=0x%x", + MAC2STR(r->bssid), r->freq, r->qual, + r->noise, r->level, r->flags); + } + pos = (u8 *) (r + 1); + if (r->ie_len) + wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len); + pos += r->ie_len; + if (r->beacon_ie_len) + wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs", + pos, r->beacon_ie_len); + } +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +/** + * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to check + * Returns: 0 if the BSSID is filtered or 1 if not + * + * This function is used to filter out specific BSSIDs from scan reslts mainly + * for testing purposes (SET bssid_filter ctrl_iface command). + */ +int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + size_t i; + + if (wpa_s->bssid_filter == NULL) + return 1; + + for (i = 0; i < wpa_s->bssid_filter_count; i++) { + if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid, + ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +static void filter_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *res) +{ + size_t i, j; + + if (wpa_s->bssid_filter == NULL) + return; + + for (i = 0, j = 0; i < res->num; i++) { + if (wpa_supplicant_filter_bssid_match(wpa_s, + res->res[i]->bssid)) { + res->res[j++] = res->res[i]; + } else { + os_free(res->res[i]); + res->res[i] = NULL; + } + } + + if (res->num != j) { + wpa_printf(MSG_DEBUG, "Filtered out %d scan results", + (int) (res->num - j)); + res->num = j; + } +} /** @@ -641,18 +1507,26 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, { struct wpa_scan_results *scan_res; size_t i; + int (*compar)(const void *, const void *) = wpa_scan_result_compar; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - scan_res = ieee80211_sta_get_scan_results(wpa_s); - else - scan_res = wpa_drv_get_scan_results2(wpa_s); + scan_res = wpa_drv_get_scan_results2(wpa_s); if (scan_res == NULL) { - wpa_printf(MSG_DEBUG, "Failed to get scan results"); + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results"); return NULL; } + filter_scan_res(wpa_s, scan_res); + +#ifdef CONFIG_WPS + if (wpas_wps_in_progress(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS " + "provisioning rules"); + compar = wpa_scan_result_wps_compar; + } +#endif /* CONFIG_WPS */ qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), - wpa_scan_result_compar); + compar); + dump_scan_res(scan_res); wpa_bss_update_start(wpa_s); for (i = 0; i < scan_res->num; i++) @@ -663,6 +1537,18 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, } +/** + * wpa_supplicant_update_scan_results - Update scan results from the driver + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function updates the BSS table within wpa_supplicant based on the + * currently available scan results from the driver without requesting a new + * scan. This is used in cases where the driver indicates an association + * (including roaming within ESS) and wpa_supplicant does not yet have the + * needed information to complete the connection (e.g., to perform validation + * steps in 4-way handshake). + */ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) { struct wpa_scan_results *scan_res; @@ -673,17 +1559,3 @@ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) return 0; } - - -void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} |