Browse Source

Add support for specifying subset of enabled frequencies to scan

A new network block parameter, scan_freq, can be used to specify subset
of frequencies to scan. This can speed up scanning process considerably
if it is known that only a small subset of channels is actually used in
the network. A union of configured frequencies for all enabled network
blocks is used in scan requests.

Currently, only driver_nl80211.c has support for this functionality.

For example, following parameter marks 2.4 GHz channels 1, 6, 11 to be
scanned: scan_freq=2412 2437 2462
Jouni Malinen 16 years ago
parent
commit
d3a9822542

+ 8 - 1
src/drivers/driver.h

@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - driver interface definition
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2009, 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
@@ -188,6 +188,13 @@ struct wpa_driver_scan_params {
 	 * extra_ies_len - Length of extra_ies in octets
 	 */
 	size_t extra_ies_len;
+
+	/**
+	 * freqs - Array of frequencies to scan or %NULL for all frequencies
+	 *
+	 * The frequency is set in MHz. The array is zero-terminated.
+	 */
+	int *freqs;
 };
 
 /**

+ 11 - 2
src/drivers/driver_nl80211.c

@@ -1596,14 +1596,16 @@ static int wpa_driver_nl80211_scan(void *priv,
 {
 	struct wpa_driver_nl80211_data *drv = priv;
 	int ret = 0, timeout;
-	struct nl_msg *msg, *ssids;
+	struct nl_msg *msg, *ssids, *freqs;
 	size_t i;
 
 	msg = nlmsg_alloc();
 	ssids = nlmsg_alloc();
-	if (!msg || !ssids) {
+	freqs = nlmsg_alloc();
+	if (!msg || !ssids || !freqs) {
 		nlmsg_free(msg);
 		nlmsg_free(ssids);
+		nlmsg_free(freqs);
 		return -1;
 	}
 
@@ -1624,6 +1626,12 @@ static int wpa_driver_nl80211_scan(void *priv,
 			params->extra_ies);
 	}
 
+	if (params->freqs) {
+		for (i = 0; params->freqs[i]; i++)
+			NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
+		nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -1652,6 +1660,7 @@ static int wpa_driver_nl80211_scan(void *priv,
 nla_put_failure:
 	nlmsg_free(ssids);
 	nlmsg_free(msg);
+	nlmsg_free(freqs);
 	return ret;
 }
 

+ 83 - 0
wpa_supplicant/config.c

@@ -917,6 +917,87 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_scan_freq(const struct parse_data *data,
+				      struct wpa_ssid *ssid, int line,
+				      const char *value)
+{
+	int *freqs;
+	size_t used, len;
+	const char *pos;
+
+	used = 0;
+	len = 10;
+	freqs = os_zalloc((len + 1) * sizeof(int));
+	if (freqs == NULL)
+		return -1;
+
+	pos = value;
+	while (pos) {
+		while (*pos == ' ')
+			pos++;
+		if (used == len) {
+			int *n;
+			size_t i;
+			n = os_realloc(freqs, (len * 2 + 1) * sizeof(int));
+			if (n == NULL) {
+				os_free(freqs);
+				return -1;
+			}
+			for (i = len; i <= len * 2; i++)
+				n[i] = 0;
+			freqs = n;
+			len *= 2;
+		}
+
+		freqs[used] = atoi(pos);
+		if (freqs[used] == 0)
+			break;
+		used++;
+		pos = os_strchr(pos + 1, ' ');
+	}
+
+	os_free(ssid->scan_freq);
+	ssid->scan_freq = freqs;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_scan_freq(const struct parse_data *data,
+					 struct wpa_ssid *ssid)
+{
+	char *buf, *pos, *end;
+	int i, ret;
+	size_t count;
+
+	if (ssid->scan_freq == NULL)
+		return NULL;
+
+	count = 0;
+	for (i = 0; ssid->scan_freq[i]; i++)
+		count++;
+
+	pos = buf = os_zalloc(10 * count + 1);
+	if (buf == NULL)
+		return NULL;
+	end = buf + 10 * count + 1;
+
+	for (i = 0; ssid->scan_freq[i]; i++) {
+		ret = os_snprintf(pos, end - pos, "%s%u",
+				  i == 0 ? "" : " ", ssid->scan_freq[i]);
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 #ifdef IEEE8021X_EAPOL
 static int wpa_config_parse_eap(const struct parse_data *data,
 				struct wpa_ssid *ssid, int line,
@@ -1317,6 +1398,7 @@ static const struct parse_data ssid_fields[] = {
 	{ FUNC(pairwise) },
 	{ FUNC(group) },
 	{ FUNC(auth_alg) },
+	{ FUNC(scan_freq) },
 #ifdef IEEE8021X_EAPOL
 	{ FUNC(eap) },
 	{ STR_LENe(identity) },
@@ -1540,6 +1622,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
 	eap_peer_config_free(&ssid->eap);
 #endif /* IEEE8021X_EAPOL */
 	os_free(ssid->id_str);
+	os_free(ssid->scan_freq);
 	os_free(ssid);
 }
 

+ 10 - 0
wpa_supplicant/config_ssid.h

@@ -340,6 +340,16 @@ struct wpa_ssid {
 	 * attacks against TKIP deficiencies.
 	 */
 	int wpa_ptk_rekey;
+
+	/**
+	 * scan_freq - Array of frequencies to scan or %NULL for all
+	 *
+	 * This is an optional zero-terminated array of frequencies in
+	 * megahertz (MHz) to include in scan requests when searching for this
+	 * network. This can be used to speed up scanning when the network is
+	 * known to not use all possible channels.
+	 */
+	int *scan_freq;
 };
 
 #endif /* CONFIG_SSID_H */

+ 83 - 0
wpa_supplicant/scan.c

@@ -107,6 +107,73 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
 }
 
 
+static int int_array_len(const int *a)
+{
+	int i;
+	for (i = 0; a && a[i]; i++)
+		;
+	return i;
+}
+
+
+static void int_array_concat(int **res, const int *a)
+{
+	int reslen, alen, i;
+	int *n;
+
+	reslen = int_array_len(*res);
+	alen = int_array_len(a);
+
+	n = os_realloc(*res, (reslen + alen + 1) * sizeof(int));
+	if (n == NULL) {
+		os_free(*res);
+		*res = NULL;
+	}
+	for (i = 0; i <= alen; i++)
+		n[reslen + i] = a[i];
+	*res = n;
+}
+
+
+static int freq_cmp(const void *a, const void *b)
+{
+	int _a = *(int *) a;
+	int _b = *(int *) b;
+
+	if (_a == 0)
+		return 1;
+	if (_b == 0)
+		return -1;
+	return _a - _b;
+}
+
+
+static void int_array_sort_unique(int *a)
+{
+	int alen;
+	int i, j;
+
+	if (a == NULL)
+		return;
+
+	alen = int_array_len(a);
+	qsort(a, alen, sizeof(int), freq_cmp);
+
+	i = 0;
+	j = 1;
+	while (a[i] && a[j]) {
+		if (a[i] == a[j]) {
+			j++;
+			continue;
+		}
+		a[++i] = a[j++];
+	}
+	if (a[i])
+		i++;
+	a[i] = 0;
+}
+
+
 static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -198,6 +265,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 		ssid = NULL;
 	} else {
 		struct wpa_ssid *start = ssid;
+		int freqs_set = 0;
 		if (ssid == NULL && max_ssids > 1)
 			ssid = wpa_s->conf->ssid;
 		while (ssid) {
@@ -219,6 +287,20 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 			    start != wpa_s->conf->ssid)
 				ssid = wpa_s->conf->ssid;
 		}
+
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+			if (ssid->disabled)
+				continue;
+			if ((params.freqs || !freqs_set) && ssid->scan_freq) {
+				int_array_concat(&params.freqs,
+						 ssid->scan_freq);
+			} else {
+				os_free(params.freqs);
+				params.freqs = NULL;
+			}
+			freqs_set = 1;
+		}
+		int_array_sort_unique(params.freqs);
 	}
 
 	if (ssid) {
@@ -258,6 +340,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 	}
 
 	wpabuf_free(wps_ie);
+	os_free(params.freqs);
 
 	if (ret) {
 		wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");

+ 6 - 0
wpa_supplicant/wpa_supplicant.conf

@@ -256,6 +256,12 @@ fast_reauth=1
 # an IBSS network with the configured SSID is already present, the frequency of
 # the network will be used instead of this configured value.
 #
+# scan_freq: List of frequencies to scan
+# Space-separated list of frequencies in MHz to scan when searching for this
+# BSS. If the subset of channels used by the network is known, this option can
+# be used to optimize scanning to not occur on channels that the network does
+# not use. Example: scan_freq=2412 2437 2462
+#
 # proto: list of accepted protocols
 # WPA = WPA/IEEE 802.11i/D3.0
 # RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)