use pcap::Capture; use stable_eyre::eyre::{eyre, Result}; use std::collections::HashSet; use std::convert::TryInto; mod ieee80211 { use int_enum::IntEnum; use std::convert::TryFrom; use std::convert::TryInto; use std::fmt; // data/extension frames are irrelevant so just ignore their subtype #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum FrameType { Management(ManagementSubtype), Control(ControlSubtype), Data, Extension } #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, IntEnum)] pub enum ManagementSubtype { AssociationRequest = 0b0000, AssociationResponse = 0b0001, ReassociationRequest = 0b0010, ReassociationResponse = 0b0011, ProbeRequest = 0b0100, ProbeResponse = 0b0101, Beacon = 0b1000, Disassociation = 0b1010, Authentication = 0b1011, Deauthentication = 0b1100, Other = 0b1111 } #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, IntEnum)] pub enum ControlSubtype { Rts = 0b1011, Cts = 0b1100, Ack = 0b1101, Other = 0b1111 } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct FrameControl { pub version: u8, pub ftype: FrameType, pub to_ds: bool, pub from_ds: bool, pub more_fragments: bool, pub retry: bool, pub more_data: bool, pub protected: bool, pub htc_order: bool, subtype: u8 } // read a bit out of a number (0 = LSB) fn read_bit(x: u8, bit: u8) -> bool { x >> bit & 0b1 == 1 } pub fn parse_frame_control(version_type_subtype: u8, flags: u8) -> Option { let subtype = version_type_subtype >> 4 & 0b1111; let ftype = match version_type_subtype >> 2 & 0b11 { 0b00 => FrameType::Management(ManagementSubtype::try_from(subtype).unwrap_or(ManagementSubtype::Other)), 0b01 => FrameType::Control(ControlSubtype::try_from(subtype).unwrap_or(ControlSubtype::Other)), 0b10 => FrameType::Data, 0b11 => FrameType::Extension, _ => unreachable!() }; //println!("{:?} {:08b}", ftype, flags); Some(FrameControl { version: version_type_subtype & 0b11, ftype: ftype, to_ds: read_bit(flags, 0), from_ds: read_bit(flags, 1), more_fragments: read_bit(flags, 2), retry: read_bit(flags, 3), more_data: read_bit(flags, 5), protected: read_bit(flags, 6), htc_order: read_bit(flags, 7), subtype: subtype }) } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct MacAddress([u8; 6]); impl fmt::Display for MacAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]) } } impl MacAddress { pub fn is_local(&self) -> bool { read_bit(self.0[0], 1) } pub fn is_group(&self) -> bool { read_bit(self.0[0], 0) } } pub fn read_addresses(frame: &[u8]) -> Vec { let mut out = Vec::with_capacity(4); if frame.len() >= 10 { out.push(MacAddress(frame[4..10].try_into().unwrap())); } if frame.len() >= 16 { out.push(MacAddress(frame[10..16].try_into().unwrap())); } if frame.len() >= 22 { out.push(MacAddress(frame[16..22].try_into().unwrap())); } //if frame.len() >= 30 { out.push(MacAddress(frame[24..30].try_into().unwrap())); } out } } fn main() -> Result<()> { let cap = Capture::from_device("mon0")?.immediate_mode(true); let mut cap = cap.open()?; if cap.get_datalink() != pcap::Linktype::IEEE802_11_RADIOTAP { return Err(eyre!("device link type wrong")) } let mut seen = HashSet::new(); while let Ok(packet) = cap.next() { //println!("{:?}", packet); match radiotap::parse(packet.data) { Ok(header) => { let data = &packet.data[header.length()..]; match ieee80211::parse_frame_control(data[0], data[1]) { Some(ieee80211::FrameControl { ftype: ieee80211::FrameType::Control(ieee80211::ControlSubtype::Other), .. }) => {}, Some(x) => { let sequence = if data.len() >= 24 { let sn_fst = data[22] as u16; let sn_snd = data[23] as u16; let seq = (sn_snd << 4) + (sn_fst >> 4); Some((seq, sn_fst & 0b1111)) } else { None }; let addrs = ieee80211::read_addresses(data); for addr in &addrs { if !seen.contains(addr) && !addr.is_group() && !addr.is_local() { println!("attained new MAC {} from {:?} {:?} {:?} local={} group={}", addr, x, addrs, sequence, addr.is_local(), addr.is_group()); seen.insert(addr.clone()); } } //println!("{:?} {:?}\nADDR={:?} SEQ={:?}\n", header.antenna_signal.unwrap().into_inner(), x, addrs, sequence); }, _ => {} } }, // This happens sometimes. I don't know why, since it really shouldn't (the radiotap headers are applied by the kernel). Thusly, just ignore it and hope it goes away. Err(e) => { eprintln!("header decoding failed: {:?}", e); } } } Ok(()) }