diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index d7c874f66..c0e8b71d1 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1162,6 +1162,11 @@ OBJS += $(SHA1OBJS) $(DESOBJS) OBJS_p += $(SHA1OBJS) +ifdef CONFIG_BSS_TABLE +CFLAGS += -DCONFIG_BSS_TABLE +OBJS += bss.o +endif + ifdef CONFIG_BGSCAN_SIMPLE CFLAGS += -DCONFIG_BGSCAN_SIMPLE OBJS += bgscan_simple.o diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c new file mode 100644 index 000000000..a19ac269a --- /dev/null +++ b/wpa_supplicant/bss.c @@ -0,0 +1,262 @@ +/* + * BSS table + * 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 "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" +#include "wpa_supplicant_i.h" +#include "notify.h" +#include "bss.h" + + +#ifndef WPA_BSS_MAX_COUNT +#define WPA_BSS_MAX_COUNT 200 +#endif /* WPA_BSS_MAX_COUNT */ + +/** + * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds + */ +#define WPA_BSS_EXPIRATION_PERIOD 10 + +/** + * WPA_BSS_EXPIRATION_AGE - BSS entry age after which it can be expired + * + * This value control the time in seconds after which a BSS entry gets removed + * if it has not been updated or is not in use. + */ +#define WPA_BSS_EXPIRATION_AGE 180 + +/** + * WPA_BSS_EXPIRATION_SCAN_COUNT - Expire BSS after number of scans + * + * If the BSS entry has not been seen in this many scans, it will be removed. + * Value 1 means that the entry is removed after the first scan without the + * BSSID being seen. Larger values can be used to avoid BSS entries + * disappearing if they are not visible in every scan (e.g., low signal quality + * or interference). + */ +#define WPA_BSS_EXPIRATION_SCAN_COUNT 2 + + +static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + dl_list_del(&bss->list); + wpa_s->num_bss--; + wpa_printf(MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR " SSID '%s'", + bss->id, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + wpas_notify_bss_removed(wpa_s, bss->bssid); + os_free(bss); +} + + +static struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, + const u8 *bssid, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_bss *bss; + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && + bss->ssid_len == ssid_len && + os_memcmp(bss->ssid, ssid, ssid_len) == 0) + return bss; + } + return NULL; +} + + +static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) +{ + os_time_t usec; + + dst->flags = src->flags; + os_memcpy(dst->bssid, src->bssid, ETH_ALEN); + dst->freq = src->freq; + dst->beacon_int = src->beacon_int; + dst->caps = src->caps; + dst->qual = src->qual; + dst->noise = src->noise; + dst->level = src->level; + dst->tsf = src->tsf; + + os_get_time(&dst->last_update); + dst->last_update.sec -= src->age / 1000; + usec = (src->age % 1000) * 1000; + if (dst->last_update.usec < usec) { + dst->last_update.sec--; + dst->last_update.usec += 1000000; + } + dst->last_update.usec -= usec; +} + + +static void wpa_bss_add(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len, + struct wpa_scan_res *res) +{ + struct wpa_bss *bss; + + bss = os_zalloc(sizeof(*bss) + res->ie_len); + if (bss == NULL) + return; + bss->id = wpa_s->bss_next_id++; + bss->last_update_idx = wpa_s->bss_update_idx; + wpa_bss_copy_res(bss, res); + os_memcpy(bss->ssid, ssid, ssid_len); + bss->ssid_len = ssid_len; + bss->ie_len = res->ie_len; + os_memcpy(bss + 1, res + 1, res->ie_len); + + dl_list_add_tail(&wpa_s->bss, &bss->list); + wpa_s->num_bss++; + wpa_printf(MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR " SSID '%s'", + bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); + wpas_notify_bss_added(wpa_s, res->bssid); + if (wpa_s->num_bss > WPA_BSS_MAX_COUNT) { + /* Remove the oldest entry */ + wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss, + struct wpa_bss, list)); + } +} + + +static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + struct wpa_scan_res *res) +{ + bss->scan_miss_count = 0; + bss->last_update_idx = wpa_s->bss_update_idx; + wpa_bss_copy_res(bss, res); + /* Move the entry to the end of the list */ + dl_list_del(&bss->list); + if (bss->ie_len >= res->ie_len) { + os_memcpy(bss + 1, res + 1, res->ie_len); + bss->ie_len = res->ie_len; + } else { + struct wpa_bss *nbss; + nbss = os_realloc(bss, sizeof(*bss) + res->ie_len); + if (nbss) { + bss = nbss; + os_memcpy(bss + 1, res + 1, res->ie_len); + bss->ie_len = res->ie_len; + } + } + dl_list_add_tail(&wpa_s->bss, &bss->list); +} + + +void wpa_bss_update_start(struct wpa_supplicant *wpa_s) +{ + wpa_s->bss_update_idx++; + wpa_printf(MSG_DEBUG, "BSS: Start scan result update %u", + wpa_s->bss_update_idx); +} + + +void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) +{ + const u8 *ssid; + struct wpa_bss *bss; + + ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "BSS: No SSID IE included for " MACSTR, + MAC2STR(res->bssid)); + return; + } + if (ssid[1] > 32) { + wpa_printf(MSG_DEBUG, "BSS: Too long SSID IE included for " + MACSTR, MAC2STR(res->bssid)); + return; + } + + /* TODO: add option for ignoring BSSes we are not interested in + * (to save memory) */ + bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); + if (bss == NULL) + wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); + else + wpa_bss_update(wpa_s, bss, res); +} + + +void wpa_bss_update_end(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *n; + + /* TODO: expire only entries that were on the scanned frequencies/SSIDs + * list; need to get info from driver about scanned frequencies and + * SSIDs to be able to figure out which entries should be expired based + * on this */ + + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { + if (bss->last_update_idx < wpa_s->bss_update_idx) + bss->scan_miss_count++; + if (bss->scan_miss_count >= WPA_BSS_EXPIRATION_SCAN_COUNT) { + wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to no " + "match in scan", bss->id); + wpa_bss_remove(wpa_s, bss); + } + } +} + + +static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_bss *bss, *n; + struct os_time t; + + if (dl_list_empty(&wpa_s->bss)) + return; + + os_get_time(&t); + t.sec -= WPA_BSS_EXPIRATION_AGE; + + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0) + continue; /* do not expire BSSes that are in use */ + + if (os_time_before(&bss->last_update, &t)) { + wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to age", + bss->id); + wpa_bss_remove(wpa_s, bss); + } else + break; + } + eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, + wpa_bss_timeout, wpa_s, NULL); +} + + +int wpa_bss_init(struct wpa_supplicant *wpa_s) +{ + dl_list_init(&wpa_s->bss); + eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, + wpa_bss_timeout, wpa_s, NULL); + return 0; +} + + +void wpa_bss_deinit(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *n; + eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) + wpa_bss_remove(wpa_s, bss); +} diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h new file mode 100644 index 000000000..dde4ed9b6 --- /dev/null +++ b/wpa_supplicant/bss.h @@ -0,0 +1,74 @@ +/* + * BSS table + * 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 BSS_H +#define BSS_H + +#define WPA_BSS_QUAL_INVALID BIT(0) +#define WPA_BSS_NOISE_INVALID BIT(1) +#define WPA_BSS_LEVEL_INVALID BIT(2) +#define WPA_BSS_LEVEL_DBM BIT(3) +#define WPA_BSS_AUTHENTICATED BIT(4) +#define WPA_BSS_ASSOCIATED BIT(5) + +/** + * struct wpa_bss - BSS table + * @list: List entry for struct wpa_supplicant::bss + * @id: Unique identifier for this BSS entry + * @scan_miss_count: Number of counts without seeing this BSS + * @flags: information flags about the BSS/IBSS (WPA_BSS_*) + * @last_update_idx: Index of the last scan update + * @bssid: BSSID + * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) + * @beacon_int: beacon interval in TUs (host byte order) + * @caps: capability information field in host byte order + * @qual: signal quality + * @noise: noise level + * @level: signal level + * @tsf: Timestamp of last Beacon/Probe Response frame + * @last_update: Time of the last update (i.e., Beacon or Probe Response RX) + * @ie_len: length of the following IE field in octets + * + * This structure is used to store information about neighboring BSSes in + * generic format. It is mainly updated based on scan results from the driver. + */ +struct wpa_bss { + struct dl_list list; + unsigned int id; + unsigned int scan_miss_count; + unsigned int last_update_idx; + unsigned int flags; + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + int freq; + u16 beacon_int; + u16 caps; + int qual; + int noise; + int level; + u64 tsf; + struct os_time last_update; + size_t ie_len; + /* followed by ie_len octets of IEs */ +}; + +void wpa_bss_update_start(struct wpa_supplicant *wpa_s); +void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res); +void wpa_bss_update_end(struct wpa_supplicant *wpa_s); +int wpa_bss_init(struct wpa_supplicant *wpa_s); +void wpa_bss_deinit(struct wpa_supplicant *wpa_s); + +#endif /* BSS_H */ diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index de71c58ce..91c3cad11 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -344,6 +344,8 @@ CONFIG_PEERKEY=y # Add support for new DBus control interface # (fi.w1.hostap.wpa_supplicant1) +# Note: You will also need to enable CONFIG_BSS_TABLE if you want to have full +# support for the documented D-Bus signals. #CONFIG_CTRL_IFACE_DBUS_NEW=y # Add introspection support for new DBus control interface (requires libxml2) @@ -392,3 +394,6 @@ CONFIG_PEERKEY=y # This enables use of libbfd to get more detailed symbols for the backtraces # generated by CONFIG_WPA_TRACE=y. #CONFIG_WPA_TRACE_BFD=y + +# Maintain a local BSS table to track neighboring BSSes. +CONFIG_BSS_TABLE=y diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index e764624c3..7d18b6396 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -44,6 +44,7 @@ #include "ap.h" #include "notify.h" #include "bgscan.h" +#include "bss.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" @@ -398,6 +399,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_scan_results_free(wpa_s->scan_res); wpa_s->scan_res = NULL; +#ifdef CONFIG_BSS_TABLE + wpa_bss_deinit(wpa_s); +#endif /* CONFIG_BSS_TABLE */ wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_cancel_auth_timeout(wpa_s); @@ -1559,64 +1563,16 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, } -static void notify_bss_changes(struct wpa_supplicant *wpa_s, - u8 (*prev_bssids)[ETH_ALEN], int prev_num, - struct wpa_scan_results *new) -{ - int new_num, i, j; - - new_num = new != NULL ? new->num : 0; - if (prev_bssids == NULL) - prev_num = 0; - - for (i = 0; i < prev_num; i++) { - for (j = 0; j < new_num; j++) { - if (!os_memcmp(prev_bssids[i], new->res[j]->bssid, - ETH_ALEN)) - break; - } - if (j == new_num) - wpas_notify_bss_removed(wpa_s, prev_bssids[i]); - } - for (i = 0; i < new_num; i++) { - for (j = 0; j < prev_num; j++) { - if (!os_memcmp(new->res[i]->bssid, prev_bssids[j], - ETH_ALEN)) - break; - } - if (j == prev_num) - wpas_notify_bss_added(wpa_s, new->res[i]->bssid); - } -} - - /** * wpa_supplicant_get_scan_results - Get scan results * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 on success, -1 on failure * - * This function is request the current scan results from the driver and stores - * a local copy of the results in wpa_s->scan_res. + * This function request the current scan results from the driver and updates + * the local BSS list wpa_s->bss. */ int wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s) { - int ret, i, prev_scan_res_num; - u8 (*prev_scan_bssids)[ETH_ALEN]; - - prev_scan_res_num = wpa_s->scan_res ? wpa_s->scan_res->num : 0; - prev_scan_bssids = os_malloc(prev_scan_res_num * ETH_ALEN); - - if (prev_scan_bssids) { - for (i = 0; i < prev_scan_res_num; i++) { - os_memcpy(prev_scan_bssids[i], - wpa_s->scan_res->res[i]->bssid, ETH_ALEN); - } - } else { - wpa_printf(MSG_WARNING, "Not enough memory for old scan " - "results list"); - prev_scan_res_num = 0; - } - wpa_scan_results_free(wpa_s->scan_res); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) wpa_s->scan_res = ieee80211_sta_get_scan_results(wpa_s); @@ -1624,17 +1580,23 @@ int wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s) wpa_s->scan_res = wpa_drv_get_scan_results2(wpa_s); if (wpa_s->scan_res == NULL) { wpa_printf(MSG_DEBUG, "Failed to get scan results"); - ret = -1; - } else { - ret = 0; - wpa_scan_sort_results(wpa_s->scan_res); + return -1; } - notify_bss_changes(wpa_s, prev_scan_bssids, prev_scan_res_num, - wpa_s->scan_res); - os_free(prev_scan_bssids); + wpa_scan_sort_results(wpa_s->scan_res); - return ret; +#ifdef CONFIG_BSS_TABLE + { + size_t i; + wpa_bss_update_start(wpa_s); + for (i = 0; i < wpa_s->scan_res->num; i++) + wpa_bss_update_scan_res(wpa_s, + wpa_s->scan_res->res[i]); + wpa_bss_update_end(wpa_s); + } +#endif /* CONFIG_BSS_TABLE */ + + return 0; } @@ -2137,6 +2099,11 @@ next_driver: } #endif /* CONFIG_IBSS_RSN */ +#ifdef CONFIG_BSS_TABLE + if (wpa_bss_init(wpa_s) < 0) + return -1; +#endif /* CONFIG_BSS_TABLE */ + return 0; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index ee6dce239..67f67b5ed 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - Internal definitions - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-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 @@ -15,6 +15,7 @@ #ifndef WPA_SUPPLICANT_I_H #define WPA_SUPPLICANT_I_H +#include "utils/list.h" #include "common/defs.h" extern const char *wpa_supplicant_version; @@ -344,6 +345,10 @@ struct wpa_supplicant { #define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1) struct wpa_scan_results *scan_res; + struct dl_list bss; /* struct wpa_bss::list */ + size_t num_bss; + unsigned int bss_update_idx; + unsigned int bss_next_id; struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been