use core::any::Any;
use core::ops::Div;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{IntoPacket, Layer, LayerContext, Packet};
use crate::registry::ProtocolRegistry;
use super::decode_dot11_with_registry;
pub(crate) const RADIOTAP_FIXED_HEADER_LEN: usize = 4;
pub(crate) const RADIOTAP_MIN_HEADER_LEN: usize = 8;
const RADIOTAP_PRESENT_WORD_LEN: usize = 4;
const RADIOTAP_PRESENT_EXTENSION_BIT: u16 = 31;
pub const RADIOTAP_FIELD_TSFT: u8 = 0;
pub const RADIOTAP_FIELD_FLAGS: u8 = 1;
pub const RADIOTAP_FIELD_RATE: u8 = 2;
pub const RADIOTAP_FIELD_CHANNEL: u8 = 3;
pub const RADIOTAP_FIELD_FHSS: u8 = 4;
pub const RADIOTAP_FIELD_ANTENNA_SIGNAL: u8 = 5;
pub const RADIOTAP_FIELD_ANTENNA_NOISE: u8 = 6;
pub const RADIOTAP_FIELD_LOCK_QUALITY: u8 = 7;
pub const RADIOTAP_FIELD_TX_ATTENUATION: u8 = 8;
pub const RADIOTAP_FIELD_DB_TX_ATTENUATION: u8 = 9;
pub const RADIOTAP_FIELD_DBM_TX_POWER: u8 = 10;
pub const RADIOTAP_FIELD_ANTENNA: u8 = 11;
pub const RADIOTAP_FIELD_RX_FLAGS: u8 = 14;
pub const RADIOTAP_FIELD_TX_FLAGS: u8 = 15;
pub const RADIOTAP_FIELD_RTS_RETRIES: u8 = 16;
pub const RADIOTAP_FIELD_DATA_RETRIES: u8 = 17;
pub const RADIOTAP_FIELD_MCS: u8 = 19;
pub const RADIOTAP_FIELD_A_MPDU_STATUS: u8 = 20;
pub const RADIOTAP_FIELD_VHT: u8 = 21;
pub const RADIOTAP_PRESENT_EXTENDED: u32 = 0x8000_0000;
pub const RADIOTAP_PRESENT_TSFT: u32 = present_mask(RADIOTAP_FIELD_TSFT);
pub const RADIOTAP_PRESENT_FLAGS: u32 = present_mask(RADIOTAP_FIELD_FLAGS);
pub const RADIOTAP_PRESENT_RATE: u32 = present_mask(RADIOTAP_FIELD_RATE);
pub const RADIOTAP_PRESENT_CHANNEL: u32 = present_mask(RADIOTAP_FIELD_CHANNEL);
pub const RADIOTAP_PRESENT_FHSS: u32 = present_mask(RADIOTAP_FIELD_FHSS);
pub const RADIOTAP_PRESENT_ANTENNA_SIGNAL: u32 = present_mask(RADIOTAP_FIELD_ANTENNA_SIGNAL);
pub const RADIOTAP_PRESENT_ANTENNA_NOISE: u32 = present_mask(RADIOTAP_FIELD_ANTENNA_NOISE);
pub const RADIOTAP_PRESENT_LOCK_QUALITY: u32 = present_mask(RADIOTAP_FIELD_LOCK_QUALITY);
pub const RADIOTAP_PRESENT_TX_ATTENUATION: u32 = present_mask(RADIOTAP_FIELD_TX_ATTENUATION);
pub const RADIOTAP_PRESENT_DB_TX_ATTENUATION: u32 = present_mask(RADIOTAP_FIELD_DB_TX_ATTENUATION);
pub const RADIOTAP_PRESENT_DBM_TX_POWER: u32 = present_mask(RADIOTAP_FIELD_DBM_TX_POWER);
pub const RADIOTAP_PRESENT_ANTENNA: u32 = present_mask(RADIOTAP_FIELD_ANTENNA);
pub const RADIOTAP_PRESENT_RX_FLAGS: u32 = present_mask(RADIOTAP_FIELD_RX_FLAGS);
pub const RADIOTAP_PRESENT_TX_FLAGS: u32 = present_mask(RADIOTAP_FIELD_TX_FLAGS);
pub const RADIOTAP_PRESENT_RTS_RETRIES: u32 = present_mask(RADIOTAP_FIELD_RTS_RETRIES);
pub const RADIOTAP_PRESENT_DATA_RETRIES: u32 = present_mask(RADIOTAP_FIELD_DATA_RETRIES);
pub const RADIOTAP_PRESENT_MCS: u32 = present_mask(RADIOTAP_FIELD_MCS);
pub const RADIOTAP_PRESENT_A_MPDU_STATUS: u32 = present_mask(RADIOTAP_FIELD_A_MPDU_STATUS);
pub const RADIOTAP_PRESENT_VHT: u32 = present_mask(RADIOTAP_FIELD_VHT);
pub const RADIOTAP_FLAGS_FCS_PRESENT: u8 = 0x10;
pub const RADIOTAP_FLAGS_FAILED_FCS: u8 = 0x40;
pub const RADIOTAP_FLAGS_BAD_FCS: u8 = RADIOTAP_FLAGS_FAILED_FCS;
pub const RADIOTAP_RX_FLAGS_RESERVED_WAS_FCS_FAILED: u16 = 0x0001;
pub const RADIOTAP_RX_FLAGS_PLCP_CRC_FAILED: u16 = 0x0002;
const fn present_mask(bit: u8) -> u32 {
1u32 << (bit as u32)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RadiotapFieldMetadata {
bit: u8,
present_mask: u32,
alignment: usize,
size: usize,
}
impl RadiotapFieldMetadata {
const fn new(bit: u8, alignment: usize, size: usize) -> Self {
Self {
bit,
present_mask: present_mask(bit),
alignment,
size,
}
}
pub const fn bit(&self) -> u8 {
self.bit
}
pub const fn present_mask(&self) -> u32 {
self.present_mask
}
pub const fn alignment(&self) -> usize {
self.alignment
}
pub const fn size(&self) -> usize {
self.size
}
}
pub const RADIOTAP_SELECTED_FIELD_METADATA: [RadiotapFieldMetadata; 19] = [
RadiotapFieldMetadata::new(RADIOTAP_FIELD_TSFT, 8, 8),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_FLAGS, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_RATE, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_CHANNEL, 2, 4),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_FHSS, 1, 2),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_ANTENNA_SIGNAL, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_ANTENNA_NOISE, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_LOCK_QUALITY, 2, 2),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_TX_ATTENUATION, 2, 2),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_DB_TX_ATTENUATION, 2, 2),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_DBM_TX_POWER, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_ANTENNA, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_RX_FLAGS, 2, 2),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_TX_FLAGS, 2, 2),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_RTS_RETRIES, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_DATA_RETRIES, 1, 1),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_MCS, 1, 3),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_A_MPDU_STATUS, 4, 8),
RadiotapFieldMetadata::new(RADIOTAP_FIELD_VHT, 2, 12),
];
pub fn radiotap_field_metadata(bit: u16) -> Option<RadiotapFieldMetadata> {
RADIOTAP_SELECTED_FIELD_METADATA
.iter()
.copied()
.find(|metadata| metadata.bit as u16 == bit)
}
pub const fn radiotap_field_padding(offset_from_header_start: usize, alignment: usize) -> usize {
if alignment <= 1 {
0
} else {
let remainder = offset_from_header_start % alignment;
if remainder == 0 {
0
} else {
alignment - remainder
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RadiotapPresent {
words: Vec<u32>,
}
impl RadiotapPresent {
pub fn new() -> Self {
Self { words: vec![0] }
}
pub fn from_words(words: impl Into<Vec<u32>>) -> Self {
let mut present = Self {
words: words.into(),
};
present.normalize_extension_bits();
present
}
pub fn decode(bytes: &[u8]) -> Result<(Self, usize)> {
let mut offset = 0;
let mut words = Vec::new();
loop {
let required = offset + RADIOTAP_PRESENT_WORD_LEN;
if bytes.len() < required {
return Err(CrafterError::buffer_too_short(
"radiotap.present",
required,
bytes.len(),
));
}
let word = u32::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
]);
let has_extension = word & RADIOTAP_PRESENT_EXTENDED != 0;
words.push(word);
offset = required;
if !has_extension {
break;
}
}
Ok((Self::from_words(words), offset))
}
pub fn from_field_bits<Typed, Raw>(typed_bits: Typed, raw_bits: Raw) -> Result<Self>
where
Typed: IntoIterator<Item = u16>,
Raw: IntoIterator<Item = u16>,
{
let mut present = Self::new();
for bit in typed_bits {
present.insert_field_bit(bit)?;
}
for bit in raw_bits {
present.insert_field_bit(bit)?;
}
Ok(present)
}
pub fn insert_field_bit(&mut self, bit: u16) -> Result<()> {
validate_radiotap_field_bit(bit)?;
let word_index = usize::from(bit / 32);
let bit_index = u32::from(bit % 32);
if self.words.len() <= word_index {
self.words.resize(word_index + 1, 0);
}
self.words[word_index] |= 1u32 << bit_index;
self.normalize_extension_bits();
Ok(())
}
pub fn is_field_present(&self, bit: u16) -> bool {
if is_extension_field_bit(bit) {
return false;
}
let word_index = usize::from(bit / 32);
let bit_index = u32::from(bit % 32);
self.words
.get(word_index)
.map(|word| word & (1u32 << bit_index) != 0)
.unwrap_or(false)
}
pub fn field_bits(&self) -> impl Iterator<Item = u16> + '_ {
self.words
.iter()
.enumerate()
.flat_map(|(word_index, word)| {
(0u16..32).filter_map(move |bit_index| {
if bit_index == RADIOTAP_PRESENT_EXTENSION_BIT {
return None;
}
let mask = 1u32 << u32::from(bit_index);
if word & mask == 0 {
return None;
}
let base_bit = word_index
.checked_mul(32)
.and_then(|bit| u16::try_from(bit).ok())?;
Some(base_bit + bit_index)
})
})
}
pub fn words(&self) -> &[u32] {
&self.words
}
pub fn encoded_len(&self) -> usize {
self.words.len() * RADIOTAP_PRESENT_WORD_LEN
}
pub fn encode(&self, out: &mut Vec<u8>) {
for word in &self.words {
out.extend_from_slice(&word.to_le_bytes());
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(self.encoded_len());
self.encode(&mut bytes);
bytes
}
fn normalize_extension_bits(&mut self) {
if self.words.is_empty() {
self.words.push(0);
}
let last_index = self.words.len() - 1;
for (index, word) in self.words.iter_mut().enumerate() {
*word &= !RADIOTAP_PRESENT_EXTENDED;
if index != last_index {
*word |= RADIOTAP_PRESENT_EXTENDED;
}
}
}
}
impl Default for RadiotapPresent {
fn default() -> Self {
Self::new()
}
}
fn validate_radiotap_field_bit(bit: u16) -> Result<()> {
if is_extension_field_bit(bit) {
return Err(CrafterError::invalid_field_value(
"radiotap.present.bit",
"bit 31 of each present word is the extension flag",
));
}
Ok(())
}
fn is_extension_field_bit(bit: u16) -> bool {
bit % 32 == RADIOTAP_PRESENT_EXTENSION_BIT
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct RadiotapFlags {
bits: u8,
}
impl RadiotapFlags {
pub const fn from_bits(bits: u8) -> Self {
Self { bits }
}
pub const fn bits(&self) -> u8 {
self.bits
}
pub const fn fcs_present(&self) -> bool {
self.bits & RADIOTAP_FLAGS_FCS_PRESENT != 0
}
pub const fn failed_fcs(&self) -> bool {
self.bits & RADIOTAP_FLAGS_FAILED_FCS != 0
}
pub const fn fcs_status(&self) -> RadiotapFcsStatus {
RadiotapFcsStatus::new(self.fcs_present(), self.failed_fcs())
}
}
impl From<u8> for RadiotapFlags {
fn from(bits: u8) -> Self {
Self::from_bits(bits)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct RadiotapFcsStatus {
present: bool,
failed: bool,
}
impl RadiotapFcsStatus {
pub const fn new(present: bool, failed: bool) -> Self {
Self { present, failed }
}
pub const fn present(&self) -> bool {
self.present
}
pub const fn failed(&self) -> bool {
self.failed
}
}
pub const RADIOTAP_CHANNEL_TURBO: u16 = 0x0010;
pub const RADIOTAP_CHANNEL_CCK: u16 = 0x0020;
pub const RADIOTAP_CHANNEL_OFDM: u16 = 0x0040;
pub const RADIOTAP_CHANNEL_2GHZ: u16 = 0x0080;
pub const RADIOTAP_CHANNEL_5GHZ: u16 = 0x0100;
pub const RADIOTAP_CHANNEL_2GHZ_CCK: u16 = RADIOTAP_CHANNEL_2GHZ | RADIOTAP_CHANNEL_CCK;
pub const RADIOTAP_CHANNEL_5GHZ_OFDM: u16 = RADIOTAP_CHANNEL_5GHZ | RADIOTAP_CHANNEL_OFDM;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct RadiotapChannel {
frequency: u16,
flags: u16,
}
impl RadiotapChannel {
pub const fn new(frequency: u16, flags: u16) -> Self {
Self { frequency, flags }
}
pub const fn channel_2ghz(channel: u8) -> Self {
let frequency = if channel == 14 {
2484
} else {
2407 + channel as u16 * 5
};
Self::new(frequency, RADIOTAP_CHANNEL_2GHZ_CCK)
}
pub const fn frequency(&self) -> u16 {
self.frequency
}
pub const fn flags(&self) -> u16 {
self.flags
}
pub const fn to_bytes(self) -> [u8; 4] {
let frequency = self.frequency.to_le_bytes();
let flags = self.flags.to_le_bytes();
[frequency[0], frequency[1], flags[0], flags[1]]
}
}
impl From<(u16, u16)> for RadiotapChannel {
fn from((frequency, flags): (u16, u16)) -> Self {
Self::new(frequency, flags)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct RadiotapRxFlags {
bits: u16,
}
impl RadiotapRxFlags {
pub const fn from_bits(bits: u16) -> Self {
Self { bits }
}
pub const fn bits(&self) -> u16 {
self.bits
}
pub const fn reserved_fcs_failed_bit(&self) -> bool {
self.bits & RADIOTAP_RX_FLAGS_RESERVED_WAS_FCS_FAILED != 0
}
pub const fn plcp_crc_failed(&self) -> bool {
self.bits & RADIOTAP_RX_FLAGS_PLCP_CRC_FAILED != 0
}
}
impl From<u16> for RadiotapRxFlags {
fn from(bits: u16) -> Self {
Self::from_bits(bits)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct RadiotapTxFlags {
bits: u16,
}
impl RadiotapTxFlags {
pub const ORDER: Self = Self::from_bits(0x0001);
pub const NO_REORDER: Self = Self::from_bits(0x0002);
pub const NO_ACK: Self = Self::from_bits(0x0008);
pub const NO_SEQ: Self = Self::from_bits(0x0010);
pub const FIXED_RATE: Self = Self::from_bits(0x0020);
pub const fn from_bits(bits: u16) -> Self {
Self { bits }
}
pub const fn bits(&self) -> u16 {
self.bits
}
pub const fn contains(&self, other: Self) -> bool {
self.bits & other.bits == other.bits
}
}
impl From<u16> for RadiotapTxFlags {
fn from(bits: u16) -> Self {
Self::from_bits(bits)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RadiotapUnknownField {
present_bit: u16,
alignment: usize,
raw_bytes: Vec<u8>,
}
impl RadiotapUnknownField {
pub fn new(present_bit: u16, alignment: usize, raw_bytes: impl Into<Vec<u8>>) -> Result<Self> {
validate_radiotap_field_bit(present_bit)?;
if alignment == 0 {
return Err(CrafterError::invalid_field_value(
"radiotap.field.alignment",
"alignment must be at least one octet",
));
}
Ok(Self {
present_bit,
alignment,
raw_bytes: raw_bytes.into(),
})
}
pub const fn present_bit(&self) -> u16 {
self.present_bit
}
pub const fn alignment(&self) -> usize {
self.alignment
}
pub fn raw_bytes(&self) -> &[u8] {
&self.raw_bytes
}
pub fn size(&self) -> usize {
self.raw_bytes.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RadiotapField {
Tsft(u64),
Flags(RadiotapFlags),
Rate(u8),
Channel(RadiotapChannel),
Fhss([u8; 2]),
AntennaSignal(i8),
AntennaNoise(i8),
LockQuality(u16),
TxAttenuation(u16),
DbTxAttenuation(u16),
DbmTxPower(i8),
Antenna(u8),
RxFlags(RadiotapRxFlags),
TxFlags(RadiotapTxFlags),
RtsRetries(u8),
DataRetries(u8),
Mcs([u8; 3]),
AMpduStatus([u8; 8]),
Vht([u8; 12]),
Unknown(RadiotapUnknownField),
}
impl RadiotapField {
pub fn unknown(
present_bit: u16,
alignment: usize,
raw_bytes: impl Into<Vec<u8>>,
) -> Result<Self> {
Ok(Self::Unknown(RadiotapUnknownField::new(
present_bit,
alignment,
raw_bytes,
)?))
}
pub const fn present_bit(&self) -> u16 {
match self {
Self::Tsft(_) => RADIOTAP_FIELD_TSFT as u16,
Self::Flags(_) => RADIOTAP_FIELD_FLAGS as u16,
Self::Rate(_) => RADIOTAP_FIELD_RATE as u16,
Self::Channel(_) => RADIOTAP_FIELD_CHANNEL as u16,
Self::Fhss(_) => RADIOTAP_FIELD_FHSS as u16,
Self::AntennaSignal(_) => RADIOTAP_FIELD_ANTENNA_SIGNAL as u16,
Self::AntennaNoise(_) => RADIOTAP_FIELD_ANTENNA_NOISE as u16,
Self::LockQuality(_) => RADIOTAP_FIELD_LOCK_QUALITY as u16,
Self::TxAttenuation(_) => RADIOTAP_FIELD_TX_ATTENUATION as u16,
Self::DbTxAttenuation(_) => RADIOTAP_FIELD_DB_TX_ATTENUATION as u16,
Self::DbmTxPower(_) => RADIOTAP_FIELD_DBM_TX_POWER as u16,
Self::Antenna(_) => RADIOTAP_FIELD_ANTENNA as u16,
Self::RxFlags(_) => RADIOTAP_FIELD_RX_FLAGS as u16,
Self::TxFlags(_) => RADIOTAP_FIELD_TX_FLAGS as u16,
Self::RtsRetries(_) => RADIOTAP_FIELD_RTS_RETRIES as u16,
Self::DataRetries(_) => RADIOTAP_FIELD_DATA_RETRIES as u16,
Self::Mcs(_) => RADIOTAP_FIELD_MCS as u16,
Self::AMpduStatus(_) => RADIOTAP_FIELD_A_MPDU_STATUS as u16,
Self::Vht(_) => RADIOTAP_FIELD_VHT as u16,
Self::Unknown(field) => field.present_bit(),
}
}
pub fn alignment(&self) -> usize {
match self {
Self::Unknown(field) => field.alignment(),
_ => radiotap_field_metadata(self.present_bit())
.map(|metadata| metadata.alignment())
.unwrap_or(1),
}
}
pub fn size(&self) -> usize {
match self {
Self::Unknown(field) => field.size(),
_ => radiotap_field_metadata(self.present_bit())
.map(|metadata| metadata.size())
.unwrap_or(0),
}
}
pub const fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown(_))
}
pub const fn unknown_field(&self) -> Option<&RadiotapUnknownField> {
match self {
Self::Unknown(field) => Some(field),
_ => None,
}
}
pub(crate) fn encode_body(&self, out: &mut Vec<u8>) {
match self {
Self::Tsft(value) => out.extend_from_slice(&value.to_le_bytes()),
Self::Flags(value) => out.push(value.bits()),
Self::Rate(value) => out.push(*value),
Self::Channel(value) => out.extend_from_slice(&value.to_bytes()),
Self::Fhss(value) => out.extend_from_slice(value),
Self::AntennaSignal(value) => out.push(*value as u8),
Self::AntennaNoise(value) => out.push(*value as u8),
Self::LockQuality(value) => out.extend_from_slice(&value.to_le_bytes()),
Self::TxAttenuation(value) => out.extend_from_slice(&value.to_le_bytes()),
Self::DbTxAttenuation(value) => out.extend_from_slice(&value.to_le_bytes()),
Self::DbmTxPower(value) => out.push(*value as u8),
Self::Antenna(value) => out.push(*value),
Self::RxFlags(value) => out.extend_from_slice(&value.bits().to_le_bytes()),
Self::TxFlags(value) => out.extend_from_slice(&value.bits().to_le_bytes()),
Self::RtsRetries(value) => out.push(*value),
Self::DataRetries(value) => out.push(*value),
Self::Mcs(value) => out.extend_from_slice(value),
Self::AMpduStatus(value) => out.extend_from_slice(value),
Self::Vht(value) => out.extend_from_slice(value),
Self::Unknown(field) => out.extend_from_slice(field.raw_bytes()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Radiotap {
version: Field<u8>,
pad: Field<u8>,
length: Field<u16>,
present: Field<RadiotapPresent>,
fields: Vec<RadiotapField>,
raw_fields: Vec<u8>,
}
impl Radiotap {
pub fn new() -> Self {
Self {
version: Field::defaulted(0),
pad: Field::defaulted(0),
length: Field::unset(),
present: Field::unset(),
fields: Vec::new(),
raw_fields: Vec::new(),
}
}
pub fn monitor_tx(
rate: u8,
channel: impl Into<RadiotapChannel>,
tx_flags: impl Into<RadiotapTxFlags>,
) -> Self {
Self::new().rate(rate).channel(channel).tx_flags(tx_flags)
}
pub fn version(mut self, version: u8) -> Self {
self.version.set_user(version);
self
}
pub fn version_value(&self) -> Option<u8> {
self.version.value().copied()
}
pub fn pad(mut self, pad: u8) -> Self {
self.pad.set_user(pad);
self
}
pub fn pad_value(&self) -> Option<u8> {
self.pad.value().copied()
}
pub fn length_value(&self) -> Option<u16> {
self.length.value().copied()
}
pub fn fields(&self) -> &[RadiotapField] {
&self.fields
}
pub fn typed_fields(&self) -> impl Iterator<Item = &RadiotapField> {
self.fields.iter().filter(|field| !field.is_unknown())
}
pub fn unknown_fields(&self) -> impl Iterator<Item = &RadiotapUnknownField> {
self.fields.iter().filter_map(RadiotapField::unknown_field)
}
pub fn raw_fields(&self) -> &[u8] {
&self.raw_fields
}
pub fn field(&self, present_bit: u16) -> Option<&RadiotapField> {
self.fields
.iter()
.find(|field| field.present_bit() == present_bit)
}
pub fn with_field(mut self, field: RadiotapField) -> Self {
self.set_field(field);
self
}
pub fn unknown_field(
self,
present_bit: u16,
alignment: usize,
raw_bytes: impl Into<Vec<u8>>,
) -> Result<Self> {
Ok(self.with_field(RadiotapField::unknown(present_bit, alignment, raw_bytes)?))
}
pub fn present(&self) -> Result<RadiotapPresent> {
if let Some(present) = self.present.value() {
Ok(present.clone())
} else {
self.inferred_present()
}
}
pub fn typed_field_bits(&self) -> impl Iterator<Item = u16> + '_ {
self.typed_fields().map(RadiotapField::present_bit)
}
pub fn unknown_field_bits(&self) -> impl Iterator<Item = u16> + '_ {
self.unknown_fields().map(RadiotapUnknownField::present_bit)
}
pub fn tsft(mut self, tsft: u64) -> Self {
self.set_field(RadiotapField::Tsft(tsft));
self
}
pub fn tsft_value(&self) -> Option<u64> {
match self.field(RADIOTAP_FIELD_TSFT.into()) {
Some(RadiotapField::Tsft(value)) => Some(*value),
_ => None,
}
}
pub fn flags(mut self, flags: impl Into<RadiotapFlags>) -> Self {
self.set_field(RadiotapField::Flags(flags.into()));
self
}
pub fn flags_value(&self) -> Option<RadiotapFlags> {
match self.field(RADIOTAP_FIELD_FLAGS.into()) {
Some(RadiotapField::Flags(value)) => Some(*value),
_ => None,
}
}
pub fn fcs_status(&self) -> Option<RadiotapFcsStatus> {
self.flags_value().map(|flags| flags.fcs_status())
}
pub fn rate(mut self, rate: u8) -> Self {
self.set_field(RadiotapField::Rate(rate));
self
}
pub fn rate_value(&self) -> Option<u8> {
match self.field(RADIOTAP_FIELD_RATE.into()) {
Some(RadiotapField::Rate(value)) => Some(*value),
_ => None,
}
}
pub fn channel(mut self, channel: impl Into<RadiotapChannel>) -> Self {
self.set_field(RadiotapField::Channel(channel.into()));
self
}
pub fn channel_value(&self) -> Option<RadiotapChannel> {
match self.field(RADIOTAP_FIELD_CHANNEL.into()) {
Some(RadiotapField::Channel(value)) => Some(*value),
_ => None,
}
}
pub fn fhss(mut self, fhss: [u8; 2]) -> Self {
self.set_field(RadiotapField::Fhss(fhss));
self
}
pub fn fhss_value(&self) -> Option<[u8; 2]> {
match self.field(RADIOTAP_FIELD_FHSS.into()) {
Some(RadiotapField::Fhss(value)) => Some(*value),
_ => None,
}
}
pub fn antenna_signal(mut self, antenna_signal: i8) -> Self {
self.set_field(RadiotapField::AntennaSignal(antenna_signal));
self
}
pub fn antenna_signal_value(&self) -> Option<i8> {
match self.field(RADIOTAP_FIELD_ANTENNA_SIGNAL.into()) {
Some(RadiotapField::AntennaSignal(value)) => Some(*value),
_ => None,
}
}
pub fn antenna_noise(mut self, antenna_noise: i8) -> Self {
self.set_field(RadiotapField::AntennaNoise(antenna_noise));
self
}
pub fn antenna_noise_value(&self) -> Option<i8> {
match self.field(RADIOTAP_FIELD_ANTENNA_NOISE.into()) {
Some(RadiotapField::AntennaNoise(value)) => Some(*value),
_ => None,
}
}
pub fn lock_quality(mut self, lock_quality: u16) -> Self {
self.set_field(RadiotapField::LockQuality(lock_quality));
self
}
pub fn lock_quality_value(&self) -> Option<u16> {
match self.field(RADIOTAP_FIELD_LOCK_QUALITY.into()) {
Some(RadiotapField::LockQuality(value)) => Some(*value),
_ => None,
}
}
pub fn tx_attenuation(mut self, tx_attenuation: u16) -> Self {
self.set_field(RadiotapField::TxAttenuation(tx_attenuation));
self
}
pub fn tx_attenuation_value(&self) -> Option<u16> {
match self.field(RADIOTAP_FIELD_TX_ATTENUATION.into()) {
Some(RadiotapField::TxAttenuation(value)) => Some(*value),
_ => None,
}
}
pub fn db_tx_attenuation(mut self, db_tx_attenuation: u16) -> Self {
self.set_field(RadiotapField::DbTxAttenuation(db_tx_attenuation));
self
}
pub fn db_tx_attenuation_value(&self) -> Option<u16> {
match self.field(RADIOTAP_FIELD_DB_TX_ATTENUATION.into()) {
Some(RadiotapField::DbTxAttenuation(value)) => Some(*value),
_ => None,
}
}
pub fn dbm_tx_power(mut self, dbm_tx_power: i8) -> Self {
self.set_field(RadiotapField::DbmTxPower(dbm_tx_power));
self
}
pub fn dbm_tx_power_value(&self) -> Option<i8> {
match self.field(RADIOTAP_FIELD_DBM_TX_POWER.into()) {
Some(RadiotapField::DbmTxPower(value)) => Some(*value),
_ => None,
}
}
pub fn antenna(mut self, antenna: u8) -> Self {
self.set_field(RadiotapField::Antenna(antenna));
self
}
pub fn antenna_value(&self) -> Option<u8> {
match self.field(RADIOTAP_FIELD_ANTENNA.into()) {
Some(RadiotapField::Antenna(value)) => Some(*value),
_ => None,
}
}
pub fn rx_flags(mut self, rx_flags: impl Into<RadiotapRxFlags>) -> Self {
self.set_field(RadiotapField::RxFlags(rx_flags.into()));
self
}
pub fn rx_flags_value(&self) -> Option<RadiotapRxFlags> {
match self.field(RADIOTAP_FIELD_RX_FLAGS.into()) {
Some(RadiotapField::RxFlags(value)) => Some(*value),
_ => None,
}
}
pub fn tx_flags(mut self, tx_flags: impl Into<RadiotapTxFlags>) -> Self {
self.set_field(RadiotapField::TxFlags(tx_flags.into()));
self
}
pub fn tx_flags_value(&self) -> Option<RadiotapTxFlags> {
match self.field(RADIOTAP_FIELD_TX_FLAGS.into()) {
Some(RadiotapField::TxFlags(value)) => Some(*value),
_ => None,
}
}
pub fn rts_retries(mut self, rts_retries: u8) -> Self {
self.set_field(RadiotapField::RtsRetries(rts_retries));
self
}
pub fn rts_retries_value(&self) -> Option<u8> {
match self.field(RADIOTAP_FIELD_RTS_RETRIES.into()) {
Some(RadiotapField::RtsRetries(value)) => Some(*value),
_ => None,
}
}
pub fn data_retries(mut self, data_retries: u8) -> Self {
self.set_field(RadiotapField::DataRetries(data_retries));
self
}
pub fn data_retries_value(&self) -> Option<u8> {
match self.field(RADIOTAP_FIELD_DATA_RETRIES.into()) {
Some(RadiotapField::DataRetries(value)) => Some(*value),
_ => None,
}
}
pub fn mcs(mut self, mcs: [u8; 3]) -> Self {
self.set_field(RadiotapField::Mcs(mcs));
self
}
pub fn mcs_value(&self) -> Option<[u8; 3]> {
match self.field(RADIOTAP_FIELD_MCS.into()) {
Some(RadiotapField::Mcs(value)) => Some(*value),
_ => None,
}
}
pub fn a_mpdu_status(mut self, a_mpdu_status: [u8; 8]) -> Self {
self.set_field(RadiotapField::AMpduStatus(a_mpdu_status));
self
}
pub fn a_mpdu_status_value(&self) -> Option<[u8; 8]> {
match self.field(RADIOTAP_FIELD_A_MPDU_STATUS.into()) {
Some(RadiotapField::AMpduStatus(value)) => Some(*value),
_ => None,
}
}
pub fn vht(mut self, vht: [u8; 12]) -> Self {
self.set_field(RadiotapField::Vht(vht));
self
}
pub fn vht_value(&self) -> Option<[u8; 12]> {
match self.field(RADIOTAP_FIELD_VHT.into()) {
Some(RadiotapField::Vht(value)) => Some(*value),
_ => None,
}
}
fn set_field(&mut self, field: RadiotapField) {
let present_bit = field.present_bit();
if let Some(existing) = self
.fields
.iter_mut()
.find(|existing| existing.present_bit() == present_bit)
{
*existing = field;
} else {
self.fields.push(field);
}
}
pub(crate) fn fields_in_present_order(&self) -> Vec<&RadiotapField> {
let mut fields = self.fields.iter().collect::<Vec<_>>();
fields.sort_by_key(|field| field.present_bit());
fields
}
pub(crate) fn encoded_fields_len_from_offset(
&self,
fields_offset_from_header_start: usize,
) -> usize {
let mut offset = fields_offset_from_header_start;
for field in self.fields_in_present_order() {
offset += radiotap_field_padding(offset, field.alignment());
offset += field.size();
}
offset - fields_offset_from_header_start + self.raw_fields.len()
}
pub(crate) fn compile_fields_from_offset(
&self,
fields_offset_from_header_start: usize,
out: &mut Vec<u8>,
) -> usize {
let initial_len = out.len();
let mut offset = fields_offset_from_header_start;
for field in self.fields_in_present_order() {
let padding = radiotap_field_padding(offset, field.alignment());
out.resize(out.len() + padding, 0);
offset += padding;
field.encode_body(out);
offset += field.size();
}
out.extend_from_slice(&self.raw_fields);
out.len() - initial_len
}
pub(crate) fn decode_fields_from_header(
present: &RadiotapPresent,
header: &[u8],
fields_offset_from_header_start: usize,
) -> Result<(Vec<RadiotapField>, usize)> {
let mut fields = Vec::new();
let mut offset = fields_offset_from_header_start;
for bit in present.field_bits() {
let Some(metadata) = radiotap_field_metadata(bit) else {
break;
};
offset += radiotap_field_padding(offset, metadata.alignment());
let required = offset + metadata.size();
if header.len() < required {
return Err(CrafterError::buffer_too_short(
"radiotap.field",
required,
header.len(),
));
}
fields.push(decode_radiotap_field(bit, &header[offset..required])?);
offset = required;
}
Ok((fields, offset))
}
fn inferred_present(&self) -> Result<RadiotapPresent> {
RadiotapPresent::from_field_bits(self.typed_field_bits(), self.unknown_field_bits())
}
fn effective_version(&self) -> u8 {
self.version.value().copied().unwrap_or(0)
}
fn effective_pad(&self) -> u8 {
self.pad.value().copied().unwrap_or(0)
}
fn header_len_with_present(&self, present: &RadiotapPresent) -> usize {
let fields_offset = RADIOTAP_FIXED_HEADER_LEN + present.encoded_len();
RADIOTAP_FIXED_HEADER_LEN
+ present.encoded_len()
+ self.encoded_fields_len_from_offset(fields_offset)
}
fn fallback_header_len(&self) -> usize {
let present_len = self
.present
.value()
.map(RadiotapPresent::encoded_len)
.unwrap_or(RADIOTAP_PRESENT_WORD_LEN);
let fields_offset = RADIOTAP_FIXED_HEADER_LEN + present_len;
RADIOTAP_FIXED_HEADER_LEN + present_len + self.encoded_fields_len_from_offset(fields_offset)
}
fn compiled_header_len(&self) -> Result<u16> {
let present = self.present()?;
let header_len = self.header_len_with_present(&present);
u16::try_from(header_len).map_err(|_| {
CrafterError::invalid_field_value(
"radiotap.length",
"encoded radiotap header exceeds u16 length",
)
})
}
}
impl Default for Radiotap {
fn default() -> Self {
Self::new()
}
}
impl Layer for Radiotap {
fn name(&self) -> &'static str {
"Radiotap"
}
fn summary(&self) -> String {
let present = self.present();
let length = match &present {
Ok(present) => self
.compiled_header_len()
.map(usize::from)
.unwrap_or_else(|_| self.header_len_with_present(present)),
Err(_) => self
.length_value()
.map(usize::from)
.unwrap_or_else(|| self.fallback_header_len()),
};
let mut fields = vec![
format!("version={}", self.effective_version()),
format!("len={length}"),
format!("fields={}", self.fields.len()),
];
match present {
Ok(present) => fields.push(format!("present={}", radiotap_present_summary(&present))),
Err(err) => {
fields.push("present=malformed".to_string());
fields.push(format!("present_error={err}"));
}
}
if !self.raw_fields.is_empty() {
fields.push(format!("raw_fields_len={}", self.raw_fields.len()));
}
if let Some(fcs) = self.fcs_status() {
fields.push(format!("fcs_present={}", fcs.present()));
fields.push(format!("failed_fcs={}", fcs.failed()));
}
format!("Radiotap({})", fields.join(", "))
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
let present = self.present();
let length = match &present {
Ok(present) => self
.compiled_header_len()
.map(usize::from)
.unwrap_or_else(|_| self.header_len_with_present(present)),
Err(_) => self
.length_value()
.map(usize::from)
.unwrap_or_else(|| self.fallback_header_len()),
};
let mut fields = vec![
("version", self.effective_version().to_string()),
("pad", format!("0x{:02x}", self.effective_pad())),
("length", length.to_string()),
("field_count", self.fields.len().to_string()),
];
match present {
Ok(present) => fields.push(("present", radiotap_present_summary(&present))),
Err(err) => {
fields.push(("present", "malformed".to_string()));
fields.push(("present_error", err.to_string()));
}
}
for field in self.fields_in_present_order() {
fields.push((field.inspection_name(), field.inspection_value()));
}
if !self.raw_fields.is_empty() {
fields.push(("raw_fields_len", self.raw_fields.len().to_string()));
fields.push(("raw_fields", radiotap_hex_bytes(&self.raw_fields)));
}
if let Some(fcs) = self.fcs_status() {
fields.push(("fcs_present", fcs.present().to_string()));
fields.push(("failed_fcs", fcs.failed().to_string()));
}
fields
}
fn encoded_len(&self) -> usize {
self.present()
.map(|present| self.header_len_with_present(&present))
.unwrap_or_else(|_| self.fallback_header_len())
}
fn compile(&self, _ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
let present = self.present()?;
let length = self.compiled_header_len()?;
let fields_offset = RADIOTAP_FIXED_HEADER_LEN + present.encoded_len();
out.push(self.effective_version());
out.push(self.effective_pad());
out.extend_from_slice(&length.to_le_bytes());
present.encode(out);
self.compile_fields_from_offset(fields_offset, out);
Ok(())
}
fn clone_layer(&self) -> Box<dyn Layer> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
impl<R> Div<R> for Radiotap
where
R: IntoPacket,
{
type Output = Packet;
fn div(self, rhs: R) -> Self::Output {
Packet::from_layer(self).concat(rhs)
}
}
pub(crate) fn decode_radiotap_with_registry(
registry: &ProtocolRegistry,
bytes: &[u8],
) -> Result<Packet> {
let (radiotap, tail) = decode_radiotap(bytes)?;
let packet = Packet::new().push(radiotap);
if tail.is_empty() {
Ok(packet)
} else {
decode_dot11_with_registry(registry, tail).map(|dot11| packet.concat(dot11))
}
}
fn decode_radiotap(bytes: &[u8]) -> Result<(Radiotap, &[u8])> {
if bytes.len() < RADIOTAP_FIXED_HEADER_LEN {
return Err(CrafterError::buffer_too_short(
"radiotap.header",
RADIOTAP_FIXED_HEADER_LEN,
bytes.len(),
));
}
let declared_len = u16::from_le_bytes([bytes[2], bytes[3]]);
let header_len = usize::from(declared_len);
if header_len < RADIOTAP_MIN_HEADER_LEN {
return Err(CrafterError::buffer_too_short(
"radiotap.header",
RADIOTAP_MIN_HEADER_LEN,
header_len,
));
}
if bytes.len() < header_len {
return Err(CrafterError::buffer_too_short(
"radiotap.header",
header_len,
bytes.len(),
));
}
let header = &bytes[..header_len];
let (present, present_len) = RadiotapPresent::decode(&header[RADIOTAP_FIXED_HEADER_LEN..])?;
let fields_offset = RADIOTAP_FIXED_HEADER_LEN + present_len;
let (fields, consumed) = Radiotap::decode_fields_from_header(&present, header, fields_offset)?;
let radiotap = Radiotap {
version: Field::user(bytes[0]),
pad: Field::user(bytes[1]),
length: Field::user(declared_len),
present: Field::user(present),
fields,
raw_fields: header[consumed..].to_vec(),
};
Ok((radiotap, &bytes[header_len..]))
}
fn radiotap_field_array<const N: usize>(context: &'static str, bytes: &[u8]) -> Result<[u8; N]> {
bytes
.get(..N)
.and_then(|bytes| bytes.try_into().ok())
.ok_or_else(|| CrafterError::buffer_too_short(context, N, bytes.len()))
}
pub(crate) fn decode_radiotap_field(bit: u16, bytes: &[u8]) -> Result<RadiotapField> {
let Some(metadata) = radiotap_field_metadata(bit) else {
return Err(CrafterError::invalid_field_value(
"radiotap.field.bit",
"unknown radiotap field bit",
));
};
if bytes.len() < metadata.size() {
return Err(CrafterError::buffer_too_short(
"radiotap.field",
metadata.size(),
bytes.len(),
));
}
let bytes = &bytes[..metadata.size()];
let field = match bit {
bit if bit == u16::from(RADIOTAP_FIELD_TSFT) => RadiotapField::Tsft(u64::from_le_bytes(
radiotap_field_array("radiotap.field.tsft", bytes)?,
)),
bit if bit == u16::from(RADIOTAP_FIELD_FLAGS) => {
RadiotapField::Flags(RadiotapFlags::from_bits(bytes[0]))
}
bit if bit == u16::from(RADIOTAP_FIELD_RATE) => RadiotapField::Rate(bytes[0]),
bit if bit == u16::from(RADIOTAP_FIELD_CHANNEL) => {
RadiotapField::Channel(RadiotapChannel::new(
u16::from_le_bytes([bytes[0], bytes[1]]),
u16::from_le_bytes([bytes[2], bytes[3]]),
))
}
bit if bit == u16::from(RADIOTAP_FIELD_FHSS) => {
RadiotapField::Fhss(radiotap_field_array("radiotap.field.fhss", bytes)?)
}
bit if bit == u16::from(RADIOTAP_FIELD_ANTENNA_SIGNAL) => {
RadiotapField::AntennaSignal(bytes[0] as i8)
}
bit if bit == u16::from(RADIOTAP_FIELD_ANTENNA_NOISE) => {
RadiotapField::AntennaNoise(bytes[0] as i8)
}
bit if bit == u16::from(RADIOTAP_FIELD_LOCK_QUALITY) => {
RadiotapField::LockQuality(u16::from_le_bytes([bytes[0], bytes[1]]))
}
bit if bit == u16::from(RADIOTAP_FIELD_TX_ATTENUATION) => {
RadiotapField::TxAttenuation(u16::from_le_bytes([bytes[0], bytes[1]]))
}
bit if bit == u16::from(RADIOTAP_FIELD_DB_TX_ATTENUATION) => {
RadiotapField::DbTxAttenuation(u16::from_le_bytes([bytes[0], bytes[1]]))
}
bit if bit == u16::from(RADIOTAP_FIELD_DBM_TX_POWER) => {
RadiotapField::DbmTxPower(bytes[0] as i8)
}
bit if bit == u16::from(RADIOTAP_FIELD_ANTENNA) => RadiotapField::Antenna(bytes[0]),
bit if bit == u16::from(RADIOTAP_FIELD_RX_FLAGS) => {
RadiotapField::RxFlags(RadiotapRxFlags::from_bits(u16::from_le_bytes([
bytes[0], bytes[1],
])))
}
bit if bit == u16::from(RADIOTAP_FIELD_TX_FLAGS) => {
RadiotapField::TxFlags(RadiotapTxFlags::from_bits(u16::from_le_bytes([
bytes[0], bytes[1],
])))
}
bit if bit == u16::from(RADIOTAP_FIELD_RTS_RETRIES) => RadiotapField::RtsRetries(bytes[0]),
bit if bit == u16::from(RADIOTAP_FIELD_DATA_RETRIES) => {
RadiotapField::DataRetries(bytes[0])
}
bit if bit == u16::from(RADIOTAP_FIELD_MCS) => {
RadiotapField::Mcs(radiotap_field_array("radiotap.field.mcs", bytes)?)
}
bit if bit == u16::from(RADIOTAP_FIELD_A_MPDU_STATUS) => {
RadiotapField::AMpduStatus(radiotap_field_array("radiotap.field.a_mpdu_status", bytes)?)
}
bit if bit == u16::from(RADIOTAP_FIELD_VHT) => {
RadiotapField::Vht(radiotap_field_array("radiotap.field.vht", bytes)?)
}
_ => {
return Err(CrafterError::invalid_field_value(
"radiotap.field.bit",
"unknown radiotap field bit",
))
}
};
Ok(field)
}
impl RadiotapField {
fn inspection_name(&self) -> &'static str {
match self {
Self::Tsft(_) => "tsft",
Self::Flags(_) => "flags",
Self::Rate(_) => "rate_500kbps",
Self::Channel(_) => "channel",
Self::Fhss(_) => "fhss",
Self::AntennaSignal(_) => "antenna_signal_dbm",
Self::AntennaNoise(_) => "antenna_noise_dbm",
Self::LockQuality(_) => "lock_quality",
Self::TxAttenuation(_) => "tx_attenuation",
Self::DbTxAttenuation(_) => "db_tx_attenuation",
Self::DbmTxPower(_) => "dbm_tx_power_dbm",
Self::Antenna(_) => "antenna",
Self::RxFlags(_) => "rx_flags",
Self::TxFlags(_) => "tx_flags",
Self::RtsRetries(_) => "rts_retries",
Self::DataRetries(_) => "data_retries",
Self::Mcs(_) => "mcs",
Self::AMpduStatus(_) => "a_mpdu_status",
Self::Vht(_) => "vht",
Self::Unknown(_) => "unknown_field",
}
}
fn inspection_value(&self) -> String {
match self {
Self::Tsft(value) => value.to_string(),
Self::Flags(value) => format!("0x{:02x}", value.bits()),
Self::Rate(value) => value.to_string(),
Self::Channel(value) => {
format!(
"frequency={}, flags=0x{:04x}",
value.frequency(),
value.flags()
)
}
Self::Fhss(value) => radiotap_hex_bytes(value),
Self::AntennaSignal(value) => value.to_string(),
Self::AntennaNoise(value) => value.to_string(),
Self::LockQuality(value) => value.to_string(),
Self::TxAttenuation(value) => value.to_string(),
Self::DbTxAttenuation(value) => value.to_string(),
Self::DbmTxPower(value) => value.to_string(),
Self::Antenna(value) => value.to_string(),
Self::RxFlags(value) => format!("0x{:04x}", value.bits()),
Self::TxFlags(value) => format!("0x{:04x}", value.bits()),
Self::RtsRetries(value) => value.to_string(),
Self::DataRetries(value) => value.to_string(),
Self::Mcs(value) => radiotap_hex_bytes(value),
Self::AMpduStatus(value) => radiotap_hex_bytes(value),
Self::Vht(value) => radiotap_hex_bytes(value),
Self::Unknown(value) => format!(
"bit={}, align={}, bytes={}",
value.present_bit(),
value.alignment(),
radiotap_hex_bytes(value.raw_bytes())
),
}
}
}
fn radiotap_present_summary(present: &RadiotapPresent) -> String {
present
.words()
.iter()
.map(|word| format!("0x{word:08x}"))
.collect::<Vec<_>>()
.join(",")
}
fn radiotap_hex_bytes(bytes: &[u8]) -> String {
let mut output = String::new();
for (index, byte) in bytes.iter().enumerate() {
if index > 0 {
output.push(' ');
}
output.push_str(&format!("{byte:02x}"));
}
output
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mac::MacAddr;
use crate::packet::{LinkType, Raw};
use crate::protocols::link::Dot11;
#[test]
fn radiotap_present_bitmap_decodes_single_word_and_reencodes_little_endian() {
let word = RADIOTAP_PRESENT_FLAGS | RADIOTAP_PRESENT_RATE;
let mut bytes = word.to_le_bytes().to_vec();
bytes.extend_from_slice(b"tail");
let (present, consumed) = RadiotapPresent::decode(&bytes).unwrap();
assert_eq!(consumed, 4);
assert_eq!(present.words(), &[word]);
assert!(present.is_field_present(RADIOTAP_FIELD_FLAGS.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_RATE.into()));
assert!(!present.is_field_present(RADIOTAP_FIELD_TSFT.into()));
assert_eq!(present.encoded_len(), 4);
assert_eq!(present.to_bytes(), word.to_le_bytes().to_vec());
}
#[test]
fn radiotap_present_bitmap_decodes_extended_words_until_terminator() {
let first = RADIOTAP_PRESENT_EXTENDED | RADIOTAP_PRESENT_FLAGS;
let second = 0x0000_0005_u32;
let mut bytes = Vec::new();
bytes.extend_from_slice(&first.to_le_bytes());
bytes.extend_from_slice(&second.to_le_bytes());
bytes.extend_from_slice(b"payload");
let (present, consumed) = RadiotapPresent::decode(&bytes).unwrap();
assert_eq!(consumed, 8);
assert_eq!(present.words(), &[first, second]);
assert!(present.is_field_present(RADIOTAP_FIELD_FLAGS.into()));
assert!(present.is_field_present(32));
assert!(present.is_field_present(34));
assert!(!present.is_field_present(31));
assert!(!present.is_field_present(63));
assert_eq!(
present.to_bytes(),
[first.to_le_bytes(), second.to_le_bytes()].concat()
);
}
#[test]
fn radiotap_present_bitmap_encodes_deterministically_from_typed_and_raw_bits() {
let present = RadiotapPresent::from_field_bits(
[u16::from(RADIOTAP_FIELD_RATE), 34],
[32, u16::from(RADIOTAP_FIELD_FLAGS)],
)
.unwrap();
let same_present = RadiotapPresent::from_field_bits(
[32, u16::from(RADIOTAP_FIELD_FLAGS)],
[u16::from(RADIOTAP_FIELD_RATE), 34],
)
.unwrap();
assert_eq!(
present.words(),
&[
RADIOTAP_PRESENT_EXTENDED | RADIOTAP_PRESENT_FLAGS | RADIOTAP_PRESENT_RATE,
0x5
]
);
assert_eq!(present, same_present);
assert_eq!(
present.to_bytes(),
[
(RADIOTAP_PRESENT_EXTENDED | RADIOTAP_PRESENT_FLAGS | RADIOTAP_PRESENT_RATE)
.to_le_bytes(),
0x5_u32.to_le_bytes(),
]
.concat()
);
let err = RadiotapPresent::from_field_bits([31], []).unwrap_err();
assert_eq!(
err,
CrafterError::invalid_field_value(
"radiotap.present.bit",
"bit 31 of each present word is the extension flag",
)
);
}
#[test]
fn radiotap_present_bitmap_truncated_extension_word_is_structured_error() {
let first = RADIOTAP_PRESENT_EXTENDED | RADIOTAP_PRESENT_FLAGS;
let mut bytes = first.to_le_bytes().to_vec();
bytes.extend_from_slice(&[0xaa, 0xbb]);
let err = RadiotapPresent::decode(&bytes).unwrap_err();
assert_eq!(
err,
CrafterError::buffer_too_short("radiotap.present", 8, 6)
);
}
#[test]
fn radiotap_constants_selected_present_bits_have_source_backed_masks() {
let cases = [
(RADIOTAP_FIELD_TSFT, RADIOTAP_PRESENT_TSFT),
(RADIOTAP_FIELD_FLAGS, RADIOTAP_PRESENT_FLAGS),
(RADIOTAP_FIELD_RATE, RADIOTAP_PRESENT_RATE),
(RADIOTAP_FIELD_CHANNEL, RADIOTAP_PRESENT_CHANNEL),
(RADIOTAP_FIELD_FHSS, RADIOTAP_PRESENT_FHSS),
(
RADIOTAP_FIELD_ANTENNA_SIGNAL,
RADIOTAP_PRESENT_ANTENNA_SIGNAL,
),
(RADIOTAP_FIELD_ANTENNA_NOISE, RADIOTAP_PRESENT_ANTENNA_NOISE),
(RADIOTAP_FIELD_LOCK_QUALITY, RADIOTAP_PRESENT_LOCK_QUALITY),
(
RADIOTAP_FIELD_TX_ATTENUATION,
RADIOTAP_PRESENT_TX_ATTENUATION,
),
(
RADIOTAP_FIELD_DB_TX_ATTENUATION,
RADIOTAP_PRESENT_DB_TX_ATTENUATION,
),
(RADIOTAP_FIELD_DBM_TX_POWER, RADIOTAP_PRESENT_DBM_TX_POWER),
(RADIOTAP_FIELD_ANTENNA, RADIOTAP_PRESENT_ANTENNA),
(RADIOTAP_FIELD_RX_FLAGS, RADIOTAP_PRESENT_RX_FLAGS),
(RADIOTAP_FIELD_TX_FLAGS, RADIOTAP_PRESENT_TX_FLAGS),
(RADIOTAP_FIELD_RTS_RETRIES, RADIOTAP_PRESENT_RTS_RETRIES),
(RADIOTAP_FIELD_DATA_RETRIES, RADIOTAP_PRESENT_DATA_RETRIES),
(RADIOTAP_FIELD_MCS, RADIOTAP_PRESENT_MCS),
(RADIOTAP_FIELD_A_MPDU_STATUS, RADIOTAP_PRESENT_A_MPDU_STATUS),
(RADIOTAP_FIELD_VHT, RADIOTAP_PRESENT_VHT),
];
for (bit, mask) in cases {
assert_eq!(mask, 1u32 << bit);
}
assert_eq!(RADIOTAP_FIELD_TSFT, 0);
assert_eq!(RADIOTAP_FIELD_TX_FLAGS, 15);
assert_eq!(RADIOTAP_FIELD_RTS_RETRIES, 16);
assert_eq!(RADIOTAP_FIELD_DATA_RETRIES, 17);
assert_eq!(RADIOTAP_FIELD_MCS, 19);
assert_eq!(RADIOTAP_FIELD_A_MPDU_STATUS, 20);
assert_eq!(RADIOTAP_FIELD_VHT, 21);
assert_eq!(RADIOTAP_PRESENT_EXTENDED, 0x8000_0000);
}
#[test]
fn radiotap_constants_metadata_lookup_reports_alignment_and_size() {
let cases = [
(RADIOTAP_FIELD_TSFT, RADIOTAP_PRESENT_TSFT, 8, 8),
(RADIOTAP_FIELD_FLAGS, RADIOTAP_PRESENT_FLAGS, 1, 1),
(RADIOTAP_FIELD_RATE, RADIOTAP_PRESENT_RATE, 1, 1),
(RADIOTAP_FIELD_CHANNEL, RADIOTAP_PRESENT_CHANNEL, 2, 4),
(RADIOTAP_FIELD_FHSS, RADIOTAP_PRESENT_FHSS, 1, 2),
(
RADIOTAP_FIELD_ANTENNA_SIGNAL,
RADIOTAP_PRESENT_ANTENNA_SIGNAL,
1,
1,
),
(
RADIOTAP_FIELD_ANTENNA_NOISE,
RADIOTAP_PRESENT_ANTENNA_NOISE,
1,
1,
),
(
RADIOTAP_FIELD_LOCK_QUALITY,
RADIOTAP_PRESENT_LOCK_QUALITY,
2,
2,
),
(
RADIOTAP_FIELD_TX_ATTENUATION,
RADIOTAP_PRESENT_TX_ATTENUATION,
2,
2,
),
(
RADIOTAP_FIELD_DB_TX_ATTENUATION,
RADIOTAP_PRESENT_DB_TX_ATTENUATION,
2,
2,
),
(
RADIOTAP_FIELD_DBM_TX_POWER,
RADIOTAP_PRESENT_DBM_TX_POWER,
1,
1,
),
(RADIOTAP_FIELD_ANTENNA, RADIOTAP_PRESENT_ANTENNA, 1, 1),
(RADIOTAP_FIELD_RX_FLAGS, RADIOTAP_PRESENT_RX_FLAGS, 2, 2),
(RADIOTAP_FIELD_TX_FLAGS, RADIOTAP_PRESENT_TX_FLAGS, 2, 2),
(
RADIOTAP_FIELD_RTS_RETRIES,
RADIOTAP_PRESENT_RTS_RETRIES,
1,
1,
),
(
RADIOTAP_FIELD_DATA_RETRIES,
RADIOTAP_PRESENT_DATA_RETRIES,
1,
1,
),
(RADIOTAP_FIELD_MCS, RADIOTAP_PRESENT_MCS, 1, 3),
(
RADIOTAP_FIELD_A_MPDU_STATUS,
RADIOTAP_PRESENT_A_MPDU_STATUS,
4,
8,
),
(RADIOTAP_FIELD_VHT, RADIOTAP_PRESENT_VHT, 2, 12),
];
assert_eq!(RADIOTAP_SELECTED_FIELD_METADATA.len(), cases.len());
for (bit, mask, alignment, size) in cases {
let metadata = radiotap_field_metadata(bit.into()).unwrap();
assert_eq!(metadata.bit(), bit);
assert_eq!(metadata.present_mask(), mask);
assert_eq!(metadata.alignment(), alignment);
assert_eq!(metadata.size(), size);
}
}
#[test]
fn radiotap_constants_metadata_lookup_treats_unselected_bits_as_unknown() {
for bit in [12, 13, 18, 22, 29, 30, 31, 32, 63] {
assert_eq!(radiotap_field_metadata(bit), None);
}
}
#[test]
fn radiotap_alignment_padding_from_header_start_handles_required_boundaries() {
assert_eq!(radiotap_field_padding(8, 1), 0);
assert_eq!(radiotap_field_padding(9, 2), 1);
assert_eq!(radiotap_field_padding(10, 4), 2);
assert_eq!(radiotap_field_padding(12, 8), 4);
assert_eq!(radiotap_field_padding(16, 8), 0);
}
#[test]
fn radiotap_alignment_compile_fields_inserts_padding_by_present_order() {
let radiotap = Radiotap::new()
.unknown_field(32, 4, [0xde, 0xad, 0xbe, 0xef])
.unwrap()
.with_field(RadiotapField::AMpduStatus([
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
]))
.channel((0x1122, 0x3344))
.flags(0x5a)
.tsft(0x0102_0304_0506_0708);
let present = radiotap.present().unwrap();
let fields_offset = RADIOTAP_FIXED_HEADER_LEN + present.encoded_len();
let mut encoded = Vec::new();
let emitted = radiotap.compile_fields_from_offset(fields_offset, &mut encoded);
assert_eq!(fields_offset, 12);
assert_eq!(emitted, encoded.len());
assert_eq!(
radiotap.encoded_fields_len_from_offset(fields_offset),
encoded.len()
);
assert_eq!(
encoded,
vec![
0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
0x5a, 0x00, 0x22, 0x11, 0x44, 0x33,
0x00, 0x00, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xde, 0xad, 0xbe, 0xef,
]
);
}
#[test]
fn radiotap_alignment_decode_fields_skips_padding_by_present_order() {
let present = RadiotapPresent::from_words([
RADIOTAP_PRESENT_EXTENDED
| RADIOTAP_PRESENT_TSFT
| RADIOTAP_PRESENT_FLAGS
| RADIOTAP_PRESENT_CHANNEL
| RADIOTAP_PRESENT_A_MPDU_STATUS,
0,
]);
let fields_offset = RADIOTAP_FIXED_HEADER_LEN + present.encoded_len();
let mut header = vec![0; fields_offset];
header.extend_from_slice(&[
0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
0x5a, 0x00, 0x22, 0x11, 0x44, 0x33,
0x00, 0x00, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
]);
let (fields, consumed) =
Radiotap::decode_fields_from_header(&present, &header, fields_offset).unwrap();
assert_eq!(fields_offset, 12);
assert_eq!(consumed, header.len());
assert_eq!(
fields,
vec![
RadiotapField::Tsft(0x0102_0304_0506_0708),
RadiotapField::Flags(RadiotapFlags::from_bits(0x5a)),
RadiotapField::Channel(RadiotapChannel::new(0x1122, 0x3344)),
RadiotapField::AMpduStatus([0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,]),
]
);
}
#[test]
fn radiotap_fields_empty_layer_does_not_invent_defaults() {
let radiotap = Radiotap::new();
assert!(radiotap.fields().is_empty());
assert_eq!(radiotap.tsft_value(), None);
assert_eq!(radiotap.flags_value(), None);
assert_eq!(radiotap.rate_value(), None);
assert_eq!(radiotap.channel_value(), None);
assert_eq!(radiotap.antenna_signal_value(), None);
assert_eq!(radiotap.antenna_value(), None);
assert_eq!(radiotap.rx_flags_value(), None);
assert_eq!(radiotap.tx_flags_value(), None);
assert_eq!(radiotap.present().unwrap().words(), &[0]);
}
#[test]
fn radiotap_fields_typed_setters_store_values_and_present_bits() {
let radiotap = Radiotap::new()
.tsft(0x0102_0304_0506_0708)
.flags(RADIOTAP_FLAGS_FCS_PRESENT | RADIOTAP_FLAGS_FAILED_FCS)
.rate(12)
.channel((2412, 0x00a0))
.antenna_signal(-42)
.antenna(2)
.rx_flags(0x0002)
.tx_flags(0x0008);
assert_eq!(radiotap.tsft_value(), Some(0x0102_0304_0506_0708));
assert_eq!(
radiotap.flags_value().map(|flags| flags.bits()),
Some(RADIOTAP_FLAGS_FCS_PRESENT | RADIOTAP_FLAGS_FAILED_FCS)
);
assert_eq!(
radiotap.fcs_status(),
Some(RadiotapFcsStatus::new(true, true))
);
assert_eq!(radiotap.rate_value(), Some(12));
assert_eq!(
radiotap.channel_value(),
Some(RadiotapChannel::new(2412, 0x00a0))
);
assert_eq!(
radiotap.channel_value().map(RadiotapChannel::to_bytes),
Some([0x6c, 0x09, 0xa0, 0x00])
);
assert_eq!(radiotap.antenna_signal_value(), Some(-42));
assert_eq!(radiotap.antenna_value(), Some(2));
assert_eq!(
radiotap.rx_flags_value().map(|flags| flags.bits()),
Some(0x0002)
);
assert_eq!(
radiotap.tx_flags_value().map(|flags| flags.bits()),
Some(0x0008)
);
let present = radiotap.present().unwrap();
assert!(present.is_field_present(RADIOTAP_FIELD_TSFT.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_FLAGS.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_RATE.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_CHANNEL.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_ANTENNA_SIGNAL.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_ANTENNA.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_RX_FLAGS.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_TX_FLAGS.into()));
assert!(!present.is_field_present(RADIOTAP_FIELD_ANTENNA_NOISE.into()));
assert_eq!(
radiotap.field(RADIOTAP_FIELD_CHANNEL.into()),
Some(&RadiotapField::Channel(RadiotapChannel::new(2412, 0x00a0)))
);
assert_eq!(
radiotap
.typed_field_bits()
.collect::<std::collections::BTreeSet<_>>(),
[
RADIOTAP_FIELD_TSFT.into(),
RADIOTAP_FIELD_FLAGS.into(),
RADIOTAP_FIELD_RATE.into(),
RADIOTAP_FIELD_CHANNEL.into(),
RADIOTAP_FIELD_ANTENNA_SIGNAL.into(),
RADIOTAP_FIELD_ANTENNA.into(),
RADIOTAP_FIELD_RX_FLAGS.into(),
RADIOTAP_FIELD_TX_FLAGS.into(),
]
.into_iter()
.collect()
);
}
#[test]
fn radiotap_tx_flag_constants_match_spec_bits_and_round_trip() {
assert_eq!(RadiotapTxFlags::ORDER.bits(), 0x0001);
assert_eq!(RadiotapTxFlags::NO_REORDER.bits(), 0x0002);
assert_eq!(RadiotapTxFlags::NO_ACK.bits(), 0x0008);
assert_eq!(RadiotapTxFlags::NO_SEQ.bits(), 0x0010);
assert_eq!(RadiotapTxFlags::FIXED_RATE.bits(), 0x0020);
let combined = RadiotapTxFlags::from_bits(
RadiotapTxFlags::NO_ACK.bits() | RadiotapTxFlags::NO_SEQ.bits(),
);
assert!(combined.contains(RadiotapTxFlags::NO_ACK));
assert!(combined.contains(RadiotapTxFlags::NO_SEQ));
assert!(!combined.contains(RadiotapTxFlags::FIXED_RATE));
let dot11 = Dot11::data()
.addr1(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x01]))
.addr2(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x02]))
.addr3(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x03]))
.sequence_number(7);
let radiotap = Radiotap::new().tx_flags(RadiotapTxFlags::NO_ACK);
let bytes = (Packet::from_layer(radiotap) / dot11).compile().unwrap();
let decoded = Packet::decode_from_link(LinkType::Radiotap, bytes.as_bytes()).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
assert_eq!(radiotap.tx_flags_value(), Some(RadiotapTxFlags::NO_ACK));
assert_eq!(
radiotap.tx_flags_value().map(|flags| flags.bits()),
Some(0x0008)
);
}
#[test]
fn radiotap_channel_helpers_map_channel_numbers_and_expose_flag_bits() {
assert_eq!(RADIOTAP_CHANNEL_TURBO, 0x0010);
assert_eq!(RADIOTAP_CHANNEL_CCK, 0x0020);
assert_eq!(RADIOTAP_CHANNEL_OFDM, 0x0040);
assert_eq!(RADIOTAP_CHANNEL_2GHZ, 0x0080);
assert_eq!(RADIOTAP_CHANNEL_5GHZ, 0x0100);
assert_eq!(RADIOTAP_CHANNEL_2GHZ_CCK, 0x00a0);
assert_eq!(RADIOTAP_CHANNEL_5GHZ_OFDM, 0x0140);
assert_eq!(RadiotapChannel::channel_2ghz(1).frequency(), 2412);
assert_eq!(RadiotapChannel::channel_2ghz(6).frequency(), 2437);
assert_eq!(RadiotapChannel::channel_2ghz(11).frequency(), 2462);
assert_eq!(RadiotapChannel::channel_2ghz(14).frequency(), 2484);
let channel = RadiotapChannel::channel_2ghz(6);
assert_eq!(channel.flags(), RADIOTAP_CHANNEL_2GHZ_CCK);
assert_eq!(channel, RadiotapChannel::new(2437, 0x00a0));
assert_eq!(channel.to_bytes(), [0x85, 0x09, 0xa0, 0x00]);
}
#[test]
fn radiotap_monitor_tx_round_trips_rate_channel_and_tx_flags() {
let channel = RadiotapChannel::channel_2ghz(6);
let radiotap = Radiotap::monitor_tx(2, channel, RadiotapTxFlags::NO_ACK);
let bytes = Packet::from_layer(radiotap).compile().unwrap();
let decoded = Packet::decode_from_link(LinkType::Radiotap, bytes.as_bytes()).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
assert_eq!(radiotap.rate_value(), Some(2));
assert_eq!(radiotap.channel_value(), Some(channel));
assert_eq!(radiotap.tx_flags_value(), Some(RadiotapTxFlags::NO_ACK));
}
#[test]
fn radiotap_monitor_tx_header_bytes_pin_present_bitmap_and_round_trip() {
let channel = RadiotapChannel::channel_2ghz(6);
let radiotap = Radiotap::monitor_tx(2, channel, RadiotapTxFlags::NO_ACK);
let bytes = Packet::from_layer(radiotap).compile().unwrap();
let header = bytes.as_bytes();
assert_eq!(header[0], 0x00);
assert_eq!(header[1], 0x00);
let it_len = u16::from_le_bytes([header[2], header[3]]);
assert_eq!(it_len, 16);
assert_eq!(usize::from(it_len), header.len());
let present_word = u32::from_le_bytes([header[4], header[5], header[6], header[7]]);
assert_eq!(
present_word,
RADIOTAP_PRESENT_RATE | RADIOTAP_PRESENT_CHANNEL | RADIOTAP_PRESENT_TX_FLAGS
);
assert_ne!(present_word & RADIOTAP_PRESENT_RATE, 0);
assert_ne!(present_word & RADIOTAP_PRESENT_CHANNEL, 0);
assert_ne!(present_word & RADIOTAP_PRESENT_TX_FLAGS, 0);
let decoded = Packet::decode_from_link(LinkType::Radiotap, header).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
let present = radiotap.present().unwrap();
assert!(present.is_field_present(RADIOTAP_FIELD_RATE.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_CHANNEL.into()));
assert!(present.is_field_present(RADIOTAP_FIELD_TX_FLAGS.into()));
assert_eq!(
present.field_bits().collect::<Vec<_>>(),
vec![
RADIOTAP_FIELD_RATE.into(),
RADIOTAP_FIELD_CHANNEL.into(),
RADIOTAP_FIELD_TX_FLAGS.into(),
]
);
assert_eq!(radiotap.length_value(), Some(16));
assert_eq!(radiotap.rate_value(), Some(2));
let decoded_channel = radiotap.channel_value().unwrap();
assert_eq!(decoded_channel, channel);
assert_eq!(decoded_channel.frequency(), 2437);
let decoded_tx_flags = radiotap.tx_flags_value().unwrap();
assert!(decoded_tx_flags.contains(RadiotapTxFlags::NO_ACK));
assert_eq!(
decoded_tx_flags.bits() & RadiotapTxFlags::NO_ACK.bits(),
0x0008
);
}
#[test]
fn radiotap_fields_unknown_preserves_present_bit_alignment_and_bytes() {
let radiotap = Radiotap::new()
.unknown_field(32, 4, [0xde, 0xad, 0xbe, 0xef])
.unwrap();
let unknown = radiotap.unknown_fields().next().unwrap();
assert_eq!(unknown.present_bit(), 32);
assert_eq!(unknown.alignment(), 4);
assert_eq!(unknown.raw_bytes(), &[0xde, 0xad, 0xbe, 0xef]);
assert_eq!(unknown.size(), 4);
assert_eq!(radiotap.unknown_field_bits().collect::<Vec<_>>(), vec![32]);
let field = radiotap.field(32).unwrap();
assert!(field.is_unknown());
assert_eq!(field.present_bit(), 32);
assert_eq!(field.alignment(), 4);
assert_eq!(field.size(), 4);
assert_eq!(
radiotap.present().unwrap().words(),
&[RADIOTAP_PRESENT_EXTENDED, 0x1]
);
}
#[test]
fn radiotap_fields_setters_replace_by_present_bit_without_defaults() {
let radiotap = Radiotap::new()
.rate(11)
.rate(22)
.with_field(RadiotapField::Unknown(
RadiotapUnknownField::new(RADIOTAP_FIELD_RATE.into(), 1, [0xff]).unwrap(),
));
assert_eq!(radiotap.rate_value(), None);
assert_eq!(radiotap.fields().len(), 1);
assert_eq!(
radiotap
.unknown_fields()
.next()
.map(|field| field.raw_bytes()),
Some(&[0xff][..])
);
}
#[test]
fn radiotap_fields_reject_extension_bit_as_unknown_field() {
let err = RadiotapUnknownField::new(31, 1, []).unwrap_err();
assert_eq!(
err,
CrafterError::invalid_field_value(
"radiotap.present.bit",
"bit 31 of each present word is the extension flag",
)
);
let err = RadiotapUnknownField::new(32, 0, []).unwrap_err();
assert_eq!(
err,
CrafterError::invalid_field_value(
"radiotap.field.alignment",
"alignment must be at least one octet",
)
);
}
#[test]
fn radiotap_layer_compile_computes_length_present_and_inspection() {
let packet = Radiotap::new()
.flags(RADIOTAP_FLAGS_FCS_PRESENT)
.rate(12)
.channel((2412, 0x00a0))
/ Raw::from([0xaa, 0xbb]);
let radiotap = packet.layer::<Radiotap>().unwrap();
assert_eq!(radiotap.encoded_len(), 14);
assert_eq!(radiotap.version_value(), Some(0));
assert_eq!(radiotap.pad_value(), Some(0));
assert_eq!(radiotap.length_value(), None);
assert_eq!(
radiotap.fcs_status(),
Some(RadiotapFcsStatus::new(true, false))
);
assert_eq!(
radiotap.present().unwrap().words(),
&[RADIOTAP_PRESENT_FLAGS | RADIOTAP_PRESENT_RATE | RADIOTAP_PRESENT_CHANNEL]
);
let compiled = packet.compile().unwrap();
assert_eq!(
compiled.as_bytes(),
&[
0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x0c, 0x6c, 0x09, 0xa0, 0x00, 0xaa, 0xbb,
]
);
assert!(packet.summary().contains("Radiotap("));
assert!(packet.show().contains("fcs_present: true"));
}
#[test]
fn radiotap_layer_decode_dispatches_dot11_and_round_trips() {
let dot11 = Dot11::data()
.addr1(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]))
.addr2(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]))
.addr3(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x03]))
.sequence_number(7);
let dot11_bytes = Packet::from_layer(dot11).compile().unwrap();
let mut bytes = vec![
0x00, 0x00, 0x0a, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x16,
];
bytes.extend_from_slice(dot11_bytes.as_bytes());
let decoded = Packet::decode_from_link(LinkType::Radiotap, &bytes).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
assert_eq!(radiotap.length_value(), Some(10));
assert_eq!(radiotap.flags_value(), Some(RadiotapFlags::from_bits(0x50)));
assert_eq!(
radiotap.fcs_status(),
Some(RadiotapFcsStatus::new(true, true))
);
assert_eq!(radiotap.rate_value(), Some(0x16));
assert!(radiotap.raw_fields().is_empty());
assert!(decoded.layer::<Dot11>().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_slice());
}
#[test]
fn radiotap_decode_from_link_dispatches_radiotap_root_and_inner_dot11() {
let dot11 = Dot11::data()
.addr1(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x01]))
.addr2(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x02]))
.addr3(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x03]))
.sequence_number(37);
let dot11_bytes = Packet::from_layer(dot11).compile().unwrap();
let mut bytes = vec![
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, ];
bytes.extend_from_slice(dot11_bytes.as_bytes());
let decoded = Packet::decode_from_link(LinkType::Radiotap, &bytes).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
let dot11 = decoded.layer::<Dot11>().unwrap();
assert_eq!(radiotap.version_value(), Some(0));
assert_eq!(radiotap.length_value(), Some(8));
assert!(radiotap.fields().is_empty());
assert_eq!(dot11.sequence_number_value(), Some(37));
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_slice());
}
#[test]
fn radiotap_decode_from_link_keeps_bare_dot11_link_root_separate() {
let dot11 = Dot11::data()
.addr1(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x11]))
.addr2(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x12]))
.addr3(MacAddr::new([0x02, 0x00, 0x5e, 0x10, 0x00, 0x13]))
.sequence_number(38);
let bytes = Packet::from_layer(dot11).compile().unwrap();
let decoded = Packet::decode_from_link(LinkType::Ieee80211, bytes.as_bytes()).unwrap();
let dot11 = decoded.layer::<Dot11>().unwrap();
assert!(decoded.layer::<Radiotap>().is_none());
assert_eq!(dot11.sequence_number_value(), Some(38));
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_bytes());
}
#[test]
fn radiotap_fcs_metadata_failed_fcs_decodes_typed_layers_and_preserves_tail() {
let dot11 = Dot11::data()
.addr1(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]))
.addr2(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]))
.addr3(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x03]))
.sequence_number(11);
let dot11_bytes = Packet::from_layer(dot11).compile().unwrap();
let fcs = [0xde, 0xad, 0xbe, 0xef];
let present = RADIOTAP_PRESENT_FLAGS | RADIOTAP_PRESENT_RX_FLAGS;
let mut bytes = vec![
0x00, 0x00, 0x0c, 0x00, ];
bytes.extend_from_slice(&present.to_le_bytes());
bytes.extend_from_slice(&[
RADIOTAP_FLAGS_FCS_PRESENT | RADIOTAP_FLAGS_FAILED_FCS,
0x00, ]);
bytes.extend_from_slice(&RADIOTAP_RX_FLAGS_PLCP_CRC_FAILED.to_le_bytes());
bytes.extend_from_slice(dot11_bytes.as_bytes());
bytes.extend_from_slice(&fcs);
let decoded = Packet::decode_from_link(LinkType::Radiotap, &bytes).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
let rx_flags = radiotap.rx_flags_value().unwrap();
assert_eq!(
radiotap.fcs_status(),
Some(RadiotapFcsStatus::new(true, true))
);
assert_eq!(rx_flags.bits(), RADIOTAP_RX_FLAGS_PLCP_CRC_FAILED);
assert!(rx_flags.plcp_crc_failed());
assert!(!rx_flags.reserved_fcs_failed_bit());
assert!(decoded.layer::<Dot11>().is_some());
assert_eq!(decoded.layer::<Raw>().unwrap().as_bytes(), &fcs);
let summary = decoded.summary();
assert!(summary.contains("fcs_present=true"), "{summary}");
assert!(summary.contains("failed_fcs=true"), "{summary}");
let show = decoded.show();
assert!(show.contains("rx_flags: 0x0002"), "{show}");
assert!(show.contains("failed_fcs: true"), "{show}");
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_slice());
}
#[test]
fn radiotap_fcs_metadata_rx_reserved_bit_is_not_failed_fcs_status() {
let dot11 = Dot11::data()
.addr1(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]))
.addr2(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]))
.addr3(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x03]))
.sequence_number(12);
let dot11_bytes = Packet::from_layer(dot11).compile().unwrap();
let mut bytes = vec![
0x00, 0x00, 0x0a, 0x00, ];
bytes.extend_from_slice(&RADIOTAP_PRESENT_RX_FLAGS.to_le_bytes());
bytes.extend_from_slice(&RADIOTAP_RX_FLAGS_RESERVED_WAS_FCS_FAILED.to_le_bytes());
bytes.extend_from_slice(dot11_bytes.as_bytes());
let decoded = Packet::decode_from_link(LinkType::Radiotap, &bytes).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
let rx_flags = radiotap.rx_flags_value().unwrap();
assert_eq!(radiotap.fcs_status(), None);
assert_eq!(rx_flags.bits(), RADIOTAP_RX_FLAGS_RESERVED_WAS_FCS_FAILED);
assert!(rx_flags.reserved_fcs_failed_bit());
assert!(!rx_flags.plcp_crc_failed());
assert!(decoded.layer::<Dot11>().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_slice());
}
#[test]
fn radiotap_layer_decode_preserves_unknown_header_tail() {
let bytes = [
0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x20, 0xde, 0xad, 0xbe, 0xef,
];
let decoded = Packet::decode_from_link(LinkType::Radiotap, bytes).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
assert_eq!(radiotap.present().unwrap().words(), &[0x2000_0000]);
assert_eq!(radiotap.raw_fields(), &[0xde, 0xad, 0xbe, 0xef]);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_slice());
}
#[test]
fn radiotap_decode_preserves_header_fields_raw_tail_and_inner_dot11() {
let dot11 = Dot11::data()
.addr1(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]))
.addr2(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]))
.addr3(MacAddr::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x03]))
.sequence_number(9);
let dot11_bytes = Packet::from_layer(dot11).compile().unwrap();
let present =
RADIOTAP_PRESENT_FLAGS | RADIOTAP_PRESENT_RATE | RADIOTAP_PRESENT_CHANNEL | 0x2000_0000;
let mut bytes = vec![
0x00, 0x2a, 0x10, 0x00, ];
bytes.extend_from_slice(&present.to_le_bytes());
bytes.extend_from_slice(&[
0x50, 0x16, 0x6c, 0x09, 0xa0, 0x00, 0xde, 0xad, ]);
bytes.extend_from_slice(dot11_bytes.as_bytes());
let decoded = Packet::decode_from_link(LinkType::Radiotap, &bytes).unwrap();
let radiotap = decoded.layer::<Radiotap>().unwrap();
let dot11 = decoded.layer::<Dot11>().unwrap();
assert_eq!(radiotap.version_value(), Some(0));
assert_eq!(radiotap.pad_value(), Some(0x2a));
assert_eq!(radiotap.length_value(), Some(16));
assert_eq!(radiotap.present().unwrap().words(), &[present]);
assert_eq!(radiotap.flags_value(), Some(RadiotapFlags::from_bits(0x50)));
assert_eq!(
radiotap.fcs_status(),
Some(RadiotapFcsStatus::new(true, true))
);
assert_eq!(radiotap.rate_value(), Some(0x16));
assert_eq!(
radiotap.channel_value(),
Some(RadiotapChannel::new(2412, 0x00a0))
);
assert_eq!(radiotap.raw_fields(), &[0xde, 0xad]);
assert_eq!(dot11.sequence_number_value(), Some(9));
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_slice());
}
#[test]
fn radiotap_decode_returns_structured_errors_for_header_lengths() {
let short_base = [
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
];
let short_base_err = Packet::decode_from_link(LinkType::Radiotap, short_base).unwrap_err();
assert_eq!(
short_base_err,
CrafterError::buffer_too_short("radiotap.header", 8, 7)
);
let invalid_len = [
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let invalid_len_err =
Packet::decode_from_link(LinkType::Radiotap, invalid_len).unwrap_err();
assert_eq!(
invalid_len_err,
CrafterError::buffer_too_short("radiotap.header", 8, 4)
);
}
#[test]
fn radiotap_decode_returns_structured_errors_for_truncated_fields() {
let truncated_present = [
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, ];
let truncated_present_err =
Packet::decode_from_link(LinkType::Radiotap, truncated_present).unwrap_err();
assert_eq!(
truncated_present_err,
CrafterError::buffer_too_short("radiotap.present", 8, 4)
);
let truncated_channel = [
0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6c, 0x09,
];
let truncated_channel_err =
Packet::decode_from_link(LinkType::Radiotap, truncated_channel).unwrap_err();
assert_eq!(
truncated_channel_err,
CrafterError::buffer_too_short("radiotap.field", 12, 10)
);
}
}