diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c index 91ac80d6f..2de5c35e2 100644 --- a/wpa_supplicant/bgscan_learn.c +++ b/wpa_supplicant/bgscan_learn.c @@ -16,6 +16,8 @@ #include "common.h" #include "eloop.h" +#include "list.h" +#include "common/ieee802_11_defs.h" #include "drivers/driver.h" #include "config_ssid.h" #include "wpa_supplicant_i.h" @@ -23,6 +25,12 @@ #include "scan.h" #include "bgscan.h" +struct bgscan_learn_bss { + struct dl_list list; + u8 bssid[ETH_ALEN]; + int freq; +}; + struct bgscan_learn_data { struct wpa_supplicant *wpa_s; const struct wpa_ssid *ssid; @@ -32,9 +40,23 @@ struct bgscan_learn_data { int long_interval; /* use if signal > threshold */ struct os_time last_bgscan; char *fname; + struct dl_list bss; }; +static struct bgscan_learn_bss * bgscan_learn_get_bss( + struct bgscan_learn_data *data, const u8 *bssid) +{ + struct bgscan_learn_bss *bss; + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + return bss; + } + return NULL; +} + + static int bgscan_learn_load(struct bgscan_learn_data *data) { FILE *f; @@ -58,6 +80,24 @@ static int bgscan_learn_load(struct bgscan_learn_data *data) return -1; } + while (fgets(buf, sizeof(buf), f)) { + if (os_strncmp(buf, "BSS ", 4) == 0) { + struct bgscan_learn_bss *bss; + bss = os_zalloc(sizeof(*bss)); + if (!bss) + continue; + if (hwaddr_aton(buf + 4, bss->bssid) < 0) { + os_free(bss); + continue; + } + bss->freq = atoi(buf + 4 + 18); + dl_list_add(&data->bss, &bss->list); + wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS " + "entry: " MACSTR " freq=%d", + MAC2STR(bss->bssid), bss->freq); + } + } + fclose(f); return 0; } @@ -66,6 +106,7 @@ static int bgscan_learn_load(struct bgscan_learn_data *data) static void bgscan_learn_save(struct bgscan_learn_data *data) { FILE *f; + struct bgscan_learn_bss *bss; if (data->fname == NULL) return; @@ -78,27 +119,79 @@ static void bgscan_learn_save(struct bgscan_learn_data *data) return; fprintf(f, "wpa_supplicant-bgscan-learn\n"); + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + fprintf(f, "BSS " MACSTR " %d\n", + MAC2STR(bss->bssid), bss->freq); + } + fclose(f); } +static int in_array(int *array, int val) +{ + int i; + + if (array == NULL) + return 0; + + for (i = 0; array[i]; i++) { + if (array[i] == val) + return 1; + } + + return 0; +} + + +static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data, + size_t *count) +{ + struct bgscan_learn_bss *bss; + int *freqs = NULL, *n; + + *count = 0; + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + if (in_array(freqs, bss->freq)) + continue; + n = os_realloc(freqs, (*count + 2) * sizeof(int)); + if (n == NULL) + return freqs; + freqs = n; + freqs[*count] = bss->freq; + (*count)++; + freqs[*count] = 0; + } + + return freqs; +} + + static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) { struct bgscan_learn_data *data = eloop_ctx; struct wpa_supplicant *wpa_s = data->wpa_s; struct wpa_driver_scan_params params; + int *freqs = NULL; + size_t count; 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. - */ + if (data->ssid->scan_freq) + params.freqs = data->ssid->scan_freq; + else { + freqs = bgscan_learn_get_freqs(data, &count); + wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have " + "been seen on %u channels", (unsigned int) count); + /* + * TODO: add other frequencies (rotate through one or couple at + * a time, etc., to find APs from new channels) + */ + params.freqs = freqs; + } wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan"); if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { @@ -107,6 +200,7 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) bgscan_learn_timeout, data, NULL); } else os_get_time(&data->last_bgscan); + os_free(freqs); } @@ -152,6 +246,7 @@ static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; + dl_list_init(&data->bss); data->wpa_s = wpa_s; data->ssid = ssid; if (bgscan_learn_get_params(data, params) < 0) { @@ -191,19 +286,44 @@ static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, static void bgscan_learn_deinit(void *priv) { struct bgscan_learn_data *data = priv; + struct bgscan_learn_bss *bss, *n; + bgscan_learn_save(data); eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); if (data->signal_threshold) wpa_drv_signal_monitor(data->wpa_s, 0, 0); os_free(data->fname); + dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss, + list) { + dl_list_del(&bss->list); + os_free(bss); + } os_free(data); } +static int bgscan_learn_bss_match(struct bgscan_learn_data *data, + struct wpa_scan_res *bss) +{ + const u8 *ie; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL) + return 0; + + if (data->ssid->ssid_len != ie[1] || + os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0) + return 0; /* SSID mismatch */ + + return 1; +} + + static int bgscan_learn_notify_scan(void *priv, struct wpa_scan_results *scan_res) { struct bgscan_learn_data *data = priv; + size_t i; wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification"); @@ -211,6 +331,30 @@ static int bgscan_learn_notify_scan(void *priv, eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *res = scan_res->res[i]; + struct bgscan_learn_bss *bss; + + if (!bgscan_learn_bss_match(data, res)) + continue; + bss = bgscan_learn_get_bss(data, res->bssid); + if (bss && bss->freq != res->freq) { + wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS " + MACSTR " freq %d -> %d", + MAC2STR(res->bssid), bss->freq, res->freq); + bss->freq = res->freq; + } else if (!bss) { + wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR + " freq=%d", MAC2STR(res->bssid), res->freq); + bss = os_zalloc(sizeof(*bss)); + if (!bss) + continue; + os_memcpy(bss->bssid, res->bssid, ETH_ALEN); + bss->freq = res->freq; + dl_list_add(&data->bss, &bss->list); + } + } + /* * A more advanced bgscan could process scan results internally, select * the BSS and request roam if needed. This sample uses the existing