diff --git a/fuzz/fuzz_targets/sixlowpan_packet.rs b/fuzz/fuzz_targets/sixlowpan_packet.rs index e88285d29..a6f2e8df4 100644 --- a/fuzz/fuzz_targets/sixlowpan_packet.rs +++ b/fuzz/fuzz_targets/sixlowpan_packet.rs @@ -39,12 +39,10 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { } Ok(SixlowpanPacket::IphcHeader) => { if let Ok(frame) = SixlowpanIphcPacket::new_checked(fuzz.data) { - if let Ok(iphc_repr) = SixlowpanIphcRepr::parse( - &frame, - fuzz.ll_src_addr.map(Into::into), - fuzz.ll_dst_addr.map(Into::into), - &[], - ) { + if let Ok(iphc_repr) = SixlowpanIphcRepr::parse(&frame) { + let src_addr = iphc_repr.src_addr.resolve(fuzz.ll_src_addr.map(Into::into), &[], iphc_repr.context_identifier).unwrap(); + let dst_addr = iphc_repr.dst_addr.resolve(fuzz.ll_dst_addr.map(Into::into), &[], iphc_repr.context_identifier).unwrap(); + let mut buffer = vec![0; iphc_repr.buffer_len()]; let mut iphc_frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); iphc_repr.emit(&mut iphc_frame); @@ -75,8 +73,8 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { { if let Ok(repr) = SixlowpanUdpNhcRepr::parse( &frame, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, + &src_addr, + &dst_addr, &Default::default(), ) { let mut buffer = vec![ @@ -90,8 +88,8 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { ); repr.emit( &mut udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, + &src_addr, + &dst_addr, frame.payload().len(), |b| b.copy_from_slice(frame.payload()), &ChecksumCapabilities::ignored(), @@ -141,16 +139,16 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { if let Ok(frame) = TcpPacket::new_checked(payload) { if let Ok(repr) = TcpRepr::parse( &frame, - &iphc_repr.src_addr.into_address(), - &iphc_repr.dst_addr.into_address(), + &src_addr.into_address(), + &dst_addr.into_address(), &ChecksumCapabilities::default(), ) { let mut buffer = vec![0; repr.buffer_len()]; let mut frame = TcpPacket::new_unchecked(&mut buffer[..]); repr.emit( &mut frame, - &iphc_repr.src_addr.into_address(), - &iphc_repr.dst_addr.into_address(), + &src_addr.into_address(), + &dst_addr.into_address(), &ChecksumCapabilities::default(), ); } @@ -160,8 +158,8 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { if let Ok(frame) = UdpPacket::new_checked(payload) { if let Ok(repr) = UdpRepr::parse( &frame, - &iphc_repr.src_addr.into_address(), - &iphc_repr.dst_addr.into_address(), + &src_addr.into_address(), + &dst_addr.into_address(), &ChecksumCapabilities::default(), ) { let mut buffer = @@ -169,8 +167,8 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { let mut packet = UdpPacket::new_unchecked(&mut buffer[..]); repr.emit( &mut packet, - &iphc_repr.src_addr.into_address(), - &iphc_repr.dst_addr.into_address(), + &src_addr.into_address(), + &dst_addr.into_address(), frame.payload().len(), |b| b.copy_from_slice(frame.payload()), &ChecksumCapabilities::default(), @@ -200,8 +198,8 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { IpProtocol::Icmpv6 => { if let Ok(packet) = Icmpv6Packet::new_checked(payload) { if let Ok(repr) = Icmpv6Repr::parse( - &iphc_repr.src_addr.into_address(), - &iphc_repr.dst_addr.into_address(), + &src_addr.into_address(), + &dst_addr.into_address(), &packet, &ChecksumCapabilities::default(), ) { @@ -209,8 +207,8 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { let mut packet = Icmpv6Packet::new_unchecked(&mut buffer[..]); repr.emit( - &iphc_repr.src_addr.into_address(), - &iphc_repr.dst_addr.into_address(), + &src_addr.into_address(), + &dst_addr.into_address(), &mut packet, &ChecksumCapabilities::default(), ); diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index d5e3cafb8..68970adbf 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -1,4 +1,5 @@ use super::*; +use crate::wire::ieee802154::{compress_destination_address, compress_source_address}; use crate::wire::Result; // Max len of non-fragmented packets after decompression (including ipv6 header and payload) @@ -206,12 +207,7 @@ impl InterfaceInner { buffer: &mut [u8], ) -> Result { let iphc = SixlowpanIphcPacket::new_checked(iphc_payload)?; - let iphc_repr = SixlowpanIphcRepr::parse( - &iphc, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - address_context, - )?; + let iphc_repr = SixlowpanIphcRepr::parse(&iphc)?; // The first thing we have to decompress is the IPv6 header. However, at this point we // don't know the total size of the packet, neither the next header, since that can be a @@ -245,6 +241,8 @@ impl InterfaceInner { } SixlowpanNhcPacket::UdpHeader => { decompress_udp( + address_context, + ieee802154_repr, data, &iphc_repr, buffer, @@ -282,8 +280,16 @@ impl InterfaceInner { } let ipv6_repr = Ipv6Repr { - src_addr: iphc_repr.src_addr, - dst_addr: iphc_repr.dst_addr, + src_addr: iphc_repr.src_addr.resolve( + ieee802154_repr.src_addr, + address_context, + iphc_repr.context_identifier, + )?, + dst_addr: iphc_repr.dst_addr.resolve( + ieee802154_repr.dst_addr, + address_context, + iphc_repr.context_identifier, + )?, next_header: decompress_next_header(iphc_repr.next_header, iphc.payload())?, payload_len: total_len.unwrap_or(payload_len) - 40, hop_limit: iphc_repr.hop_limit, @@ -446,10 +452,9 @@ impl InterfaceInner { }; let iphc_repr = SixlowpanIphcRepr { - src_addr: packet.header.src_addr, - ll_src_addr: ieee_repr.src_addr, - dst_addr: packet.header.dst_addr, - ll_dst_addr: ieee_repr.dst_addr, + src_addr: compress_source_address(packet.header.src_addr, ieee_repr.src_addr), + dst_addr: compress_destination_address(packet.header.dst_addr, ieee_repr.dst_addr), + context_identifier: None, next_header, hop_limit: packet.header.hop_limit, ecn: None, @@ -529,8 +534,8 @@ impl InterfaceInner { &mut SixlowpanUdpNhcPacket::new_unchecked( &mut buffer[..udp_repr.header_len() + payload.len()], ), - &iphc_repr.src_addr, - &iphc_repr.dst_addr, + &packet.header.src_addr, + &packet.header.dst_addr, payload.len(), |buf| buf.copy_from_slice(payload), checksum_caps, @@ -581,10 +586,9 @@ impl InterfaceInner { }; let iphc = SixlowpanIphcRepr { - src_addr: packet.header.src_addr, - ll_src_addr: ieee_repr.src_addr, - dst_addr: packet.header.dst_addr, - ll_dst_addr: ieee_repr.dst_addr, + src_addr: compress_source_address(packet.header.src_addr, ieee_repr.src_addr), + dst_addr: compress_destination_address(packet.header.dst_addr, ieee_repr.dst_addr), + context_identifier: None, next_header, hop_limit: packet.header.hop_limit, ecn: None, @@ -744,8 +748,11 @@ fn decompress_ext_hdr<'d>( } // NOTE: we always inline this function into the sixlowpan_to_ipv6 function, since it is only used there. +#[allow(clippy::too_many_arguments)] #[inline(always)] fn decompress_udp( + address_context: &[SixlowpanAddressContext], + ieee802154_repr: &Ieee802154Repr, data: &[u8], iphc_repr: &SixlowpanIphcRepr, buffer: &mut [u8], @@ -757,8 +764,16 @@ fn decompress_udp( let payload = udp_packet.payload(); let udp_repr = SixlowpanUdpNhcRepr::parse( &udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, + &iphc_repr.src_addr.resolve( + ieee802154_repr.src_addr, + address_context, + iphc_repr.context_identifier, + )?, + &iphc_repr.dst_addr.resolve( + ieee802154_repr.dst_addr, + address_context, + iphc_repr.context_identifier, + )?, &ChecksumCapabilities::ignored(), )?; if udp_repr.header_len() + payload.len() > buffer.len() { diff --git a/src/iface/interface/tests/sixlowpan.rs b/src/iface/interface/tests/sixlowpan.rs index 56e1fb8ca..2e6559af0 100644 --- a/src/iface/interface/tests/sixlowpan.rs +++ b/src/iface/interface/tests/sixlowpan.rs @@ -138,20 +138,29 @@ fn test_echo_request_sixlowpan_128_bytes() { let request_first_part_iphc_packet = SixlowpanIphcPacket::new_checked(request_first_part_packet.payload()).unwrap(); - let request_first_part_iphc_repr = SixlowpanIphcRepr::parse( - &request_first_part_iphc_packet, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - &iface.inner.sixlowpan_address_context, - ) - .unwrap(); + let request_first_part_iphc_repr = + SixlowpanIphcRepr::parse(&request_first_part_iphc_packet).unwrap(); assert_eq!( - request_first_part_iphc_repr.src_addr, + request_first_part_iphc_repr + .src_addr + .resolve( + ieee802154_repr.src_addr, + &iface.inner.sixlowpan_address_context, + request_first_part_iphc_repr.context_identifier + ) + .unwrap(), Ipv6Address::new(0xfe80, 0, 0, 0, 0x4042, 0x4242, 0x4242, 0x0b1a), ); assert_eq!( - request_first_part_iphc_repr.dst_addr, + request_first_part_iphc_repr + .dst_addr + .resolve( + ieee802154_repr.dst_addr, + &iface.inner.sixlowpan_address_context, + request_first_part_iphc_repr.context_identifier + ) + .unwrap(), Ipv6Address::new(0xfe80, 0, 0, 0, 0x92fc, 0x48c2, 0xa441, 0xfc76), ); diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 43d656b0f..7148ef079 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -2,7 +2,10 @@ use core::fmt; use byteorder::{ByteOrder, LittleEndian}; -use super::{Error, Result}; +use super::{ipv6, Error, Result}; +use crate::wire::sixlowpan::{ + DestinationAddress, LinkLocalAddress, MulticastAddress, SourceAddress, +}; use crate::wire::{Ipv6Address, Ipv6AddressExt}; enum_with_unknown! { @@ -1006,6 +1009,105 @@ impl Repr { } } +pub fn compress_source_address( + src_addr: ipv6::Address, + ll_src_addr: Option
, +) -> SourceAddress { + let src = src_addr.octets(); + + if src_addr == ipv6::Address::UNSPECIFIED { + SourceAddress::Unspecified + } else if src_addr.is_link_local() { + // We have a link local address. + // The remainder of the address can be elided when the context contains + // a 802.15.4 short address or a 802.15.4 extended address which can be + // converted to a eui64 address. + let is_eui_64 = ll_src_addr + .map(|addr| { + addr.as_eui_64() + .map(|addr| addr[..] == src[8..]) + .unwrap_or(false) + }) + .unwrap_or(false); + + if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { + let ll = [src[14], src[15]]; + + if ll_src_addr == Some(crate::wire::ieee802154::Address::Short(ll)) { + // We have the context from the 802.15.4 frame. + // The context contains the short address. + // We can elide the source address. + SourceAddress::LinkLocal(LinkLocalAddress::FullyElided) + } else { + // We don't have the context from the 802.15.4 frame. + // We cannot elide the source address, however we can elide 112 bits. + SourceAddress::LinkLocal(LinkLocalAddress::InLine16bits( + src[14..].try_into().unwrap(), + )) + } + } else if is_eui_64 { + // We have the context from the 802.15.4 frame. + // The context contains the extended address. + // We can elide the source address. + SourceAddress::LinkLocal(LinkLocalAddress::FullyElided) + } else { + // We cannot elide the source address, however we can elide 64 bits. + SourceAddress::LinkLocal(LinkLocalAddress::InLine64bits(src[8..].try_into().unwrap())) + } + } else { + // We cannot elide anything. + SourceAddress::LinkLocal(LinkLocalAddress::InLine128bits(src)) + } +} + +pub fn compress_destination_address( + dst_addr: ipv6::Address, + ll_dst_addr: Option
, +) -> DestinationAddress { + let dst = dst_addr.octets(); + if dst_addr.is_multicast() { + if dst[1] == 0x02 && dst[2..15] == [0; 13] { + DestinationAddress::Multicast(MulticastAddress::Inline8bits(dst[15])) + } else if dst[2..13] == [0; 11] { + let address = [dst[1], dst[13], dst[14], dst[15]]; + DestinationAddress::Multicast(MulticastAddress::Inline32bits(address)) + } else if dst[2..11] == [0; 9] { + let address = [dst[1], dst[11], dst[12], dst[13], dst[14], dst[15]]; + DestinationAddress::Multicast(MulticastAddress::Inline48bits(address)) + } else { + DestinationAddress::Multicast(MulticastAddress::FullInline(dst)) + } + } else if dst_addr.is_link_local() { + let is_eui_64 = ll_dst_addr + .map(|addr| { + addr.as_eui_64() + .map(|addr| addr[..] == dst[8..]) + .unwrap_or(false) + }) + .unwrap_or(false); + + if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { + let ll = [dst[14], dst[15]]; + + if ll_dst_addr == Some(crate::wire::ieee802154::Address::Short(ll)) { + DestinationAddress::LinkLocal(LinkLocalAddress::FullyElided) + } else { + DestinationAddress::LinkLocal(LinkLocalAddress::InLine16bits( + dst[14..].try_into().unwrap(), + )) + } + } else if is_eui_64 { + DestinationAddress::LinkLocal(LinkLocalAddress::FullyElided) + } else { + DestinationAddress::LinkLocal(LinkLocalAddress::InLine64bits( + dst[8..].try_into().unwrap(), + )) + } + } else { + DestinationAddress::LinkLocal(LinkLocalAddress::InLine128bits(dst)) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/wire/rpl.rs b/src/wire/rpl.rs index 088a8771b..f1a036fd0 100644 --- a/src/wire/rpl.rs +++ b/src/wire/rpl.rs @@ -2406,16 +2406,22 @@ mod tests { let ll_dst_address = Ieee802154Address::Short([0xff, 0xff]); let packet = SixlowpanIphcPacket::new_checked(&data).unwrap(); - let repr = - SixlowpanIphcRepr::parse(&packet, Some(ll_src_address), Some(ll_dst_address), &[]) - .unwrap(); + let repr = SixlowpanIphcRepr::parse(&packet).unwrap(); + let src_addr = &repr + .src_addr + .resolve(Some(ll_src_address), &[], repr.context_identifier) + .unwrap(); + let dst_addr = &repr + .dst_addr + .resolve(Some(ll_dst_address), &[], repr.context_identifier) + .unwrap(); let icmp_repr = match repr.next_header { SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6) => { let icmp_packet = Icmpv6Packet::new_checked(packet.payload()).unwrap(); match Icmpv6Repr::parse( - &repr.src_addr, - &repr.dst_addr, + &src_addr, + &dst_addr, &icmp_packet, &ChecksumCapabilities::ignored(), ) { @@ -2434,8 +2440,8 @@ mod tests { &mut buffer[..repr.buffer_len()], )); icmp_repr.emit( - &repr.src_addr.into(), - &repr.dst_addr.into(), + &src_addr, + &dst_addr, &mut Icmpv6Packet::new_unchecked( &mut buffer[repr.buffer_len()..][..icmp_repr.buffer_len()], ), diff --git a/src/wire/sixlowpan/iphc.rs b/src/wire/sixlowpan/iphc.rs index 429114128..791cd94e9 100644 --- a/src/wire/sixlowpan/iphc.rs +++ b/src/wire/sixlowpan/iphc.rs @@ -4,9 +4,10 @@ //! [RFC 6282 ยง 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1 use super::{ - AddressContext, AddressMode, Error, NextHeader, Result, UnresolvedAddress, DISPATCH_IPHC_HEADER, + AddressMode, ContextualAddress, DestinationAddress, Error, LinkLocalAddress, MulticastAddress, + NextHeader, Result, SourceAddress, UnresolvedAddress, DISPATCH_IPHC_HEADER, }; -use crate::wire::{ieee802154::Address as LlAddress, ipv6, ipv6::AddressExt, IpProtocol}; +use crate::wire::IpProtocol; use byteorder::{ByteOrder, NetworkEndian}; mod field { @@ -153,24 +154,24 @@ impl> Packet { } } - /// Return the Source Context Identifier. - pub fn src_context_id(&self) -> Option { + /// Return the Context Identifier. + pub fn context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); - Some(data[2] >> 4) + Some(data[2]) } else { None } } + /// Return the Source Context Identifier. + pub fn src_context_id(&self) -> Option { + self.context_id().map(|id| id >> 4) + } + /// Return the Destination Context Identifier. pub fn dst_context_id(&self) -> Option { - if self.cid_field() == 1 { - let data = self.buffer.as_ref(); - Some(data[2] & 0x0f) - } else { - None - } + self.context_id().map(|id| id & 0x0f) } /// Return the ECN field (when it is inlined). @@ -515,67 +516,71 @@ impl + AsMut<[u8]>> Packet { idx } - /// Set the Source Address based on the IPv6 address and the Link-Local address. + /// Set the Context Identifier Extension /// /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. - fn set_src_address( - &mut self, - src_addr: ipv6::Address, - ll_src_addr: Option, - mut idx: usize, - ) -> usize { - self.set_cid_field(0); - self.set_sac_field(0); - let src = src_addr.octets(); - if src_addr == ipv6::Address::UNSPECIFIED { - self.set_sac_field(1); - self.set_sam_field(0b00); - } else if src_addr.is_link_local() { - // We have a link local address. - // The remainder of the address can be elided when the context contains - // a 802.15.4 short address or a 802.15.4 extended address which can be - // converted to a eui64 address. - let is_eui_64 = ll_src_addr - .map(|addr| { - addr.as_eui_64() - .map(|addr| addr[..] == src[8..]) - .unwrap_or(false) - }) - .unwrap_or(false); - - if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { - let ll = [src[14], src[15]]; - - if ll_src_addr == Some(LlAddress::Short(ll)) { - // We have the context from the 802.15.4 frame. - // The context contains the short address. - // We can elide the source address. - self.set_sam_field(0b11); - } else { - // We don't have the context from the 802.15.4 frame. - // We cannot elide the source address, however we can elide 112 bits. - self.set_sam_field(0b10); + fn set_context_identifier(&mut self, cid: Option, mut idx: usize) -> usize { + if let Some(cid) = cid { + self.set_cid_field(1); + self.set_field(idx, &[cid]); + idx += 1; + } else { + self.set_cid_field(0); + } - self.set_field(idx, &src[14..]); - idx += 2; + idx + } + + /// Set the Source Address based on the IPv6 address and the Link-Local address. + /// + /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. + fn set_src_address(&mut self, src_addr: SourceAddress, mut idx: usize) -> usize { + match src_addr { + SourceAddress::LinkLocal(address) => { + self.set_sac_field(0); + match address { + LinkLocalAddress::InLine128bits(address) => { + self.set_sam_field(0b00); + self.set_field(idx, &address); + idx += address.len(); + } + LinkLocalAddress::InLine64bits(address) => { + self.set_sam_field(0b01); + self.set_field(idx, &address); + idx += address.len(); + } + LinkLocalAddress::InLine16bits(address) => { + self.set_sam_field(0b10); + self.set_field(idx, &address); + idx += address.len(); + } + LinkLocalAddress::FullyElided => { + self.set_sam_field(0b11); + } + } + } + SourceAddress::Unspecified => { + self.set_sac_field(1); + self.set_sam_field(0b00); + } + SourceAddress::Contextual(address) => { + self.set_sac_field(1); + match address { + ContextualAddress::InLine64bits(address) => { + self.set_sam_field(0b01); + self.set_field(idx, &address); + idx += address.len(); + } + ContextualAddress::InLine16bits(address) => { + self.set_sam_field(0b10); + self.set_field(idx, &address); + idx += address.len(); + } + ContextualAddress::FullyElided => { + self.set_sam_field(0b11); + } } - } else if is_eui_64 { - // We have the context from the 802.15.4 frame. - // The context contains the extended address. - // We can elide the source address. - self.set_sam_field(0b11); - } else { - // We cannot elide the source address, however we can elide 64 bits. - self.set_sam_field(0b01); - - self.set_field(idx, &src[8..]); - idx += 8; } - } else { - // We cannot elide anything. - self.set_sam_field(0b00); - self.set_field(idx, &src); - idx += 16; } idx @@ -584,77 +589,85 @@ impl + AsMut<[u8]>> Packet { /// Set the Destination Address based on the IPv6 address and the Link-Local address. /// /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. - fn set_dst_address( - &mut self, - dst_addr: ipv6::Address, - ll_dst_addr: Option, - mut idx: usize, - ) -> usize { - self.set_dac_field(0); - self.set_dam_field(0); - self.set_m_field(0); - let dst = dst_addr.octets(); - if dst_addr.is_multicast() { - self.set_m_field(1); - - if dst[1] == 0x02 && dst[2..15] == [0; 13] { - self.set_dam_field(0b11); - - self.set_field(idx, &[dst[15]]); - idx += 1; - } else if dst[2..13] == [0; 11] { - self.set_dam_field(0b10); - - self.set_field(idx, &[dst[1]]); - idx += 1; - self.set_field(idx, &dst[13..]); - idx += 3; - } else if dst[2..11] == [0; 9] { - self.set_dam_field(0b01); - - self.set_field(idx, &[dst[1]]); - idx += 1; - self.set_field(idx, &dst[11..]); - idx += 5; - } else { - self.set_dam_field(0b11); - - self.set_field(idx, &dst); - idx += 16; + fn set_dst_address(&mut self, dst_addr: DestinationAddress, mut idx: usize) -> usize { + match dst_addr { + DestinationAddress::LinkLocal(address) => { + self.set_m_field(0); + self.set_dac_field(0); + match address { + LinkLocalAddress::InLine128bits(address) => { + self.set_dam_field(0b00); + self.set_field(idx, &address); + idx += address.len(); + } + LinkLocalAddress::InLine64bits(address) => { + self.set_dam_field(0b01); + self.set_field(idx, &address); + idx += address.len(); + } + LinkLocalAddress::InLine16bits(address) => { + self.set_dam_field(0b10); + self.set_field(idx, &address); + idx += address.len(); + } + LinkLocalAddress::FullyElided => { + self.set_dam_field(0b11); + } + } } - } else if dst_addr.is_link_local() { - let is_eui_64 = ll_dst_addr - .map(|addr| { - addr.as_eui_64() - .map(|addr| addr[..] == dst[8..]) - .unwrap_or(false) - }) - .unwrap_or(false); - - if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { - let ll = [dst[14], dst[15]]; - - if ll_dst_addr == Some(LlAddress::Short(ll)) { - self.set_dam_field(0b11); - } else { - self.set_dam_field(0b10); - - self.set_field(idx, &dst[14..]); - idx += 2; + DestinationAddress::Contextual(address) => { + self.set_m_field(0); + self.set_dac_field(1); + match address { + ContextualAddress::InLine64bits(address) => { + self.set_dam_field(0b01); + self.set_field(idx, &address); + idx += address.len(); + } + ContextualAddress::InLine16bits(address) => { + self.set_dam_field(0b10); + self.set_field(idx, &address); + idx += address.len(); + } + ContextualAddress::FullyElided => { + self.set_dam_field(0b11); + } } - } else if is_eui_64 { - self.set_dam_field(0b11); - } else { - self.set_dam_field(0b01); - - self.set_field(idx, &dst[8..]); - idx += 8; } - } else { - self.set_dam_field(0b00); - - self.set_field(idx, &dst); - idx += 16; + DestinationAddress::NotSupported => unimplemented!(), + DestinationAddress::Multicast(address) => { + self.set_m_field(1); + self.set_dac_field(0); + match address { + MulticastAddress::FullInline(address) => { + self.set_dam_field(0b00); + self.set_field(idx, &address); + idx += address.len(); + } + MulticastAddress::Inline48bits(address) => { + self.set_dam_field(0b01); + self.set_field(idx, &address); + idx += address.len(); + } + MulticastAddress::Inline32bits(address) => { + self.set_dam_field(0b10); + self.set_field(idx, &address); + idx += address.len(); + } + MulticastAddress::Inline8bits(address) => { + self.set_dam_field(0b11); + self.set_field(idx, &[address]); + idx += 1; + } + } + } + DestinationAddress::ContextualMulticast(address) => { + self.set_m_field(1); + self.set_dac_field(1); + self.set_dam_field(0b00); + self.set_field(idx, &address); + idx += address.len(); + } } idx @@ -680,10 +693,9 @@ impl + AsMut<[u8]>> Packet { /// A high-level representation of a 6LoWPAN IPHC header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Repr { - pub src_addr: ipv6::Address, - pub ll_src_addr: Option, - pub dst_addr: ipv6::Address, - pub ll_dst_addr: Option, + pub src_addr: SourceAddress, + pub dst_addr: DestinationAddress, + pub context_identifier: Option, pub next_header: NextHeader, pub hop_limit: u8, // TODO(thvdveld): refactor the following fields into something else @@ -696,7 +708,7 @@ impl core::fmt::Display for Repr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, - "IPHC src={} dst={} nxt-hdr={} hop-limit={}", + "IPHC src={:?} dst={:?} nxt-hdr={} hop-limit={}", self.src_addr, self.dst_addr, self.next_header, self.hop_limit ) } @@ -718,15 +730,7 @@ impl defmt::Format for Repr { impl Repr { /// Parse a 6LoWPAN IPHC header and return a high-level representation. - /// - /// The `ll_src_addr` and `ll_dst_addr` are the link-local addresses used for resolving the - /// IPv6 packets. - pub fn parse + ?Sized>( - packet: &Packet<&T>, - ll_src_addr: Option, - ll_dst_addr: Option, - addr_context: &[AddressContext], - ) -> Result { + pub fn parse + ?Sized>(packet: &Packet<&T>) -> Result { // Ensure basic accessors will work. packet.check_len()?; @@ -735,14 +739,103 @@ impl Repr { return Err(Error); } - let src_addr = packet.src_addr()?.resolve(ll_src_addr, addr_context)?; - let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr, addr_context)?; + let src_addr = match packet.src_addr()? { + UnresolvedAddress::WithoutContext(mode) => match mode { + AddressMode::FullInline(d) => SourceAddress::LinkLocal( + LinkLocalAddress::InLine128bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine64bits(d) => SourceAddress::LinkLocal( + LinkLocalAddress::InLine64bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine16bits(d) => SourceAddress::LinkLocal( + LinkLocalAddress::InLine16bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::FullyElided => SourceAddress::LinkLocal(LinkLocalAddress::FullyElided), + AddressMode::Multicast48bits(_) => unreachable!(), + AddressMode::Multicast32bits(_) => unreachable!(), + AddressMode::Multicast8bits(_) => unreachable!(), + AddressMode::Unspecified => SourceAddress::Unspecified, + AddressMode::NotSupported => unreachable!(), + }, + UnresolvedAddress::WithContext((_, mode)) => match mode { + AddressMode::FullInline(d) => SourceAddress::LinkLocal( + LinkLocalAddress::InLine128bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine64bits(d) => SourceAddress::Contextual( + ContextualAddress::InLine64bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine16bits(d) => SourceAddress::Contextual( + ContextualAddress::InLine16bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::FullyElided => { + SourceAddress::Contextual(ContextualAddress::FullyElided) + } + AddressMode::Multicast48bits(_) => unreachable!(), + AddressMode::Multicast32bits(_) => unreachable!(), + AddressMode::Multicast8bits(_) => unreachable!(), + AddressMode::Unspecified => SourceAddress::Unspecified, + AddressMode::NotSupported => unreachable!(), + }, + UnresolvedAddress::Reserved => unreachable!(), + }; + let dst_addr = match packet.dst_addr()? { + UnresolvedAddress::WithoutContext(mode) => match mode { + AddressMode::FullInline(d) => DestinationAddress::LinkLocal( + LinkLocalAddress::InLine128bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine64bits(d) => DestinationAddress::LinkLocal( + LinkLocalAddress::InLine64bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine16bits(d) => DestinationAddress::LinkLocal( + LinkLocalAddress::InLine16bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::FullyElided => { + DestinationAddress::LinkLocal(LinkLocalAddress::FullyElided) + } + AddressMode::Multicast48bits(d) => DestinationAddress::Multicast( + MulticastAddress::Inline48bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::Multicast32bits(d) => DestinationAddress::Multicast( + MulticastAddress::Inline32bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::Multicast8bits(d) => { + DestinationAddress::Multicast(MulticastAddress::Inline8bits(d[0])) + } + AddressMode::Unspecified => unreachable!(), + AddressMode::NotSupported => DestinationAddress::NotSupported, + }, + UnresolvedAddress::WithContext((_, mode)) => match mode { + AddressMode::FullInline(d) => DestinationAddress::LinkLocal( + LinkLocalAddress::InLine128bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine64bits(d) => DestinationAddress::Contextual( + ContextualAddress::InLine64bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::InLine16bits(d) => DestinationAddress::Contextual( + ContextualAddress::InLine16bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::FullyElided => { + DestinationAddress::Contextual(ContextualAddress::FullyElided) + } + AddressMode::Multicast48bits(d) => DestinationAddress::Multicast( + MulticastAddress::Inline48bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::Multicast32bits(d) => DestinationAddress::Multicast( + MulticastAddress::Inline32bits(d.try_into().map_err(|_| Error)?), + ), + AddressMode::Multicast8bits(d) => { + DestinationAddress::Multicast(MulticastAddress::Inline8bits(d[0])) + } + AddressMode::Unspecified => unreachable!(), + AddressMode::NotSupported => DestinationAddress::NotSupported, + }, + UnresolvedAddress::Reserved => DestinationAddress::NotSupported, + }; Ok(Self { src_addr, - ll_src_addr, dst_addr, - ll_dst_addr, + context_identifier: packet.src_context_id(), next_header: packet.next_header(), hop_limit: packet.hop_limit(), ecn: packet.ecn_field(), @@ -767,74 +860,48 @@ impl Repr { _ => 1, }; + len += match self.context_identifier { + None => 0, + Some(_) => 1, + }; + // Add the length of the source address - len += if self.src_addr == ipv6::Address::UNSPECIFIED { - 0 - } else if self.src_addr.is_link_local() { - let src = self.src_addr.octets(); - let ll = [src[14], src[15]]; - - let is_eui_64 = self - .ll_src_addr - .map(|addr| { - addr.as_eui_64() - .map(|addr| addr[..] == src[8..]) - .unwrap_or(false) - }) - .unwrap_or(false); - - if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { - if self.ll_src_addr == Some(LlAddress::Short(ll)) { - 0 - } else { - 2 - } - } else if is_eui_64 { - 0 - } else { - 8 - } - } else { - 16 + len += match self.src_addr { + SourceAddress::LinkLocal(address) => match address { + LinkLocalAddress::InLine128bits(a) => a.len(), + LinkLocalAddress::InLine64bits(a) => a.len(), + LinkLocalAddress::InLine16bits(a) => a.len(), + LinkLocalAddress::FullyElided => 0, + }, + SourceAddress::Unspecified => 0, + SourceAddress::Contextual(address) => match address { + ContextualAddress::InLine64bits(a) => a.len(), + ContextualAddress::InLine16bits(a) => a.len(), + ContextualAddress::FullyElided => 0, + }, }; // Add the size of the destination header - let dst = self.dst_addr.octets(); - len += if self.dst_addr.is_multicast() { - if dst[1] == 0x02 && dst[2..15] == [0; 13] { - 1 - } else if dst[2..13] == [0; 11] { - 4 - } else if dst[2..11] == [0; 9] { - 6 - } else { - 16 - } - } else if self.dst_addr.is_link_local() { - let is_eui_64 = self - .ll_dst_addr - .map(|addr| { - addr.as_eui_64() - .map(|addr| addr[..] == dst[8..]) - .unwrap_or(false) - }) - .unwrap_or(false); - - if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { - let ll = [dst[14], dst[15]]; - - if self.ll_dst_addr == Some(LlAddress::Short(ll)) { - 0 - } else { - 2 - } - } else if is_eui_64 { - 0 - } else { - 8 - } - } else { - 16 + len += match self.dst_addr { + DestinationAddress::LinkLocal(address) => match address { + LinkLocalAddress::InLine128bits(a) => a.len(), + LinkLocalAddress::InLine64bits(a) => a.len(), + LinkLocalAddress::InLine16bits(a) => a.len(), + LinkLocalAddress::FullyElided => 0, + }, + DestinationAddress::Contextual(address) => match address { + ContextualAddress::InLine64bits(a) => a.len(), + ContextualAddress::InLine16bits(a) => a.len(), + ContextualAddress::FullyElided => 0, + }, + DestinationAddress::NotSupported => unimplemented!(), + DestinationAddress::Multicast(address) => match address { + MulticastAddress::FullInline(a) => a.len(), + MulticastAddress::Inline48bits(a) => a.len(), + MulticastAddress::Inline32bits(a) => a.len(), + MulticastAddress::Inline8bits(_) => 1, + }, + DestinationAddress::ContextualMulticast(a) => a.len(), }; len += match (self.ecn, self.dscp, self.flow_label) { @@ -859,8 +926,9 @@ impl Repr { let idx = packet.set_next_header(self.next_header, idx); let idx = packet.set_hop_limit(self.hop_limit, idx); - let idx = packet.set_src_address(self.src_addr, self.ll_src_addr, idx); - packet.set_dst_address(self.dst_addr, self.ll_dst_addr, idx); + let idx = packet.set_context_identifier(self.context_identifier, idx); + let idx = packet.set_src_address(self.src_addr, idx); + packet.set_dst_address(self.dst_addr, idx); } } diff --git a/src/wire/sixlowpan/mod.rs b/src/wire/sixlowpan/mod.rs index 59c13bfe2..74a03200b 100644 --- a/src/wire/sixlowpan/mod.rs +++ b/src/wire/sixlowpan/mod.rs @@ -168,6 +168,297 @@ impl<'a> UnresolvedAddress<'a> { } } +impl SourceAddress { + pub fn resolve( + self, + ll_address: Option, + addr_context: &[AddressContext], + context_identifier: Option, + ) -> Result { + let mut bytes = [0; 16]; + + let copy_context = |bytes: &mut [u8]| -> Result<()> { + let index = (context_identifier.unwrap_or(0) >> 4) as usize; + if index >= addr_context.len() { + return Err(Error); + } + + let context = addr_context[index]; + bytes[..ADDRESS_CONTEXT_LENGTH].copy_from_slice(&context.0); + + Ok(()) + }; + + match self { + SourceAddress::LinkLocal(address) => match address { + LinkLocalAddress::InLine128bits(addr) => Ok(ipv6::Address::from_bytes(&addr)), + LinkLocalAddress::InLine64bits(addr) => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + bytes[8..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + LinkLocalAddress::InLine16bits(addr) => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]); + bytes[14..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + LinkLocalAddress::FullyElided => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + 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), + } + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + }, + SourceAddress::Unspecified => Ok(ipv6::Address::UNSPECIFIED), + SourceAddress::Contextual(address) => match address { + ContextualAddress::InLine64bits(addr) => { + copy_context(&mut bytes[..])?; + bytes[16 - addr.len()..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + ContextualAddress::InLine16bits(addr) => { + copy_context(&mut bytes[..])?; + bytes[16 - addr.len()..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + ContextualAddress::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(&mut bytes[..])?; + + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + }, + } + } +} + +impl DestinationAddress { + pub fn resolve( + self, + ll_address: Option, + addr_context: &[AddressContext], + context_identifier: Option, + ) -> Result { + let mut bytes = [0; 16]; + + let copy_context = |bytes: &mut [u8]| -> Result<()> { + let index = (context_identifier.unwrap_or(0) & 0xF) as usize; + if index >= addr_context.len() { + return Err(Error); + } + + let context = addr_context[index]; + bytes[..ADDRESS_CONTEXT_LENGTH].copy_from_slice(&context.0); + + Ok(()) + }; + + match self { + DestinationAddress::LinkLocal(address) => match address { + LinkLocalAddress::InLine128bits(addr) => Ok(ipv6::Address::from_bytes(&addr)), + LinkLocalAddress::InLine64bits(addr) => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + bytes[8..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + LinkLocalAddress::InLine16bits(addr) => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]); + bytes[14..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + LinkLocalAddress::FullyElided => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + 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), + } + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + }, + DestinationAddress::Contextual(address) => match address { + ContextualAddress::InLine64bits(addr) => { + copy_context(&mut bytes[..])?; + bytes[16 - addr.len()..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + ContextualAddress::InLine16bits(addr) => { + copy_context(&mut bytes[..])?; + bytes[16 - addr.len()..].copy_from_slice(&addr); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + ContextualAddress::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(&mut bytes[..])?; + + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + }, + DestinationAddress::NotSupported => Err(Error), + DestinationAddress::Multicast(address) => match address { + MulticastAddress::FullInline(addr) => Ok(ipv6::Address::from_bytes(&addr)), + MulticastAddress::Inline48bits(addr) => { + bytes[0] = 0xff; + bytes[1] = addr[0]; + bytes[11..].copy_from_slice(&addr[1..][..5]); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + MulticastAddress::Inline32bits(addr) => { + bytes[0] = 0xff; + bytes[1] = addr[0]; + bytes[13..].copy_from_slice(&addr[1..][..3]); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + MulticastAddress::Inline8bits(addr) => { + bytes[0] = 0xff; + bytes[1] = 0x02; + bytes[15] = addr; + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + }, + DestinationAddress::ContextualMulticast(addr) => { + bytes[3] = ADDRESS_CONTEXT_LENGTH as _; + copy_context(&mut bytes[4..])?; + bytes[0] = 0xff; + bytes[1] = addr[0]; + bytes[2] = addr[1]; + bytes[12] = addr[2]; + bytes[13] = addr[3]; + bytes[14] = addr[4]; + bytes[15] = addr[5]; + + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum LinkLocalAddress { + /// The full address is carried in-line. + InLine128bits([u8; 16]), + /// The first 64-bits of the address are elided. The value of those bits + /// is the link-local prefix padded with zeros. The remaining 64 bits are + /// carried in-line. + InLine64bits([u8; 8]), + /// The first 112 bits of the address are elided. The value of the first + /// 64 bits is the link-local prefix padded with zeros. The following 64 bits + /// are 0000:00ff:fe00:XXXX, where XXXX are the 16 bits carried in-line. + InLine16bits([u8; 2]), + /// The address is fully elided. The first 64 bits of the address are + /// the link-local prefix padded with zeros. The remaining 64 bits are + /// computed from the encapsulating header (e.g., 802.15.4 or IPv6 address) + /// as specified in Section 3.2.2. + FullyElided, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ContextualAddress { + /// The address is derived using context information and the 64 bits carried in-line. + /// Bits covered by context information are always used. Any IID bits not covered by + /// context information are taken directly from the corresponding bits carried in-line. + /// Any remaining bits are zero. + InLine64bits([u8; 8]), + /// The address is derived using context information and the 16 bits carried in-line. + /// Bits covered by context information are always used. + /// Any IID bits not covered by context information are taken directly from their + /// corresponding bits in the 16-bit to IID mapping given by 0000:00ff:fe00:XXXX, + /// where XXXX are the 16 bits carried in-line. + /// Any remaining bits are zero. + InLine16bits([u8; 2]), + /// The address is fully elided and is derived using context information and + /// the encapsulating header (e.g., 802.15.4 or IPv6 source address). + /// Bits covered by context information are always used. + /// Any IID bits not covered by context information are computed from + /// the encapsulating header as specified in Section 3.2.2. + /// Any remaining bits are zero. + FullyElided, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SourceAddress { + LinkLocal(LinkLocalAddress), + /// The UNSPECIFIED address, :: + Unspecified, + Contextual(ContextualAddress), +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MulticastAddress { + /// The full address is carried in-line. + FullInline([u8; 16]), + /// The address takes the form ffXX::00XX:XXXX:XXXX. + Inline48bits([u8; 6]), + /// The address takes the form ffXX::00XX:XXXX. + Inline32bits([u8; 4]), + /// The address takes the form ff02::00XX. + Inline8bits(u8), +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum DestinationAddress { + LinkLocal(LinkLocalAddress), + Contextual(ContextualAddress), + NotSupported, + Multicast(MulticastAddress), + /// This format is designed to match Unicast-Prefix-based IPv6 Multicast Addresses + /// as defined in [RFC3306] and [RFC3956]. The multicast address takes the + /// form ffXX:XXLL:PPPP:PPPP:PPPP:PPPP:XXXX:XXXX. where the X are the nibbles + /// that are carried in-line, in the order in which they appear in this format. + /// P denotes nibbles used to encode the prefix itself. + /// L denotes nibbles used to encode the prefix length. + /// The prefix information P and L is taken from the specified context. + ContextualMulticast([u8; 6]), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SixlowpanPacket {