ie: scan: use VHT rates in scan ranking

This adds support for parsing the VHT IE, which allows a BSS supporting
VHT (80211ac) to be ranked higher than a BSS supporting only HT/basic
rates. Now, with basic/HT/VHT parsing we can calculate the theoretical
maximum data rate for all three and rank the BSS based on that.
This commit is contained in:
James Prestwood 2019-02-25 12:29:32 -08:00 committed by Denis Kenzior
parent ad2bf340a4
commit 49b02907a8
4 changed files with 227 additions and 2 deletions

213
src/ie.c
View File

@ -1664,6 +1664,210 @@ static int ie_parse_ht_capability_from_data(const uint8_t *data, uint8_t len,
return ie_parse_ht_capability(&iter, rssi, data_rate);
}
/*
* IEEE 802.11 - Table 9-250
*
* For simplicity, we are ignoring the Extended BSS BW support, per NOTE 11:
*
* NOTE 11A receiving STA in which dot11VHTExtendedNSSCapable is false will
* ignore the Extended NSS BW Support subfield and effectively evaluate this
* table only at the entries where Extended NSS BW Support is 0.
*
* This also allows us to group the 160/80+80 widths together, since they are
* the same when Extended NSS BW is zero.
*/
static const uint8_t vht_width_map[3][5] = {
[0] = { 1, 1, 1, 0, 0 },
[1] = { 1, 1, 1, 1, 0 },
[2] = { 1, 1, 1, 1, 1 },
};
static int ie_parse_vht_capability(struct ie_tlv_iter *vht_iter,
struct ie_tlv_iter *ht_iter, int32_t rssi,
uint64_t *data_rate)
{
int width;
int mcs;
unsigned int nss;
unsigned int len;
const uint8_t *data;
uint8_t channel_width_set;
uint8_t rx_mcs_map[2];
uint8_t tx_mcs_map[2];
unsigned int max_rx_mcs = 0;
unsigned int rx_nss;
unsigned int max_tx_mcs = 0;
unsigned int tx_nss;
uint8_t ht_cap;
bool short_gi_20mhz;
bool short_gi_40mhz;
bool short_gi_80mhz;
bool short_gi_160mhz;
uint64_t highest_rate = 0;
/* grab the short GI bits from the HT IE */
len = ie_tlv_iter_get_length(ht_iter);
if (len != 26)
return -EINVAL;
data = ie_tlv_iter_get_data(ht_iter);
ht_cap = l_get_u8(data);
short_gi_20mhz = util_is_bit_set(ht_cap, 5);
short_gi_40mhz = util_is_bit_set(ht_cap, 6);
/* now move onto VHT */
len = ie_tlv_iter_get_length(vht_iter);
if (len != 12)
return -EINVAL;
data = ie_tlv_iter_get_data(vht_iter);
channel_width_set = util_bit_field(*data, 2, 2);
short_gi_80mhz = util_bit_field(*data, 5, 1);
short_gi_160mhz = util_bit_field(*data, 6, 1);
data += 4;
rx_mcs_map[0] = *data++;
rx_mcs_map[1] = *data++;
data += 2;
tx_mcs_map[0] = *data++;
tx_mcs_map[1] = *data++;
/* NSS->MCS map values are grouped in 2-bit values */
for (mcs = 15; mcs >= 0; mcs -= 2) {
uint8_t rx_val = util_bit_field(rx_mcs_map[mcs / 8],
mcs % 8, 2);
uint8_t tx_val = util_bit_field(tx_mcs_map[mcs / 8],
mcs % 8, 2);
/*
* 0 indicates support for MCS 0-7
* 1 indicates support for MCS 0-8
* 2 indicates support for MCS 0-9
*
* Therefore 7 + rx/tx_val gives us our max MCS index.
*/
if (!max_rx_mcs && rx_val < 3) {
max_rx_mcs = 7 + rx_val;
rx_nss = (mcs / 2) + 1;
}
if (!max_tx_mcs && tx_val < 3) {
max_tx_mcs = 7 + tx_val;
tx_nss = (mcs / 2) + 1;
}
if (max_rx_mcs && max_tx_mcs)
break;
}
if (!max_rx_mcs && !max_tx_mcs)
return -EINVAL;
/*
* Now, using channel width, MCS index, and NSS we can determine the
* theoretical maximum data rate. We iterate through all possible
* combinations (width, MCS, NSS), saving the highest data rate we find.
*
* We could calculate a maximum data rate separately for TX/RX, but
* since this is only used for BSS ranking, the minumum between the
* two should be good enough.
*/
for (width = sizeof(vht_width_map[0]) - 1; width >= 0; width--) {
bool sgi = false;
if (!vht_width_map[channel_width_set][width])
continue;
/*
* Consolidate short GI support into a single boolean, dependent
* on the channel width for this iteration.
*/
switch (width) {
case HT_VHT_CHANNEL_WIDTH_20MHZ:
sgi = short_gi_20mhz;
break;
case HT_VHT_CHANNEL_WIDTH_40MHZ:
sgi = short_gi_40mhz;
break;
case HT_VHT_CHANNEL_WIDTH_80MHZ:
sgi = short_gi_80mhz;
break;
case HT_VHT_CHANNEL_WIDTH_160MHZ:
sgi = short_gi_160mhz;
break;
}
for (nss = minsize(rx_nss, tx_nss); nss > 0; nss--) {
/* NSS > 4 does not apply to 20/40MHz */
if (width <= HT_VHT_CHANNEL_WIDTH_40MHZ && nss > 4)
continue;
for (mcs = minsize(max_rx_mcs, max_tx_mcs);
mcs >= 0; mcs--) {
uint64_t drate;
if (!calculate_ht_vht_data_rate(mcs, width,
rssi, nss, sgi, &drate))
continue;
if (drate > highest_rate)
highest_rate = drate;
/* Lower MCS index will only have lower rates */
goto next_chanwidth;
}
}
next_chanwidth: ; /* empty statement */
}
if (highest_rate == 0)
return -ENOTSUP;
*data_rate = highest_rate;
return 0;
}
static int ie_parse_vht_capability_from_data(const uint8_t *vht_ie,
size_t vht_len, const uint8_t *ht_ie,
size_t ht_len, int32_t rssi,
uint64_t *data_rate)
{
struct ie_tlv_iter vht_iter;
struct ie_tlv_iter ht_iter;
uint8_t tag;
ie_tlv_iter_init(&vht_iter, vht_ie, vht_len);
if (!ie_tlv_iter_next(&vht_iter))
return -EMSGSIZE;
tag = ie_tlv_iter_get_tag(&vht_iter);
if (tag != IE_TYPE_VHT_CAPABILITIES)
return -EPROTOTYPE;
ie_tlv_iter_init(&ht_iter, ht_ie, ht_len);
if (!ie_tlv_iter_next(&ht_iter))
return -EMSGSIZE;
tag = ie_tlv_iter_get_tag(&ht_iter);
if (tag != IE_TYPE_HT_CAPABILITIES)
return -EPROTOTYPE;
return ie_parse_vht_capability(&vht_iter, &ht_iter, rssi, data_rate);
}
/*
* Calculates the theoretical maximum data rates out of the provided
* supported rates IE, HT IE, and VHT IE. All 3 parsing functions are allowed
@ -1674,6 +1878,7 @@ static int ie_parse_ht_capability_from_data(const uint8_t *data, uint8_t len,
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
const uint8_t *ext_supp_rates_ie,
const uint8_t *ht_ie,
const uint8_t *vht_ie,
int32_t rssi,
uint64_t *data_rate)
{
@ -1684,6 +1889,14 @@ int ie_parse_data_rates(const uint8_t *supp_rates_ie,
if (rssi < -82)
return -ENOTSUP;
if (ht_ie && vht_ie) {
ret = ie_parse_vht_capability_from_data(vht_ie, IE_LEN(vht_ie),
ht_ie, IE_LEN(ht_ie),
rssi, &rate);
if (ret == 0)
goto done;
}
if (ht_ie) {
ret = ie_parse_ht_capability_from_data(ht_ie, IE_LEN(ht_ie),
rssi, &rate);

View File

@ -422,6 +422,7 @@ int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie,
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
const uint8_t *ext_supp_rates_ie,
const uint8_t *ht_ie,
const uint8_t *vht_ie,
int32_t rssi,
uint64_t *data_rate);

View File

@ -873,6 +873,14 @@ static bool scan_parse_bss_information_elements(struct scan_bss *bss,
bss->ht_capable = true;
memcpy(bss->ht_ie, iter.data - 2, iter.len + 2);
break;
case IE_TYPE_VHT_CAPABILITIES:
if (iter.len != 12)
return false;
bss->vht_capable = true;
memcpy(bss->vht_ie, iter.data - 2, iter.len + 2);
break;
}
}
@ -1061,15 +1069,16 @@ static void scan_bss_compute_rank(struct scan_bss *bss)
bss->supp_rates_ie : NULL,
bss->ext_supp_rates_ie,
bss->ht_capable ? bss->ht_ie : NULL,
bss->vht_capable ? bss->vht_ie : NULL,
bss->signal_strength / 100,
&data_rate) == 0) {
double factor = RANK_MAX_SUPPORTED_RATE_FACTOR -
RANK_MIN_SUPPORTED_RATE_FACTOR;
/*
* Maximum rate is 600 Mbps (HT)
* Maximum rate is 2340Mbps (VHT)
*/
factor = factor * data_rate / 600000000 +
factor = factor * data_rate / 2340000000 +
RANK_MIN_SUPPORTED_RATE_FACTOR;
rank *= factor;
} else

View File

@ -60,11 +60,13 @@ struct scan_bss {
uint8_t cc[3];
uint16_t rank;
uint8_t ht_ie[28];
uint8_t vht_ie[14];
bool mde_present : 1;
bool cc_present : 1;
bool cap_rm_neighbor_report : 1;
bool has_sup_rates : 1;
bool ht_capable : 1;
bool vht_capable : 1;
};
struct scan_parameters {