radiotap_parse/radiotap_fields: add VHT metadata

Signed-off-by: Tucker Polomik <t.polomik@cablelabs.com>
This commit is contained in:
Tucker Polomik
2023-07-13 16:45:38 -06:00
parent dbd877508b
commit d292e203a4
3 changed files with 102 additions and 19 deletions

View File

@@ -1,9 +1,28 @@
#pragma once
#include <cstdint>
struct bandwidth_metadata {
// MHz
uint8_t total_bandwidth;
// -1 if unknown
int8_t sideband;
// -1 if unknown
int8_t sideband_index;
};
// MCS rate and # spatial streams for VHT data
struct vht_mcs_nss {
// Number of Spatial Streams
uint8_t nss;
// MCS index
uint8_t mcs;
// Number of Temporal Spatial Streams
uint8_t nsts;
};
struct radiotap_fields {
int8_t rssi{-127}; // dBm
uint16_t channel_number;
uint16_t channel_frequency; // mHz
bool bad_fcs; // bad frame control sequence, indicates that this is bogus data.
vht_mcs_nss vht_mcs_nss_;
};

View File

@@ -1,5 +1,8 @@
#include "radiotap_parse.h"
#include "radiotap_fields.h"
#include <cassert>
#include <map>
#include <vector>
/**
* @brief Convert a sub-WiFi 6 frequency (MHz) to channel number.
@@ -35,6 +38,49 @@ static int frequency_to_channel_number(int freq_)
return (channel);
}
bandwidth_metadata parse_radiotap_bandwidth(uint8_t radiotap_bw_data)
{
// static data, just gen once for fast lookup
static std::map<int, bandwidth_metadata> lookup_table;
lookup_table[0] = {20, -1, -1};
lookup_table[1] = {40, -1, -1};
lookup_table[2] = {40, 20, 0};
lookup_table[3] = {40, 20, 1};
lookup_table[4] = {80, -1, -1};
lookup_table[5] = {80, 40, 0};
lookup_table[6] = {80, 40, 1};
lookup_table[7] = {80, 20, 0};
lookup_table[8] = {80, 20, 1};
lookup_table[9] = {80, 20, 2};
lookup_table[10] = {80, 20, 3};
lookup_table[11] = {160, -1, -1};
lookup_table[12] = {160, 80, 0};
lookup_table[13] = {160, 80, 1};
lookup_table[14] = {160, 40, 0};
lookup_table[15] = {160, 40, 1};
lookup_table[16] = {160, 40, 2};
lookup_table[17] = {160, 40, 3};
lookup_table[18] = {160, 20, 0};
lookup_table[19] = {160, 20, 1};
lookup_table[20] = {160, 20, 2};
lookup_table[21] = {160, 20, 3};
lookup_table[22] = {160, 20, 4};
lookup_table[23] = {160, 20, 5};
lookup_table[24] = {160, 20, 6};
lookup_table[25] = {160, 20, 7};
assert(radiotap_bw_data < lookup_table.size());
return lookup_table[radiotap_bw_data];
}
vht_mcs_nss parse_radiotap_vht_mcs_nss(uint8_t radiotap_vht_mcs_nss)
{
vht_mcs_nss parsed{};
parsed.nss = radiotap_vht_mcs_nss & 0x0f;
// Radiotap encodes a decimal MCS index as the high nibble in the MCS/NSS hex field.
parsed.mcs = (radiotap_vht_mcs_nss & 0xf0) >> 4;
return parsed;
}
void parse_radiotap_buf(struct ieee80211_radiotap_iterator &iter, const uint8_t *buf, size_t buflen,
radiotap_fields &rt_fields)
{
@@ -43,46 +89,64 @@ void parse_radiotap_buf(struct ieee80211_radiotap_iterator &iter, const uint8_t
// typically, you only want the measurement made on the first antenna
// for example, a 4 antenna phone will see something like:
// ant1: -43 dBm, ant2: -45dBm, ant3: -99 dBm, ant4: -99 dBm
int n_meas = 0;
int current_ant = 0;
int err;
while (!(err = ieee80211_radiotap_iterator_next(&iter))) {
switch (iter.this_arg_index) {
case IEEE80211_RADIOTAP_TSFT:
break;
case IEEE80211_RADIOTAP_FLAGS:
rt_fields.bad_fcs = (uint8_t)*iter.this_arg & IEEE80211_RADIOTAP_F_BADFCS;
break;
case IEEE80211_RADIOTAP_RATE:
break;
case IEEE80211_RADIOTAP_CHANNEL:
rt_fields.channel_frequency = le16_to_cpu(*(uint16_t *)iter.this_arg);
rt_fields.channel_number = frequency_to_channel_number(rt_fields.channel_frequency);
break;
case IEEE80211_RADIOTAP_FHSS:
break;
case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
// Only want the first measurement per packet (antenna 1).
if (n_meas == 0)
// Only want the first measurement per packet (antenna 1, 0-indexed).
if (0 == current_ant)
rt_fields.rssi = (int8_t)*iter.this_arg;
n_meas++;
break;
case IEEE80211_RADIOTAP_RATE:
// The rate field reports the rx/tx data rate in units of 500 kbps
// This field is generally considered unreliable.
// The radiotap header reports ~1.6 Mbps on a fully saturated 802.11ac stream
// See: https://gitlab.com/wireshark/wireshark/-/issues/5280
// Don't use this metric for any serious work.
break;
case IEEE80211_RADIOTAP_MCS: {
[[maybe_unused]] uint8_t known = iter.this_arg[0];
[[maybe_unused]] uint8_t flags = iter.this_arg[1];
[[maybe_unused]] uint8_t mcs = iter.this_arg[2];
} break;
case IEEE80211_RADIOTAP_VHT: {
std::vector<vht_mcs_nss> vht_mcs_nss_list;
// u16 known, u8 flags, u8 bandwidth, u8 vht_mcs_nss[4], u8 coding, u8 group_id, u16 partial_aid
[[maybe_unused]] uint8_t bw = iter.this_arg[3] & 0x1f;
for (int i = 4; i < 8; i++) {
vht_mcs_nss_list.push_back(parse_radiotap_vht_mcs_nss(iter.this_arg[i]));
}
for (int i = 0; i < (int)vht_mcs_nss_list.size(); i++) {
// If MCS index is zero, there's no data there.
if (vht_mcs_nss_list[i].mcs > 0) {
rt_fields.vht_mcs_nss_ = vht_mcs_nss_list[i];
}
}
break;
}
case IEEE80211_RADIOTAP_TSFT:
case IEEE80211_RADIOTAP_FHSS:
case IEEE80211_RADIOTAP_DBM_ANTNOISE:
break;
case IEEE80211_RADIOTAP_LOCK_QUALITY:
case IEEE80211_RADIOTAP_TX_ATTENUATION:
case IEEE80211_RADIOTAP_DB_TX_ATTENUATION:
case IEEE80211_RADIOTAP_DBM_TX_POWER:
case IEEE80211_RADIOTAP_ANTENNA:
case IEEE80211_RADIOTAP_ANTENNA: {
current_ant = iter.this_arg[0];
} break;
case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
break;
case IEEE80211_RADIOTAP_DB_ANTNOISE:
break;
case IEEE80211_RADIOTAP_TX_FLAGS:
break;
case IEEE80211_RADIOTAP_RTS_RETRIES:
case IEEE80211_RADIOTAP_DATA_RETRIES:
break;
break;
default:
break;
}
@@ -91,4 +155,4 @@ void parse_radiotap_buf(struct ieee80211_radiotap_iterator &iter, const uint8_t
if (err != -ENOENT) {
return;
}
}
}

View File

@@ -21,4 +21,4 @@ struct radiotap_fields;
* @param[out] rt_fields Radiotap fields of interest.
*/
void parse_radiotap_buf(struct ieee80211_radiotap_iterator &iter, const uint8_t *buf, size_t buflen,
radiotap_fields &rt_fields);
radiotap_fields &rt_fields);