mirror of
https://gitlab.com/prpl-foundation/prplmesh/stationsniffer.git
synced 2025-12-20 02:20:31 +08:00
radiotap_parse/radiotap_fields: add VHT metadata
Signed-off-by: Tucker Polomik <t.polomik@cablelabs.com>
This commit is contained in:
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user