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.
This commit is contained in:
Jouni Malinen 2010-03-05 21:42:06 +02:00
parent c9c38b0996
commit 3812464cda
7 changed files with 155 additions and 4 deletions

View File

@ -237,6 +237,30 @@ struct wpa_driver_scan_params {
* The frequency is set in MHz. The array is zero-terminated. * The frequency is set in MHz. The array is zero-terminated.
*/ */
int *freqs; 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;
}; };
/** /**

View File

@ -108,6 +108,9 @@ struct wpa_driver_nl80211_data {
u64 remain_on_chan_cookie; u64 remain_on_chan_cookie;
u64 send_action_cookie; u64 send_action_cookie;
struct wpa_driver_scan_filter *filter_ssids;
size_t num_filter_ssids;
#ifdef HOSTAPD #ifdef HOSTAPD
int eapol_sock; /* socket for EAPOL frames */ 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, eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
drv, NULL); drv, NULL);
os_free(drv->filter_ssids);
os_free(drv); os_free(drv);
} }
@ -1519,6 +1524,11 @@ static int wpa_driver_nl80211_scan(void *priv,
return -1; 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, genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
NL80211_CMD_TRIGGER_SCAN, 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) static int bss_info_handler(struct nl_msg *msg, void *arg)
{ {
struct nlattr *tb[NL80211_ATTR_MAX + 1]; 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_SEEN_MS_AGO] = { .type = NLA_U32 },
[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, [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 **tmp;
struct wpa_scan_res *r; struct wpa_scan_res *r;
const u8 *ie, *beacon_ie; const u8 *ie, *beacon_ie;
@ -1645,6 +1707,10 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
beacon_ie_len = 0; 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); r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
if (r == NULL) if (r == NULL)
return NL_SKIP; return NL_SKIP;
@ -1793,6 +1859,7 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
struct nl_msg *msg; struct nl_msg *msg;
struct wpa_scan_results *res; struct wpa_scan_results *res;
int ret; int ret;
struct nl80211_bss_info_arg arg;
res = os_zalloc(sizeof(*res)); res = os_zalloc(sizeof(*res));
if (res == NULL) if (res == NULL)
@ -1805,7 +1872,9 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
NL80211_CMD_GET_SCAN, 0); NL80211_CMD_GET_SCAN, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); 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; msg = NULL;
if (ret == 0) { if (ret == 0) {
wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",

View File

@ -337,6 +337,14 @@ struct wpa_config {
* bss_max_count - Maximum number of BSS entries to keep in memory * bss_max_count - Maximum number of BSS entries to keep in memory
*/ */
unsigned int bss_max_count; 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;
}; };

View File

@ -458,7 +458,8 @@ static const struct global_parse_data global_fields[] = {
{ INT_RANGE(wps_cred_processing, 0, 2) }, { INT_RANGE(wps_cred_processing, 0, 2) },
#endif /* CONFIG_WPS */ #endif /* CONFIG_WPS */
{ FUNC(country) }, { FUNC(country) },
{ INT(bss_max_count) } { INT(bss_max_count) },
{ INT_RANGE(filter_ssids, 0, 1) }
}; };
#undef FUNC #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) if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
fprintf(f, "bss_max_count=%u\n", config->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 */ #endif /* CONFIG_NO_CONFIG_WRITE */

View File

@ -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"), wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
&config->bss_max_count); &config->bss_max_count);
wpa_config_read_reg_dword(hk, TEXT("filter_ssids"),
&config->filter_ssids);
return errors ? -1 : 0; 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"), wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),
config->bss_max_count, config->bss_max_count,
DEFAULT_BSS_MAX_COUNT); DEFAULT_BSS_MAX_COUNT);
wpa_config_write_reg_dword(hk, TEXT("filter_ssids"),
config->filter_ssids, 0);
return 0; return 0;
} }

View File

@ -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) static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
{ {
struct wpa_supplicant *wpa_s = eloop_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 && if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) && !(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_s->scan_res_tried++;
wpa_printf(MSG_DEBUG, "Trying to get current scan results " wpa_printf(MSG_DEBUG, "Trying to get current scan results "
"first without requesting a new scan to speed up " "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 */ #endif /* CONFIG_WPS */
params.filter_ssids = wpa_supplicant_build_filter_ssids(
wpa_s->conf, &params.num_filter_ssids);
ret = wpa_supplicant_trigger_scan(wpa_s, &params); ret = wpa_supplicant_trigger_scan(wpa_s, &params);
wpabuf_free(wps_ie); wpabuf_free(wps_ie);
os_free(params.freqs); os_free(params.freqs);
os_free(params.filter_ssids);
if (ret) { if (ret) {
wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");

View File

@ -218,6 +218,12 @@ fast_reauth=1
#bss_max_count=200 #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 # network block
# #
# Each network (usually AP's sharing the same SSID) is configured as a separate # Each network (usually AP's sharing the same SSID) is configured as a separate