diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 74b268993..354f917f7 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1037,6 +1037,17 @@ OBJS_p += ../src/crypto/aes-internal-dec.o OBJS_p += ../src/crypto/aes-internal-enc.o endif +ifdef CONFIG_BGSCAN_SIMPLE +CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.o +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.o +endif + OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o ifdef CONFIG_AUTHENTICATOR diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c new file mode 100644 index 000000000..60dcd0d67 --- /dev/null +++ b/wpa_supplicant/bgscan.c @@ -0,0 +1,110 @@ +/* + * WPA Supplicant - background scan and roaming interface + * Copyright (c) 2009, Jouni Malinen + * + * 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 + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "config_ssid.h" +#include "bgscan.h" + +#ifdef CONFIG_BGSCAN_SIMPLE +extern const struct bgscan_ops bgscan_simple_ops; +#endif /* CONFIG_BGSCAN_SIMPLE */ + +static const struct bgscan_ops * bgscan_modules[] = { +#ifdef CONFIG_BGSCAN_SIMPLE + &bgscan_simple_ops, +#endif /* CONFIG_BGSCAN_SIMPLE */ + NULL +}; + + +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + const char *name = ssid->bgscan; + const char *params; + size_t nlen; + int i; + const struct bgscan_ops *ops = NULL; + + bgscan_deinit(wpa_s); + if (name == NULL) + return 0; + + params = os_strchr(name, ':'); + if (params == NULL) { + params = ""; + nlen = os_strlen(name); + } else { + nlen = params - name; + params++; + } + + for (i = 0; bgscan_modules[i]; i++) { + if (os_strncmp(name, bgscan_modules[i]->name, nlen) == 0) { + ops = bgscan_modules[i]; + break; + } + } + + if (ops == NULL) { + wpa_printf(MSG_ERROR, "bgscan: Could not find module " + "matching the parameter '%s'", name); + return -1; + } + + wpa_s->bgscan_priv = ops->init(wpa_s, params, ssid); + if (wpa_s->bgscan_priv == NULL) + return -1; + wpa_s->bgscan = ops; + wpa_printf(MSG_DEBUG, "bgscan: Initialized module '%s' with " + "parameters '%s'", ops->name, params); + + return 0; +} + + +void bgscan_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) { + wpa_printf(MSG_DEBUG, "bgscan: Deinitializing module '%s'", + wpa_s->bgscan->name); + wpa_s->bgscan->deinit(wpa_s->bgscan_priv); + wpa_s->bgscan = NULL; + wpa_s->bgscan_priv = NULL; + } +} + + +int bgscan_notify_scan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) + return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv); + return 0; +} + + +void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) + wpa_s->bgscan->notify_beacon_loss(wpa_s->bgscan_priv); +} + + +void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) + wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv); +} diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h new file mode 100644 index 000000000..dfd4cab8a --- /dev/null +++ b/wpa_supplicant/bgscan.h @@ -0,0 +1,68 @@ +/* + * WPA Supplicant - background scan and roaming interface + * Copyright (c) 2009, Jouni Malinen + * + * 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 + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BGSCAN_H +#define BGSCAN_H + +struct wpa_supplicant; +struct wpa_ssid; + +struct bgscan_ops { + const char *name; + + void * (*init)(struct wpa_supplicant *wpa_s, const char *params, + const struct wpa_ssid *ssid); + void (*deinit)(void *priv); + + int (*notify_scan)(void *priv); + void (*notify_beacon_loss)(void *priv); + void (*notify_signal_change)(void *priv); +}; + +#ifdef CONFIG_BGSCAN + +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void bgscan_deinit(struct wpa_supplicant *wpa_s); +int bgscan_notify_scan(struct wpa_supplicant *wpa_s); +void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s); +void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s); + +#else /* CONFIG_BGSCAN */ + +static inline int bgscan_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + return 0; +} + +static inline void bgscan_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s) +{ +} + +static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s) +{ +} + +#endif /* CONFIG_BGSCAN */ + +#endif /* BGSCAN_H */ diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c new file mode 100644 index 000000000..dacddff4f --- /dev/null +++ b/wpa_supplicant/bgscan_simple.c @@ -0,0 +1,130 @@ +/* + * WPA Supplicant - background scan and roaming module: simple + * Copyright (c) 2009, Jouni Malinen + * + * 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 + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "drivers/driver.h" +#include "config_ssid.h" +#include "wpa_supplicant_i.h" +#include "bgscan.h" + +struct bgscan_simple_data { + struct wpa_supplicant *wpa_s; + const struct wpa_ssid *ssid; + int scan_interval; +}; + + +static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct bgscan_simple_data *data = eloop_ctx; + struct wpa_supplicant *wpa_s = data->wpa_s; + struct wpa_driver_scan_params params; + + os_memset(¶ms, 0, sizeof(params)); + params.num_ssids = 1; + params.ssids[0].ssid = data->ssid->ssid; + params.ssids[0].ssid_len = data->ssid->ssid_len; + params.freqs = data->ssid->scan_freq; + + /* + * A more advanced bgscan module would learn about most like channels + * over time and request scans only for some channels (probing others + * every now and then) to reduce effect on the data connection. + */ + + wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan"); + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { + wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan"); + eloop_register_timeout(data->scan_interval, 0, + bgscan_simple_timeout, data, NULL); + } +} + + +static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, + const char *params, + const struct wpa_ssid *ssid) +{ + struct bgscan_simple_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->wpa_s = wpa_s; + data->ssid = ssid; + if (params) + data->scan_interval = atoi(params); + if (data->scan_interval <= 0) + data->scan_interval = 30; + eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, + data, NULL); + return data; +} + + +static void bgscan_simple_deinit(void *priv) +{ + struct bgscan_simple_data *data = priv; + eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); + os_free(data); +} + + +static int bgscan_simple_notify_scan(void *priv) +{ + struct bgscan_simple_data *data = priv; + + wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification"); + + eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, + data, NULL); + + /* + * A more advanced bgscan could process scan results internally, select + * the BSS and request roam if needed. This sample uses the existing + * BSS/ESS selection routine. Change this to return 1 if selection is + * done inside the bgscan module. + */ + + return 0; +} + + +static void bgscan_simple_notify_beacon_loss(void *priv) +{ + wpa_printf(MSG_DEBUG, "bgscan simple: beacon loss"); + /* TODO: speed up background scanning */ +} + + +static void bgscan_simple_notify_signal_change(void *priv) +{ + wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed"); + /* TODO: if signal strength dropped enough, speed up background + * scanning */ +} + + +const struct bgscan_ops bgscan_simple_ops = { + .name = "simple", + .init = bgscan_simple_init, + .deinit = bgscan_simple_deinit, + .notify_scan = bgscan_simple_notify_scan, + .notify_beacon_loss = bgscan_simple_notify_beacon_loss, + .notify_signal_change = bgscan_simple_notify_signal_change, +}; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 5f7f34fd9..97c303d8b 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1458,7 +1458,8 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(peerkey, 0, 1) }, { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 10000) }, - { INT(wpa_ptk_rekey) } + { INT(wpa_ptk_rekey) }, + { STR(bgscan) }, }; #undef OFFSET @@ -1623,6 +1624,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) #endif /* IEEE8021X_EAPOL */ os_free(ssid->id_str); os_free(ssid->scan_freq); + os_free(ssid->bgscan); os_free(ssid); } diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index c12036056..e09e3edcd 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -350,6 +350,15 @@ struct wpa_ssid { * known to not use all possible channels. */ int *scan_freq; + + /** + * bgscan - Background scan and roaming parameters or %NULL if none + * + * This is an optional set of parameters for background scanning and + * roaming within a network (ESS) in following format: + * : + */ + char *bgscan; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 03d0e1fde..de4c6fb5e 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -34,6 +34,7 @@ #include "wps_supplicant.h" #include "ibss_rsn.h" #include "sme.h" +#include "bgscan.h" static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) @@ -658,6 +659,9 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) return; } + if (bgscan_notify_scan(wpa_s) == 1) + return; + while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { selected = wpa_supplicant_select_bss( @@ -981,6 +985,25 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = NULL; } + +#ifdef CONFIG_BGSCAN + if (wpa_s->current_ssid != wpa_s->bgscan_ssid) { + bgscan_deinit(wpa_s); + if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) { + if (bgscan_init(wpa_s, wpa_s->current_ssid)) { + wpa_printf(MSG_DEBUG, "Failed to initialize " + "bgscan"); + /* + * Live without bgscan; it is only used as a + * roaming optimization, so the initial + * connection is not affected. + */ + } else + wpa_s->bgscan_ssid = wpa_s->current_ssid; + } else + wpa_s->bgscan_ssid = NULL; + } +#endif /* CONFIG_BGSCAN */ } @@ -1018,6 +1041,7 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s) wpa_clear_keys(wpa_s, wpa_s->bssid); } wpa_supplicant_mark_disassoc(wpa_s); + bgscan_deinit(wpa_s); } diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index e122e670d..b39b8ee82 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -178,6 +178,34 @@ static void int_array_sort_unique(int *a) } +int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + int ret; + + wpa_supplicant_notify_scanning(wpa_s, 1); + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { + ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies, + params->extra_ies_len); + ret = ieee80211_sta_req_scan(wpa_s, params->ssids[0].ssid, + params->ssids[0].ssid_len); + } else { + wpa_drv_set_probe_req_ie(wpa_s, params->extra_ies, + params->extra_ies_len); + ret = wpa_drv_scan(wpa_s, params); + } + + if (ret) { + wpa_supplicant_notify_scanning(wpa_s, 0); + wpas_notify_scan_done(wpa_s, 0); + } else + wpa_s->scan_runs++; + + return ret; +} + + static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -335,26 +363,13 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } #endif /* CONFIG_WPS */ - wpa_supplicant_notify_scanning(wpa_s, 1); - - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { - ieee80211_sta_set_probe_req_ie(wpa_s, params.extra_ies, - params.extra_ies_len); - ret = ieee80211_sta_req_scan(wpa_s, params.ssids[0].ssid, - params.ssids[0].ssid_len); - } else { - wpa_drv_set_probe_req_ie(wpa_s, params.extra_ies, - params.extra_ies_len); - ret = wpa_drv_scan(wpa_s, ¶ms); - } + ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); wpabuf_free(wps_ie); os_free(params.freqs); if (ret) { wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); - wpa_supplicant_notify_scanning(wpa_s, 0); - wpas_notify_scan_done(wpa_s, 0); wpa_supplicant_req_scan(wpa_s, 10, 0); } else wpa_s->scan_runs++; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index df1d182ab..c2e5e55a7 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -44,6 +44,7 @@ #include "sme.h" #include "ap.h" #include "notify.h" +#include "bgscan.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" @@ -362,6 +363,7 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { + bgscan_deinit(wpa_s); scard_deinit(wpa_s->scard); wpa_s->scard = NULL; wpa_sm_set_scard_ctx(wpa_s->wpa, NULL); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index a5eaaf1fb..0872efc7e 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -388,6 +388,10 @@ struct wpa_supplicant { #ifdef CONFIG_AP struct hostapd_iface *ap_iface; #endif /* CONFIG_AP */ + + struct wpa_ssid *bgscan_ssid; + const struct bgscan_ops *bgscan; + void *bgscan_priv; }; @@ -450,6 +454,9 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s); void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning); +struct wpa_driver_scan_params; +int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params); /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);