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.
This commit is contained in:
James Prestwood 2019-02-22 15:39:40 -08:00 committed by Denis Kenzior
parent 922e10e82c
commit 7d7fcff03b
4 changed files with 136 additions and 42 deletions

121
src/ie.c
View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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 {