use data_frame::DataFrame;
use mac_parser::MACAddress;
use mgmt_frame::{body::action::RawActionBody, RawActionFrame};
use scroll::{ctx::TryFromCtx, Endian, Pread};
use crate::common::{
strip_and_validate_fcs, FrameControlField, FrameType, ManagementFrameSubtype, SequenceControl,
};
pub mod control_frame;
pub mod data_frame;
pub mod mgmt_frame;
pub trait IEEE80211Frame {
const TYPE: FrameType;
#[doc(hidden)]
fn read_action_body_matches(_action_body: RawActionBody<'_>) -> bool {
false
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GenericFrame<'a> {
bytes: &'a [u8],
}
impl<'a> GenericFrame<'a> {
pub fn new(bytes: &'a [u8], with_fcs: bool) -> Result<Self, scroll::Error> {
let bytes = if with_fcs {
strip_and_validate_fcs(bytes)?
} else {
bytes
};
if bytes.len() < 10 {
return Err(scroll::Error::BadInput {
size: 0,
msg: "Byte slice for generic frame was shorter than 10.",
});
}
Ok(Self { bytes })
}
pub fn frame_control_field(&self) -> FrameControlField {
FrameControlField::from_bits(self.bytes.pread_with(0, Endian::Little).unwrap())
}
pub fn duration(&self) -> u16 {
self.bytes.pread_with(2, Endian::Little).unwrap()
}
pub fn address_1(&self) -> MACAddress {
self.bytes.pread(4).unwrap()
}
pub fn address_2(&self) -> Option<MACAddress> {
if self.frame_control_field().frame_type().has_address_2() {
self.bytes.pread(10).ok()
} else {
None
}
}
pub fn address_3(&self) -> Option<MACAddress> {
if self.frame_control_field().frame_type().has_address_3() {
self.bytes.pread(16).ok()
} else {
None
}
}
pub fn sequence_control(&self) -> Option<SequenceControl> {
if self
.frame_control_field()
.frame_type()
.has_sequence_control()
{
self.bytes.pread(22).map(SequenceControl::from_bits).ok()
} else {
None
}
}
pub fn matches<Frame: IEEE80211Frame>(self) -> bool {
let fcf = self.frame_control_field();
match (fcf.frame_type(), Frame::TYPE) {
(FrameType::Control(_), FrameType::Control(_))
| (FrameType::Data(_), FrameType::Data(_)) => true,
_ if fcf.frame_type() == Frame::TYPE => match Frame::TYPE {
FrameType::Management(ManagementFrameSubtype::Action)
| FrameType::Management(ManagementFrameSubtype::ActionNoACK) => {
let Ok(raw_action_frame) = self.bytes.pread_with::<RawActionFrame>(0, false)
else {
return false;
};
Frame::read_action_body_matches(raw_action_frame.body)
}
_ => true,
},
_ => false,
}
}
pub fn parse_to_typed<Frame: IEEE80211Frame + TryFromCtx<'a, bool, Error = scroll::Error>>(
&self,
) -> Option<Result<Frame, scroll::Error>> {
if self.matches::<Frame>() {
Some(self.bytes.pread_with(0, false))
} else {
None
}
}
pub fn is_eapol_key_frame(&self) -> bool {
if let FrameType::Data(subtype) = self.frame_control_field().frame_type() {
if !subtype.has_payload() {
return false;
}
let Some(Ok(data_frame)) = self.parse_to_typed::<DataFrame>() else {
return false;
};
let Some(payload) = data_frame.payload else {
return false;
};
payload.get(6..8) == Some(&[0x88, 0x8e])
} else {
false
}
}
}
#[macro_export]
macro_rules! match_frames {
(
$bytes:expr,
$(with_fcs: $ctx:expr,)?
$(
$binding:pat = $frame_type:ty => $block:block
)+
) => {
{
use ieee80211::{GenericFrame, scroll};
const WITH_FCS: bool = {
let mut with_fcs = false;
$(
let _ = $ctx;
with_fcs = true;
)?
with_fcs
};
let generic_frame = GenericFrame::new($bytes, WITH_FCS);
match generic_frame {
Ok(generic_frame) => {
if false {
unreachable!()
}
$(
else if let Some(frame_res) = generic_frame.parse_to_typed::<$frame_type>() {
match frame_res {
Ok($binding) => Ok($block),
Err(err) => Err(err)
}
}
)*
else {
Err(scroll::Error::BadInput { size: 0, msg: "Frame type not matched." })
}
}
Err(err) => Err(err)
}
}
};
}