Browse Source

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.
Thibaut Vandervelden 1 year ago
parent
commit
26d949a8b0
2 changed files with 151 additions and 79 deletions
  1. 9 0
      src/iface/interface/sixlowpan.rs
  2. 142 79
      src/wire/ieee802154.rs

+ 9 - 0
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

+ 142 - 79
src/wire/ieee802154.rs

@@ -276,11 +276,26 @@ impl<T: AsRef<[u8]>> Frame<T> {
         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<T: AsRef<[u8]>> Frame<T> {
             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<T: AsRef<[u8]>> Frame<T> {
             | 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<Pan> {
-        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<Address> {
-        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<Pan> {
-        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<Address> {
-        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
         }
     }