X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=wpa_supplicant%2Fbgscan_learn.c;h=a320cc43068c96f6bde1354af5954dad164de807;hb=4424aa5d7d1a1624b2a2e0241430da7ee3e80a54;hp=2de5c35e2cb2abff48eb363975193ea996ef48e6;hpb=fc480e88bf4942a849ae0abc30fb60ea52a47cfa;p=mech_eap.git diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c index 2de5c35..a320cc4 100644 --- a/wpa_supplicant/bgscan_learn.c +++ b/wpa_supplicant/bgscan_learn.c @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming module: learn * Copyright (c) 2009-2010, 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,6 +23,8 @@ struct bgscan_learn_bss { struct dl_list list; u8 bssid[ETH_ALEN]; int freq; + u8 *neigh; /* num_neigh * ETH_ALEN buffer */ + size_t num_neigh; }; struct bgscan_learn_data { @@ -38,12 +34,57 @@ struct bgscan_learn_data { int signal_threshold; int short_interval; /* use if signal < threshold */ int long_interval; /* use if signal > threshold */ - struct os_time last_bgscan; + struct os_reltime last_bgscan; char *fname; struct dl_list bss; + int *supp_freqs; + int probe_idx; }; +static void bss_free(struct bgscan_learn_bss *bss) +{ + os_free(bss->neigh); + os_free(bss); +} + + +static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid) +{ + size_t i; + + if (array == NULL || array_len == 0) + return 0; + + for (i = 0; i < array_len; i++) { + if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss, + const u8 *bssid) +{ + u8 *n; + + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + return; + if (bssid_in_array(bss->neigh, bss->num_neigh, bssid)) + return; + + n = os_realloc_array(bss->neigh, bss->num_neigh + 1, ETH_ALEN); + if (n == NULL) + return; + + os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN); + bss->neigh = n; + bss->num_neigh++; +} + + static struct bgscan_learn_bss * bgscan_learn_get_bss( struct bgscan_learn_data *data, const u8 *bssid) { @@ -61,6 +102,7 @@ static int bgscan_learn_load(struct bgscan_learn_data *data) { FILE *f; char buf[128]; + struct bgscan_learn_bss *bss; if (data->fname == NULL) return 0; @@ -82,12 +124,11 @@ static int bgscan_learn_load(struct bgscan_learn_data *data) 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); + bss_free(bss); continue; } bss->freq = atoi(buf + 4 + 18); @@ -96,6 +137,20 @@ static int bgscan_learn_load(struct bgscan_learn_data *data) "entry: " MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); } + + if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) { + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(buf + 9, addr) < 0) + continue; + bss = bgscan_learn_get_bss(data, addr); + if (bss == NULL) + continue; + if (hwaddr_aton(buf + 9 + 18, addr) < 0) + continue; + + bgscan_learn_add_neighbor(bss, addr); + } } fclose(f); @@ -124,6 +179,15 @@ static void bgscan_learn_save(struct bgscan_learn_data *data) MAC2STR(bss->bssid), bss->freq); } + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + size_t i; + for (i = 0; i < bss->num_neigh; i++) { + fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n", + MAC2STR(bss->bssid), + MAC2STR(bss->neigh + i * ETH_ALEN)); + } + } + fclose(f); } @@ -155,7 +219,7 @@ static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data, 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)); + n = os_realloc_array(freqs, *count + 2, sizeof(int)); if (n == NULL) return freqs; freqs = n; @@ -168,13 +232,49 @@ static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data, } +static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data, + int *freqs, size_t count) +{ + int idx, *n; + + if (data->supp_freqs == NULL) + return freqs; + + idx = data->probe_idx; + do { + if (!in_array(freqs, data->supp_freqs[idx])) { + wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq " + "%u", data->supp_freqs[idx]); + data->probe_idx = idx + 1; + if (data->supp_freqs[data->probe_idx] == 0) + data->probe_idx = 0; + n = os_realloc_array(freqs, count + 2, sizeof(int)); + if (n == NULL) + return freqs; + freqs = n; + freqs[count] = data->supp_freqs[idx]; + count++; + freqs[count] = 0; + break; + } + + idx++; + if (data->supp_freqs[idx] == 0) + idx = 0; + } while (idx != data->probe_idx); + + 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; + size_t count, i; + char msg[100], *pos; os_memset(¶ms, 0, sizeof(params)); params.num_ssids = 1; @@ -186,10 +286,21 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) 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) - */ + freqs = bgscan_learn_get_probe_freq(data, freqs, count); + + msg[0] = '\0'; + pos = msg; + for (i = 0; freqs && freqs[i]; i++) { + int ret; + ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d", + freqs[i]); + if (os_snprintf_error(msg + sizeof(msg) - pos, ret)) + break; + pos += ret; + } + pos[0] = '\0'; + wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s", + msg); params.freqs = freqs; } @@ -199,7 +310,7 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); } else - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); os_free(freqs); } @@ -237,6 +348,38 @@ static int bgscan_learn_get_params(struct bgscan_learn_data *data, } +static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s) +{ + struct hostapd_hw_modes *modes; + int i, j, *freqs = NULL, *n; + size_t count = 0; + + modes = wpa_s->hw.modes; + if (modes == NULL) + return NULL; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + for (j = 0; j < modes[i].num_channels; j++) { + if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + /* some hw modes (e.g. 11b & 11g) contain same freqs */ + if (in_array(freqs, modes[i].channels[j].freq)) + continue; + n = os_realloc_array(freqs, count + 2, sizeof(int)); + if (n == NULL) + continue; + + freqs = n; + freqs[count] = modes[i].channels[j].freq; + count++; + freqs[count] = 0; + } + } + + return freqs; +} + + static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, const char *params, const struct wpa_ssid *ssid) @@ -276,9 +419,27 @@ static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, "signal strength monitoring"); } + data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s); data->scan_interval = data->short_interval; + if (data->signal_threshold) { + /* Poll for signal info to set initial scan interval */ + struct wpa_signal_info siginfo; + if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 && + siginfo.current_signal >= data->signal_threshold) + data->scan_interval = data->long_interval; + } + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); + + /* + * This function is called immediately after an association, so it is + * reasonable to assume that a scan was completed recently. This makes + * us skip an immediate new scan in cases where the current signal + * level is below the bgscan threshold. + */ + os_get_reltime(&data->last_bgscan); + return data; } @@ -296,8 +457,9 @@ static void bgscan_learn_deinit(void *priv) dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss, list) { dl_list_del(&bss->list); - os_free(bss); + bss_free(bss); } + os_free(data->supp_freqs); os_free(data); } @@ -323,7 +485,10 @@ static int bgscan_learn_notify_scan(void *priv, struct wpa_scan_results *scan_res) { struct bgscan_learn_data *data = priv; - size_t i; + size_t i, j; +#define MAX_BSS 50 + u8 bssid[MAX_BSS * ETH_ALEN]; + size_t num_bssid = 0; wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification"); @@ -333,10 +498,25 @@ static int bgscan_learn_notify_scan(void *priv, for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *res = scan_res->res[i]; + if (!bgscan_learn_bss_match(data, res)) + continue; + + if (num_bssid < MAX_BSS) { + os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid, + ETH_ALEN); + num_bssid++; + } + } + wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan " + "results", (unsigned int) num_bssid); + + 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 " @@ -353,6 +533,11 @@ static int bgscan_learn_notify_scan(void *priv, bss->freq = res->freq; dl_list_add(&data->bss, &bss->list); } + + for (j = 0; j < num_bssid; j++) { + u8 *addr = bssid + j * ETH_ALEN; + bgscan_learn_add_neighbor(bss, addr); + } } /* @@ -373,23 +558,30 @@ static void bgscan_learn_notify_beacon_loss(void *priv) } -static void bgscan_learn_notify_signal_change(void *priv, int above) +static void bgscan_learn_notify_signal_change(void *priv, int above, + int current_signal, + int current_noise, + int current_txrate) { struct bgscan_learn_data *data = priv; + int scan = 0; + struct os_reltime now; if (data->short_interval == data->long_interval || data->signal_threshold == 0) return; wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed " - "(above=%d)", above); + "(above=%d current_signal=%d current_noise=%d " + "current_txrate=%d)", above, current_signal, + current_noise, current_txrate); if (data->scan_interval == data->long_interval && !above) { - wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan " - "and start using short bgscan interval"); + wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan " + "interval"); data->scan_interval = data->short_interval; - eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); - eloop_register_timeout(0, 0, bgscan_learn_timeout, data, - NULL); + os_get_reltime(&now); + if (now.sec > data->last_bgscan.sec + 1) + scan = 1; } else if (data->scan_interval == data->short_interval && above) { wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan " "interval"); @@ -398,20 +590,19 @@ static void bgscan_learn_notify_signal_change(void *priv, int above) eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); } else if (!above) { - struct os_time now; /* * Signal dropped further 4 dB. Request a new scan if we have * not yet scanned in a while. */ - os_get_time(&now); - if (now.sec > data->last_bgscan.sec + 10) { - wpa_printf(MSG_DEBUG, "bgscan learn: Trigger " - "immediate scan"); - eloop_cancel_timeout(bgscan_learn_timeout, data, - NULL); - eloop_register_timeout(0, 0, bgscan_learn_timeout, - data, NULL); - } + os_get_reltime(&now); + if (now.sec > data->last_bgscan.sec + 10) + scan = 1; + } + + if (scan) { + wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan"); + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL); } }