From 3812464cda18b98875d5c080faf9167dffc5078d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 5 Mar 2010 21:42:06 +0200 Subject: [PATCH] Add optional scan result filter based on SSID filter_ssids=1 global configuration parameter can now be used to enable scan result filtering (with -Dnl80211 only for now) based on the configured SSIDs. In other words, only the scan results that have an SSID matching with one of the configured networks are included in the BSS table. This can be used to reduce memory needs in environments that have huge number of APs. --- src/drivers/driver.h | 24 ++++++++++ src/drivers/driver_nl80211.c | 73 +++++++++++++++++++++++++++++- wpa_supplicant/config.h | 8 ++++ wpa_supplicant/config_file.c | 5 +- wpa_supplicant/config_winreg.c | 4 ++ wpa_supplicant/scan.c | 39 +++++++++++++++- wpa_supplicant/wpa_supplicant.conf | 6 +++ 7 files changed, 155 insertions(+), 4 deletions(-) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 04125869e..7f8473dfe 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -237,6 +237,30 @@ struct wpa_driver_scan_params { * The frequency is set in MHz. The array is zero-terminated. */ int *freqs; + + /** + * filter_ssids - Filter for reporting SSIDs + * + * This optional parameter can be used to request the driver wrapper to + * filter scan results to include only the specified SSIDs. %NULL + * indicates that no filtering is to be done. This can be used to + * reduce memory needs for scan results in environments that have large + * number of APs with different SSIDs. + * + * The driver wrapper is allowed to take this allocated buffer into its + * own use by setting the pointer to %NULL. In that case, the driver + * wrapper is responsible for freeing the buffer with os_free() once it + * is not needed anymore. + */ + struct wpa_driver_scan_filter { + u8 ssid[32]; + size_t ssid_len; + } *filter_ssids; + + /** + * num_filter_ssids - Number of entries in filter_ssids array + */ + size_t num_filter_ssids; }; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 6c2949d3a..a5622d5ac 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -108,6 +108,9 @@ struct wpa_driver_nl80211_data { u64 remain_on_chan_cookie; u64 send_action_cookie; + struct wpa_driver_scan_filter *filter_ssids; + size_t num_filter_ssids; + #ifdef HOSTAPD int eapol_sock; /* socket for EAPOL frames */ @@ -1471,6 +1474,8 @@ static void wpa_driver_nl80211_deinit(void *priv) eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout, drv, NULL); + os_free(drv->filter_ssids); + os_free(drv); } @@ -1519,6 +1524,11 @@ static int wpa_driver_nl80211_scan(void *priv, return -1; } + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_TRIGGER_SCAN, 0); @@ -1598,6 +1608,57 @@ nla_put_failure: } +static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +{ + const u8 *end, *pos; + + if (ies == NULL) + return NULL; + + pos = ies; + end = ies + ies_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, + const u8 *ie, size_t ie_len) +{ + const u8 *ssid; + size_t i; + + if (drv->filter_ssids == NULL) + return 0; + + ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); + if (ssid == NULL) + return 1; + + for (i = 0; i < drv->num_filter_ssids; i++) { + if (ssid[1] == drv->filter_ssids[i].ssid_len && + os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == + 0) + return 0; + } + + return 1; +} + + +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; +}; + static int bss_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1616,7 +1677,8 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, }; - struct wpa_scan_results *res = arg; + struct nl80211_bss_info_arg *_arg = arg; + struct wpa_scan_results *res = _arg->res; struct wpa_scan_res **tmp; struct wpa_scan_res *r; const u8 *ie, *beacon_ie; @@ -1645,6 +1707,10 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) beacon_ie_len = 0; } + if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, + ie ? ie_len : beacon_ie_len)) + return NL_SKIP; + r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); if (r == NULL) return NL_SKIP; @@ -1793,6 +1859,7 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) struct nl_msg *msg; struct wpa_scan_results *res; int ret; + struct nl80211_bss_info_arg arg; res = os_zalloc(sizeof(*res)); if (res == NULL) @@ -1805,7 +1872,9 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) NL80211_CMD_GET_SCAN, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); + arg.drv = drv; + arg.res = res; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index cdf3d4080..21c2fdd60 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -337,6 +337,14 @@ struct wpa_config { * bss_max_count - Maximum number of BSS entries to keep in memory */ unsigned int bss_max_count; + + /** + * filter_ssids - SSID-based scan result filtering + * + * 0 = do not filter scan results + * 1 = only include configured SSIDs in scan results/BSS table + */ + int filter_ssids; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index fa477fa49..5f0704576 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -458,7 +458,8 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(wps_cred_processing, 0, 2) }, #endif /* CONFIG_WPS */ { FUNC(country) }, - { INT(bss_max_count) } + { INT(bss_max_count) }, + { INT_RANGE(filter_ssids, 0, 1) } }; #undef FUNC @@ -892,6 +893,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) } if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT) fprintf(f, "bss_max_count=%u\n", config->bss_max_count); + if (config->filter_ssids) + fprintf(f, "filter_ssids=%d\n", config->filter_ssids); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 6c2507325..811eb7ec8 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -259,6 +259,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk) wpa_config_read_reg_dword(hk, TEXT("bss_max_count"), &config->bss_max_count); + wpa_config_read_reg_dword(hk, TEXT("filter_ssids"), + &config->filter_ssids); return errors ? -1 : 0; } @@ -589,6 +591,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) wpa_config_write_reg_dword(hk, TEXT("bss_max_count"), config->bss_max_count, DEFAULT_BSS_MAX_COUNT); + wpa_config_write_reg_dword(hk, TEXT("filter_ssids"), + config->filter_ssids, 0); return 0; } diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 9c61690fd..e2d95b3d1 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -204,6 +204,39 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, } +static struct wpa_driver_scan_filter * +wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) +{ + struct wpa_driver_scan_filter *ssids; + struct wpa_ssid *ssid; + size_t count; + + *num_ssids = 0; + if (!conf->filter_ssids) + return NULL; + + for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) { + if (ssid->ssid && ssid->ssid_len) + count++; + } + if (count == 0) + return NULL; + ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter)); + if (ssids == NULL) + return NULL; + + for (ssid = conf->ssid; ssid; ssid = ssid->next) { + if (!ssid->ssid || !ssid->ssid_len) + continue; + os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len); + ssids[*num_ssids].ssid_len = ssid->ssid_len; + (*num_ssids)++; + } + + return ssids; +} + + static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -258,7 +291,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) && - wps != 2) { + wps != 2 && !wpa_s->conf->filter_ssids) { wpa_s->scan_res_tried++; wpa_printf(MSG_DEBUG, "Trying to get current scan results " "first without requesting a new scan to speed up " @@ -363,10 +396,14 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } #endif /* CONFIG_WPS */ + params.filter_ssids = wpa_supplicant_build_filter_ssids( + wpa_s->conf, ¶ms.num_filter_ssids); + ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); wpabuf_free(wps_ie); os_free(params.freqs); + os_free(params.filter_ssids); if (ret) { wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 070abcf29..39058d847 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -218,6 +218,12 @@ fast_reauth=1 #bss_max_count=200 +# filter_ssids - SSID-based scan result filtering +# 0 = do not filter scan results (default) +# 1 = only include configured SSIDs in scan results/BSS table +#filter_ssids=0 + + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate