From 26d949a8b004f6b44c25ec61aed6d59aa3eb7b64 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 7 Dec 2023 17:19:59 +0100 Subject: [PATCH] ieee: better handle addr for new frame version The standard defines when addresses and pan ids are present or not. This depends on the frame version. smoltcp supported old versions (2003 and 2006). However, newer versions are more complicated. This adds better handling for new versions. --- src/iface/interface/sixlowpan.rs | 9 ++ src/wire/ieee802154.rs | 221 ++++++++++++++++++++----------- 2 files changed, 151 insertions(+), 79 deletions(-) diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index bd079bd0e..4dd0282ed 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -174,6 +174,11 @@ impl InterfaceInner { decompressed_size += 2; decompressed_size -= ext_repr.buffer_len(); next_header = Some(ext_repr.next_header); + + if ext_repr.buffer_len() + ext_repr.length as usize > data.len() { + return Err(Error); + } + data = &data[ext_repr.buffer_len() + ext_repr.length as usize..]; } SixlowpanNhcPacket::UdpHeader => { @@ -283,6 +288,10 @@ impl InterfaceInner { &ChecksumCapabilities::ignored(), )?; + if payload.len() + 8 > buffer.len() { + return Err(Error); + } + let mut udp = UdpPacket::new_unchecked(&mut buffer[..payload.len() + 8]); udp_repr .0 diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 7359f8b5e..33fafb60e 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -276,11 +276,26 @@ impl> Frame { let packet = Self::new_unchecked(buffer); packet.check_len()?; - if matches!(packet.dst_addressing_mode(), AddressingMode::Unknown(_)) { + // We don't handle unknown frame versions. + if matches!(packet.frame_version(), FrameVersion::Unknown(_)) { return Err(Error); } - if matches!(packet.src_addressing_mode(), AddressingMode::Unknown(_)) { + // We don't handle unknown addressing modes. + if matches!(packet.dst_addressing_mode(), AddressingMode::Unknown(_)) + || matches!(packet.src_addressing_mode(), AddressingMode::Unknown(_)) + { + return Err(Error); + } + + // We don't handle absent addressing mode with PAN ID compression for older frame versions. + if matches!( + packet.frame_version(), + FrameVersion::Ieee802154_2003 | FrameVersion::Ieee802154_2006 + ) && packet.pan_id_compression() + && matches!(packet.dst_addressing_mode(), AddressingMode::Absent) + && matches!(packet.src_addressing_mode(), AddressingMode::Absent) + { return Err(Error); } @@ -295,16 +310,27 @@ impl> Frame { return Err(Error); } - let mut offset = field::ADDRESSING.start + 2; - - // Calculate the size of the addressing field. - offset += self.dst_addressing_mode().size(); - offset += self.src_addressing_mode().size(); - - if !self.pan_id_compression() { - offset += 2; + // We don't handle frames with a payload larger than 127 bytes. + if self.buffer.as_ref().len() > 127 { + return Err(Error); } + let mut offset = field::ADDRESSING.start + + if let Some((dst_pan_id, dst_addr, src_pan_id, src_addr)) = self.addr_present_flags() + { + let mut offset = if dst_pan_id { 2 } else { 0 }; + offset += dst_addr.size(); + offset += if src_pan_id { 2 } else { 0 }; + offset += src_addr.size(); + + if offset > self.buffer.as_ref().len() { + return Err(Error); + } + offset + } else { + 0 + }; + if self.security_enabled() { // First check that we can access the security header control bits. if offset + 1 > self.buffer.as_ref().len() { @@ -402,103 +428,140 @@ impl> Frame { | FrameType::Unknown(_) => return None, } - let mut offset = 2; - - // Calculate the size of the addressing field. - offset += self.dst_addressing_mode().size(); - offset += self.src_addressing_mode().size(); + if let Some((dst_pan_id, dst_addr, src_pan_id, src_addr)) = self.addr_present_flags() { + let mut offset = if dst_pan_id { 2 } else { 0 }; + offset += dst_addr.size(); + offset += if src_pan_id { 2 } else { 0 }; + offset += src_addr.size(); - if !self.pan_id_compression() { - offset += 2; + let data = self.buffer.as_ref(); + Some(&data[field::ADDRESSING][..offset]) + } else { + None } + } + + fn addr_present_flags(&self) -> Option<(bool, AddressingMode, bool, AddressingMode)> { + let dst_addr_mode = self.dst_addressing_mode(); + let src_addr_mode = self.src_addressing_mode(); + let pan_id_compression = self.pan_id_compression(); - Some(&self.buffer.as_ref()[field::ADDRESSING][..offset]) + use AddressingMode::*; + match self.frame_version() { + FrameVersion::Ieee802154_2003 | FrameVersion::Ieee802154_2006 => { + match (dst_addr_mode, src_addr_mode) { + (Absent, src) => Some((false, Absent, true, src)), + (dst, Absent) => Some((true, dst, false, Absent)), + + (dst, src) if pan_id_compression => Some((true, dst, false, src)), + (dst, src) if !pan_id_compression => Some((true, dst, true, src)), + _ => None, + } + } + FrameVersion::Ieee802154 => { + Some(match (dst_addr_mode, src_addr_mode, pan_id_compression) { + (Absent, Absent, false) => (false, Absent, false, Absent), + (Absent, Absent, true) => (true, Absent, false, Absent), + (dst, Absent, false) if !matches!(dst, Absent) => (true, dst, false, Absent), + (dst, Absent, true) if !matches!(dst, Absent) => (false, dst, false, Absent), + (Absent, src, false) if !matches!(src, Absent) => (false, Absent, true, src), + (Absent, src, true) if !matches!(src, Absent) => (false, Absent, true, src), + (Extended, Extended, false) => (true, Extended, false, Extended), + (Extended, Extended, true) => (false, Extended, false, Extended), + (Short, Short, false) => (true, Short, true, Short), + (Short, Extended, false) => (true, Short, true, Extended), + (Extended, Short, false) => (true, Extended, true, Short), + (Short, Extended, true) => (true, Short, false, Extended), + (Extended, Short, true) => (true, Extended, false, Short), + (Short, Short, true) => (true, Short, false, Short), + _ => return None, + }) + } + _ => None, + } } /// Return the destination PAN field. #[inline] pub fn dst_pan_id(&self) -> Option { - let addressing_fields = self.addressing_fields()?; - match self.dst_addressing_mode() { - AddressingMode::Absent => None, - AddressingMode::Short | AddressingMode::Extended => { - Some(Pan(LittleEndian::read_u16(&addressing_fields[0..2]))) - } - AddressingMode::Unknown(_) => None, + if let Some((true, _, _, _)) = self.addr_present_flags() { + let addressing_fields = self.addressing_fields()?; + Some(Pan(LittleEndian::read_u16(&addressing_fields[..2]))) + } else { + None } } /// Return the destination address field. #[inline] pub fn dst_addr(&self) -> Option
{ - let addressing_fields = self.addressing_fields()?; - match self.dst_addressing_mode() { - AddressingMode::Absent => Some(Address::Absent), - AddressingMode::Short => { - let mut raw = [0u8; 2]; - raw.clone_from_slice(&addressing_fields[2..4]); - raw.reverse(); - Some(Address::short_from_bytes(raw)) - } - AddressingMode::Extended => { - let mut raw = [0u8; 8]; - raw.clone_from_slice(&addressing_fields[2..10]); - raw.reverse(); - Some(Address::extended_from_bytes(raw)) + if let Some((dst_pan_id, dst_addr, _, _)) = self.addr_present_flags() { + let addressing_fields = self.addressing_fields()?; + let offset = if dst_pan_id { 2 } else { 0 }; + + match dst_addr { + AddressingMode::Absent => Some(Address::Absent), + AddressingMode::Short => { + let mut raw = [0u8; 2]; + raw.clone_from_slice(&addressing_fields[offset..offset + 2]); + raw.reverse(); + Some(Address::short_from_bytes(raw)) + } + AddressingMode::Extended => { + let mut raw = [0u8; 8]; + raw.clone_from_slice(&addressing_fields[offset..offset + 8]); + raw.reverse(); + Some(Address::extended_from_bytes(raw)) + } + AddressingMode::Unknown(_) => None, } - AddressingMode::Unknown(_) => None, + } else { + None } } /// Return the destination PAN field. #[inline] pub fn src_pan_id(&self) -> Option { - if self.pan_id_compression() { - return None; - } - - let addressing_fields = self.addressing_fields()?; - let offset = self.dst_addressing_mode().size() + 2; - - match self.src_addressing_mode() { - AddressingMode::Absent => None, - AddressingMode::Short | AddressingMode::Extended => Some(Pan(LittleEndian::read_u16( - &addressing_fields[offset..offset + 2], - ))), - AddressingMode::Unknown(_) => None, + if let Some((dst_pan_id, dst_addr, true, _)) = self.addr_present_flags() { + let mut offset = if dst_pan_id { 2 } else { 0 }; + offset += dst_addr.size(); + let addressing_fields = self.addressing_fields()?; + Some(Pan(LittleEndian::read_u16( + &addressing_fields[offset..][..2], + ))) + } else { + None } } /// Return the source address field. #[inline] pub fn src_addr(&self) -> Option
{ - let addressing_fields = self.addressing_fields()?; - let mut offset = match self.dst_addressing_mode() { - AddressingMode::Absent => 0, - AddressingMode::Short => 2, - AddressingMode::Extended => 8, - _ => return None, // TODO(thvdveld): what do we do here? - } + 2; - - if !self.pan_id_compression() { - offset += 2; - } - - match self.src_addressing_mode() { - AddressingMode::Absent => Some(Address::Absent), - AddressingMode::Short => { - let mut raw = [0u8; 2]; - raw.clone_from_slice(&addressing_fields[offset..offset + 2]); - raw.reverse(); - Some(Address::short_from_bytes(raw)) - } - AddressingMode::Extended => { - let mut raw = [0u8; 8]; - raw.clone_from_slice(&addressing_fields[offset..offset + 8]); - raw.reverse(); - Some(Address::extended_from_bytes(raw)) + if let Some((dst_pan_id, dst_addr, src_pan_id, src_addr)) = self.addr_present_flags() { + let addressing_fields = self.addressing_fields()?; + let mut offset = if dst_pan_id { 2 } else { 0 }; + offset += dst_addr.size(); + offset += if src_pan_id { 2 } else { 0 }; + + match src_addr { + AddressingMode::Absent => Some(Address::Absent), + AddressingMode::Short => { + let mut raw = [0u8; 2]; + raw.clone_from_slice(&addressing_fields[offset..offset + 2]); + raw.reverse(); + Some(Address::short_from_bytes(raw)) + } + AddressingMode::Extended => { + let mut raw = [0u8; 8]; + raw.clone_from_slice(&addressing_fields[offset..offset + 8]); + raw.reverse(); + Some(Address::extended_from_bytes(raw)) + } + AddressingMode::Unknown(_) => None, } - AddressingMode::Unknown(_) => None, + } else { + None } }