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