use core::any::Any;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{IntoPacket, Layer, LayerContext, Packet};
const APS_FC_FRAME_TYPE_SHIFT: u8 = 0;
const APS_FC_DELIVERY_MODE_SHIFT: u8 = 2;
const APS_FC_ACK_FORMAT_SHIFT: u8 = 4;
const APS_FC_SECURITY_SHIFT: u8 = 5;
const APS_FC_ACK_REQUEST_SHIFT: u8 = 6;
const APS_FC_EXT_HEADER_SHIFT: u8 = 7;
const APS_FRAME_TYPE_DATA: u8 = 0b00;
const APS_FRAME_TYPE_COMMAND: u8 = 0b01;
const APS_FRAME_TYPE_ACK: u8 = 0b10;
const APS_DELIVERY_MODE_UNICAST: u8 = 0b00;
const APS_DELIVERY_MODE_BROADCAST: u8 = 0b10;
const APS_DELIVERY_MODE_GROUP: u8 = 0b11;
const APS_COUNTER_DEFAULT: u8 = 0;
const APS_FRAME_CONTROL_LEN: usize = 1;
const APS_ENDPOINT_LEN: usize = 1;
const APS_CLUSTER_LEN: usize = 2;
const APS_PROFILE_LEN: usize = 2;
const APS_COUNTER_LEN: usize = 1;
#[derive(Debug)]
pub struct ZigbeeAps {
frame_type: Field<u8>,
delivery_mode: Field<u8>,
ack_format: Field<bool>,
security: Field<bool>,
ack_request: Field<bool>,
ext_header: Field<bool>,
dest_endpoint: Field<u8>,
cluster: Field<u16>,
profile: Field<u16>,
src_endpoint: Field<u8>,
counter: Field<u8>,
payload: Vec<u8>,
}
impl Clone for ZigbeeAps {
fn clone(&self) -> Self {
Self {
frame_type: self.frame_type.clone(),
delivery_mode: self.delivery_mode.clone(),
ack_format: self.ack_format.clone(),
security: self.security.clone(),
ack_request: self.ack_request.clone(),
ext_header: self.ext_header.clone(),
dest_endpoint: self.dest_endpoint.clone(),
cluster: self.cluster.clone(),
profile: self.profile.clone(),
src_endpoint: self.src_endpoint.clone(),
counter: self.counter.clone(),
payload: self.payload.clone(),
}
}
}
impl ZigbeeAps {
pub fn new() -> Self {
Self {
frame_type: Field::unset(),
delivery_mode: Field::unset(),
ack_format: Field::unset(),
security: Field::unset(),
ack_request: Field::unset(),
ext_header: Field::unset(),
dest_endpoint: Field::unset(),
cluster: Field::unset(),
profile: Field::unset(),
src_endpoint: Field::unset(),
counter: Field::unset(),
payload: Vec::new(),
}
}
pub fn data() -> Self {
Self::new().frame_type(APS_FRAME_TYPE_DATA)
}
pub fn command() -> Self {
Self::new().frame_type(APS_FRAME_TYPE_COMMAND)
}
pub fn ack() -> Self {
Self::new().frame_type(APS_FRAME_TYPE_ACK)
}
pub fn frame_type(mut self, frame_type: u8) -> Self {
self.frame_type.set_user(frame_type);
self
}
pub fn delivery_mode(mut self, delivery_mode: u8) -> Self {
self.delivery_mode.set_user(delivery_mode);
self
}
pub fn dest_endpoint(mut self, dest_endpoint: u8) -> Self {
self.dest_endpoint.set_user(dest_endpoint);
self
}
pub fn cluster(mut self, cluster: u16) -> Self {
self.cluster.set_user(cluster);
self
}
pub fn profile(mut self, profile: u16) -> Self {
self.profile.set_user(profile);
self
}
pub fn src_endpoint(mut self, src_endpoint: u8) -> Self {
self.src_endpoint.set_user(src_endpoint);
self
}
pub fn counter(mut self, counter: u8) -> Self {
self.counter.set_user(counter);
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(APS_FRAME_TYPE_DATA)
}
fn effective_delivery_mode(&self) -> u8 {
self.delivery_mode
.value()
.copied()
.unwrap_or(APS_DELIVERY_MODE_UNICAST)
}
fn effective_dest_endpoint_present(&self) -> bool {
matches!(
self.effective_delivery_mode() & 0b11,
APS_DELIVERY_MODE_UNICAST | APS_DELIVERY_MODE_BROADCAST
)
}
fn effective_cluster_profile_present(&self) -> bool {
matches!(
self.effective_frame_type() & 0b11,
APS_FRAME_TYPE_DATA | APS_FRAME_TYPE_ACK
)
}
fn frame_control(&self) -> u8 {
let frame_type = self.effective_frame_type() & 0b11;
let delivery_mode = self.effective_delivery_mode() & 0b11;
let ack_format = u8::from(self.ack_format.value().copied().unwrap_or(false));
let security = u8::from(self.security.value().copied().unwrap_or(false));
let ack_request = u8::from(self.ack_request.value().copied().unwrap_or(false));
let ext_header = u8::from(self.ext_header.value().copied().unwrap_or(false));
(frame_type << APS_FC_FRAME_TYPE_SHIFT)
| (delivery_mode << APS_FC_DELIVERY_MODE_SHIFT)
| (ack_format << APS_FC_ACK_FORMAT_SHIFT)
| (security << APS_FC_SECURITY_SHIFT)
| (ack_request << APS_FC_ACK_REQUEST_SHIFT)
| (ext_header << APS_FC_EXT_HEADER_SHIFT)
}
pub(crate) fn encoded_len(&self) -> usize {
let mut len = APS_FRAME_CONTROL_LEN;
if self.effective_dest_endpoint_present() {
len += APS_ENDPOINT_LEN;
}
if self.effective_cluster_profile_present() {
len += APS_CLUSTER_LEN + APS_PROFILE_LEN;
}
if self.effective_cluster_profile_present() {
len += APS_ENDPOINT_LEN;
}
len += APS_COUNTER_LEN;
len += self.payload.len();
len
}
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.encoded_len());
out.push(self.frame_control());
if self.effective_dest_endpoint_present() {
out.push(self.dest_endpoint.value().copied().unwrap_or(0));
}
if self.effective_cluster_profile_present() {
out.extend_from_slice(&self.cluster.value().copied().unwrap_or(0).to_le_bytes());
out.extend_from_slice(&self.profile.value().copied().unwrap_or(0).to_le_bytes());
out.push(self.src_endpoint.value().copied().unwrap_or(0));
}
out.push(self.counter.value().copied().unwrap_or(APS_COUNTER_DEFAULT));
out.extend_from_slice(&self.payload);
out
}
}
impl Default for ZigbeeAps {
fn default() -> Self {
Self::new()
}
}
fn aps_frame_type_label(frame_type: u8) -> String {
match frame_type & 0b11 {
APS_FRAME_TYPE_DATA => "Data".to_string(),
APS_FRAME_TYPE_COMMAND => "Command".to_string(),
APS_FRAME_TYPE_ACK => "Ack".to_string(),
other => format!("Type({other})"),
}
}
impl Layer for ZigbeeAps {
fn name(&self) -> &'static str {
"ZigbeeAps"
}
fn summary(&self) -> String {
let mut summary = format!(
"ZigbeeAps({}",
aps_frame_type_label(self.effective_frame_type())
);
if self.effective_cluster_profile_present() {
summary.push_str(&format!(
", cluster={:#06x}, profile={:#06x}",
self.cluster.value().copied().unwrap_or(0),
self.profile.value().copied().unwrap_or(0),
));
}
if self.effective_dest_endpoint_present() {
summary.push_str(&format!(
", dst_ep={}",
self.dest_endpoint.value().copied().unwrap_or(0),
));
}
summary.push(')');
summary
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
let mut fields = vec![
(
"frame_type",
aps_frame_type_label(self.effective_frame_type()),
),
("delivery_mode", self.effective_delivery_mode().to_string()),
];
if self.effective_dest_endpoint_present() {
fields.push((
"dest_endpoint",
self.dest_endpoint.value().copied().unwrap_or(0).to_string(),
));
}
if self.effective_cluster_profile_present() {
fields.push((
"cluster",
format!("{:#06x}", self.cluster.value().copied().unwrap_or(0)),
));
fields.push((
"profile",
format!("{:#06x}", self.profile.value().copied().unwrap_or(0)),
));
fields.push((
"src_endpoint",
self.src_endpoint.value().copied().unwrap_or(0).to_string(),
));
}
fields.push((
"counter",
self.counter
.value()
.copied()
.unwrap_or(APS_COUNTER_DEFAULT)
.to_string(),
));
fields
}
fn encoded_len(&self) -> usize {
ZigbeeAps::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 ZigbeeAps {
type Output = Packet;
fn div(self, rhs: R) -> Self::Output {
Packet::from_layer(self).concat(rhs)
}
}
pub(crate) fn decode_zigbee_aps(bytes: &[u8]) -> Result<(ZigbeeAps, &[u8])> {
if bytes.len() < APS_FRAME_CONTROL_LEN {
return Err(CrafterError::buffer_too_short(
"zigbee.aps.fcf",
APS_FRAME_CONTROL_LEN,
bytes.len(),
));
}
let fcf = bytes[0];
let frame_type = (fcf >> APS_FC_FRAME_TYPE_SHIFT) & 0b11;
let delivery_mode = (fcf >> APS_FC_DELIVERY_MODE_SHIFT) & 0b11;
let ack_format = (fcf >> APS_FC_ACK_FORMAT_SHIFT) & 0b1 != 0;
let security = (fcf >> APS_FC_SECURITY_SHIFT) & 0b1 != 0;
let ack_request = (fcf >> APS_FC_ACK_REQUEST_SHIFT) & 0b1 != 0;
let ext_header = (fcf >> APS_FC_EXT_HEADER_SHIFT) & 0b1 != 0;
let dest_endpoint_present = matches!(
delivery_mode,
APS_DELIVERY_MODE_UNICAST | APS_DELIVERY_MODE_BROADCAST
);
let cluster_profile_present = matches!(frame_type, APS_FRAME_TYPE_DATA | APS_FRAME_TYPE_ACK);
let mut offset = APS_FRAME_CONTROL_LEN;
let dest_endpoint = if dest_endpoint_present {
let required = offset + APS_ENDPOINT_LEN;
if bytes.len() < required {
return Err(CrafterError::buffer_too_short(
"zigbee.aps.header",
required,
bytes.len(),
));
}
let endpoint = bytes[offset];
offset = required;
Field::user(endpoint)
} else {
Field::unset()
};
let (cluster, profile, src_endpoint) = if cluster_profile_present {
let required = offset + APS_CLUSTER_LEN + APS_PROFILE_LEN + APS_ENDPOINT_LEN;
if bytes.len() < required {
return Err(CrafterError::buffer_too_short(
"zigbee.aps.header",
required,
bytes.len(),
));
}
let cluster = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
let profile = u16::from_le_bytes([bytes[offset + 2], bytes[offset + 3]]);
let src_endpoint = bytes[offset + 4];
offset = required;
(
Field::user(cluster),
Field::user(profile),
Field::user(src_endpoint),
)
} else {
(Field::unset(), Field::unset(), Field::unset())
};
let required = offset + APS_COUNTER_LEN;
if bytes.len() < required {
return Err(CrafterError::buffer_too_short(
"zigbee.aps.header",
required,
bytes.len(),
));
}
let counter = bytes[offset];
offset = required;
let payload = bytes[offset..].to_vec();
let aps = ZigbeeAps {
frame_type: Field::user(frame_type),
delivery_mode: Field::user(delivery_mode),
ack_format: Field::user(ack_format),
security: Field::user(security),
ack_request: Field::user(ack_request),
ext_header: Field::user(ext_header),
dest_endpoint,
cluster,
profile,
src_endpoint,
counter: Field::user(counter),
payload,
};
Ok((aps, &bytes[offset..]))
}
#[cfg(test)]
mod tests {
use super::{decode_zigbee_aps, ZigbeeAps};
use crate::error::CrafterError;
use crate::packet::Packet;
#[test]
fn zigbee_aps_encode() {
let frame = ZigbeeAps::data()
.dest_endpoint(0x0A)
.cluster(0x0006)
.profile(0x0104)
.src_endpoint(0x01)
.counter(0x2A)
.payload(&[0xAA, 0xBB]);
let bytes = frame.encode();
assert_eq!(
bytes,
vec![
0x00, 0x0A, 0x06, 0x00, 0x04, 0x01, 0x01, 0x2A, 0xAA, 0xBB, ]
);
assert_eq!(frame.encoded_len(), bytes.len());
}
#[test]
fn zigbee_aps_frame_control_packs_subfields() {
let frame = ZigbeeAps::command()
.delivery_mode(0b10)
.counter(0x01)
.ack_request_for_test();
let bytes = frame.encode();
assert_eq!(bytes[0], 0x49);
}
#[test]
fn zigbee_aps_command_omits_cluster_and_profile() {
let frame = ZigbeeAps::command().dest_endpoint(0x05).counter(0x07);
let bytes = frame.encode();
assert_eq!(
bytes,
vec![
0x01, 0x05, 0x07, ]
);
assert_eq!(frame.encoded_len(), bytes.len());
}
#[test]
fn zigbee_aps_group_delivery_omits_dest_endpoint() {
let frame = ZigbeeAps::data()
.delivery_mode(0b11)
.cluster(0x0006)
.profile(0x0104)
.src_endpoint(0x01)
.counter(0x2A);
let bytes = frame.encode();
assert_eq!(
bytes,
vec![
0x0C, 0x06, 0x00, 0x04, 0x01, 0x01, 0x2A, ]
);
assert_eq!(frame.encoded_len(), bytes.len());
}
#[test]
fn zigbee_aps_layer() {
let frame = ZigbeeAps::data()
.dest_endpoint(0x0A)
.cluster(0x0006)
.profile(0x0104)
.src_endpoint(0x01)
.counter(0x2A)
.payload(&[0xAA, 0xBB]);
let expected = frame.encode();
let bytes = Packet::from_layer(frame.clone())
.compile()
.expect("compile ZigbeeAps layer");
assert_eq!(bytes.as_bytes(), expected.as_slice());
let (decoded, tail) = decode_zigbee_aps(bytes.as_bytes()).expect("decode ZigbeeAps layer");
assert_eq!(decoded.dest_endpoint.value().copied(), Some(0x0A));
assert_eq!(decoded.cluster.value().copied(), Some(0x0006));
assert_eq!(decoded.profile.value().copied(), Some(0x0104));
assert_eq!(decoded.src_endpoint.value().copied(), Some(0x01));
assert_eq!(decoded.counter.value().copied(), Some(0x2A));
assert_eq!(decoded.payload, vec![0xAA, 0xBB]);
assert_eq!(tail, &[0xAA, 0xBB]);
assert_eq!(decoded.encode(), expected);
let err = decode_zigbee_aps(&[]).expect_err("must reject a truncated frame control");
assert_eq!(err, CrafterError::buffer_too_short("zigbee.aps.fcf", 1, 0));
let err = decode_zigbee_aps(&[0x00, 0x0A]).expect_err("must reject a truncated APS header");
assert_eq!(
err,
CrafterError::buffer_too_short("zigbee.aps.header", 7, 2)
);
}
}
#[cfg(test)]
impl ZigbeeAps {
fn ack_request_for_test(mut self) -> Self {
self.ack_request.set_user(true);
self
}
}