Add support for configuring scheduled scan plans

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>
This commit is contained in:
Avraham Stern 2015-11-17 15:08:24 +02:00 committed by Jouni Malinen
parent 09ea4309b6
commit 32c02261dd
9 changed files with 263 additions and 37 deletions

View 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);
}

View 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

View 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;
};

View 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 */

View 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:
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,
"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 timeout %d",
wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
"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: interval %d (no timeout)",
wpa_s->sched_scan_interval);
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;
}

View 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);

View 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;
}

View 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

View 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 */