use core::any::Any;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{IntoPacket, Layer, LayerContext, Packet};
const NWK_FC_FRAME_TYPE_SHIFT: u16 = 0;
const NWK_FC_PROTOCOL_VERSION_SHIFT: u16 = 2;
const NWK_FC_DISCOVER_ROUTE_SHIFT: u16 = 6;
const NWK_FC_MULTICAST_SHIFT: u16 = 8;
const NWK_FC_SECURITY_SHIFT: u16 = 9;
const NWK_FC_SOURCE_ROUTE_SHIFT: u16 = 10;
const NWK_FC_DEST_IEEE_SHIFT: u16 = 11;
const NWK_FC_SRC_IEEE_SHIFT: u16 = 12;
const NWK_FRAME_TYPE_DATA: u8 = 0b00;
const NWK_FRAME_TYPE_COMMAND: u8 = 0b01;
const NWK_PROTOCOL_VERSION_DEFAULT: u8 = 0x02;
const NWK_RADIUS_DEFAULT: u8 = 0;
const NWK_SEQ_DEFAULT: u8 = 0;
const NWK_FRAME_CONTROL_LEN: usize = 2;
const NWK_SHORT_ADDR_LEN: usize = 2;
const NWK_RADIUS_LEN: usize = 1;
const NWK_SEQ_LEN: usize = 1;
const NWK_IEEE_ADDR_LEN: usize = 8;
#[derive(Debug)]
pub struct ZigbeeNwk {
frame_type: Field<u8>,
protocol_version: Field<u8>,
discover_route: Field<u8>,
multicast: Field<bool>,
security: Field<bool>,
source_route: Field<bool>,
dest_ieee_flag: Field<bool>,
src_ieee_flag: Field<bool>,
dest: Field<u16>,
src: Field<u16>,
radius: Field<u8>,
seq: Field<u8>,
dest_ieee: Field<u64>,
src_ieee: Field<u64>,
payload: Vec<u8>,
}
impl Clone for ZigbeeNwk {
fn clone(&self) -> Self {
Self {
frame_type: self.frame_type.clone(),
protocol_version: self.protocol_version.clone(),
discover_route: self.discover_route.clone(),
multicast: self.multicast.clone(),
security: self.security.clone(),
source_route: self.source_route.clone(),
dest_ieee_flag: self.dest_ieee_flag.clone(),
src_ieee_flag: self.src_ieee_flag.clone(),
dest: self.dest.clone(),
src: self.src.clone(),
radius: self.radius.clone(),
seq: self.seq.clone(),
dest_ieee: self.dest_ieee.clone(),
src_ieee: self.src_ieee.clone(),
payload: self.payload.clone(),
}
}
}
impl ZigbeeNwk {
pub fn new() -> Self {
Self {
frame_type: Field::unset(),
protocol_version: Field::unset(),
discover_route: Field::unset(),
multicast: Field::unset(),
security: Field::unset(),
source_route: Field::unset(),
dest_ieee_flag: Field::unset(),
src_ieee_flag: Field::unset(),
dest: Field::unset(),
src: Field::unset(),
radius: Field::unset(),
seq: Field::unset(),
dest_ieee: Field::unset(),
src_ieee: Field::unset(),
payload: Vec::new(),
}
}
pub fn data() -> Self {
Self::new().frame_type(NWK_FRAME_TYPE_DATA)
}
pub fn command() -> Self {
Self::new().frame_type(NWK_FRAME_TYPE_COMMAND)
}
pub fn frame_type(mut self, frame_type: u8) -> Self {
self.frame_type.set_user(frame_type);
self
}
pub fn protocol_version(mut self, protocol_version: u8) -> Self {
self.protocol_version.set_user(protocol_version);
self
}
pub fn dest(mut self, dest: u16) -> Self {
self.dest.set_user(dest);
self
}
pub fn src(mut self, src: u16) -> Self {
self.src.set_user(src);
self
}
pub fn radius(mut self, radius: u8) -> Self {
self.radius.set_user(radius);
self
}
pub fn seq(mut self, seq: u8) -> Self {
self.seq.set_user(seq);
self
}
pub fn payload(mut self, payload: &[u8]) -> Self {
self.payload = payload.to_vec();
self
}
fn effective_frame_type(&self) -> u8 {
self.frame_type
.value()
.copied()
.unwrap_or(NWK_FRAME_TYPE_DATA)
}
fn effective_protocol_version(&self) -> u8 {
self.protocol_version
.value()
.copied()
.unwrap_or(NWK_PROTOCOL_VERSION_DEFAULT)
}
fn effective_discover_route(&self) -> u8 {
self.discover_route.value().copied().unwrap_or(0)
}
fn effective_dest_ieee_flag(&self) -> bool {
match self.dest_ieee_flag.value() {
Some(flag) => *flag,
None => self.dest_ieee.value().is_some(),
}
}
fn effective_src_ieee_flag(&self) -> bool {
match self.src_ieee_flag.value() {
Some(flag) => *flag,
None => self.src_ieee.value().is_some(),
}
}
fn frame_control(&self) -> u16 {
let frame_type = u16::from(self.effective_frame_type() & 0b11);
let protocol_version = u16::from(self.effective_protocol_version() & 0b1111);
let discover_route = u16::from(self.effective_discover_route() & 0b11);
let multicast = u16::from(self.multicast.value().copied().unwrap_or(false));
let security = u16::from(self.security.value().copied().unwrap_or(false));
let source_route = u16::from(self.source_route.value().copied().unwrap_or(false));
let dest_ieee = u16::from(self.effective_dest_ieee_flag());
let src_ieee = u16::from(self.effective_src_ieee_flag());
(frame_type << NWK_FC_FRAME_TYPE_SHIFT)
| (protocol_version << NWK_FC_PROTOCOL_VERSION_SHIFT)
| (discover_route << NWK_FC_DISCOVER_ROUTE_SHIFT)
| (multicast << NWK_FC_MULTICAST_SHIFT)
| (security << NWK_FC_SECURITY_SHIFT)
| (source_route << NWK_FC_SOURCE_ROUTE_SHIFT)
| (dest_ieee << NWK_FC_DEST_IEEE_SHIFT)
| (src_ieee << NWK_FC_SRC_IEEE_SHIFT)
}
pub(crate) fn encoded_len(&self) -> usize {
let mut len = NWK_FRAME_CONTROL_LEN
+ NWK_SHORT_ADDR_LEN
+ NWK_SHORT_ADDR_LEN
+ NWK_RADIUS_LEN
+ NWK_SEQ_LEN;
if self.effective_dest_ieee_flag() {
len += NWK_IEEE_ADDR_LEN;
}
if self.effective_src_ieee_flag() {
len += NWK_IEEE_ADDR_LEN;
}
len += self.payload.len();
len
}
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.encoded_len());
out.extend_from_slice(&self.frame_control().to_le_bytes());
out.extend_from_slice(&self.dest.value().copied().unwrap_or(0).to_le_bytes());
out.extend_from_slice(&self.src.value().copied().unwrap_or(0).to_le_bytes());
out.push(self.radius.value().copied().unwrap_or(NWK_RADIUS_DEFAULT));
out.push(self.seq.value().copied().unwrap_or(NWK_SEQ_DEFAULT));
if self.effective_dest_ieee_flag() {
out.extend_from_slice(&self.dest_ieee.value().copied().unwrap_or(0).to_le_bytes());
}
if self.effective_src_ieee_flag() {
out.extend_from_slice(&self.src_ieee.value().copied().unwrap_or(0).to_le_bytes());
}
out.extend_from_slice(&self.payload);
out
}
}
impl Default for ZigbeeNwk {
fn default() -> Self {
Self::new()
}
}
fn nwk_frame_type_label(frame_type: u8) -> String {
match frame_type & 0b11 {
NWK_FRAME_TYPE_DATA => "Data".to_string(),
NWK_FRAME_TYPE_COMMAND => "Command".to_string(),
other => format!("Type({other})"),
}
}
impl Layer for ZigbeeNwk {
fn name(&self) -> &'static str {
"ZigbeeNwk"
}
fn summary(&self) -> String {
format!(
"ZigbeeNwk({}, dst={:#06x}, src={:#06x}, r={})",
nwk_frame_type_label(self.effective_frame_type()),
self.dest.value().copied().unwrap_or(0),
self.src.value().copied().unwrap_or(0),
self.radius.value().copied().unwrap_or(NWK_RADIUS_DEFAULT),
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
let mut fields = vec![
(
"frame_type",
nwk_frame_type_label(self.effective_frame_type()),
),
(
"protocol_version",
self.effective_protocol_version().to_string(),
),
(
"dest",
format!("{:#06x}", self.dest.value().copied().unwrap_or(0)),
),
(
"src",
format!("{:#06x}", self.src.value().copied().unwrap_or(0)),
),
(
"radius",
self.radius
.value()
.copied()
.unwrap_or(NWK_RADIUS_DEFAULT)
.to_string(),
),
(
"seq",
self.seq
.value()
.copied()
.unwrap_or(NWK_SEQ_DEFAULT)
.to_string(),
),
];
if self.effective_dest_ieee_flag() {
fields.push((
"dest_ieee",
format!("{:#018x}", self.dest_ieee.value().copied().unwrap_or(0)),
));
}
if self.effective_src_ieee_flag() {
fields.push((
"src_ieee",
format!("{:#018x}", self.src_ieee.value().copied().unwrap_or(0)),
));
}
fields
}
fn encoded_len(&self) -> usize {
ZigbeeNwk::encoded_len(self)
}
fn compile(&self, _ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
out.extend_from_slice(&self.encode());
Ok(())
}
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 ZigbeeNwk {
type Output = Packet;
fn div(self, rhs: R) -> Self::Output {
Packet::from_layer(self).concat(rhs)
}
}
fn read_nwk_ieee_addr(bytes: &[u8], offset: &mut usize, context: &'static str) -> Result<u64> {
let required = *offset + NWK_IEEE_ADDR_LEN;
if bytes.len() < required {
return Err(CrafterError::buffer_too_short(
context,
required,
bytes.len(),
));
}
let addr = 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)
}
pub(crate) fn decode_zigbee_nwk(bytes: &[u8]) -> Result<(ZigbeeNwk, &[u8])> {
if bytes.len() < NWK_FRAME_CONTROL_LEN {
return Err(CrafterError::buffer_too_short(
"zigbee.nwk.fcf",
NWK_FRAME_CONTROL_LEN,
bytes.len(),
));
}
let fcf = u16::from_le_bytes([bytes[0], bytes[1]]);
let frame_type = ((fcf >> NWK_FC_FRAME_TYPE_SHIFT) & 0b11) as u8;
let protocol_version = ((fcf >> NWK_FC_PROTOCOL_VERSION_SHIFT) & 0b1111) as u8;
let discover_route = ((fcf >> NWK_FC_DISCOVER_ROUTE_SHIFT) & 0b11) as u8;
let multicast = (fcf >> NWK_FC_MULTICAST_SHIFT) & 0b1 != 0;
let security = (fcf >> NWK_FC_SECURITY_SHIFT) & 0b1 != 0;
let source_route = (fcf >> NWK_FC_SOURCE_ROUTE_SHIFT) & 0b1 != 0;
let dest_ieee_flag = (fcf >> NWK_FC_DEST_IEEE_SHIFT) & 0b1 != 0;
let src_ieee_flag = (fcf >> NWK_FC_SRC_IEEE_SHIFT) & 0b1 != 0;
let header_end = NWK_FRAME_CONTROL_LEN
+ NWK_SHORT_ADDR_LEN
+ NWK_SHORT_ADDR_LEN
+ NWK_RADIUS_LEN
+ NWK_SEQ_LEN;
if bytes.len() < header_end {
return Err(CrafterError::buffer_too_short(
"zigbee.nwk.header",
header_end,
bytes.len(),
));
}
let dest = u16::from_le_bytes([bytes[2], bytes[3]]);
let src = u16::from_le_bytes([bytes[4], bytes[5]]);
let radius = bytes[6];
let seq = bytes[7];
let mut offset = header_end;
let dest_ieee = if dest_ieee_flag {
Field::user(read_nwk_ieee_addr(bytes, &mut offset, "zigbee.nwk.header")?)
} else {
Field::unset()
};
let src_ieee = if src_ieee_flag {
Field::user(read_nwk_ieee_addr(bytes, &mut offset, "zigbee.nwk.header")?)
} else {
Field::unset()
};
if source_route {
let counts_end = offset + 2;
if bytes.len() < counts_end {
return Err(CrafterError::buffer_too_short(
"zigbee.nwk.header",
counts_end,
bytes.len(),
));
}
let relay_count = bytes[offset] as usize;
offset = counts_end;
let relay_list_end = offset + relay_count * NWK_SHORT_ADDR_LEN;
if bytes.len() < relay_list_end {
return Err(CrafterError::buffer_too_short(
"zigbee.nwk.header",
relay_list_end,
bytes.len(),
));
}
offset = relay_list_end;
}
let payload = bytes[offset..].to_vec();
let nwk = ZigbeeNwk {
frame_type: Field::user(frame_type),
protocol_version: Field::user(protocol_version),
discover_route: Field::user(discover_route),
multicast: Field::user(multicast),
security: Field::user(security),
source_route: Field::user(source_route),
dest_ieee_flag: Field::user(dest_ieee_flag),
src_ieee_flag: Field::user(src_ieee_flag),
dest: Field::user(dest),
src: Field::user(src),
radius: Field::user(radius),
seq: Field::user(seq),
dest_ieee,
src_ieee,
payload,
};
Ok((nwk, &bytes[offset..]))
}
#[cfg(test)]
mod tests {
use super::{decode_zigbee_nwk, ZigbeeNwk};
use crate::error::CrafterError;
use crate::packet::Packet;
#[test]
fn zigbee_nwk_encode() {
let frame = ZigbeeNwk::data()
.dest(0x1234)
.src(0x5678)
.radius(30)
.seq(42)
.payload(&[0xAA, 0xBB]);
let bytes = frame.encode();
assert_eq!(
bytes,
vec![
0x08, 0x00, 0x34, 0x12, 0x78, 0x56, 0x1E, 0x2A, 0xAA, 0xBB, ]
);
assert_eq!(frame.encoded_len(), bytes.len());
}
#[test]
fn zigbee_nwk_command_frame_type_and_default_version() {
let frame = ZigbeeNwk::command().dest(0x0000).src(0x0001);
let bytes = frame.encode();
assert_eq!(&bytes[..2], &[0x09, 0x00]);
}
#[test]
fn zigbee_nwk_dest_ieee_flag_includes_address() {
let mut frame = ZigbeeNwk::data().dest(0x1234).src(0x5678).radius(1).seq(2);
frame.dest_ieee.set_user(0x0011_2233_4455_6677);
let bytes = frame.encode();
assert_eq!(&bytes[..2], &[0x08, 0x08]);
assert_eq!(
&bytes[8..16],
&[0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00]
);
assert_eq!(frame.encoded_len(), bytes.len());
}
#[test]
fn zigbee_nwk_layer_compile_equals_encode() {
let frame = ZigbeeNwk::data()
.dest(0x1234)
.src(0x5678)
.radius(30)
.seq(42)
.payload(&[0xAA, 0xBB]);
let expected = frame.encode();
let bytes = Packet::from_layer(frame.clone())
.compile()
.expect("compile ZigbeeNwk layer");
assert_eq!(bytes.as_bytes(), expected.as_slice());
let (decoded, tail) = decode_zigbee_nwk(bytes.as_bytes()).expect("decode ZigbeeNwk layer");
assert_eq!(decoded.dest.value().copied(), Some(0x1234));
assert_eq!(decoded.src.value().copied(), Some(0x5678));
assert_eq!(decoded.radius.value().copied(), Some(30));
assert_eq!(decoded.seq.value().copied(), Some(42));
assert_eq!(decoded.payload, vec![0xAA, 0xBB]);
assert_eq!(tail, &[0xAA, 0xBB]);
assert_eq!(decoded.encode(), expected);
}
#[test]
fn zigbee_nwk_layer_decode_truncated_header_is_structured_error() {
let err = decode_zigbee_nwk(&[0x08]).expect_err("must reject a truncated frame control");
assert_eq!(err, CrafterError::buffer_too_short("zigbee.nwk.fcf", 2, 1));
let err = decode_zigbee_nwk(&[0x08, 0x00, 0x34, 0x12, 0x78, 0x56])
.expect_err("must reject a truncated NWK header");
assert_eq!(
err,
CrafterError::buffer_too_short("zigbee.nwk.header", 8, 6)
);
}
}