]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Add support for configuring scheduled scan plans
authorAvraham Stern <avraham.stern@intel.com>
Tue, 17 Nov 2015 13:08:24 +0000 (15:08 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 30 Nov 2015 12:03:28 +0000 (14:03 +0200)
Add the option to configure scheduled scan plans in the config file.
Each scan plan specifies the interval between scans and the number
of scan iterations. The last plan will run infinitely and thus
specifies only the interval between scan iterations.

usage:
sched_scan_plans=<interval:iterations> <interval2:iterations2> ... <interval>

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
wpa_supplicant/autoscan.c
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/scan.c
wpa_supplicant/scan.h
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant.conf
wpa_supplicant/wpa_supplicant_i.h

index a2cf7a5ef23288655042c84d16de82de947ffc4e..d12eb21589fff5165e58f461ca0d57fbb7e6040f 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * WPA Supplicant - auto scan
  * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ * Copyright 2015      Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -50,6 +51,11 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
        size_t nlen;
        int i;
        const struct autoscan_ops *ops = NULL;
+       struct sched_scan_plan *scan_plans;
+
+       /* Give preference to scheduled scan plans if supported/configured */
+       if (wpa_s->sched_scan_plans)
+               return 0;
 
        if (wpa_s->autoscan && wpa_s->autoscan_priv)
                return 0;
@@ -79,11 +85,23 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
                return -1;
        }
 
+       scan_plans = os_malloc(sizeof(*wpa_s->sched_scan_plans));
+       if (!scan_plans)
+               return -1;
+
        wpa_s->autoscan_params = NULL;
 
        wpa_s->autoscan_priv = ops->init(wpa_s, params);
-       if (wpa_s->autoscan_priv == NULL)
+       if (!wpa_s->autoscan_priv) {
+               os_free(scan_plans);
                return -1;
+       }
+
+       scan_plans[0].interval = 5;
+       scan_plans[0].iterations = 0;
+       os_free(wpa_s->sched_scan_plans);
+       wpa_s->sched_scan_plans = scan_plans;
+       wpa_s->sched_scan_plans_num = 1;
        wpa_s->autoscan = ops;
 
        wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
@@ -116,7 +134,10 @@ void autoscan_deinit(struct wpa_supplicant *wpa_s)
                wpa_s->autoscan_priv = NULL;
 
                wpa_s->scan_interval = 5;
-               wpa_s->sched_scan_interval = 0;
+
+               os_free(wpa_s->sched_scan_plans);
+               wpa_s->sched_scan_plans = NULL;
+               wpa_s->sched_scan_plans_num = 0;
        }
 }
 
@@ -134,7 +155,7 @@ int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
                        return -1;
 
                wpa_s->scan_interval = interval;
-               wpa_s->sched_scan_interval = interval;
+               wpa_s->sched_scan_plans[0].interval = interval;
 
                request_scan(wpa_s);
        }
index bdcfe9f72516601069b499ca730dc726383f6510..b8b19de895adbcebd0c9c9cd12fbf5360de59d38 100644 (file)
@@ -2281,6 +2281,8 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->bgscan);
        os_free(config->wowlan_triggers);
        os_free(config->fst_group_id);
+       os_free(config->sched_scan_plans);
+
        os_free(config);
 }
 
@@ -4258,6 +4260,7 @@ static const struct global_parse_data global_fields[] = {
        { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
 #endif /* CONFIG_FST */
        { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
+       { STR(sched_scan_plans), 0 },
 };
 
 #undef FUNC
index 2dd14757473237c0c20697ec712f7e80e3e02b84..e93a0a3f8e2147adf3286dcea9bc2bd1881a39a1 100644 (file)
@@ -1263,6 +1263,17 @@ struct wpa_config {
          * of 4-Way Handshake or message 1 of Group Key Handshake.
          */
         int wpa_rsc_relaxation;
+
+       /**
+        * sched_scan_plans - Scan plans for scheduled scan
+        *
+        * Each scan plan specifies the interval between scans and the number of
+        * iterations. The last scan plan only specifies the scan interval and
+        * will be run infinitely.
+        *
+        * format: <interval:iterations> <interval2:iterations2> ... <interval>
+        */
+        char *sched_scan_plans;
 };
 
 
index be721579150ebe44e723ea7cbe762abf1abe5b4a..80e3e56009793aaf551b397fbc5c8868b5911d1a 100644 (file)
@@ -1304,6 +1304,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
        if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION)
                fprintf(f, "wpa_rsc_relaxation=%d\n",
                        config->wpa_rsc_relaxation);
+
+       if (config->sched_scan_plans)
+               fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
index 25a1011b32873006defd21ac26b2519393893ba1..fd01f4871e85f7b15243ff7ed3442b626ba84744 100644 (file)
@@ -267,17 +267,9 @@ wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 
 
 int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-                                   struct wpa_driver_scan_params *params,
-                                   int interval)
+                                   struct wpa_driver_scan_params *params)
 {
        int ret;
-       struct sched_scan_plan scan_plan = {
-               .interval = interval,
-               .iterations = 0,
-       };
-
-       params->sched_scan_plans = &scan_plan;
-       params->sched_scan_plans_num = 1;
 
        wpa_supplicant_notify_scanning(wpa_s, 1);
        ret = wpa_drv_sched_scan(wpa_s, params);
@@ -286,8 +278,6 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
        else
                wpa_s->sched_scanning = 1;
 
-       params->sched_scan_plans = NULL;
-       params->sched_scan_plans_num = 0;
        return ret;
 }
 
@@ -1191,6 +1181,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
        unsigned int max_sched_scan_ssids;
        int wildcard = 0;
        int need_ssids;
+       struct sched_scan_plan scan_plan;
 
        if (!wpa_s->sched_scan_supported)
                return -1;
@@ -1280,11 +1271,6 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
 
        if (!ssid || !wpa_s->prev_sched_ssid) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
-               if (wpa_s->conf->sched_scan_interval)
-                       wpa_s->sched_scan_interval =
-                               wpa_s->conf->sched_scan_interval;
-               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;
@@ -1369,14 +1355,51 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
        scan_params = &params;
 
 scan:
-       if (ssid || !wpa_s->first_sched_scan) {
+       wpa_s->sched_scan_timed_out = 0;
+
+       /*
+        * We cannot support multiple scan plans if the scan request includes
+        * too many SSID's, so in this case use only the last scan plan and make
+        * it run infinitely. It will be stopped by the timeout.
+        */
+       if (wpa_s->sched_scan_plans_num == 1 ||
+           (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
+               params.sched_scan_plans = wpa_s->sched_scan_plans;
+               params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+       } else if (wpa_s->sched_scan_plans_num > 1) {
                wpa_dbg(wpa_s, MSG_DEBUG,
-                       "Starting sched scan: interval %d timeout %d",
-                       wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+                       "Too many SSIDs. Default to using single scheduled_scan plan");
+               params.sched_scan_plans =
+                       &wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
+                                                1];
+               params.sched_scan_plans_num = 1;
        } else {
+               if (wpa_s->conf->sched_scan_interval)
+                       scan_plan.interval = wpa_s->conf->sched_scan_interval;
+               else
+                       scan_plan.interval = 10;
+
+               if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
+                       wpa_printf(MSG_WARNING,
+                                  "Scan interval too long(%u), use the maximum allowed(%u)",
+                                  scan_plan.interval,
+                                  wpa_s->max_sched_scan_plan_interval);
+                       scan_plan.interval =
+                               wpa_s->max_sched_scan_plan_interval;
+               }
+
+               scan_plan.iterations = 0;
+               params.sched_scan_plans = &scan_plan;
+               params.sched_scan_plans_num = 1;
+       }
+
+       if (ssid || !wpa_s->first_sched_scan) {
                wpa_dbg(wpa_s, MSG_DEBUG,
-                       "Starting sched scan: interval %d (no timeout)",
-                       wpa_s->sched_scan_interval);
+                       "Starting sched scan: interval %u timeout %d",
+                       params.sched_scan_plans[0].interval,
+                       wpa_s->sched_scan_timeout);
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
        }
 
        wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -1390,8 +1413,7 @@ scan:
                }
        }
 
-       ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
-                                             wpa_s->sched_scan_interval);
+       ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
        wpabuf_free(extra_ie);
        os_free(params.filter_ssids);
        if (ret) {
@@ -1409,9 +1431,12 @@ scan:
                                       wpa_s, NULL);
                wpa_s->first_sched_scan = 0;
                wpa_s->sched_scan_timeout /= 2;
-               wpa_s->sched_scan_interval *= 2;
-               if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
-                       wpa_s->sched_scan_interval = 10;
+               params.sched_scan_plans[0].interval *= 2;
+               if ((unsigned int) wpa_s->sched_scan_timeout <
+                   params.sched_scan_plans[0].interval ||
+                   params.sched_scan_plans[0].interval >
+                   wpa_s->max_sched_scan_plan_interval) {
+                       params.sched_scan_plans[0].interval = 10;
                        wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
                }
        }
@@ -2290,10 +2315,11 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params)
 
 int wpas_start_pno(struct wpa_supplicant *wpa_s)
 {
-       int ret, interval, prio;
+       int ret, prio;
        size_t i, num_ssid, num_match_ssid;
        struct wpa_ssid *ssid;
        struct wpa_driver_scan_params params;
+       struct sched_scan_plan scan_plan;
 
        if (!wpa_s->sched_scan_supported)
                return -1;
@@ -2387,8 +2413,20 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
        if (wpa_s->conf->filter_rssi)
                params.filter_rssi = wpa_s->conf->filter_rssi;
 
-       interval = wpa_s->conf->sched_scan_interval ?
-               wpa_s->conf->sched_scan_interval : 10;
+       if (wpa_s->sched_scan_plans_num) {
+               params.sched_scan_plans = wpa_s->sched_scan_plans;
+               params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+       } else {
+               /* Set one scan plan that will run infinitely */
+               if (wpa_s->conf->sched_scan_interval)
+                       scan_plan.interval = wpa_s->conf->sched_scan_interval;
+               else
+                       scan_plan.interval = 10;
+
+               scan_plan.iterations = 0;
+               params.sched_scan_plans = &scan_plan;
+               params.sched_scan_plans_num = 1;
+       }
 
        if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
@@ -2403,7 +2441,7 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
                }
        }
 
-       ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+       ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
        os_free(params.filter_ssids);
        if (ret == 0)
                wpa_s->pno = 1;
@@ -2499,3 +2537,114 @@ int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
 
        return 0;
 }
+
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+       struct sched_scan_plan *scan_plans = NULL;
+       const char *token, *context = NULL;
+       unsigned int num = 0;
+
+       if (!cmd)
+               return -1;
+
+       while ((token = cstr_token(cmd, " ", &context))) {
+               int ret;
+               struct sched_scan_plan *scan_plan, *n;
+
+               n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
+               if (!n)
+                       goto fail;
+
+               scan_plans = n;
+               scan_plan = &scan_plans[num];
+               num++;
+
+               ret = sscanf(token, "%u:%u", &scan_plan->interval,
+                            &scan_plan->iterations);
+               if (ret <= 0 || ret > 2 || !scan_plan->interval) {
+                       wpa_printf(MSG_ERROR,
+                                  "Invalid sched scan plan input: %s", token);
+                       goto fail;
+               }
+
+               if (!scan_plan->interval) {
+                       wpa_printf(MSG_ERROR,
+                                  "scan plan %u: Interval cannot be zero",
+                                  num);
+                       goto fail;
+               }
+
+               if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
+                       wpa_printf(MSG_WARNING,
+                                  "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
+                                  num, scan_plan->interval,
+                                  wpa_s->max_sched_scan_plan_interval);
+                       scan_plan->interval =
+                               wpa_s->max_sched_scan_plan_interval;
+               }
+
+               if (ret == 1) {
+                       scan_plan->iterations = 0;
+                       break;
+               }
+
+               if (!scan_plan->iterations) {
+                       wpa_printf(MSG_ERROR,
+                                  "scan plan %u: Number of iterations cannot be zero",
+                                  num);
+                       goto fail;
+               }
+
+               if (scan_plan->iterations >
+                   wpa_s->max_sched_scan_plan_iterations) {
+                       wpa_printf(MSG_WARNING,
+                                  "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
+                                  num, scan_plan->iterations,
+                                  wpa_s->max_sched_scan_plan_iterations);
+                       scan_plan->iterations =
+                               wpa_s->max_sched_scan_plan_iterations;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "scan plan %u: interval=%u iterations=%u",
+                          num, scan_plan->interval, scan_plan->iterations);
+       }
+
+       if (!scan_plans) {
+               wpa_printf(MSG_ERROR, "Invalid scan plans entry");
+               goto fail;
+       }
+
+       if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
+               wpa_printf(MSG_ERROR,
+                          "All scan plans but the last must specify a number of iterations");
+               goto fail;
+       }
+
+       wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
+                  num, scan_plans[num - 1].interval);
+
+       if (num > wpa_s->max_sched_scan_plans) {
+               wpa_printf(MSG_WARNING,
+                          "Too many scheduled scan plans (only %u supported)",
+                          wpa_s->max_sched_scan_plans);
+               wpa_printf(MSG_WARNING,
+                          "Use only the first %u scan plans, and the last one (in infinite loop)",
+                          wpa_s->max_sched_scan_plans - 1);
+               os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
+                         &scan_plans[num - 1], sizeof(*scan_plans));
+               num = wpa_s->max_sched_scan_plans;
+       }
+
+       os_free(wpa_s->sched_scan_plans);
+       wpa_s->sched_scan_plans = scan_plans;
+       wpa_s->sched_scan_plans_num = num;
+
+       return 0;
+
+fail:
+       os_free(scan_plans);
+       wpa_printf(MSG_ERROR, "invalid scan plans list");
+       return -1;
+}
index 0f1c8e49833965f96f021b8780b3484262195b7e..93ec9b38b071baa150b11b8bcec9bacf6c891665 100644 (file)
@@ -40,8 +40,7 @@ void scan_only_handler(struct wpa_supplicant *wpa_s,
                       struct wpa_scan_results *scan_res);
 int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-                                   struct wpa_driver_scan_params *params,
-                                   int interval);
+                                   struct wpa_driver_scan_params *params);
 int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
 struct wpa_driver_scan_params *
 wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
index 94b5eff34c58e5da6259dffa206b98cb47d931a0..c9b78de1da0715b7dadb0cdaf629aedfb26c4741 100644 (file)
@@ -545,6 +545,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        }
 
        wmm_ac_notify_disassoc(wpa_s);
+
+       wpa_s->sched_scan_plans_num = 0;
+       os_free(wpa_s->sched_scan_plans);
+       wpa_s->sched_scan_plans = NULL;
 }
 
 
@@ -4600,6 +4604,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
                wpa_s->max_scan_ssids = capa.max_scan_ssids;
                wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+               wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
+               wpa_s->max_sched_scan_plan_interval =
+                       capa.max_sched_scan_plan_interval;
+               wpa_s->max_sched_scan_plan_iterations =
+                       capa.max_sched_scan_plan_iterations;
                wpa_s->sched_scan_supported = capa.sched_scan_supported;
                wpa_s->max_match_sets = capa.max_match_sets;
                wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
@@ -4739,6 +4748,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 
        wpas_rrm_reset(wpa_s);
 
+       wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
        return 0;
 }
 
index bcb6247f4758ac4e8c9233f8f12fcd8c59c3ac5a..2ce1cc4e376c45351de36337bf4ee25a03ab6873 100644 (file)
@@ -314,7 +314,9 @@ fast_reauth=1
 # up to the limit of 300 seconds (3, 9, 27 ... 300)
 # For periodic module, parameters would be <fixed interval>
 #autoscan=periodic:30
-# So a delay of 30 seconds will be applied between each scan
+# So a delay of 30 seconds will be applied between each scan.
+# Note: If sched_scan_plans are configured and supported by the driver,
+# autoscan is ignored.
 
 # filter_ssids - SSID-based scan result filtering
 # 0 = do not filter scan results (default)
@@ -616,6 +618,27 @@ fast_reauth=1
 # Hotspot 2.0
 # hs20=1
 
+# Scheduled scan plans
+#
+# A space delimited list of scan plans. Each scan plan specifies the scan
+# interval and number of iterations, delimited by a colon. The last scan plan
+# will run infinitely and thus must specify only the interval and not the number
+# of iterations.
+#
+# The driver advertises the maximum number of scan plans supported. If more scan
+# plans than supported are configured, only the first ones are set (up to the
+# maximum supported). The last scan plan that specifies only the interval is
+# always set as the last plan.
+#
+# If the scan interval or the number of iterations for a scan plan exceeds the
+# maximum supported, it will be set to the maximum supported value.
+#
+# Format:
+# sched_scan_plans=<interval:iterations> <interval:iterations> ... <interval>
+#
+# Example:
+# sched_scan_plans=10:100 20:200 30
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
index c66539d07deb70de504da085ec506a4f509fd812..a8b273bc087d999dd2727debc67636a6f91c479e 100644 (file)
@@ -511,9 +511,10 @@ struct wpa_supplicant {
 
        struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
        int sched_scan_timeout;
-       int sched_scan_interval;
        int first_sched_scan;
        int sched_scan_timed_out;
+       struct sched_scan_plan *sched_scan_plans;
+       size_t sched_scan_plans_num;
 
        void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
                                 struct wpa_scan_results *scan_res);
@@ -645,6 +646,9 @@ struct wpa_supplicant {
 
        int max_scan_ssids;
        int max_sched_scan_ssids;
+       unsigned int max_sched_scan_plans;
+       unsigned int max_sched_scan_plan_interval;
+       unsigned int max_sched_scan_plan_iterations;
        int sched_scan_supported;
        unsigned int max_match_sets;
        unsigned int max_remain_on_chan;
@@ -1184,4 +1188,6 @@ void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
 
 #endif /* CONFIG_FST */
 
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
+
 #endif /* WPA_SUPPLICANT_I_H */