diff --git a/fuzz/fuzz_targets/sixlowpan_packet.rs b/fuzz/fuzz_targets/sixlowpan_packet.rs index d9ba99bb..37038e45 100644 --- a/fuzz/fuzz_targets/sixlowpan_packet.rs +++ b/fuzz/fuzz_targets/sixlowpan_packet.rs @@ -43,6 +43,7 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { &frame, fuzz.ll_src_addr.map(Into::into), fuzz.ll_dst_addr.map(Into::into), + &[], ) { let mut buffer = vec![0; iphc_repr.buffer_len()]; let mut iphc_frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ee04c5cb..96840722 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -247,6 +247,8 @@ pub struct InterfaceInner<'a> { pan_id: Option, #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: u16, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, ip_addrs: ManagedSlice<'a, IpCidr>, @@ -289,6 +291,9 @@ pub struct InterfaceBuilder<'a> { sixlowpan_reassembly_buffer_timeout: Duration, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: ManagedSlice<'a, u8>, + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], } impl<'a> InterfaceBuilder<'a> { @@ -362,6 +367,9 @@ let iface = builder.finalize(&mut device); sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], } } @@ -473,12 +481,14 @@ let iface = builder.finalize(&mut device); self } + /// Set the IPv4 reassembly buffer the interface will use. #[cfg(feature = "proto-ipv4-fragmentation")] pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { self.ipv4_fragments = storage; self } + /// Set the IPv4 fragments buffer the interface will use. #[cfg(feature = "proto-ipv4-fragmentation")] pub fn ipv4_fragmentation_buffer(mut self, storage: T) -> Self where @@ -488,6 +498,17 @@ let iface = builder.finalize(&mut device); self } + /// Set the address contexts the interface will use. + #[cfg(feature = "proto-sixlowpan")] + pub fn sixlowpan_address_context( + mut self, + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], + ) -> Self { + self.sixlowpan_address_context = sixlowpan_address_context; + self + } + + /// Set the 6LoWPAN reassembly buffer the interface will use. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_reassembly_buffer( mut self, @@ -497,6 +518,7 @@ let iface = builder.finalize(&mut device); self } + /// Set the timeout value the 6LoWPAN reassembly buffer will use. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_reassembly_buffer_timeout(mut self, timeout: Duration) -> Self { if timeout > Duration::from_secs(60) { @@ -506,6 +528,7 @@ let iface = builder.finalize(&mut device); self } + /// Set the 6LoWPAN fragments buffer the interface will use. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_fragmentation_buffer(mut self, storage: T) -> Self where @@ -651,6 +674,8 @@ let iface = builder.finalize(&mut device); tag, #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], rand, }, } @@ -1534,6 +1559,9 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: 1, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], + #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: 1, @@ -1804,6 +1832,7 @@ impl<'a> InterfaceInner<'a> { &iphc, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, + self.sixlowpan_address_context )); // The uncompressed header size always starts with 40, since this is the size @@ -1888,6 +1917,7 @@ impl<'a> InterfaceInner<'a> { &iphc_packet, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, + self.sixlowpan_address_context, )); let payload = iphc_packet.payload(); diff --git a/src/wire/mod.rs b/src/wire/mod.rs index ed8f64c3..30f4a825 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -151,7 +151,7 @@ pub use self::sixlowpan::{ NhcPacket as SixlowpanNhcPacket, UdpNhcPacket as SixlowpanUdpNhcPacket, UdpNhcRepr as SixlowpanUdpNhcRepr, }, - NextHeader as SixlowpanNextHeader, SixlowpanPacket, + AddressContext as SixlowpanAddressContext, NextHeader as SixlowpanNextHeader, SixlowpanPacket, }; #[cfg(feature = "medium-ieee802154")] diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 1012b5a1..42031601 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -2,11 +2,25 @@ //! IEEE802.154-based networks. //! //! [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282 +use core::ops::Deref; + use super::{Error, Result}; use crate::wire::ieee802154::Address as LlAddress; use crate::wire::ipv6; use crate::wire::IpProtocol; +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AddressContext<'a>(pub &'a [u8]); + +impl<'a> Deref for AddressContext<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + /// The representation of an unresolved address. 6LoWPAN compression of IPv6 addresses can be with /// and without context information. The decompression with context information is not yet /// implemented. @@ -14,7 +28,7 @@ use crate::wire::IpProtocol; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum UnresolvedAddress<'a> { WithoutContext(AddressMode<'a>), - WithContext(AddressMode<'a>), + WithContext((usize, AddressMode<'a>)), Reserved, } @@ -51,8 +65,30 @@ const LINK_LOCAL_PREFIX: [u8; 2] = [0xfe, 0x80]; const EUI64_MIDDLE_VALUE: [u8; 2] = [0xff, 0xfe]; impl<'a> UnresolvedAddress<'a> { - pub fn resolve(self, ll_address: Option) -> Result { + pub fn resolve( + self, + ll_address: Option, + addr_context: &[AddressContext<'_>], + ) -> Result { let mut bytes = [0; 16]; + + let copy_context = |index: usize, bytes: &mut [u8]| -> Result<()> { + if index >= addr_context.len() { + return Err(Error); + } + + let context = addr_context[index]; + let len = context.len(); + + if len > 8 { + return Err(Error); + } + + bytes[..len].copy_from_slice(&context); + + Ok(()) + }; + match self { UnresolvedAddress::WithoutContext(mode) => match mode { AddressMode::FullInline(addr) => Ok(ipv6::Address::from_bytes(addr)), @@ -104,8 +140,35 @@ impl<'a> UnresolvedAddress<'a> { _ => Err(Error), }, UnresolvedAddress::WithContext(mode) => match mode { - AddressMode::Unspecified => Ok(ipv6::Address::UNSPECIFIED), - AddressMode::NotSupported => Err(Error), + (_, AddressMode::Unspecified) => Ok(ipv6::Address::UNSPECIFIED), + (index, AddressMode::InLine64bits(inline)) => { + copy_context(index, &mut bytes[..])?; + bytes[16 - inline.len()..].copy_from_slice(inline); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + (index, AddressMode::InLine16bits(inline)) => { + copy_context(index, &mut bytes[..])?; + bytes[16 - inline.len()..].copy_from_slice(inline); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + (index, AddressMode::FullyElided) => { + match ll_address { + Some(LlAddress::Short(ll)) => { + bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]); + bytes[14..].copy_from_slice(&ll); + } + Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() { + Some(addr) => bytes[8..].copy_from_slice(&addr), + None => return Err(Error), + }, + Some(LlAddress::Absent) => return Err(Error), + None => return Err(Error), + } + + copy_context(index, &mut bytes[..])?; + + Ok(ipv6::Address::from_bytes(&bytes[..])) + } _ => Err(Error), }, UnresolvedAddress::Reserved => Err(Error), @@ -421,7 +484,10 @@ pub mod iphc { //! //! [RFC 6282 ยง 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1 - use super::{AddressMode, Error, NextHeader, Result, UnresolvedAddress, DISPATCH_IPHC_HEADER}; + use super::{ + AddressContext, AddressMode, Error, NextHeader, Result, UnresolvedAddress, + DISPATCH_IPHC_HEADER, + }; use crate::wire::{ieee802154::Address as LlAddress, ipv6, IpProtocol}; use byteorder::{ByteOrder, NetworkEndian}; @@ -573,7 +639,7 @@ pub mod iphc { pub fn src_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); - Some(data[1] >> 4) + Some(data[2] >> 4) } else { None } @@ -583,7 +649,7 @@ pub mod iphc { pub fn dst_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); - Some(data[1] & 0x0f) + Some(data[2] & 0x0f) } else { None } @@ -640,30 +706,52 @@ pub mod iphc { + self.next_header_size() + self.hop_limit_size()) as usize; + let data = self.buffer.as_ref(); match (self.sac_field(), self.sam_field()) { - (0, 0b00) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( - &data[start..][..16], - ))) - } - (0, 0b01) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine64bits(&data[start..][..8]), - )) - } - (0, 0b10) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine16bits(&data[start..][..2]), - )) - } + (0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], + ))), + (0, 0b01) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine64bits(&data[start..][..8]), + )), + (0, 0b10) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine16bits(&data[start..][..2]), + )), (0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)), - (1, 0b00) => Ok(UnresolvedAddress::WithContext(AddressMode::Unspecified)), - (1, 0b01) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (1, 0b10) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (1, 0b11) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (1, 0b00) => Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::Unspecified, + ))), + (1, 0b01) => { + if let Some(id) = self.src_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine64bits(&data[start..][..8]), + ))) + } else { + Err(Error) + } + } + (1, 0b10) => { + if let Some(id) = self.src_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine16bits(&data[start..][..2]), + ))) + } else { + Err(Error) + } + } + (1, 0b11) => { + if let Some(id) = self.src_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::FullyElided, + ))) + } else { + Err(Error) + } + } _ => Err(Error), } } @@ -676,55 +764,65 @@ pub mod iphc { + self.hop_limit_size() + self.src_address_size()) as usize; + let data = self.buffer.as_ref(); match (self.m_field(), self.dac_field(), self.dam_field()) { - (0, 0, 0b00) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( - &data[start..][..16], - ))) - } - (0, 0, 0b01) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine64bits(&data[start..][..8]), - )) - } - (0, 0, 0b10) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine16bits(&data[start..][..2]), - )) - } + (0, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], + ))), + (0, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine64bits(&data[start..][..8]), + )), + (0, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine16bits(&data[start..][..2]), + )), (0, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)), (0, 1, 0b00) => Ok(UnresolvedAddress::Reserved), - (0, 1, 0b01) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (0, 1, 0b10) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (0, 1, 0b11) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (1, 0, 0b00) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( - &data[start..][..16], - ))) + (0, 1, 0b01) => { + if let Some(id) = self.dst_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine64bits(&data[start..][..8]), + ))) + } else { + Err(Error) + } } - (1, 0, 0b01) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::Multicast48bits(&data[start..][..6]), - )) + (0, 1, 0b10) => { + if let Some(id) = self.dst_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine16bits(&data[start..][..2]), + ))) + } else { + Err(Error) + } } - (1, 0, 0b10) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::Multicast32bits(&data[start..][..4]), - )) + (0, 1, 0b11) => { + if let Some(id) = self.dst_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::FullyElided, + ))) + } else { + Err(Error) + } } - (1, 0, 0b11) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::Multicast8bits(&data[start..][..1]), - )) - } - (1, 1, 0b00) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (1, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], + ))), + (1, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast48bits(&data[start..][..6]), + )), + (1, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast32bits(&data[start..][..4]), + )), + (1, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast8bits(&data[start..][..1]), + )), + (1, 1, 0b00) => Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::NotSupported, + ))), (1, 1, 0b01 | 0b10 | 0b11) => Ok(UnresolvedAddress::Reserved), _ => Err(Error), } @@ -1086,6 +1184,7 @@ pub mod iphc { packet: &Packet<&T>, ll_src_addr: Option, ll_dst_addr: Option, + addr_context: &[AddressContext<'_>], ) -> Result { // Ensure basic accessors will work. packet.check_len()?; @@ -1095,8 +1194,8 @@ pub mod iphc { return Err(Error); } - let src_addr = packet.src_addr()?.resolve(ll_src_addr)?; - let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr)?; + let src_addr = packet.src_addr()?.resolve(ll_src_addr, addr_context)?; + let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr, addr_context)?; Ok(Self { src_addr, @@ -1293,11 +1392,17 @@ pub mod iphc { assert_eq!( packet.src_addr(), - Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)) + Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::FullyElided + ))) ); assert_eq!( packet.dst_addr(), - Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)) + Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::FullyElided + ))) ); } } @@ -2205,8 +2310,13 @@ mod test { unreachable!() }; - let iphc_repr = - iphc::Repr::parse(&iphc, ieee802154_repr.src_addr, ieee802154_repr.dst_addr).unwrap(); + let iphc_repr = iphc::Repr::parse( + &iphc, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + &[], + ) + .unwrap(); assert_eq!( iphc_repr.dst_addr,