//! Parser mínimo de cabeçalho Radiotap — só os campos que usamos
//! (canal/frequência e força de sinal). Especificação:
//!
#[derive(Debug, Clone, Copy, Default)]
pub struct RadiotapInfo {
pub freq_mhz: Option,
pub signal_dbm: Option,
pub header_len: usize,
}
// Índices de bit no bitmap "present" que nos interessam.
const BIT_FLAGS: u32 = 1 << 1;
const BIT_RATE: u32 = 1 << 2;
const BIT_CHANNEL: u32 = 1 << 3;
const BIT_ANTSIGNAL: u32 = 1 << 5;
/// Retorna as infos extraídas e quantos bytes o cabeçalho ocupa (para o
/// chamador saber onde o frame 802.11 propriamente começa).
pub fn parse(buf: &[u8]) -> Option {
if buf.len() < 8 || buf[0] != 0 {
return None; // version deve ser 0
}
let header_len = u16::from_le_bytes([buf[2], buf[3]]) as usize;
if header_len > buf.len() || header_len < 8 {
return None;
}
// Pode haver múltiplas palavras de "present" (bit 31 = continua).
let mut present_words = Vec::new();
let mut offset = 4;
loop {
if offset + 4 > buf.len() {
return None;
}
let word = u32::from_le_bytes([buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]]);
present_words.push(word);
offset += 4;
if word & (1 << 31) == 0 {
break;
}
}
let present = present_words[0]; // só o primeiro conjunto de campos padrão nos interessa
let mut info = RadiotapInfo { header_len, ..Default::default() };
let mut cursor = offset;
// TSFT (u64, align 8) viria antes de Flags; para o nosso subconjunto de
// campos simplificamos assumindo captura via mac80211 (ordem padrão:
// TSFT, Flags, Rate, Channel, ..., AntSignal). Avançamos campo a campo
// respeitando alinhamento, sem travar se algum bit não estiver setado.
macro_rules! align {
($a:expr) => {{
let rem = cursor % $a;
if rem != 0 {
cursor += $a - rem;
}
}};
}
if present & (1 << 0) != 0 {
align!(8);
cursor += 8; // TSFT
}
if present & BIT_FLAGS != 0 {
align!(1);
cursor += 1;
}
if present & BIT_RATE != 0 {
align!(1);
cursor += 1;
}
if present & BIT_CHANNEL != 0 {
align!(2);
if cursor + 4 <= buf.len() {
let freq = u16::from_le_bytes([buf[cursor], buf[cursor + 1]]);
info.freq_mhz = Some(freq);
}
cursor += 4;
}
// FHSS (bit 4) — raro, ignorado.
if present & (1 << 4) != 0 {
align!(2);
cursor += 2;
}
if present & BIT_ANTSIGNAL != 0 {
align!(1);
if cursor < buf.len() {
info.signal_dbm = Some(buf[cursor] as i8);
}
}
Some(info)
}
pub fn freq_to_channel(freq_mhz: u16) -> Option {
match freq_mhz {
2412..=2472 => Some(((freq_mhz - 2412) / 5 + 1) as u32),
2484 => Some(14),
5000..=5900 => Some(((freq_mhz - 5000) / 5) as u32),
_ => None,
}
}