Add support for specifying subset of enabled frequencies to scan
authorJouni Malinen <j@w1.fi>
Thu, 26 Mar 2009 19:55:01 +0000 (21:55 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 26 Mar 2009 19:55:01 +0000 (21:55 +0200)
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

src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/config.c
wpa_supplicant/config_ssid.h
wpa_supplicant/scan.c
wpa_supplicant/wpa_supplicant.conf

index 8257991..80205a4 100644 (file)
@@ -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;
 };
 
 /**
index a2f0fc7..4a286b5 100644 (file)
@@ -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;
 }
 
index 355b38f..5f7f34f 100644 (file)
@@ -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);
 }
 
index 3ebf228..c120360 100644 (file)
@@ -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 */
index 0726f53..1f53e23 100644 (file)
@@ -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.");
index 2e6a152..49ed96d 100644 (file)
@@ -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)