From c930ad2448250f3f2a1217c66d6f2851bb1e6d35 Mon Sep 17 00:00:00 2001 From: Tamme Dittrich Date: Tue, 8 Oct 2024 13:20:46 +0200 Subject: [PATCH] Expose more fields in beacon as pub This allows to not just parse but also generate these frames. --- Cargo.toml | 41 +++++---- src/mac/beacon.rs | 160 ++++++++++++++++------------------ src/mac/frame/mod.rs | 8 +- src/mac/frame/security/mod.rs | 2 +- 4 files changed, 100 insertions(+), 111 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6b159d3..690d257 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,46 +1,49 @@ [package] -name = "ieee802154" -version = "0.6.1" +name = "ieee802154" +version = "0.7.0" authors = [ - "Erik Henriksson ", - "Hanno Braun ", - "Ryan Kurte ", + "Erik Henriksson ", + "Hanno Braun ", + "Ryan Kurte ", ] -edition = "2018" +edition = "2021" -description = "Partial implementation of the IEEE 802.15.4 standard for low-rate wireless personal area networks" +description = "Partial implementation of the IEEE 802.15.4 standard for low-rate wireless personal area networks" documentation = "https://docs.rs/ieee802154" -repository = "https://github.com/rust-iot/rust-ieee802.15.4" -license = "0BSD" -readme = "README.md" -categories = ["embedded", "network-programming", "no-std"] -keywords = ["WPAN"] +repository = "https://github.com/rust-iot/rust-ieee802.15.4" +license = "0BSD" +readme = "README.md" +categories = ["embedded", "network-programming", "no-std"] +keywords = ["WPAN"] [features] +defmt = ["dep:defmt", "heapless/defmt-03"] +serde = ["dep:serde"] [dependencies] -hash32 = "0.2.1" +hash32 = "0.2.1" hash32-derive = "0.1" byte = "0.2.7" defmt = { version = ">=0.2.0,<0.4", optional = true } +heapless = "0.8.0" [dependencies.ccm] -version = "0.4.0" +version = "0.4.0" default-features = false [dependencies.cipher] -version = "0.3.0" +version = "0.3.0" default-features = false [dependencies.serde] -version = "1.0" +version = "1.0" default-features = false -optional = true -features = [ "derive" ] +optional = true +features = ["derive"] [dev-dependencies] rand = "0.8.3" [dev-dependencies.aes] -version = "0.7.0" +version = "0.7.0" default-features = false diff --git a/src/mac/beacon.rs b/src/mac/beacon.rs index fadf991..542ee90 100644 --- a/src/mac/beacon.rs +++ b/src/mac/beacon.rs @@ -4,7 +4,6 @@ use byte::{check_len, BytesExt, TryRead, TryWrite}; use core::convert::From; -use core::mem; use crate::mac::{ExtendedAddress, ShortAddress}; @@ -40,11 +39,11 @@ impl From for u8 { } } -/// Superframe order, amount of time during wich this superframe is active +/// Superframe order, amount of time during which this superframe is active #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SuperframeOrder { - /// Ammount of time that the superframe is active + /// Amount of time that the superframe is active /// /// superframe duration = base superframe duration × (2 ^ superframe order) SuperframeOrder(u8), @@ -83,7 +82,7 @@ pub struct SuperframeSpecification { /// /// Beacon interval = BaseSuperframeDuration × (2 ^ BeaconOrder) pub beacon_order: BeaconOrder, - /// Superframe order, amount of time during wich this superframe is active + /// Superframe order, amount of time during which this superframe is active pub superframe_order: SuperframeOrder, /// final contention access period slot used pub final_cap_slot: u8, @@ -91,7 +90,7 @@ pub struct SuperframeSpecification { pub battery_life_extension: bool, /// Frame sent by a coordinator pub pan_coordinator: bool, - /// The coordinator acceppts associations to the PAN + /// The coordinator accepts associations to the PAN pub association_permit: bool, } @@ -156,7 +155,7 @@ impl TryWrite for SuperframeSpecification { /// Direction of data #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum Direction { +pub enum Direction { /// Receive data Receive, /// Transmit data @@ -168,13 +167,13 @@ enum Direction { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GuaranteedTimeSlotDescriptor { /// Device short address used by this slot - short_address: ShortAddress, + pub short_address: ShortAddress, /// Slot start - starting_slot: u8, + pub starting_slot: u8, /// Slot length - length: u8, + pub length: u8, /// Direction of the slot, either transmit or receive - direction: Direction, + pub direction: Direction, } impl GuaranteedTimeSlotDescriptor { @@ -234,13 +233,13 @@ const COUNT_MASK: u8 = 0b0000_0111; const PERMIT: u8 = 0b1000_0000; /// Information of the guaranteed time slots (GTSs) -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GuaranteedTimeSlotInformation { /// Permit GTS pub permit: bool, - slot_count: usize, - slots: [GuaranteedTimeSlotDescriptor; 7], + /// Time slot information + pub slots: heapless::Vec, } impl GuaranteedTimeSlotInformation { @@ -248,32 +247,30 @@ impl GuaranteedTimeSlotInformation { pub fn new() -> Self { GuaranteedTimeSlotInformation { permit: false, - slot_count: 0, - slots: [GuaranteedTimeSlotDescriptor::new(); 7], + slots: heapless::Vec::new(), } } /// Get the slots as a slice pub fn slots(&self) -> &[GuaranteedTimeSlotDescriptor] { - &self.slots[..self.slot_count] + self.slots.as_slice() } } impl TryWrite for GuaranteedTimeSlotInformation { fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result { let offset = &mut 0; - assert!(self.slot_count <= 7); + assert!(self.slots.len() <= 7); let permit = if self.permit { PERMIT } else { 0 }; - let header = ((self.slot_count as u8) & COUNT_MASK) | permit; + let header = ((self.slots.len() as u8) & COUNT_MASK) | permit; bytes.write(offset, header)?; - if self.slot_count > 0 { + if !self.slots.is_empty() { let direction_mask = { let mut dir = 0x01; let mut direction_mask = 0u8; - for n in 0..self.slot_count { - let slot = self.slots[n]; + for slot in &self.slots { if slot.direction_transmit() { direction_mask = direction_mask | dir; } @@ -284,8 +281,8 @@ impl TryWrite for GuaranteedTimeSlotInformation { bytes.write(offset, direction_mask)?; - for n in 0..self.slot_count { - bytes.write(offset, self.slots[n])?; + for slot in self.slots { + bytes.write(offset, slot)?; } } Ok(*offset) @@ -298,16 +295,12 @@ impl TryRead<'_> for GuaranteedTimeSlotInformation { let byte: u8 = bytes.read(offset)?; let slot_count = (byte & COUNT_MASK) as usize; let permit = (byte & PERMIT) == PERMIT; - let mut slots = [GuaranteedTimeSlotDescriptor { - short_address: ShortAddress::broadcast(), - starting_slot: 0, - length: 0, - direction: Direction::Receive, - }; 7]; + let mut slots = heapless::Vec::new(); + assert!(slot_count <= slots.capacity()); if slot_count > 0 { check_len(&bytes[*offset..], 2 + (3 * slot_count))?; let mut direction_mask: u8 = bytes.read(offset)?; - for n in 0..slot_count { + for _ in 0..slot_count { let mut slot: GuaranteedTimeSlotDescriptor = bytes.read(offset)?; let direction = if direction_mask & 0b1 == 0b1 { @@ -317,17 +310,12 @@ impl TryRead<'_> for GuaranteedTimeSlotInformation { }; slot.set_direction(direction); direction_mask = direction_mask >> 1; - slots[n] = slot; + slots.push(slot).expect( + "slot_count can never be larger than Vec::capacity", + ); } } - Ok(( - Self { - permit, - slot_count, - slots, - }, - *offset, - )) + Ok((Self { permit, slots }, *offset)) } } @@ -338,7 +326,7 @@ const EXTENDED_MASK: u8 = 0b0111_0000; /// /// Addresses to devices that has pending messages with the coordinator /// -/// ```notrust +/// ```txt /// +--------+-----------------+--------------------+ /// | Header | Short Addresses | Extended Addresses | /// +--------+-----------------+--------------------+ @@ -347,64 +335,63 @@ const EXTENDED_MASK: u8 = 0b0111_0000; /// /// ## Header /// -/// ```notrust +/// ```txt /// +-------------+----------+----------------+----------+ /// | Short Count | Reserved | Extended Count | Reserved | /// +-------------+----------+----------------+----------+ /// 0 - 2 3 4 - 6 7 bit /// ``` -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Default, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PendingAddress { - short_address_count: usize, - short_addresses: [ShortAddress; 7], - extended_address_count: usize, - extended_addresses: [ExtendedAddress; 7], + /// List of pending [`ShortAddress`]es + pub short_addresses: heapless::Vec, + /// List of pending [`ExtendedAddress`]es + pub extended_addresses: heapless::Vec, } impl PendingAddress { /// Create a new empty PendingAddress struct pub fn new() -> Self { - PendingAddress { - short_address_count: 0, - short_addresses: [ShortAddress::broadcast(); 7], - extended_address_count: 0, - extended_addresses: [ExtendedAddress::broadcast(); 7], - } + Self::default() } /// Get the short addresses pub fn short_addresses(&self) -> &[ShortAddress] { - &self.short_addresses[..self.short_address_count] + self.short_addresses.as_slice() } /// Get the extended address pub fn extended_addresses(&self) -> &[ExtendedAddress] { - &self.extended_addresses[..self.extended_address_count] + self.extended_addresses.as_slice() } } impl TryRead<'_> for PendingAddress { fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> { let offset = &mut 0; - let ss = mem::size_of::(); - let es = mem::size_of::(); + let ss = size_of::(); + let es = size_of::(); let byte: u8 = bytes.read(offset)?; let sl = (byte & SHORT_MASK) as usize; let el = ((byte & EXTENDED_MASK) >> 4) as usize; check_len(&bytes[*offset..], (sl * ss) + (el * es))?; - let mut short_addresses = [ShortAddress::broadcast(); 7]; - for n in 0..sl { - short_addresses[n] = bytes.read(offset)?; + let mut short_addresses = heapless::Vec::new(); + assert!(sl <= short_addresses.capacity()); + for _ in 0..sl { + short_addresses + .push(bytes.read(offset)?) + .expect("sl can never be larger than 7"); } - let mut extended_addresses = [ExtendedAddress::broadcast(); 7]; - for n in 0..el { - extended_addresses[n] = bytes.read(offset)?; + let mut extended_addresses = heapless::Vec::new(); + assert!(el <= extended_addresses.capacity()); + for _ in 0..el { + extended_addresses + .push(bytes.read(offset)?) + .expect("el can never be larger than 7"); } Ok(( Self { - short_address_count: sl, short_addresses, - extended_address_count: el, extended_addresses, }, *offset, @@ -415,23 +402,21 @@ impl TryRead<'_> for PendingAddress { impl TryWrite for PendingAddress { fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result { let offset = &mut 0; - assert!(self.short_address_count <= 7); - assert!(self.extended_address_count <= 7); + assert!(self.short_addresses.len() <= 7); + assert!(self.extended_addresses.len() <= 7); - let sl = self.short_address_count; - let el = self.extended_address_count; + let sl = self.short_addresses.len(); + let el = self.extended_addresses.len(); let it_s_magic = (((el as u8) << 4) & EXTENDED_MASK) | ((sl as u8) & SHORT_MASK); //FIXME give variable meaningful name bytes.write(offset, it_s_magic)?; - for n in 0..self.short_address_count { - let addr = self.short_addresses[n]; + for addr in self.short_addresses { bytes.write(offset, addr)?; } - for n in 0..self.extended_address_count { - let addr = self.extended_addresses[n]; + for addr in self.extended_addresses { bytes.write(offset, addr)?; } Ok(*offset) @@ -439,7 +424,7 @@ impl TryWrite for PendingAddress { } /// Beacon frame -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Beacon { /// Superframe specification @@ -632,29 +617,30 @@ mod tests { association_permit: true, }; - let mut slots = [GuaranteedTimeSlotDescriptor::new(); 7]; - slots[0] = GuaranteedTimeSlotDescriptor { - short_address: ShortAddress(0x1234), - starting_slot: 1, - length: 1, - direction: Direction::Transmit, - }; + let mut slots = heapless::Vec::new(); + slots + .push(GuaranteedTimeSlotDescriptor { + short_address: ShortAddress(0x1234), + starting_slot: 1, + length: 1, + direction: Direction::Transmit, + }) + .unwrap(); let guaranteed_time_slot_info = GuaranteedTimeSlotInformation { permit: true, - slot_count: 1, slots, }; - let mut short_addresses = [ShortAddress::broadcast(); 7]; - short_addresses[0] = ShortAddress(0x7856); - let mut extended_addresses = [ExtendedAddress::broadcast(); 7]; - extended_addresses[0] = ExtendedAddress(0xaec24a1c2116e260); + let mut short_addresses = heapless::Vec::default(); + short_addresses.push(ShortAddress(0x7856)).unwrap(); + let mut extended_addresses = heapless::Vec::default(); + extended_addresses + .push(ExtendedAddress(0xaec24a1c2116e260)) + .unwrap(); let pending_address = PendingAddress { - short_address_count: 1, short_addresses, - extended_address_count: 1, extended_addresses, }; diff --git a/src/mac/frame/mod.rs b/src/mac/frame/mod.rs index e290f8c..cff8d2a 100644 --- a/src/mac/frame/mod.rs +++ b/src/mac/frame/mod.rs @@ -151,7 +151,7 @@ use self::security::{ /// /// [decode]: #method.try_read /// [encode]: #method.try_write -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Frame<'p> { /// Header @@ -231,13 +231,13 @@ where let offset = &mut 0; bytes.write_with(offset, self.header, &context.security_ctx)?; - bytes.write(offset, self.content)?; + bytes.write(offset, self.content.clone())?; let mut security_enabled = false; if let Some(ctx) = context.security_ctx.as_mut() { let write_secured = security::secure_frame( - self, + self.clone(), ctx, context.footer_mode, &mut bytes[*offset..], @@ -394,7 +394,7 @@ impl Default for FooterMode { } /// Content of a frame -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FrameContent { /// Beacon frame content diff --git a/src/mac/frame/security/mod.rs b/src/mac/frame/security/mod.rs index 52d878f..38262b1 100644 --- a/src/mac/frame/security/mod.rs +++ b/src/mac/frame/security/mod.rs @@ -1046,7 +1046,7 @@ mod tests { let mut buf = [0u8; 127]; let mut sec_ctx = aes_sec_ctx(source_euid, FRAME_CTR); - match frame.try_write( + match frame.clone().try_write( &mut buf, &mut FrameSerDesContext::no_security(FooterMode::None), ) {