//! 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, } }