use core::any::Any;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{IntoPacket, Layer, LayerContext, Packet};
use super::consts::{
DOT15D4_EXTENDED_ADDR_LEN, DOT15D4_FCF_LEN, DOT15D4_FCS_LEN, DOT15D4_PAN_ID_LEN,
DOT15D4_SEQ_LEN, DOT15D4_SHORT_ADDR_LEN,
};
use super::fcs::dot15d4_fcs;
pub use super::consts::{Dot15d4AddrMode, Dot15d4FrameType};
#[derive(Debug)]
pub struct Dot15d4 {
frame_type: Field<Dot15d4FrameType>,
security_enabled: Field<bool>,
frame_pending: Field<bool>,
ack_request: Field<bool>,
pan_id_compression: Field<bool>,
frame_version: Field<u8>,
dest_addr_mode: Field<Dot15d4AddrMode>,
src_addr_mode: Field<Dot15d4AddrMode>,
seq: Field<u8>,
dest_pan: Field<u16>,
dest_addr: Field<u64>,
src_pan: Field<u16>,
src_addr: Field<u64>,
payload: Vec<u8>,
fcs: Field<u16>,
}
impl Clone for Dot15d4 {
fn clone(&self) -> Self {
Self {
frame_type: self.frame_type.clone(),
security_enabled: self.security_enabled.clone(),
frame_pending: self.frame_pending.clone(),
ack_request: self.ack_request.clone(),
pan_id_compression: self.pan_id_compression.clone(),
frame_version: self.frame_version.clone(),
dest_addr_mode: self.dest_addr_mode.clone(),
src_addr_mode: self.src_addr_mode.clone(),
seq: self.seq.clone(),
dest_pan: self.dest_pan.clone(),
dest_addr: self.dest_addr.clone(),
src_pan: self.src_pan.clone(),
src_addr: self.src_addr.clone(),
payload: self.payload.clone(),
fcs: self.fcs.clone(),
}
}
}
impl Dot15d4 {
pub fn new() -> Self {
Self {
frame_type: Field::unset(),
security_enabled: Field::unset(),
frame_pending: Field::unset(),
ack_request: Field::unset(),
pan_id_compression: Field::unset(),
frame_version: Field::unset(),
dest_addr_mode: Field::unset(),
src_addr_mode: Field::unset(),
seq: Field::unset(),
dest_pan: Field::unset(),
dest_addr: Field::unset(),
src_pan: Field::unset(),
src_addr: Field::unset(),
payload: Vec::new(),
fcs: Field::unset(),
}
}
pub fn data() -> Self {
Self::new().frame_type(Dot15d4FrameType::Data)
}
pub fn beacon() -> Self {
Self::new().frame_type(Dot15d4FrameType::Beacon)
}
pub fn ack() -> Self {
Self::new().frame_type(Dot15d4FrameType::Ack)
}
pub fn command() -> Self {
Self::new().frame_type(Dot15d4FrameType::MacCommand)
}
pub fn frame_type(mut self, frame_type: Dot15d4FrameType) -> Self {
self.frame_type.set_user(frame_type);
self
}
pub fn seq(mut self, seq: u8) -> Self {
self.seq.set_user(seq);
self
}
pub fn security(mut self, security_enabled: bool) -> Self {
self.security_enabled.set_user(security_enabled);
self
}
pub fn frame_pending(mut self, frame_pending: bool) -> Self {
self.frame_pending.set_user(frame_pending);
self
}
pub fn ack_request(mut self, ack_request: bool) -> Self {
self.ack_request.set_user(ack_request);
self
}
pub fn pan_id_compression(mut self, pan_id_compression: bool) -> Self {
self.pan_id_compression.set_user(pan_id_compression);
self
}
pub fn frame_version(mut self, frame_version: u8) -> Self {
self.frame_version.set_user(frame_version);
self
}
pub fn payload(mut self, payload: &[u8]) -> Self {
self.payload = payload.to_vec();
self
}
pub fn dest_short(mut self, pan: u16, addr: u16) -> Self {
self.dest_pan.set_user(pan);
self.dest_addr.set_user(u64::from(addr));
if !self.dest_addr_mode.is_user_set() {
self.dest_addr_mode.set_user(Dot15d4AddrMode::Short);
}
self
}
pub fn dest_extended(mut self, pan: u16, addr: u64) -> Self {
self.dest_pan.set_user(pan);
self.dest_addr.set_user(addr);
if !self.dest_addr_mode.is_user_set() {
self.dest_addr_mode.set_user(Dot15d4AddrMode::Extended);
}
self
}
pub fn src_short(mut self, pan: u16, addr: u16) -> Self {
self.src_pan.set_user(pan);
self.src_addr.set_user(u64::from(addr));
if !self.src_addr_mode.is_user_set() {
self.src_addr_mode.set_user(Dot15d4AddrMode::Short);
}
self
}
pub fn src_extended(mut self, pan: u16, addr: u64) -> Self {
self.src_pan.set_user(pan);
self.src_addr.set_user(addr);
if !self.src_addr_mode.is_user_set() {
self.src_addr_mode.set_user(Dot15d4AddrMode::Extended);
}
self
}
pub(crate) fn effective_dest_addr_mode(&self) -> Dot15d4AddrMode {
match self.dest_addr_mode.value() {
Some(mode) => *mode,
None => {
if self.dest_addr.value().is_some() {
Dot15d4AddrMode::Short
} else {
Dot15d4AddrMode::None
}
}
}
}
pub(crate) fn effective_src_addr_mode(&self) -> Dot15d4AddrMode {
match self.src_addr_mode.value() {
Some(mode) => *mode,
None => {
if self.src_addr.value().is_some() {
Dot15d4AddrMode::Short
} else {
Dot15d4AddrMode::None
}
}
}
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn effective_addr_mode(&self, destination: bool) -> Dot15d4AddrMode {
if destination {
self.effective_dest_addr_mode()
} else {
self.effective_src_addr_mode()
}
}
pub(crate) fn effective_pan_id_compression(&self) -> bool {
if let Some(value) = self.pan_id_compression.value() {
return *value;
}
let dest_present = self.effective_dest_addr_mode() != Dot15d4AddrMode::None;
let src_present = self.effective_src_addr_mode() != Dot15d4AddrMode::None;
if !(dest_present && src_present) {
return false;
}
match (self.dest_pan.value(), self.src_pan.value()) {
(Some(dest_pan), Some(src_pan)) => dest_pan == src_pan,
(Some(_), None) | (None, Some(_)) => true,
(None, None) => false,
}
}
pub(crate) fn effective_dest_pan_present(&self) -> bool {
self.effective_dest_addr_mode() != Dot15d4AddrMode::None
}
pub(crate) fn effective_src_pan_present(&self) -> bool {
if self.effective_src_addr_mode() == Dot15d4AddrMode::None {
return false;
}
!self.effective_pan_id_compression()
}
fn effective_frame_type(&self) -> Dot15d4FrameType {
self.frame_type
.value()
.copied()
.unwrap_or(Dot15d4FrameType::Data)
}
fn effective_seq(&self) -> u8 {
self.seq.value().copied().unwrap_or(0)
}
fn effective_frame_version(&self) -> u8 {
self.frame_version.value().copied().unwrap_or(0)
}
fn frame_control(&self) -> u16 {
let frame_type = u16::from(self.effective_frame_type().as_u3() & 0b111);
let security = u16::from(self.security_enabled.value().copied().unwrap_or(false));
let frame_pending = u16::from(self.frame_pending.value().copied().unwrap_or(false));
let ack_request = u16::from(self.ack_request.value().copied().unwrap_or(false));
let pan_id_compression = u16::from(self.effective_pan_id_compression());
let dest_mode = u16::from(self.effective_dest_addr_mode().as_u2() & 0b11);
let frame_version = u16::from(self.effective_frame_version() & 0b11);
let src_mode = u16::from(self.effective_src_addr_mode().as_u2() & 0b11);
frame_type
| (security << 3)
| (frame_pending << 4)
| (ack_request << 5)
| (pan_id_compression << 6)
| (dest_mode << 10)
| (frame_version << 12)
| (src_mode << 14)
}
fn addr_octets(mode: Dot15d4AddrMode) -> usize {
match mode {
Dot15d4AddrMode::None => 0,
Dot15d4AddrMode::Short => DOT15D4_SHORT_ADDR_LEN,
Dot15d4AddrMode::Extended => DOT15D4_EXTENDED_ADDR_LEN,
}
}
fn encode_addr(out: &mut Vec<u8>, mode: Dot15d4AddrMode, addr: u64) {
match mode {
Dot15d4AddrMode::None => {}
Dot15d4AddrMode::Short => out.extend_from_slice(&(addr as u16).to_le_bytes()),
Dot15d4AddrMode::Extended => out.extend_from_slice(&addr.to_le_bytes()),
}
}
pub(crate) fn encoded_len(&self) -> usize {
let dest_mode = self.effective_dest_addr_mode();
let src_mode = self.effective_src_addr_mode();
let mut len = DOT15D4_FCF_LEN + DOT15D4_SEQ_LEN;
if self.effective_dest_pan_present() {
len += DOT15D4_PAN_ID_LEN;
}
len += Self::addr_octets(dest_mode);
if self.effective_src_pan_present() {
len += DOT15D4_PAN_ID_LEN;
}
len += Self::addr_octets(src_mode);
len += self.payload.len();
len += DOT15D4_FCS_LEN;
len
}
fn encode_header_and_payload(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.frame_control().to_le_bytes());
out.push(self.effective_seq());
let dest_mode = self.effective_dest_addr_mode();
let src_mode = self.effective_src_addr_mode();
if self.effective_dest_pan_present() {
out.extend_from_slice(&self.dest_pan.value().copied().unwrap_or(0).to_le_bytes());
}
Self::encode_addr(out, dest_mode, self.dest_addr.value().copied().unwrap_or(0));
if self.effective_src_pan_present() {
out.extend_from_slice(&self.src_pan.value().copied().unwrap_or(0).to_le_bytes());
}
Self::encode_addr(out, src_mode, self.src_addr.value().copied().unwrap_or(0));
out.extend_from_slice(&self.payload);
}
fn append_fcs(&self, frame_start: usize, out: &mut Vec<u8>) {
let fcs = match self.fcs.value() {
Some(value) => *value,
None => dot15d4_fcs(&out[frame_start..]),
};
out.extend_from_slice(&fcs.to_le_bytes());
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
let start = out.len();
self.encode_header_and_payload(out);
self.append_fcs(start, out);
}
}
impl Default for Dot15d4 {
fn default() -> Self {
Self::new()
}
}
fn dot15d4_frame_type_label(frame_type: Dot15d4FrameType) -> &'static str {
match frame_type {
Dot15d4FrameType::Beacon => "Beacon",
Dot15d4FrameType::Data => "Data",
Dot15d4FrameType::Ack => "Ack",
Dot15d4FrameType::MacCommand => "MacCommand",
}
}
fn dot15d4_addr_label(mode: Dot15d4AddrMode, addr: Option<u64>) -> Option<String> {
match (mode, addr) {
(Dot15d4AddrMode::Short, Some(addr)) => Some(format!("0x{:04X}", addr as u16)),
(Dot15d4AddrMode::Extended, Some(addr)) => Some(format!("0x{addr:016X}")),
_ => None,
}
}
impl Layer for Dot15d4 {
fn name(&self) -> &'static str {
"Dot15d4"
}
fn summary(&self) -> String {
let mut fields = vec![dot15d4_frame_type_label(self.effective_frame_type()).to_string()];
fields.push(format!("seq={}", self.effective_seq()));
if let Some(dst) = dot15d4_addr_label(
self.effective_dest_addr_mode(),
self.dest_addr.value().copied(),
) {
fields.push(format!("dst={dst}"));
}
if let Some(src) = dot15d4_addr_label(
self.effective_src_addr_mode(),
self.src_addr.value().copied(),
) {
fields.push(format!("src={src}"));
}
format!("Dot15d4({})", fields.join(", "))
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
let mut fields = vec![
(
"frame_type",
dot15d4_frame_type_label(self.effective_frame_type()).to_string(),
),
("seq", self.effective_seq().to_string()),
(
"security_enabled",
self.security_enabled
.value()
.copied()
.unwrap_or(false)
.to_string(),
),
(
"frame_pending",
self.frame_pending
.value()
.copied()
.unwrap_or(false)
.to_string(),
),
(
"ack_request",
self.ack_request
.value()
.copied()
.unwrap_or(false)
.to_string(),
),
(
"pan_id_compression",
self.effective_pan_id_compression().to_string(),
),
];
if let Some(dst) = dot15d4_addr_label(
self.effective_dest_addr_mode(),
self.dest_addr.value().copied(),
) {
fields.push(("dest_addr", dst));
}
if let Some(src) = dot15d4_addr_label(
self.effective_src_addr_mode(),
self.src_addr.value().copied(),
) {
fields.push(("src_addr", src));
}
fields
}
fn encoded_len(&self) -> usize {
Dot15d4::encoded_len(self)
}
fn encoded_len_with_context(&self, ctx: &LayerContext<'_>) -> usize {
Dot15d4::encoded_len(self) + ctx.packet().encoded_len_after(ctx.index())
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
let start = out.len();
self.encode_header_and_payload(out);
if let Err(err) = ctx.packet().compile_layers_after_into(ctx.index(), out) {
out.truncate(start);
return Err(err);
}
self.append_fcs(start, out);
Ok(())
}
fn consumes_following(&self) -> bool {
true
}
fn clone_layer(&self) -> Box<dyn Layer> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
impl<R: IntoPacket> core::ops::Div<R> for Dot15d4 {
type Output = Packet;
fn div(self, rhs: R) -> Self::Output {
Packet::from_layer(self).concat(rhs)
}
}
fn read_dot15d4_addr(
bytes: &[u8],
offset: &mut usize,
mode: Dot15d4AddrMode,
context: &'static str,
) -> Result<u64> {
let width = Dot15d4::addr_octets(mode);
if width == 0 {
return Ok(0);
}
let required = *offset + width;
if bytes.len() < required {
return Err(CrafterError::buffer_too_short(
context,
required,
bytes.len(),
));
}
let addr = match mode {
Dot15d4AddrMode::None => 0,
Dot15d4AddrMode::Short => {
u64::from(u16::from_le_bytes([bytes[*offset], bytes[*offset + 1]]))
}
Dot15d4AddrMode::Extended => u64::from_le_bytes([
bytes[*offset],
bytes[*offset + 1],
bytes[*offset + 2],
bytes[*offset + 3],
bytes[*offset + 4],
bytes[*offset + 5],
bytes[*offset + 6],
bytes[*offset + 7],
]),
};
*offset = required;
Ok(addr)
}
fn read_dot15d4_pan(bytes: &[u8], offset: &mut usize, context: &'static str) -> Result<u16> {
let required = *offset + DOT15D4_PAN_ID_LEN;
if bytes.len() < required {
return Err(CrafterError::buffer_too_short(
context,
required,
bytes.len(),
));
}
let pan = u16::from_le_bytes([bytes[*offset], bytes[*offset + 1]]);
*offset = required;
Ok(pan)
}
pub(crate) fn decode_dot15d4(bytes: &[u8]) -> Result<(Dot15d4, &[u8])> {
if bytes.len() < DOT15D4_FCF_LEN {
return Err(CrafterError::buffer_too_short(
"dot15d4.mac.fcf",
DOT15D4_FCF_LEN,
bytes.len(),
));
}
let fcf = u16::from_le_bytes([bytes[0], bytes[1]]);
let frame_type = Dot15d4FrameType::from_u3((fcf & 0b111) as u8).ok_or_else(|| {
CrafterError::invalid_field_value("dot15d4.mac.frame_type", "reserved frame type")
})?;
let security_enabled = (fcf & (1 << 3)) != 0;
let frame_pending = (fcf & (1 << 4)) != 0;
let ack_request = (fcf & (1 << 5)) != 0;
let pan_id_compression = (fcf & (1 << 6)) != 0;
let dest_addr_mode = Dot15d4AddrMode::from_u2(((fcf >> 10) & 0b11) as u8).ok_or_else(|| {
CrafterError::invalid_field_value(
"dot15d4.mac.addressing",
"reserved destination addressing mode",
)
})?;
let frame_version = ((fcf >> 12) & 0b11) as u8;
let src_addr_mode = Dot15d4AddrMode::from_u2(((fcf >> 14) & 0b11) as u8).ok_or_else(|| {
CrafterError::invalid_field_value(
"dot15d4.mac.addressing",
"reserved source addressing mode",
)
})?;
let seq_offset = DOT15D4_FCF_LEN;
if bytes.len() < seq_offset + DOT15D4_SEQ_LEN {
return Err(CrafterError::buffer_too_short(
"dot15d4.mac.seq",
seq_offset + DOT15D4_SEQ_LEN,
bytes.len(),
));
}
let seq = bytes[seq_offset];
let dest_present = dest_addr_mode != Dot15d4AddrMode::None;
let src_present = src_addr_mode != Dot15d4AddrMode::None;
let dest_pan_present = dest_present;
let src_pan_present = src_present && !pan_id_compression;
let mut offset = seq_offset + DOT15D4_SEQ_LEN;
let dest_pan = if dest_pan_present {
Field::user(read_dot15d4_pan(
bytes,
&mut offset,
"dot15d4.mac.addressing",
)?)
} else {
Field::unset()
};
let dest_addr = if dest_present {
Field::user(read_dot15d4_addr(
bytes,
&mut offset,
dest_addr_mode,
"dot15d4.mac.addressing",
)?)
} else {
Field::unset()
};
let src_pan = if src_pan_present {
Field::user(read_dot15d4_pan(
bytes,
&mut offset,
"dot15d4.mac.addressing",
)?)
} else {
Field::unset()
};
let src_addr = if src_present {
Field::user(read_dot15d4_addr(
bytes,
&mut offset,
src_addr_mode,
"dot15d4.mac.addressing",
)?)
} else {
Field::unset()
};
if bytes.len() < offset + DOT15D4_FCS_LEN {
return Err(CrafterError::buffer_too_short(
"dot15d4.mac.fcs",
offset + DOT15D4_FCS_LEN,
bytes.len(),
));
}
let payload_end = bytes.len() - DOT15D4_FCS_LEN;
let payload = &bytes[offset..payload_end];
let fcs = u16::from_le_bytes([bytes[payload_end], bytes[payload_end + 1]]);
let frame = Dot15d4 {
frame_type: Field::user(frame_type),
security_enabled: Field::user(security_enabled),
frame_pending: Field::user(frame_pending),
ack_request: Field::user(ack_request),
pan_id_compression: Field::user(pan_id_compression),
frame_version: Field::user(frame_version),
dest_addr_mode: Field::user(dest_addr_mode),
src_addr_mode: Field::user(src_addr_mode),
seq: Field::user(seq),
dest_pan,
dest_addr,
src_pan,
src_addr,
payload: Vec::new(),
fcs: Field::user(fcs),
};
Ok((frame, payload))
}
#[cfg(test)]
mod dot15d4_mac_builder {
use super::{Dot15d4, Dot15d4FrameType};
#[test]
fn frame_type_constructors_mark_frame_type_user_set() {
for (frame, expected) in [
(Dot15d4::data(), Dot15d4FrameType::Data),
(Dot15d4::beacon(), Dot15d4FrameType::Beacon),
(Dot15d4::ack(), Dot15d4FrameType::Ack),
(Dot15d4::command(), Dot15d4FrameType::MacCommand),
] {
assert!(frame.frame_type.is_user_set());
assert_eq!(frame.frame_type.value(), Some(&expected));
}
}
#[test]
fn setters_mark_their_fields_user_set() {
let frame = Dot15d4::new()
.frame_type(Dot15d4FrameType::Data)
.seq(7)
.security(true)
.frame_pending(true)
.ack_request(true)
.pan_id_compression(true)
.frame_version(2);
assert!(frame.frame_type.is_user_set());
assert!(frame.seq.is_user_set());
assert!(frame.security_enabled.is_user_set());
assert!(frame.frame_pending.is_user_set());
assert!(frame.ack_request.is_user_set());
assert!(frame.pan_id_compression.is_user_set());
assert!(frame.frame_version.is_user_set());
assert_eq!(frame.security_enabled.value(), Some(&true));
assert_eq!(frame.frame_pending.value(), Some(&true));
assert_eq!(frame.ack_request.value(), Some(&true));
assert_eq!(frame.pan_id_compression.value(), Some(&true));
assert_eq!(frame.frame_version.value(), Some(&2));
}
#[test]
fn data_seq_payload_holds_expected_values() {
let frame = Dot15d4::data().seq(7).payload(&[1, 2, 3]);
assert_eq!(frame.frame_type.value(), Some(&Dot15d4FrameType::Data));
assert_eq!(frame.seq.value(), Some(&7));
assert_eq!(frame.payload, vec![1, 2, 3]);
}
#[test]
fn untouched_fields_stay_unset() {
let frame = Dot15d4::data().seq(7).payload(&[1, 2, 3]);
assert!(frame.security_enabled.is_unset());
assert!(frame.frame_pending.is_unset());
assert!(frame.ack_request.is_unset());
assert!(frame.pan_id_compression.is_unset());
assert!(frame.frame_version.is_unset());
assert!(frame.fcs.is_unset());
}
}
#[cfg(test)]
mod dot15d4_mac_address {
use super::{Dot15d4, Dot15d4AddrMode};
#[test]
fn short_dest_and_src_share_pan_under_compression() {
let frame = Dot15d4::data()
.dest_short(0xABCD, 0x1234)
.src_short(0xABCD, 0x5678);
assert_eq!(frame.dest_addr_mode.value(), Some(&Dot15d4AddrMode::Short));
assert_eq!(frame.src_addr_mode.value(), Some(&Dot15d4AddrMode::Short));
assert_eq!(frame.dest_addr.value(), Some(&0x1234));
assert_eq!(frame.src_addr.value(), Some(&0x5678));
assert_eq!(frame.effective_dest_addr_mode(), Dot15d4AddrMode::Short);
assert_eq!(frame.effective_src_addr_mode(), Dot15d4AddrMode::Short);
assert_eq!(frame.effective_addr_mode(true), Dot15d4AddrMode::Short);
assert_eq!(frame.effective_addr_mode(false), Dot15d4AddrMode::Short);
assert!(frame.effective_pan_id_compression());
assert!(frame.effective_dest_pan_present());
assert!(!frame.effective_src_pan_present());
}
#[test]
fn extended_dest_and_src_addresses() {
let frame = Dot15d4::data()
.dest_extended(0x0001, 0x0011_2233_4455_6677)
.src_extended(0x0002, 0x8899_AABB_CCDD_EEFF);
assert_eq!(
frame.dest_addr_mode.value(),
Some(&Dot15d4AddrMode::Extended)
);
assert_eq!(
frame.src_addr_mode.value(),
Some(&Dot15d4AddrMode::Extended)
);
assert_eq!(frame.dest_addr.value(), Some(&0x0011_2233_4455_6677));
assert_eq!(frame.src_addr.value(), Some(&0x8899_AABB_CCDD_EEFF));
assert_eq!(frame.effective_dest_addr_mode(), Dot15d4AddrMode::Extended);
assert_eq!(frame.effective_src_addr_mode(), Dot15d4AddrMode::Extended);
assert!(!frame.effective_pan_id_compression());
assert!(frame.effective_dest_pan_present());
assert!(frame.effective_src_pan_present());
}
#[test]
fn destination_only_frame_has_no_source_addressing() {
let frame = Dot15d4::data().dest_short(0xABCD, 0x1234);
assert_eq!(frame.effective_dest_addr_mode(), Dot15d4AddrMode::Short);
assert_eq!(frame.effective_src_addr_mode(), Dot15d4AddrMode::None);
assert!(!frame.effective_pan_id_compression());
assert!(frame.effective_dest_pan_present());
assert!(!frame.effective_src_pan_present());
}
#[test]
fn typed_builder_does_not_override_explicit_addr_mode() {
let frame = Dot15d4::data();
let mut frame = frame;
frame.dest_addr_mode.set_user(Dot15d4AddrMode::Extended);
let frame = frame.dest_short(0xABCD, 0x1234);
assert_eq!(
frame.dest_addr_mode.value(),
Some(&Dot15d4AddrMode::Extended)
);
assert_eq!(frame.effective_dest_addr_mode(), Dot15d4AddrMode::Extended);
}
#[test]
fn user_set_compression_is_honored() {
let frame = Dot15d4::data()
.dest_short(0xABCD, 0x1234)
.pan_id_compression(true);
assert!(frame.effective_pan_id_compression());
let frame = Dot15d4::data()
.dest_short(0xABCD, 0x1234)
.src_short(0xABCD, 0x5678)
.pan_id_compression(false);
assert!(!frame.effective_pan_id_compression());
assert!(frame.effective_src_pan_present());
}
}
#[cfg(test)]
mod dot15d4_mac_encode {
use super::Dot15d4;
fn encode(frame: &Dot15d4) -> Vec<u8> {
let mut out = Vec::new();
frame.encode(&mut out);
out
}
#[test]
fn short_dest_src_data_frame_matches_reference() {
let frame = Dot15d4::data()
.seq(7)
.dest_short(0xABCD, 0x1234)
.src_short(0xABCD, 0x5678)
.payload(&[0xCA, 0xFE]);
let bytes = encode(&frame);
assert_eq!(
bytes,
vec![
0x41, 0x88, 0x07, 0xCD, 0xAB, 0x34, 0x12, 0x78, 0x56, 0xCA, 0xFE, 0x43, 0x8B, ]
);
assert_eq!(frame.encoded_len(), bytes.len());
}
#[test]
fn user_set_wrong_fcs_is_emitted_verbatim() {
let frame = Dot15d4::data()
.seq(7)
.dest_short(0xABCD, 0x1234)
.src_short(0xABCD, 0x5678)
.payload(&[0xCA, 0xFE]);
let mut frame = frame;
frame.fcs.set_user(0xDEAD);
let bytes = encode(&frame);
assert_eq!(
&bytes[..bytes.len() - 2],
&[0x41, 0x88, 0x07, 0xCD, 0xAB, 0x34, 0x12, 0x78, 0x56, 0xCA, 0xFE]
);
assert_eq!(&bytes[bytes.len() - 2..], &[0xAD, 0xDE]);
}
}
#[cfg(test)]
mod dot15d4_mac_layer {
use super::{decode_dot15d4, Dot15d4, Dot15d4AddrMode, Dot15d4FrameType};
use crate::error::CrafterError;
use crate::packet::{Layer, Packet, Raw};
fn reference_frame() -> Dot15d4 {
Dot15d4::data()
.seq(7)
.dest_short(0xABCD, 0x1234)
.src_short(0xABCD, 0x5678)
.payload(&[0xCA, 0xFE])
}
#[test]
fn layer_compile_equals_encode() {
let frame = reference_frame();
let mut encoded = Vec::new();
frame.encode(&mut encoded);
let compiled = Packet::from_layer(frame.clone())
.compile()
.expect("compile Dot15d4 MAC frame");
assert_eq!(compiled.as_bytes(), encoded.as_slice());
assert_eq!(compiled.len(), Layer::encoded_len(&frame));
}
#[test]
fn layer_name_and_summary() {
let frame = reference_frame();
assert_eq!(frame.name(), "Dot15d4");
assert_eq!(
frame.summary(),
"Dot15d4(Data, seq=7, dst=0x1234, src=0x5678)"
);
let fields = frame.inspection_fields();
assert!(fields.contains(&("frame_type", "Data".to_string())));
assert!(fields.contains(&("seq", "7".to_string())));
assert!(fields.contains(&("dest_addr", "0x1234".to_string())));
assert!(fields.contains(&("src_addr", "0x5678".to_string())));
}
#[test]
fn decode_round_trips_reference_frame() {
let frame = reference_frame();
let mut bytes = Vec::new();
frame.encode(&mut bytes);
let (decoded, tail) = decode_dot15d4(&bytes).expect("decode reference MAC frame");
assert_eq!(tail, &[0xCA, 0xFE]);
assert_eq!(decoded.frame_type.value(), Some(&Dot15d4FrameType::Data));
assert_eq!(decoded.seq.value(), Some(&7));
assert_eq!(
decoded.dest_addr_mode.value(),
Some(&Dot15d4AddrMode::Short)
);
assert_eq!(decoded.src_addr_mode.value(), Some(&Dot15d4AddrMode::Short));
assert_eq!(decoded.dest_pan.value(), Some(&0xABCD));
assert_eq!(decoded.dest_addr.value(), Some(&0x1234));
assert_eq!(decoded.pan_id_compression.value(), Some(&true));
assert!(decoded.src_pan.is_unset());
assert_eq!(decoded.src_addr.value(), Some(&0x5678));
assert_eq!(decoded.fcs.value(), Some(&0x8B43));
let mut reencoded = Vec::new();
decoded.payload(&[0xCA, 0xFE]).encode(&mut reencoded);
assert_eq!(reencoded, bytes);
}
#[test]
fn decode_too_short_fcf_is_structured_error() {
let err = decode_dot15d4(&[0x41]).expect_err("must reject a truncated FCF");
assert_eq!(err, CrafterError::buffer_too_short("dot15d4.mac.fcf", 2, 1));
}
#[test]
fn decode_addressing_claiming_more_bytes_than_present_is_structured_error() {
let bytes = [
0x41, 0x88, 0x07, 0xCD, 0xAB, 0x34, 0x12, 0x78, ];
let err = decode_dot15d4(&bytes)
.expect_err("must reject addressing that claims more bytes than present");
assert_eq!(
err,
CrafterError::buffer_too_short("dot15d4.mac.addressing", 9, bytes.len())
);
}
#[test]
fn decode_reserved_frame_type_is_structured_error() {
let bytes = [0x04, 0x00, 0x00, 0x00, 0x00];
let err = decode_dot15d4(&bytes).expect_err("must reject a reserved frame type");
assert_eq!(
err,
CrafterError::invalid_field_value("dot15d4.mac.frame_type", "reserved frame type")
);
}
#[test]
fn div_builds_two_layer_packet() {
let packet = Dot15d4::data().seq(1) / Raw::from_bytes([0xAA, 0xBB]);
assert_eq!(packet.len(), 2);
assert!(packet.layer::<Dot15d4>().is_some());
assert!(packet.layer::<Raw>().is_some());
}
#[test]
fn dot15d4_mac_roundtrip() {
let frame = Dot15d4::data()
.seq(7)
.dest_short(0xABCD, 0x1234)
.src_short(0xABCD, 0x5678)
.payload(&[0xCA, 0xFE]);
let compiled = Packet::from_layer(frame)
.compile()
.expect("compile short-addressed Dot15d4 MAC frame");
let bytes = compiled.as_bytes();
assert_eq!(&bytes[..2], &[0x41, 0x88]);
assert_eq!(&bytes[bytes.len() - 2..], &[0x43, 0x8B]);
assert_eq!(
bytes,
&[
0x41, 0x88, 0x07, 0xCD, 0xAB, 0x34, 0x12, 0x78, 0x56, 0xCA, 0xFE, 0x43, 0x8B, ]
);
let (decoded, tail) = decode_dot15d4(bytes).expect("decode short-addressed MAC frame");
assert_eq!(tail, &[0xCA, 0xFE]);
assert_eq!(decoded.frame_type.value(), Some(&Dot15d4FrameType::Data));
assert_eq!(decoded.seq.value(), Some(&7));
assert_eq!(
decoded.dest_addr_mode.value(),
Some(&Dot15d4AddrMode::Short)
);
assert_eq!(decoded.dest_pan.value(), Some(&0xABCD));
assert_eq!(decoded.dest_addr.value(), Some(&0x1234));
assert_eq!(decoded.src_addr_mode.value(), Some(&Dot15d4AddrMode::Short));
assert_eq!(decoded.pan_id_compression.value(), Some(&true));
assert!(decoded.src_pan.is_unset());
assert_eq!(decoded.src_addr.value(), Some(&0x5678));
assert_eq!(decoded.fcs.value(), Some(&0x8B43));
let ext = Dot15d4::data()
.seq(42)
.dest_extended(0x1AAA, 0x0011_2233_4455_6677)
.src_extended(0x1AAA, 0x8899_AABB_CCDD_EEFF)
.payload(&[0xDE, 0xAD, 0xBE, 0xEF]);
assert!(ext.effective_pan_id_compression());
assert!(ext.effective_dest_pan_present());
assert!(!ext.effective_src_pan_present());
let ext_bytes = Packet::from_layer(ext.clone())
.compile()
.expect("compile extended-addressed Dot15d4 MAC frame");
let ext_bytes = ext_bytes.as_bytes();
let (ext_decoded, ext_tail) =
decode_dot15d4(ext_bytes).expect("decode extended-addressed MAC frame");
assert_eq!(ext_tail, &[0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(
ext_decoded.frame_type.value(),
Some(&Dot15d4FrameType::Data)
);
assert_eq!(ext_decoded.seq.value(), Some(&42));
assert_eq!(
ext_decoded.dest_addr_mode.value(),
Some(&Dot15d4AddrMode::Extended)
);
assert_eq!(
ext_decoded.src_addr_mode.value(),
Some(&Dot15d4AddrMode::Extended)
);
assert_eq!(ext_decoded.dest_pan.value(), Some(&0x1AAA));
assert_eq!(ext_decoded.dest_addr.value(), Some(&0x0011_2233_4455_6677));
assert_eq!(ext_decoded.pan_id_compression.value(), Some(&true));
assert!(ext_decoded.src_pan.is_unset());
assert_eq!(ext_decoded.src_addr.value(), Some(&0x8899_AABB_CCDD_EEFF));
let reencoded = Packet::from_layer(ext_decoded.payload(&[0xDE, 0xAD, 0xBE, 0xEF]))
.compile()
.expect("recompile extended-addressed MAC frame");
assert_eq!(reencoded.as_bytes(), ext_bytes);
let err = decode_dot15d4(&[0x41]).expect_err("must reject a truncated FCF");
assert_eq!(err, CrafterError::buffer_too_short("dot15d4.mac.fcf", 2, 1));
}
}
#[cfg(test)]
mod dot15d4_stack {
use super::super::aps::{decode_zigbee_aps, ZigbeeAps};
use super::super::nwk::{decode_zigbee_nwk, ZigbeeNwk};
use super::{decode_dot15d4, dot15d4_fcs, Dot15d4, Dot15d4AddrMode, Dot15d4FrameType};
use crate::packet::Packet;
#[test]
fn dot15d4_stack_roundtrip() {
let packet = Dot15d4::data()
.dest_short(0x1234, 0x0000)
.src_short(0x1234, 0xABCD)
.seq(9)
/ ZigbeeNwk::data().dest(0x0000).src(0xABCD).radius(30).seq(5)
/ ZigbeeAps::data()
.cluster(0x0006)
.profile(0x0104)
.dest_endpoint(1)
.src_endpoint(1)
.counter(7)
.payload(&[0x01, 0x02]);
let compiled = packet
.compile()
.expect("compile Dot15d4/ZigbeeNwk/ZigbeeAps stack");
let bytes = compiled.as_bytes();
let mac_header = [0x41u8, 0x88, 0x09, 0x34, 0x12, 0x00, 0x00, 0xCD, 0xAB];
let nwk_header = [0x08u8, 0x00, 0x00, 0x00, 0xCD, 0xAB, 0x1E, 0x05];
let aps = [0x00u8, 0x01, 0x06, 0x00, 0x04, 0x01, 0x01, 0x07, 0x01, 0x02];
let mut frame_no_fcs = Vec::new();
frame_no_fcs.extend_from_slice(&mac_header);
frame_no_fcs.extend_from_slice(&nwk_header);
frame_no_fcs.extend_from_slice(&aps);
assert_eq!(bytes.len(), frame_no_fcs.len() + 2);
assert_eq!(&bytes[..frame_no_fcs.len()], frame_no_fcs.as_slice());
let expected_fcs = dot15d4_fcs(&frame_no_fcs);
assert_eq!(&bytes[frame_no_fcs.len()..], &expected_fcs.to_le_bytes());
let (mac, mac_tail) = decode_dot15d4(bytes).expect("decode MAC frame");
assert_eq!(
mac_tail,
frame_no_fcs[mac_header.len()..].to_vec().as_slice()
);
assert_eq!(mac.frame_type.value(), Some(&Dot15d4FrameType::Data));
assert_eq!(mac.seq.value(), Some(&9));
assert_eq!(mac.dest_addr_mode.value(), Some(&Dot15d4AddrMode::Short));
assert_eq!(mac.src_addr_mode.value(), Some(&Dot15d4AddrMode::Short));
assert_eq!(mac.dest_pan.value(), Some(&0x1234));
assert_eq!(mac.dest_addr.value(), Some(&0x0000));
assert_eq!(mac.pan_id_compression.value(), Some(&true));
assert!(mac.src_pan.is_unset());
assert_eq!(mac.src_addr.value(), Some(&0xABCD));
assert_eq!(mac.fcs.value(), Some(&expected_fcs));
let (nwk, nwk_tail) = decode_zigbee_nwk(mac_tail).expect("decode NWK frame");
assert_eq!(nwk_tail, aps.as_slice());
assert_eq!(nwk.encode(), frame_no_fcs[mac_header.len()..].to_vec());
let (aps_decoded, aps_tail) = decode_zigbee_aps(nwk_tail).expect("decode APS frame");
assert_eq!(aps_tail, &[0x01, 0x02]);
assert_eq!(aps_decoded.encode(), aps.to_vec());
let recompiled = Packet::from_layer(mac.payload(&nwk.encode()))
.compile()
.expect("recompile decoded layers");
assert_eq!(recompiled.as_bytes(), bytes);
}
}