pub mod command;
pub mod frame_control;
pub mod header;
use byte::ctx;
use byte::BytesExt;
use byte::TryRead;
use byte::TryWrite;
use frame_control::FrameType;
use header::Header;
use crate::internal::macros::impl_byte;
use crate::security::frame::AuxFrameHeader;
use crate::security::SecurityContext;
pub enum Frame<'a> {
Data(DataFrame<'a>),
NwkCommand(CommandFrame<'a>),
Reserved(Header<'a>),
InterPan(Header<'a>),
}
impl<'a> TryRead<'a, SecurityContext> for Frame<'a> {
fn try_read(bytes: &'a [u8], cx: SecurityContext) -> byte::Result<(Self, usize)> {
let offset = &mut 0;
let header: Header<'a> = bytes.read_with(offset, ())?;
let has_security = header.frame_control.security_flag();
let frame = match header.frame_control.frame_type() {
FrameType::Data => {
let (aux_header, payload) = if has_security {
let aux_header: AuxFrameHeader = bytes.read_with(offset, ())?;
let payload = cx.unsecure_frame(&aux_header, bytes, offset)?;
(Some(aux_header), payload)
} else {
(
None,
bytes.read_with(offset, ctx::Bytes::Len(bytes.len() - *offset))?,
)
};
let data_frame = DataFrame {
header,
aux_header,
payload,
};
Self::Data(data_frame)
}
FrameType::NwkCommand => {
let (aux_header, bytes): (_, &[u8]) = if has_security {
let aux_header: AuxFrameHeader = bytes.read_with(offset, ())?;
let payload = cx.unsecure_frame(&aux_header, bytes, offset)?;
(Some(aux_header), payload)
} else {
(
None,
bytes.read_with(offset, ctx::Bytes::Len(bytes.len() - *offset))?,
)
};
let cmd_frame = CommandFrame {
header,
aux_header,
command: bytes.read_with(&mut 0, ())?,
};
Self::NwkCommand(cmd_frame)
}
FrameType::Reserved => Self::Reserved(header),
FrameType::InterPan => Self::InterPan(header),
};
Ok((frame, *offset))
}
}
pub struct DataFrame<'a> {
pub header: Header<'a>,
pub aux_header: Option<AuxFrameHeader>,
pub payload: &'a [u8],
}
pub struct CommandFrame<'a> {
pub header: Header<'a>,
pub aux_header: Option<AuxFrameHeader>,
pub command: Command,
}
impl_byte! {
#[derive(Debug)]
#[repr(u8)]
pub enum Command {
RouteRequest = 0x01,
RouteReply = 0x02,
NetworkStatus = 0x03,
Leave = 0x04,
RouteRecord = 0x05,
RejoinRequest = 0x06,
RejoinResponse = 0x07,
LinkStatus = 0x08,
NetworkReport = 0x09,
NetworkUpdate = 0x0a,
EndDeviceTimeoutRequest = 0x0b,
EndDeviceTimeoutResponse = 0x0c,
LinkPowerDelta = 0x0d,
#[fallback = true]
Reserved,
}
}
#[cfg(test)]
mod tests {
use byte::TryRead;
use super::*;
use crate::security::SecurityContext;
const CMD_FRAME: &[u8] = &[
0x09, 0x12, 0xff, 0xff, 0x34, 0x12, 0x01, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x28, 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x01, 0x01, ];
#[test]
fn command_with_security() {
let (frame, _) = Frame::try_read(CMD_FRAME, SecurityContext::no_security()).unwrap();
let Frame::NwkCommand(frame) = frame else {
unreachable!()
};
assert!(frame.header.frame_control.security_flag());
assert_eq!(frame.aux_header.unwrap().security_control.0, 0x28);
}
}