osmarks 3d946ce89f slightly improve things
There appear to be issues with data frames containing tons of unique MAC addresses for no apparent reason (specifically QoS ones?) so there may be misparsing
2021-04-18 21:09:12 +01:00

154 lines
5.7 KiB
Rust

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<FrameControl> {
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<MacAddress> {
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(())
}