From 7d7fcff03b81e1274e36803228198a17819cc2d0 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Feb 2019 15:39:40 -0800 Subject: [PATCH] ie: scan: take into account RSSI when parsing data rate The spec dictates RSSI thresholds for different modulation schemes, which correlate to different data rates. Until now were were ranking a BSS with only looking at its advertised data rate, which may not even be possible if the RSSI does not meet the threshold. Now, RSSI is taken into consideration and the data rate returned from parsing (Ext) Supported Rates IE(s) will reflect that. --- src/ie.c | 121 +++++++++++++++++++++++++++++++++++++++++++---------- src/ie.h | 9 ++-- src/scan.c | 44 ++++++++++++------- src/scan.h | 4 +- 4 files changed, 136 insertions(+), 42 deletions(-) diff --git a/src/ie.c b/src/ie.c index 661114cd..7e9ac579 100644 --- a/src/ie.c +++ b/src/ie.c @@ -1367,51 +1367,128 @@ int ie_parse_bss_load_from_data(const uint8_t *data, uint8_t len, out_channel_utilization, out_admission_capacity); } -int ie_parse_supported_rates(struct ie_tlv_iter *iter, - struct l_uintset **set) +/* + * We have to store this mapping since basic rates dont come with a convenient + * MCS index. Rates are stored as they are encoded in the Supported Rates IE. + * This does not include non 802.11g data rates, e.g. 1/2/4Mbps. This data was + * taken from 802.11 Section 17.3.10.2 and Table 10-7. + * + * Section 17.3.10.2 defines minimum RSSI for modulations, and Table + * 10-7 defines reference rates for the different modulations. Together we + * have minimum RSSI required for a given data rate. + */ +struct basic_rate_map { + int32_t rssi; + uint8_t rate; +}; + +/* + * Rates are stored in 500Kbps increments. This is how the IE encodes the data + * so its more convenient to match by this encoding. The actual data rate is + * converted to Mbps after we find a match + */ +static struct basic_rate_map rate_rssi_map[] = { + { -82, 12 }, + { -81, 18 }, + { -79, 24 }, + { -77, 36 }, + { -74, 48 }, + { -70, 72 }, + { -66, 96 }, + { -65, 108 }, +}; + +static int ie_parse_supported_rates(struct ie_tlv_iter *supp_rates_iter, + struct ie_tlv_iter *ext_supp_rates_iter, + int32_t rssi, + uint64_t *data_rate) { + uint8_t max_rate = 0; + uint8_t highest = 0; const uint8_t *rates; unsigned int len; unsigned int i; - len = ie_tlv_iter_get_length(iter); + len = ie_tlv_iter_get_length(supp_rates_iter); - if (ie_tlv_iter_get_tag(iter) == IE_TYPE_SUPPORTED_RATES && len == 0) + if (len == 0) return -EINVAL; - rates = ie_tlv_iter_get_data(iter); + /* Find highest rates possible with our RSSI */ + for (i = 0; i < L_ARRAY_SIZE(rate_rssi_map); i++) { + struct basic_rate_map *map = &rate_rssi_map[i]; - if (!*set) - *set = l_uintset_new(108); + if (rssi < map->rssi) + break; + + max_rate = map->rate; + } + + /* Find highest rate in Supported Rates IE */ + rates = ie_tlv_iter_get_data(supp_rates_iter); for (i = 0; i < len; i++) { - if (rates[i] == 0xff) - continue; + uint8_t r = rates[i] & 0x7f; - l_uintset_put(*set, rates[i] & 0x7f); + if (r <= max_rate && r > highest) + highest = r; } + /* Find highest rate in Extended Supported Rates IE */ + if (ext_supp_rates_iter) { + len = ie_tlv_iter_get_length(ext_supp_rates_iter); + rates = ie_tlv_iter_get_data(ext_supp_rates_iter); + + for (i = 0; i < len; i++) { + uint8_t r = rates[i] & 0x7f; + + if (r <= max_rate && r > highest) + highest = r; + } + } + + *data_rate = (highest / 2) * 1000000; + return 0; } -int ie_parse_supported_rates_from_data(const uint8_t *data, uint8_t len, - struct l_uintset **set) +int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie, + uint8_t supp_rates_len, + const uint8_t *ext_supp_rates_ie, + uint8_t ext_supp_rates_len, + int32_t rssi, uint64_t *data_rate) { - struct ie_tlv_iter iter; - uint8_t tag; + struct ie_tlv_iter supp_rates_iter; + struct ie_tlv_iter ext_supp_rates_iter; - ie_tlv_iter_init(&iter, data, len); + if (supp_rates_ie) { + ie_tlv_iter_init(&supp_rates_iter, supp_rates_ie, + supp_rates_len); - if (!ie_tlv_iter_next(&iter)) - return -EMSGSIZE; + if (!ie_tlv_iter_next(&supp_rates_iter)) + return -EMSGSIZE; - tag = ie_tlv_iter_get_tag(&iter); + if (ie_tlv_iter_get_tag(&supp_rates_iter) != + IE_TYPE_SUPPORTED_RATES) + return -EPROTOTYPE; + } - if (tag != IE_TYPE_SUPPORTED_RATES && - tag != IE_TYPE_EXTENDED_SUPPORTED_RATES) - return -EPROTOTYPE; + if (ext_supp_rates_ie) { + ie_tlv_iter_init(&ext_supp_rates_iter, ext_supp_rates_ie, + ext_supp_rates_len); - return ie_parse_supported_rates(&iter, set); + if (!ie_tlv_iter_next(&ext_supp_rates_iter)) + return -EMSGSIZE; + + if (ie_tlv_iter_get_tag(&ext_supp_rates_iter) != + IE_TYPE_EXTENDED_SUPPORTED_RATES) + return -EPROTOTYPE; + } + + return ie_parse_supported_rates( + (supp_rates_ie) ? &supp_rates_iter : NULL, + (ext_supp_rates_ie) ? &ext_supp_rates_iter : NULL, + rssi, data_rate); } int ie_parse_mobility_domain(struct ie_tlv_iter *iter, uint16_t *mdid, diff --git a/src/ie.h b/src/ie.h index ce36060a..9901b063 100644 --- a/src/ie.h +++ b/src/ie.h @@ -413,10 +413,11 @@ int ie_parse_bss_load_from_data(const uint8_t *data, uint8_t len, uint8_t *out_channel_utilization, uint16_t *out_admission_capacity); -int ie_parse_supported_rates(struct ie_tlv_iter *iter, - struct l_uintset **set); -int ie_parse_supported_rates_from_data(const uint8_t *data, uint8_t len, - struct l_uintset **set); +int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie, + uint8_t supp_rates_len, + const uint8_t *ext_supp_rates_ie, + uint8_t ext_supp_rates_len, + int32_t rssi, uint64_t *data_rate); int ie_parse_mobility_domain(struct ie_tlv_iter *iter, uint16_t *mdid, bool *ft_over_ds, bool *resource_req); diff --git a/src/scan.c b/src/scan.c index 95078ad9..e44a6d4f 100644 --- a/src/scan.c +++ b/src/scan.c @@ -809,12 +809,16 @@ static bool scan_parse_bss_information_elements(struct scan_bss *bss, have_ssid = true; break; case IE_TYPE_SUPPORTED_RATES: + if (iter.len > 8) + return false; + + bss->has_sup_rates = true; + memcpy(bss->supp_rates_ie, iter.data - 2, iter.len + 2); + + break; case IE_TYPE_EXTENDED_SUPPORTED_RATES: - if (ie_parse_supported_rates(&iter, - &bss->supported_rates) < 0) - l_warn("Unable to parse [Extended] " - "Supported Rates IE for " - MAC, MAC_STR(bss->addr)); + bss->ext_supp_rates_ie = l_memdup(iter.data - 2, + iter.len + 2); break; case IE_TYPE_RSN: if (!bss->rsne) @@ -1042,17 +1046,27 @@ static void scan_bss_compute_rank(struct scan_bss *bss) else if (bss->utilization <= 63) rank *= RANK_LOW_UTILIZATION_FACTOR; - if (bss->supported_rates) { - uint8_t max = l_uintset_find_max(bss->supported_rates); - double factor = RANK_MAX_SUPPORTED_RATE_FACTOR - + if (bss->has_sup_rates || bss->ext_supp_rates_ie) { + uint64_t data_rate; + + if (ie_parse_supported_rates_from_data(bss->has_sup_rates ? + bss->supp_rates_ie : NULL, + IE_LEN(bss->supp_rates_ie), + bss->ext_supp_rates_ie, + IE_LEN(bss->ext_supp_rates_ie), + bss->signal_strength / 100, + &data_rate) == 0) { + double factor = RANK_MAX_SUPPORTED_RATE_FACTOR - RANK_MIN_SUPPORTED_RATE_FACTOR; - /* - * Maximum rate is 54 Mbps, see DATA_RATE in 802.11-2012, - * Section 6.5.5.2 - */ - factor = factor * max / 108 + RANK_MIN_SUPPORTED_RATE_FACTOR; - rank *= factor; + /* + * Maximum rate is 54 Mbps + */ + factor = factor * data_rate / 54000000 + + RANK_MIN_SUPPORTED_RATE_FACTOR; + rank *= factor; + } else + rank *= RANK_MIN_SUPPORTED_RATE_FACTOR; } irank = rank; @@ -1065,7 +1079,7 @@ static void scan_bss_compute_rank(struct scan_bss *bss) void scan_bss_free(struct scan_bss *bss) { - l_uintset_free(bss->supported_rates); + l_free(bss->ext_supp_rates_ie); l_free(bss->rsne); l_free(bss->wpa); l_free(bss->wsc); diff --git a/src/scan.h b/src/scan.h index c47cbf7a..25717ede 100644 --- a/src/scan.h +++ b/src/scan.h @@ -54,13 +54,15 @@ struct scan_bss { uint8_t mde[3]; uint8_t ssid[32]; uint8_t ssid_len; - struct l_uintset *supported_rates; + uint8_t supp_rates_ie[10]; + uint8_t *ext_supp_rates_ie; uint8_t utilization; uint8_t cc[3]; uint16_t rank; bool mde_present : 1; bool cc_present : 1; bool cap_rm_neighbor_report : 1; + bool has_sup_rates : 1; }; struct scan_parameters {