Allow channel list to be specified for SCAN command
authorJouni Malinen <j@w1.fi>
Wed, 25 Dec 2013 18:04:52 +0000 (20:04 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 26 Dec 2013 14:55:44 +0000 (16:55 +0200)
The new freq=<frequency ranges> parameter to the SCAN command can be
used to request a scan to be performed on the specified set of channels
instead of on all channels. For example, "wpa_cli scan
freq=2400-2500,5180" would scan channels 1-14 and 36. Only the channels
that the driver indicates as enabled and that are within the specified
ranges are included in the request.

Signed-hostap: Jouni Malinen <j@w1.fi>

wpa_supplicant/ctrl_iface.c
wpa_supplicant/scan.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 4051fbc..e424c6f 100644 (file)
@@ -5213,6 +5213,92 @@ static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
 }
 
 
+static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val)
+{
+       struct wpa_freq_range_list ranges;
+       int *freqs = NULL;
+       struct hostapd_hw_modes *mode;
+       u16 i;
+
+       if (wpa_s->hw.modes == NULL)
+               return -1;
+
+       os_memset(&ranges, 0, sizeof(ranges));
+       if (freq_range_list_parse(&ranges, val) < 0)
+               return -1;
+
+       for (i = 0; i < wpa_s->hw.num_modes; i++) {
+               int j;
+
+               mode = &wpa_s->hw.modes[i];
+               for (j = 0; j < mode->num_channels; j++) {
+                       unsigned int freq;
+
+                       if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+                               continue;
+
+                       freq = mode->channels[j].freq;
+                       if (!freq_range_list_includes(&ranges, freq))
+                               continue;
+
+                       int_array_add_unique(&freqs, freq);
+               }
+       }
+
+       os_free(ranges.range);
+       os_free(wpa_s->manual_scan_freqs);
+       wpa_s->manual_scan_freqs = freqs;
+
+       return 0;
+}
+
+
+static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
+                          char *reply, int reply_size, int *reply_len)
+{
+       char *pos;
+
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+               *reply_len = -1;
+               return;
+       }
+
+       if (params) {
+               if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
+                       wpa_s->scan_res_handler = scan_only_handler;
+
+               pos = os_strstr(params, "freq=");
+               if (pos && set_scan_freqs(wpa_s, pos + 5) < 0) {
+                       *reply_len = -1;
+                       return;
+               }
+       } else {
+               os_free(wpa_s->manual_scan_freqs);
+               wpa_s->manual_scan_freqs = NULL;
+               if (wpa_s->scan_res_handler == scan_only_handler)
+                       wpa_s->scan_res_handler = NULL;
+       }
+
+       if (!wpa_s->sched_scanning && !wpa_s->scanning &&
+           ((wpa_s->wpa_state <= WPA_SCANNING) ||
+            (wpa_s->wpa_state == WPA_COMPLETED))) {
+               wpa_s->normal_scans = 0;
+               wpa_s->scan_req = MANUAL_SCAN_REQ;
+               wpa_s->after_wps = 0;
+               wpa_s->known_wps_freq = 0;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       } else if (wpa_s->sched_scanning) {
+               wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+               wpa_s->scan_req = MANUAL_SCAN_REQ;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       } else {
+               wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
+               *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+       }
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                                         char *buf, size_t *resp_len)
 {
@@ -5591,36 +5677,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                wpa_supplicant_cancel_scan(wpa_s);
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
-       } else if (os_strcmp(buf, "SCAN") == 0 ||
-                  os_strncmp(buf, "SCAN ", 5) == 0) {
-               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
-                       reply_len = -1;
-               else {
-                       if (os_strlen(buf) > 4 &&
-                           os_strncasecmp(buf + 5, "TYPE=ONLY", 9) == 0)
-                               wpa_s->scan_res_handler = scan_only_handler;
-                       if (!wpa_s->sched_scanning && !wpa_s->scanning &&
-                           ((wpa_s->wpa_state <= WPA_SCANNING) ||
-                            (wpa_s->wpa_state == WPA_COMPLETED))) {
-                               wpa_s->normal_scans = 0;
-                               wpa_s->scan_req = MANUAL_SCAN_REQ;
-                               wpa_s->after_wps = 0;
-                               wpa_s->known_wps_freq = 0;
-                               wpa_supplicant_req_scan(wpa_s, 0, 0);
-                       } else if (wpa_s->sched_scanning) {
-                               wpa_printf(MSG_DEBUG, "Stop ongoing "
-                                          "sched_scan to allow requested "
-                                          "full scan to proceed");
-                               wpa_supplicant_cancel_sched_scan(wpa_s);
-                               wpa_s->scan_req = MANUAL_SCAN_REQ;
-                               wpa_supplicant_req_scan(wpa_s, 0, 0);
-                       } else {
-                               wpa_printf(MSG_DEBUG, "Ongoing scan action - "
-                                          "reject new request");
-                               reply_len = os_snprintf(reply, reply_size,
-                                                       "FAIL-BUSY\n");
-                       }
-               }
+       } else if (os_strcmp(buf, "SCAN") == 0) {
+               wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
+       } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
+               wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);
        } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_scan_results(
                        wpa_s, reply, reply_size);
index 70ce767..c22ffbf 100644 (file)
@@ -711,6 +711,13 @@ ssid_list_set:
        wpa_supplicant_optimize_freqs(wpa_s, &params);
        extra_ie = wpa_supplicant_extra_ies(wpa_s);
 
+       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->next_scan_freqs) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
                        "generated frequency list");
@@ -799,6 +806,13 @@ scan:
 
        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);
index 897974b..095ab97 100644 (file)
@@ -459,6 +459,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        os_free(wpa_s->next_scan_freqs);
        wpa_s->next_scan_freqs = NULL;
 
+       os_free(wpa_s->manual_scan_freqs);
+       wpa_s->manual_scan_freqs = NULL;
+
        gas_query_deinit(wpa_s->gas);
        wpa_s->gas = NULL;
 
index 5cb7674..68f508a 100644 (file)
@@ -482,6 +482,7 @@ struct wpa_supplicant {
        struct os_reltime scan_trigger_time;
        int scan_runs; /* number of scan runs since WPS was started */
        int *next_scan_freqs;
+       int *manual_scan_freqs;
        int scan_interval; /* time in sec between scans to find suitable AP */
        int normal_scans; /* normal scans run before sched_scan */
        int scan_for_connection; /* whether the scan request was triggered for