* WPA Supplicant - Scanning
* Copyright (c) 2003-2010, 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"
#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"
}
#ifdef CONFIG_P2P
- wpa_s->wps->dev.p2p = 1;
- if (!wps) {
- wps = 1;
- *req_type = WPS_REQ_ENROLLEE_INFO;
+ if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p) {
+ wpa_s->wps->dev.p2p = 1;
+ if (!wps) {
+ wps = 1;
+ *req_type = WPS_REQ_ENROLLEE_INFO;
+ }
}
#endif /* CONFIG_P2P */
#endif /* CONFIG_WPS */
-int wpa_supplicant_enabled_networks(struct wpa_config *conf)
+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;
while (ssid) {
- if (!ssid->disabled)
+ if (!wpas_network_disabled(wpa_s, ssid))
count++;
ssid = ssid->next;
}
struct wpa_ssid *ssid)
{
while (ssid) {
- if (!ssid->disabled)
+ if (!wpas_network_disabled(wpa_s, ssid))
break;
ssid = ssid->next;
}
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;
}
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 */
}
if (wps) {
struct wpabuf *wps_ie;
- wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
+ 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) {
int scan_req = 0, ret;
struct wpabuf *extra_ie;
struct wpa_driver_scan_params params;
+ struct wpa_driver_scan_params *scan_params;
size_t max_ssids;
enum wpa_states prev_state;
return;
}
- if (!wpa_supplicant_enabled_networks(wpa_s->conf) &&
+ if (!wpa_supplicant_enabled_networks(wpa_s) &&
!wpa_s->scan_req) {
wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
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 != 2 && wpa_s->connect_without_scan) {
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == wpa_s->connect_without_scan)
}
}
+#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) {
if (scan_req != 2 && 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) {
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 =
}
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,
int_array_sort_unique(params.freqs);
}
- if (ssid) {
- wpa_s->prev_scan_ssid = ssid;
- if (max_ssids > 1) {
- wpa_dbg(wpa_s, 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_dbg(wpa_s, 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_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, ¶ms);
+#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");
}
#ifdef CONFIG_P2P
- if (wpa_s->p2p_in_provisioning) {
+ if (wpa_s->p2p_in_provisioning ||
+ (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.
}
#endif /* CONFIG_P2P */
- ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms);
+ scan_params = ¶ms;
+
+scan:
+ ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
wpabuf_free(extra_ie);
os_free(params.freqs);
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;
}
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;
+ struct wpa_ssid *ssid = NULL;
struct wpabuf *wps_ie = NULL;
int ret;
unsigned int max_sched_scan_ssids;
+ int wildcard = 0;
+ int need_ssids;
if (!wpa_s->sched_scan_supported)
return -1;
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 */
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) {
if (!ssid || !wpa_s->prev_sched_ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
- wpa_s->sched_scan_interval = 2;
+ 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 (ssid->disabled) {
- wpa_s->prev_sched_ssid = ssid;
- ssid = ssid->next;
- continue;
- }
+ 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) {
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.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;
}
}
- if (params.num_filter_ssids >= wpa_s->max_match_sets)
- break;
+ next:
wpa_s->prev_sched_ssid = ssid;
ssid = ssid->next;
}
if (wpa_s->wps)
wps_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms);
- wpa_dbg(wpa_s, MSG_DEBUG,
- "Starting sched scan: interval %d timeout %d",
- wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+ 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, ¶ms,
+ ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
wpa_s->sched_scan_interval);
wpabuf_free(wps_ie);
os_free(params.filter_ssids);
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)
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;
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 */
+}
+
+
+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;
}
}
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)) {