浏览代码

refactor(sixlowpan): add 6LoWPAN extension headers

First, I rewrote how 6LoWPAN does the compression and decompression,
should be clearer to follow how it works.

After that I added the compression and decompression of extension
headers.
Thibaut Vandervelden 1 年之前
父节点
当前提交
5785ed7af6
共有 5 个文件被更改,包括 658 次插入235 次删除
  1. 582 196
      src/iface/interface/sixlowpan.rs
  2. 8 8
      src/iface/interface/tests/sixlowpan.rs
  3. 27 5
      src/iface/ip_packet.rs
  4. 3 3
      src/wire/mod.rs
  5. 38 23
      src/wire/sixlowpan/nhc.rs

+ 582 - 196
src/iface/interface/sixlowpan.rs

@@ -1,5 +1,6 @@
 use super::*;
 
+use crate::iface::ip_packet::Ipv6Packet;
 use crate::phy::ChecksumCapabilities;
 use crate::wire::{Ipv6Packet as Ipv6PacketWire, *};
 
@@ -33,9 +34,10 @@ impl InterfaceInner {
                 }
             }
             SixlowpanPacket::IphcHeader => {
-                match self.decompress_sixlowpan(
+                match Self::sixlowpan_to_ipv6(
+                    &self.sixlowpan_address_context,
                     ieee802154_repr,
-                    payload.as_ref(),
+                    payload,
                     None,
                     &mut f.decompress_buf,
                 ) {
@@ -101,8 +103,14 @@ impl InterfaceInner {
 
             // Decompress headers+payload into the assembler.
             if let Err(e) = frag_slot.add_with(0, |buffer| {
-                self.decompress_sixlowpan(ieee802154_repr, frag.payload(), Some(total_size), buffer)
-                    .map_err(|_| AssemblerError)
+                Self::sixlowpan_to_ipv6(
+                    &self.sixlowpan_address_context,
+                    ieee802154_repr,
+                    frag.payload(),
+                    Some(total_size),
+                    buffer,
+                )
+                .map_err(|_| AssemblerError)
             }) {
                 net_debug!("fragmentation error: {:?}", e);
                 return None;
@@ -124,8 +132,8 @@ impl InterfaceInner {
         }
     }
 
-    fn decompress_sixlowpan(
-        &self,
+    fn sixlowpan_to_ipv6(
+        address_context: &[SixlowpanAddressContext],
         ieee802154_repr: &Ieee802154Repr,
         iphc_payload: &[u8],
         total_size: Option<usize>,
@@ -136,20 +144,40 @@ impl InterfaceInner {
             &iphc,
             ieee802154_repr.src_addr,
             ieee802154_repr.dst_addr,
-            &self.sixlowpan_address_context,
+            address_context,
         )?;
 
-        let mut decompressed_size = 40 + iphc.payload().len();
-
-        let next_header = match iphc_repr.next_header {
+        let first_next_header = match iphc_repr.next_header {
             SixlowpanNextHeader::Compressed => {
                 match SixlowpanNhcPacket::dispatch(iphc.payload())? {
                     SixlowpanNhcPacket::ExtHeader => {
-                        net_debug!("Extension headers are currently not supported for 6LoWPAN");
-                        IpProtocol::Unknown(0)
+                        SixlowpanExtHeaderPacket::new_checked(iphc.payload())?
+                            .extension_header_id()
+                            .into()
+                    }
+                    SixlowpanNhcPacket::UdpHeader => IpProtocol::Udp,
+                }
+            }
+            SixlowpanNextHeader::Uncompressed(proto) => proto,
+        };
+
+        let mut decompressed_size = 40 + iphc.payload().len();
+        let mut next_header = Some(iphc_repr.next_header);
+        let mut data = iphc.payload();
+
+        while let Some(nh) = next_header {
+            match nh {
+                SixlowpanNextHeader::Compressed => match SixlowpanNhcPacket::dispatch(data)? {
+                    SixlowpanNhcPacket::ExtHeader => {
+                        let ext_hdr = SixlowpanExtHeaderPacket::new_checked(data)?;
+                        let ext_repr = SixlowpanExtHeaderRepr::parse(&ext_hdr)?;
+                        decompressed_size += 2;
+                        decompressed_size -= ext_repr.buffer_len();
+                        next_header = Some(ext_repr.next_header);
+                        data = &data[ext_repr.buffer_len() + ext_repr.length as usize..];
                     }
                     SixlowpanNhcPacket::UdpHeader => {
-                        let udp_packet = SixlowpanUdpNhcPacket::new_checked(iphc.payload())?;
+                        let udp_packet = SixlowpanUdpNhcPacket::new_checked(data)?;
                         let udp_repr = SixlowpanUdpNhcRepr::parse(
                             &udp_packet,
                             &iphc_repr.src_addr,
@@ -159,12 +187,20 @@ impl InterfaceInner {
 
                         decompressed_size += 8;
                         decompressed_size -= udp_repr.header_len();
-                        IpProtocol::Udp
+                        break;
                     }
-                }
+                },
+                SixlowpanNextHeader::Uncompressed(proto) => match proto {
+                    IpProtocol::Tcp => break,
+                    IpProtocol::Udp => break,
+                    IpProtocol::Icmpv6 => break,
+                    proto => {
+                        net_debug!("unable to decompress Uncompressed({})", proto);
+                        return Err(Error);
+                    }
+                },
             }
-            SixlowpanNextHeader::Uncompressed(proto) => proto,
-        };
+        }
 
         if buffer.len() < decompressed_size {
             net_debug!("sixlowpan decompress: buffer too short");
@@ -178,27 +214,68 @@ impl InterfaceInner {
             decompressed_size
         };
 
+        let mut rest_size = total_size;
+
         let ipv6_repr = Ipv6Repr {
             src_addr: iphc_repr.src_addr,
             dst_addr: iphc_repr.dst_addr,
-            next_header,
+            next_header: first_next_header,
             payload_len: total_size - 40,
             hop_limit: iphc_repr.hop_limit,
         };
+        rest_size -= 40;
 
         // Emit the decompressed IPHC header (decompressed to an IPv6 header).
         let mut ipv6_packet = Ipv6PacketWire::new_unchecked(&mut buffer[..ipv6_repr.buffer_len()]);
         ipv6_repr.emit(&mut ipv6_packet);
-        let buffer = &mut buffer[ipv6_repr.buffer_len()..];
+        let mut buffer = &mut buffer[ipv6_repr.buffer_len()..];
 
-        match iphc_repr.next_header {
-            SixlowpanNextHeader::Compressed => {
-                match SixlowpanNhcPacket::dispatch(iphc.payload())? {
-                    SixlowpanNhcPacket::ExtHeader => todo!(),
+        let mut next_header = Some(iphc_repr.next_header);
+        let mut data = iphc.payload();
+
+        while let Some(nh) = next_header {
+            match nh {
+                SixlowpanNextHeader::Compressed => match SixlowpanNhcPacket::dispatch(data)? {
+                    SixlowpanNhcPacket::ExtHeader => {
+                        let ext_hdr = SixlowpanExtHeaderPacket::new_checked(data)?;
+                        let ext_repr = SixlowpanExtHeaderRepr::parse(&ext_hdr)?;
+
+                        let nh = match ext_repr.next_header {
+                            SixlowpanNextHeader::Compressed => {
+                                let d = &data[ext_repr.length as usize + ext_repr.buffer_len()..];
+                                match SixlowpanNhcPacket::dispatch(d)? {
+                                    SixlowpanNhcPacket::ExtHeader => {
+                                        SixlowpanExtHeaderPacket::new_checked(d)?
+                                            .extension_header_id()
+                                            .into()
+                                    }
+                                    SixlowpanNhcPacket::UdpHeader => IpProtocol::Udp,
+                                }
+                            }
+                            SixlowpanNextHeader::Uncompressed(proto) => proto,
+                        };
+                        next_header = Some(ext_repr.next_header);
+
+                        let ipv6_ext_hdr = Ipv6ExtHeaderRepr {
+                            next_header: nh,
+                            length: ext_repr.length / 8,
+                            data: &ext_hdr.payload()[..ext_repr.length as usize],
+                        };
+
+                        ipv6_ext_hdr.emit(&mut Ipv6ExtHeader::new_unchecked(
+                            &mut buffer[..ipv6_ext_hdr.header_len()],
+                        ));
+                        buffer[ipv6_ext_hdr.header_len()..][..ipv6_ext_hdr.data.len()]
+                            .copy_from_slice(ipv6_ext_hdr.data);
+
+                        buffer = &mut buffer[ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len()..];
+
+                        rest_size -= ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len();
+                        data = &data[ext_repr.buffer_len() + ext_repr.length as usize..];
+                    }
                     SixlowpanNhcPacket::UdpHeader => {
-                        // We need to uncompress the UDP packet and emit it to the
-                        // buffer.
-                        let udp_packet = SixlowpanUdpNhcPacket::new_checked(iphc.payload())?;
+                        let udp_packet = SixlowpanUdpNhcPacket::new_checked(data)?;
+                        let payload = udp_packet.payload();
                         let udp_repr = SixlowpanUdpNhcRepr::parse(
                             &udp_packet,
                             &iphc_repr.src_addr,
@@ -206,22 +283,33 @@ impl InterfaceInner {
                             &ChecksumCapabilities::ignored(),
                         )?;
 
-                        let mut udp = UdpPacket::new_unchecked(
-                            &mut buffer[..udp_repr.0.header_len() + iphc.payload().len()
-                                - udp_repr.header_len()],
-                        );
-                        udp_repr.0.emit_header(&mut udp, ipv6_repr.payload_len - 8);
+                        let mut udp = UdpPacket::new_unchecked(&mut buffer[..payload.len() + 8]);
+                        udp_repr
+                            .0
+                            .emit_header(&mut udp, rest_size - udp_repr.0.header_len());
+                        buffer[8..][..payload.len()].copy_from_slice(payload);
 
-                        buffer[8..].copy_from_slice(&iphc.payload()[udp_repr.header_len()..]);
+                        break;
                     }
-                }
-            }
-            SixlowpanNextHeader::Uncompressed(_) => {
-                // For uncompressed headers we just copy the slice.
-                let len = iphc.payload().len();
-                buffer[..len].copy_from_slice(iphc.payload());
+                },
+                SixlowpanNextHeader::Uncompressed(proto) => match proto {
+                    IpProtocol::HopByHop => unreachable!(),
+                    IpProtocol::Tcp => {
+                        buffer.copy_from_slice(data);
+                        break;
+                    }
+                    IpProtocol::Udp => {
+                        buffer.copy_from_slice(data);
+                        break;
+                    }
+                    IpProtocol::Icmpv6 => {
+                        buffer.copy_from_slice(data);
+                        break;
+                    }
+                    _ => unreachable!(),
+                },
             }
-        };
+        }
 
         Ok(decompressed_size)
     }
@@ -234,69 +322,20 @@ impl InterfaceInner {
         ieee_repr: Ieee802154Repr,
         frag: &mut Fragmenter,
     ) {
-        let ip_repr = packet.ip_repr();
-
-        let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) {
-            (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr),
-            #[allow(unreachable_patterns)]
-            _ => {
-                unreachable!()
-            }
+        let packet = match packet {
+            #[cfg(feature = "proto-ipv4")]
+            IpPacket::Ipv4(_) => unreachable!(),
+            IpPacket::Ipv6(packet) => packet,
         };
 
-        // Create the 6LoWPAN IPHC header.
-        let iphc_repr = SixlowpanIphcRepr {
-            src_addr,
-            ll_src_addr: ieee_repr.src_addr,
-            dst_addr,
-            ll_dst_addr: ieee_repr.dst_addr,
-            next_header: match &packet.payload() {
-                IpPayload::Icmpv6(..) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
-                #[cfg(feature = "socket-tcp")]
-                IpPayload::Tcp(..) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
-                #[cfg(feature = "socket-udp")]
-                IpPayload::Udp(..) => SixlowpanNextHeader::Compressed,
-                #[allow(unreachable_patterns)]
-                _ => {
-                    net_debug!("dispatch_ieee802154: dropping, unhandled protocol.");
-                    return;
-                }
-            },
-            hop_limit: ip_repr.hop_limit(),
-            ecn: None,
-            dscp: None,
-            flow_label: None,
-        };
-
-        // Now we calculate the total size of the packet.
-        // We need to know this, such that we know when to do the fragmentation.
-        let mut total_size = 0;
-        total_size += iphc_repr.buffer_len();
-        let mut _compressed_headers_len = iphc_repr.buffer_len();
-        let mut _uncompressed_headers_len = ip_repr.header_len();
-
-        match packet.payload() {
-            #[cfg(feature = "socket-udp")]
-            IpPayload::Udp(udpv6_repr, payload) => {
-                let udp_repr = SixlowpanUdpNhcRepr(*udpv6_repr);
-                _compressed_headers_len += udp_repr.header_len();
-                _uncompressed_headers_len += udpv6_repr.header_len();
-                total_size += udp_repr.header_len() + payload.len();
-            }
-            #[cfg(feature = "socket-tcp")]
-            IpPayload::Tcp(tcp_repr) => {
-                total_size += tcp_repr.buffer_len();
-            }
-            #[cfg(feature = "proto-ipv6")]
-            IpPayload::Icmpv6(icmp_repr) => {
-                total_size += icmp_repr.buffer_len();
-            }
-            #[allow(unreachable_patterns)]
-            _ => unreachable!(),
-        }
+        // First we calculate the size we are going to need. If the size is bigger than the MTU,
+        // then we use fragmentation.
+        let (total_size, compressed_size, uncompressed_size) =
+            Self::compressed_packet_size(&packet, &ieee_repr);
 
         let ieee_len = ieee_repr.buffer_len();
 
+        // TODO(thvdveld): use the MTU of the device.
         if total_size + ieee_len > 125 {
             #[cfg(feature = "proto-sixlowpan-fragmentation")]
             {
@@ -308,8 +347,8 @@ impl InterfaceInner {
 
                 // `dispatch_ieee802154_frag` requires some information about the total packet size,
                 // the link local source and destination address...
-                let pkt = frag;
 
+                let pkt = frag;
                 if pkt.buffer.len() < total_size {
                     net_debug!(
                         "dispatch_ieee802154: dropping, \
@@ -319,63 +358,23 @@ impl InterfaceInner {
                     return;
                 }
 
-                pkt.sixlowpan.ll_dst_addr = ieee_repr.dst_addr.unwrap();
-                pkt.sixlowpan.ll_src_addr = ieee_repr.src_addr.unwrap();
+                let payload_length = packet.header.payload_len;
 
-                let mut iphc_packet =
-                    SixlowpanIphcPacket::new_unchecked(&mut pkt.buffer[..iphc_repr.buffer_len()]);
-                iphc_repr.emit(&mut iphc_packet);
-
-                let b = &mut pkt.buffer[iphc_repr.buffer_len()..];
-
-                match packet.payload() {
-                    #[cfg(feature = "socket-udp")]
-                    IpPayload::Udp(udpv6_repr, payload) => {
-                        let udp_repr = SixlowpanUdpNhcRepr(*udpv6_repr);
-                        let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked(
-                            &mut b[..udp_repr.header_len() + payload.len()],
-                        );
-                        udp_repr.emit(
-                            &mut udp_packet,
-                            &iphc_repr.src_addr,
-                            &iphc_repr.dst_addr,
-                            payload.len(),
-                            |buf| buf.copy_from_slice(payload),
-                        );
-                    }
-                    #[cfg(feature = "socket-tcp")]
-                    IpPayload::Tcp(tcp_repr) => {
-                        let mut tcp_packet =
-                            TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]);
-                        tcp_repr.emit(
-                            &mut tcp_packet,
-                            &iphc_repr.src_addr.into(),
-                            &iphc_repr.dst_addr.into(),
-                            &self.caps.checksum,
-                        );
-                    }
-                    #[cfg(feature = "proto-ipv6")]
-                    IpPayload::Icmpv6(icmp_repr) => {
-                        let mut icmp_packet =
-                            Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]);
-                        icmp_repr.emit(
-                            &iphc_repr.src_addr.into(),
-                            &iphc_repr.dst_addr.into(),
-                            &mut icmp_packet,
-                            &self.caps.checksum,
-                        );
-                    }
-                    #[allow(unreachable_patterns)]
-                    _ => unreachable!(),
-                }
+                Self::ipv6_to_sixlowpan(
+                    &self.checksum_caps(),
+                    packet,
+                    &ieee_repr,
+                    &mut pkt.buffer[..],
+                );
 
+                pkt.sixlowpan.ll_dst_addr = ieee_repr.dst_addr.unwrap();
+                pkt.sixlowpan.ll_src_addr = ieee_repr.src_addr.unwrap();
                 pkt.packet_len = total_size;
 
                 // The datagram size that we need to set in the first fragment header is equal to the
                 // IPv6 payload length + 40.
-                pkt.sixlowpan.datagram_size = (packet.ip_repr().payload_len() + 40) as u16;
+                pkt.sixlowpan.datagram_size = (payload_length + 40) as u16;
 
-                // We generate a random tag.
                 let tag = self.get_sixlowpan_fragment_tag();
                 // We save the tag for the other fragments that will be created when calling `poll`
                 // multiple times.
@@ -398,15 +397,15 @@ impl InterfaceInner {
                 //
                 // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3
 
-                let header_diff = _uncompressed_headers_len - _compressed_headers_len;
+                let header_diff = uncompressed_size - compressed_size;
                 let frag1_size =
-                    (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff);
+                    (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - header_diff;
 
                 pkt.sixlowpan.fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8;
-
                 pkt.sent_bytes = frag1_size;
                 pkt.sixlowpan.datagram_offset = frag1_size + header_diff;
 
+                tx_token.set_meta(meta);
                 tx_token.consume(ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| {
                     // Add the IEEE header.
                     let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
@@ -439,55 +438,241 @@ impl InterfaceInner {
                 ieee_repr.emit(&mut ieee_packet);
                 tx_buf = &mut tx_buf[ieee_len..];
 
-                let mut iphc_packet =
-                    SixlowpanIphcPacket::new_unchecked(&mut tx_buf[..iphc_repr.buffer_len()]);
-                iphc_repr.emit(&mut iphc_packet);
-                tx_buf = &mut tx_buf[iphc_repr.buffer_len()..];
-
-                match packet.payload() {
-                    #[cfg(feature = "socket-udp")]
-                    IpPayload::Udp(udpv6_repr, payload) => {
-                        let udp_repr = SixlowpanUdpNhcRepr(*udpv6_repr);
-                        let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked(
-                            &mut tx_buf[..udp_repr.header_len() + payload.len()],
-                        );
-                        udp_repr.emit(
-                            &mut udp_packet,
-                            &iphc_repr.src_addr,
-                            &iphc_repr.dst_addr,
-                            payload.len(),
-                            |buf| buf.copy_from_slice(payload),
-                        );
-                    }
-                    #[cfg(feature = "socket-tcp")]
-                    IpPayload::Tcp(tcp_repr) => {
-                        let mut tcp_packet =
-                            TcpPacket::new_unchecked(&mut tx_buf[..tcp_repr.buffer_len()]);
-                        tcp_repr.emit(
-                            &mut tcp_packet,
-                            &iphc_repr.src_addr.into(),
-                            &iphc_repr.dst_addr.into(),
-                            &self.caps.checksum,
-                        );
-                    }
-                    #[cfg(feature = "proto-ipv6")]
-                    IpPayload::Icmpv6(icmp_repr) => {
-                        let mut icmp_packet =
-                            Icmpv6Packet::new_unchecked(&mut tx_buf[..icmp_repr.buffer_len()]);
-                        icmp_repr.emit(
-                            &iphc_repr.src_addr.into(),
-                            &iphc_repr.dst_addr.into(),
-                            &mut icmp_packet,
-                            &self.caps.checksum,
-                        );
-                    }
-                    #[allow(unreachable_patterns)]
-                    _ => unreachable!(),
-                }
+                Self::ipv6_to_sixlowpan(&self.checksum_caps(), packet, &ieee_repr, tx_buf);
             });
         }
     }
 
+    fn ipv6_to_sixlowpan(
+        checksum_caps: &ChecksumCapabilities,
+        mut packet: Ipv6Packet,
+        ieee_repr: &Ieee802154Repr,
+        mut buffer: &mut [u8],
+    ) {
+        let last_header = packet.payload.as_sixlowpan_next_header();
+        let next_header = last_header;
+
+        #[cfg(feature = "proto-ipv6-hbh")]
+        let next_header = if packet.hop_by_hop.is_some() {
+            SixlowpanNextHeader::Compressed
+        } else {
+            next_header
+        };
+
+        #[cfg(feature = "proto-ipv6-routing")]
+        let next_header = if packet.routing.is_some() {
+            SixlowpanNextHeader::Compressed
+        } else {
+            next_header
+        };
+
+        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,
+            next_header,
+            hop_limit: packet.header.hop_limit,
+            ecn: None,
+            dscp: None,
+            flow_label: None,
+        };
+
+        iphc_repr.emit(&mut SixlowpanIphcPacket::new_unchecked(
+            &mut buffer[..iphc_repr.buffer_len()],
+        ));
+        buffer = &mut buffer[iphc_repr.buffer_len()..];
+
+        // Emit the Hop-by-Hop header
+        #[cfg(feature = "proto-ipv6-hbh")]
+        if let Some(hbh) = packet.hop_by_hop {
+            #[allow(unused)]
+            let next_header = last_header;
+
+            #[cfg(feature = "proto-ipv6-routing")]
+            let next_header = if packet.routing.is_some() {
+                SixlowpanNextHeader::Compressed
+            } else {
+                last_header
+            };
+
+            let ext_hdr = SixlowpanExtHeaderRepr {
+                ext_header_id: SixlowpanExtHeaderId::HopByHopHeader,
+                next_header,
+                length: hbh.options.iter().map(|o| o.buffer_len()).sum::<usize>() as u8,
+            };
+            ext_hdr.emit(&mut SixlowpanExtHeaderPacket::new_unchecked(
+                &mut buffer[..ext_hdr.buffer_len()],
+            ));
+            buffer = &mut buffer[ext_hdr.buffer_len()..];
+
+            for opt in &hbh.options {
+                opt.emit(&mut Ipv6Option::new_unchecked(
+                    &mut buffer[..opt.buffer_len()],
+                ));
+
+                buffer = &mut buffer[opt.buffer_len()..];
+            }
+        }
+
+        // Emit the Routing header
+        #[cfg(feature = "proto-ipv6-routing")]
+        if let Some(routing) = &packet.routing {
+            let ext_hdr = SixlowpanExtHeaderRepr {
+                ext_header_id: SixlowpanExtHeaderId::RoutingHeader,
+                next_header,
+                length: routing.buffer_len() as u8,
+            };
+            ext_hdr.emit(&mut SixlowpanExtHeaderPacket::new_unchecked(
+                &mut buffer[..ext_hdr.buffer_len()],
+            ));
+            buffer = &mut buffer[ext_hdr.buffer_len()..];
+
+            routing.emit(&mut Ipv6RoutingHeader::new_unchecked(
+                &mut buffer[..routing.buffer_len()],
+            ));
+            buffer = &mut buffer[routing.buffer_len()..];
+        }
+
+        match &mut packet.payload {
+            IpPayload::Icmpv6(icmp_repr) => {
+                icmp_repr.emit(
+                    &packet.header.src_addr.into(),
+                    &packet.header.dst_addr.into(),
+                    &mut Icmpv6Packet::new_unchecked(&mut buffer[..icmp_repr.buffer_len()]),
+                    checksum_caps,
+                );
+            }
+            #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
+            IpPayload::Udp(udp_repr, payload) => {
+                let udp_repr = SixlowpanUdpNhcRepr(*udp_repr);
+                udp_repr.emit(
+                    &mut SixlowpanUdpNhcPacket::new_unchecked(
+                        &mut buffer[..udp_repr.header_len() + payload.len()],
+                    ),
+                    &iphc_repr.src_addr,
+                    &iphc_repr.dst_addr,
+                    payload.len(),
+                    |buf| buf.copy_from_slice(payload),
+                    checksum_caps,
+                );
+            }
+            #[cfg(feature = "socket-tcp")]
+            IpPayload::Tcp(tcp_repr) => {
+                tcp_repr.emit(
+                    &mut TcpPacket::new_unchecked(&mut buffer[..tcp_repr.buffer_len()]),
+                    &packet.header.src_addr.into(),
+                    &packet.header.dst_addr.into(),
+                    checksum_caps,
+                );
+            }
+            #[cfg(feature = "socket-raw")]
+            IpPayload::Raw(_raw) => todo!(),
+
+            #[allow(unreachable_patterns)]
+            _ => unreachable!(),
+        }
+    }
+
+    /// Calculates three sizes:
+    ///  - total size: the size of a compressed IPv6 packet
+    ///  - compressed header size: the size of the compressed headers
+    ///  - uncompressed header size: the size of the headers that are not compressed
+    ///  They are returned as a tuple in the same order.
+    fn compressed_packet_size(
+        packet: &Ipv6Packet,
+        ieee_repr: &Ieee802154Repr,
+    ) -> (usize, usize, usize) {
+        let last_header = packet.payload.as_sixlowpan_next_header();
+        let next_header = last_header;
+
+        #[cfg(feature = "proto-ipv6-hbh")]
+        let next_header = if packet.hop_by_hop.is_some() {
+            SixlowpanNextHeader::Compressed
+        } else {
+            next_header
+        };
+
+        #[cfg(feature = "proto-ipv6-routing")]
+        let next_header = if packet.routing.is_some() {
+            SixlowpanNextHeader::Compressed
+        } else {
+            next_header
+        };
+
+        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,
+            next_header,
+            hop_limit: packet.header.hop_limit,
+            ecn: None,
+            dscp: None,
+            flow_label: None,
+        };
+
+        let mut total_size = iphc.buffer_len();
+        let mut compressed_hdr_size = iphc.buffer_len();
+        let mut uncompressed_hdr_size = packet.header.buffer_len();
+
+        // Add the hop-by-hop to the sizes.
+        #[cfg(feature = "proto-ipv6-hbh")]
+        if let Some(hbh) = &packet.hop_by_hop {
+            #[allow(unused)]
+            let next_header = last_header;
+
+            #[cfg(feature = "proto-ipv6-routing")]
+            let next_header = if packet.routing.is_some() {
+                SixlowpanNextHeader::Compressed
+            } else {
+                last_header
+            };
+
+            let options_size = hbh.options.iter().map(|o| o.buffer_len()).sum::<usize>();
+
+            let ext_hdr = SixlowpanExtHeaderRepr {
+                ext_header_id: SixlowpanExtHeaderId::HopByHopHeader,
+                next_header,
+                length: hbh.buffer_len() as u8 + options_size as u8,
+            };
+
+            total_size += ext_hdr.buffer_len() + options_size;
+            compressed_hdr_size += ext_hdr.buffer_len() + options_size;
+            uncompressed_hdr_size += hbh.buffer_len() + options_size;
+        }
+
+        // Add the routing header to the sizes.
+        #[cfg(feature = "proto-ipv6-routing")]
+        if let Some(routing) = &packet.routing {
+            let ext_hdr = SixlowpanExtHeaderRepr {
+                ext_header_id: SixlowpanExtHeaderId::RoutingHeader,
+                next_header,
+                length: routing.buffer_len() as u8,
+            };
+            total_size += ext_hdr.buffer_len() + routing.buffer_len();
+            compressed_hdr_size += ext_hdr.buffer_len() + routing.buffer_len();
+            uncompressed_hdr_size += routing.buffer_len();
+        }
+
+        match packet.payload {
+            #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
+            IpPayload::Udp(udp_hdr, payload) => {
+                uncompressed_hdr_size += udp_hdr.header_len();
+
+                let udp_hdr = SixlowpanUdpNhcRepr(udp_hdr);
+                compressed_hdr_size += udp_hdr.header_len();
+
+                total_size += udp_hdr.header_len() + payload.len();
+            }
+            _ => {
+                total_size += packet.header.payload_len;
+            }
+        }
+
+        (total_size, compressed_hdr_size, uncompressed_hdr_size)
+    }
+
     #[cfg(feature = "proto-sixlowpan-fragmentation")]
     pub(super) fn dispatch_sixlowpan_frag<Tx: TxToken>(
         &mut self,
@@ -526,3 +711,204 @@ impl InterfaceInner {
         );
     }
 }
+
+#[cfg(test)]
+#[cfg(all(feature = "proto-rpl", feature = "proto-ipv6-hbh"))]
+mod tests {
+    use super::*;
+
+    static SIXLOWPAN_COMPRESSED_RPL_DAO: [u8; 99] = [
+        0x61, 0xdc, 0x45, 0xcd, 0xab, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00,
+        0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x7e, 0xf7, 0x00, 0xe0, 0x3a, 0x06, 0x63, 0x04, 0x00,
+        0x1e, 0x08, 0x00, 0x9b, 0x02, 0x3e, 0x63, 0x1e, 0x40, 0x00, 0xf1, 0xfd, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x05, 0x12, 0x00,
+        0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03,
+        0x00, 0x03, 0x06, 0x14, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+    ];
+
+    static SIXLOWPAN_UNCOMPRESSED_RPL_DAO: [u8; 114] = [
+        0x60, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x40, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x3a, 0x00, 0x63, 0x04, 0x00,
+        0x1e, 0x08, 0x00, 0x9b, 0x02, 0x3e, 0x63, 0x1e, 0x40, 0x00, 0xf1, 0xfd, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x05, 0x12, 0x00,
+        0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03,
+        0x00, 0x03, 0x06, 0x14, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+    ];
+
+    #[test]
+    fn test_sixlowpan_decompress_hop_by_hop_with_icmpv6() {
+        let address_context = [SixlowpanAddressContext([
+            0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ])];
+
+        let ieee_frame = Ieee802154Frame::new_checked(&SIXLOWPAN_COMPRESSED_RPL_DAO).unwrap();
+        let ieee_repr = Ieee802154Repr::parse(&ieee_frame).unwrap();
+
+        let mut buffer = [0u8; 256];
+        let len = InterfaceInner::sixlowpan_to_ipv6(
+            &address_context,
+            &ieee_repr,
+            ieee_frame.payload().unwrap(),
+            None,
+            &mut buffer[..],
+        )
+        .unwrap();
+
+        assert_eq!(&buffer[..len], &SIXLOWPAN_UNCOMPRESSED_RPL_DAO);
+    }
+
+    #[test]
+    fn test_sixlowpan_compress_hop_by_hop_with_icmpv6() {
+        let ieee_repr = Ieee802154Repr {
+            frame_type: Ieee802154FrameType::Data,
+            security_enabled: false,
+            frame_pending: false,
+            ack_request: true,
+            sequence_number: Some(69),
+            pan_id_compression: true,
+            frame_version: Ieee802154FrameVersion::Ieee802154_2006,
+            dst_pan_id: Some(Ieee802154Pan(43981)),
+            dst_addr: Some(Ieee802154Address::Extended([0, 1, 0, 1, 0, 1, 0, 1])),
+            src_pan_id: None,
+            src_addr: Some(Ieee802154Address::Extended([0, 3, 0, 3, 0, 3, 0, 3])),
+        };
+
+        let mut ip_packet = Ipv6Packet {
+            header: Ipv6Repr {
+                src_addr: Ipv6Address::from_bytes(&[
+                    253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3,
+                ]),
+                dst_addr: Ipv6Address::from_bytes(&[
+                    253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
+                ]),
+                next_header: IpProtocol::Icmpv6,
+                payload_len: 66,
+                hop_limit: 64,
+            },
+            #[cfg(feature = "proto-ipv6-hbh")]
+            hop_by_hop: None,
+            #[cfg(feature = "proto-ipv6-fragmentation")]
+            fragment: None,
+            #[cfg(feature = "proto-ipv6-routing")]
+            routing: None,
+            payload: IpPayload::Icmpv6(Icmpv6Repr::Rpl(RplRepr::DestinationAdvertisementObject {
+                rpl_instance_id: RplInstanceId::Global(30),
+                expect_ack: false,
+                sequence: 241,
+                dodag_id: Some(Ipv6Address::from_bytes(&[
+                    253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
+                ])),
+                options: &[],
+            })),
+        };
+
+        let (total_size, _, _) = InterfaceInner::compressed_packet_size(&mut ip_packet, &ieee_repr);
+        let mut buffer = vec![0u8; total_size];
+
+        InterfaceInner::ipv6_to_sixlowpan(
+            &ChecksumCapabilities::default(),
+            ip_packet,
+            &ieee_repr,
+            &mut buffer[..total_size],
+        );
+
+        let result = [
+            0x7e, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0,
+            0x3, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+            0xe0, 0x3a, 0x6, 0x63, 0x4, 0x0, 0x1e, 0x3, 0x0, 0x9b, 0x2, 0x3e, 0x63, 0x1e, 0x40,
+            0x0, 0xf1, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
+            0x1, 0x5, 0x12, 0x0, 0x80, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3,
+            0x0, 0x3, 0x0, 0x3, 0x6, 0x14, 0x0, 0x0, 0x0, 0x1e, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+        ];
+
+        assert_eq!(&result, &result);
+    }
+
+    #[test]
+    fn test_sixlowpan_compress_hop_by_hop_with_udp() {
+        let ieee_repr = Ieee802154Repr {
+            frame_type: Ieee802154FrameType::Data,
+            security_enabled: false,
+            frame_pending: false,
+            ack_request: true,
+            sequence_number: Some(69),
+            pan_id_compression: true,
+            frame_version: Ieee802154FrameVersion::Ieee802154_2006,
+            dst_pan_id: Some(Ieee802154Pan(43981)),
+            dst_addr: Some(Ieee802154Address::Extended([0, 1, 0, 1, 0, 1, 0, 1])),
+            src_pan_id: None,
+            src_addr: Some(Ieee802154Address::Extended([0, 3, 0, 3, 0, 3, 0, 3])),
+        };
+
+        let addr = Ipv6Address::from_bytes(&[253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3]);
+        let parent_address =
+            Ipv6Address::from_bytes(&[253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1]);
+
+        let mut hbh_options = heapless::Vec::new();
+        hbh_options
+            .push(Ipv6OptionRepr::Rpl(RplHopByHopRepr {
+                down: false,
+                rank_error: false,
+                forwarding_error: false,
+                instance_id: RplInstanceId::from(0x1e),
+                sender_rank: 0x300,
+            }))
+            .unwrap();
+
+        let mut ip_packet = Ipv6Packet {
+            header: Ipv6Repr {
+                src_addr: addr,
+                dst_addr: parent_address,
+                next_header: IpProtocol::Icmpv6,
+                payload_len: 66,
+                hop_limit: 64,
+            },
+            #[cfg(feature = "proto-ipv6-hbh")]
+            hop_by_hop: Some(Ipv6HopByHopRepr {
+                options: hbh_options,
+            }),
+            #[cfg(feature = "proto-ipv6-fragmentation")]
+            fragment: None,
+            #[cfg(feature = "proto-ipv6-routing")]
+            routing: None,
+            payload: IpPayload::Icmpv6(Icmpv6Repr::Rpl(RplRepr::DestinationAdvertisementObject {
+                rpl_instance_id: RplInstanceId::Global(30),
+                expect_ack: false,
+                sequence: 241,
+                dodag_id: Some(Ipv6Address::from_bytes(&[
+                    253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
+                ])),
+                options: &[
+                    5, 18, 0, 128, 253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3, 6, 20, 0, 0,
+                    0, 30, 253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
+                ],
+            })),
+        };
+
+        let (total_size, _, _) = InterfaceInner::compressed_packet_size(&mut ip_packet, &ieee_repr);
+        let mut buffer = vec![0u8; total_size];
+
+        InterfaceInner::ipv6_to_sixlowpan(
+            &ChecksumCapabilities::default(),
+            ip_packet,
+            &ieee_repr,
+            &mut buffer[..total_size],
+        );
+
+        let result = [
+            0x7e, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0,
+            0x3, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+            0xe0, 0x3a, 0x6, 0x63, 0x4, 0x0, 0x1e, 0x3, 0x0, 0x9b, 0x2, 0x3e, 0x63, 0x1e, 0x40,
+            0x0, 0xf1, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
+            0x1, 0x5, 0x12, 0x0, 0x80, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3,
+            0x0, 0x3, 0x0, 0x3, 0x6, 0x14, 0x0, 0x0, 0x0, 0x1e, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+        ];
+
+        assert_eq!(&buffer[..total_size], &result);
+    }
+}

+ 8 - 8
src/iface/interface/tests/sixlowpan.rs

@@ -388,14 +388,14 @@ In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo n
         &[
             0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
             0x2, 0x2, 0x2, 0x2, 0xc0, 0xb4, 0x5, 0x4e, 0x7e, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x4, 0xd2, 0x4, 0xd2, 0xf6,
-            0x4d, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64,
-            0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c,
-            0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61,
-            0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74,
-            0x2e, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75,
-            0x73, 0x20, 0x74,
-        ]
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x4, 0xd2, 0x4, 0xd2, 0x0, 0x0,
+            0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f,
+            0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20,
+            0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64,
+            0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2e,
+            0x20, 0x49, 0x6e, 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75, 0x73,
+            0x20, 0x74,
+        ],
     );
 
     assert_eq!(

+ 27 - 5
src/iface/ip_packet.rs

@@ -153,14 +153,14 @@ pub(crate) struct Ipv4Packet<'p> {
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[cfg(feature = "proto-ipv6")]
 pub(crate) struct Ipv6Packet<'p> {
-    header: Ipv6Repr,
+    pub(crate) header: Ipv6Repr,
     #[cfg(feature = "proto-ipv6-hbh")]
-    hop_by_hop: Option<Ipv6HopByHopRepr<'p>>,
+    pub(crate) hop_by_hop: Option<Ipv6HopByHopRepr<'p>>,
     #[cfg(feature = "proto-ipv6-fragmentation")]
-    fragment: Option<Ipv6FragmentRepr>,
+    pub(crate) fragment: Option<Ipv6FragmentRepr>,
     #[cfg(feature = "proto-ipv6-routing")]
-    routing: Option<Ipv6RoutingRepr<'p>>,
-    payload: IpPayload<'p>,
+    pub(crate) routing: Option<Ipv6RoutingRepr<'p>>,
+    pub(crate) payload: IpPayload<'p>,
 }
 
 #[derive(Debug, PartialEq)]
@@ -182,6 +182,28 @@ pub(crate) enum IpPayload<'p> {
     Dhcpv4(UdpRepr, DhcpRepr<'p>),
 }
 
+impl<'p> IpPayload<'p> {
+    #[cfg(feature = "proto-sixlowpan")]
+    pub(crate) fn as_sixlowpan_next_header(&self) -> SixlowpanNextHeader {
+        match self {
+            #[cfg(feature = "proto-ipv4")]
+            Self::Icmpv4(_) => unreachable!(),
+            #[cfg(feature = "socket-dhcpv4")]
+            Self::Dhcpv4(..) => unreachable!(),
+            #[cfg(feature = "proto-ipv6")]
+            Self::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
+            #[cfg(feature = "proto-igmp")]
+            Self::Igmp(_) => unreachable!(),
+            #[cfg(feature = "socket-tcp")]
+            Self::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
+            #[cfg(feature = "socket-udp")]
+            Self::Udp(..) => SixlowpanNextHeader::Compressed,
+            #[cfg(feature = "socket-raw")]
+            Self::Raw(_) => todo!(),
+        }
+    }
+}
+
 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
 pub(crate) fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
     // Send back as much of the original payload as will fit within

+ 3 - 3
src/wire/mod.rs

@@ -158,9 +158,9 @@ pub use self::sixlowpan::{
     frag::{Key as SixlowpanFragKey, Packet as SixlowpanFragPacket, Repr as SixlowpanFragRepr},
     iphc::{Packet as SixlowpanIphcPacket, Repr as SixlowpanIphcRepr},
     nhc::{
-        ExtHeaderPacket as SixlowpanExtHeaderPacket, ExtHeaderRepr as SixlowpanExtHeaderRepr,
-        NhcPacket as SixlowpanNhcPacket, UdpNhcPacket as SixlowpanUdpNhcPacket,
-        UdpNhcRepr as SixlowpanUdpNhcRepr,
+        ExtHeaderId as SixlowpanExtHeaderId, ExtHeaderPacket as SixlowpanExtHeaderPacket,
+        ExtHeaderRepr as SixlowpanExtHeaderRepr, NhcPacket as SixlowpanNhcPacket,
+        UdpNhcPacket as SixlowpanUdpNhcPacket, UdpNhcRepr as SixlowpanUdpNhcRepr,
     },
     AddressContext as SixlowpanAddressContext, NextHeader as SixlowpanNextHeader, SixlowpanPacket,
 };

+ 38 - 23
src/wire/sixlowpan/nhc.rs

@@ -179,6 +179,11 @@ impl<T: AsRef<[u8]>> ExtHeaderPacket<T> {
         }
     }
 
+    /// Return the length field.
+    pub fn length(&self) -> u8 {
+        self.buffer.as_ref()[1 + self.next_header_size()]
+    }
+
     /// Parse the next header field.
     pub fn next_header(&self) -> NextHeader {
         if self.nh_field() == 1 {
@@ -208,7 +213,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> ExtHeaderPacket<&'a T> {
     /// Return a pointer to the payload.
     pub fn payload(&self) -> &'a [u8] {
         let start = 2 + self.next_header_size();
-        &self.buffer.as_ref()[start..]
+        let len = self.length() as usize;
+        &self.buffer.as_ref()[start..][..len]
     }
 }
 
@@ -216,7 +222,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> ExtHeaderPacket<T> {
     /// Return a mutable pointer to the payload.
     pub fn payload_mut(&mut self) -> &mut [u8] {
         let start = 2 + self.next_header_size();
-        &mut self.buffer.as_mut()[start..]
+        let len = self.length() as usize;
+        &mut self.buffer.as_mut()[start..][..len]
     }
 
     /// Set the dispatch field to `0b1110`.
@@ -270,9 +277,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> ExtHeaderPacket<T> {
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct ExtHeaderRepr {
-    ext_header_id: ExtHeaderId,
-    next_header: NextHeader,
-    length: u8,
+    pub ext_header_id: ExtHeaderId,
+    pub next_header: NextHeader,
+    pub length: u8,
 }
 
 impl ExtHeaderRepr {
@@ -288,7 +295,7 @@ impl ExtHeaderRepr {
         Ok(Self {
             ext_header_id: packet.extension_header_id(),
             next_header: packet.next_header(),
-            length: packet.payload().len() as u8,
+            length: packet.length(),
         })
     }
 
@@ -752,25 +759,28 @@ impl<'a> UdpNhcRepr {
         dst_addr: &Address,
         payload_len: usize,
         emit_payload: impl FnOnce(&mut [u8]),
+        checksum_caps: &ChecksumCapabilities,
     ) {
         packet.set_dispatch_field();
         packet.set_ports(self.src_port, self.dst_port);
         emit_payload(packet.payload_mut());
 
-        let chk_sum = !checksum::combine(&[
-            checksum::pseudo_header(
-                &IpAddress::Ipv6(*src_addr),
-                &IpAddress::Ipv6(*dst_addr),
-                crate::wire::ip::Protocol::Udp,
-                payload_len as u32 + 8,
-            ),
-            self.src_port,
-            self.dst_port,
-            payload_len as u16 + 8,
-            checksum::data(packet.payload_mut()),
-        ]);
-
-        packet.set_checksum(chk_sum);
+        if checksum_caps.udp.tx() {
+            let chk_sum = !checksum::combine(&[
+                checksum::pseudo_header(
+                    &IpAddress::Ipv6(*src_addr),
+                    &IpAddress::Ipv6(*dst_addr),
+                    crate::wire::ip::Protocol::Udp,
+                    payload_len as u32 + 8,
+                ),
+                self.src_port,
+                self.dst_port,
+                payload_len as u16 + 8,
+                checksum::data(packet.payload_mut()),
+            ]);
+
+            packet.set_checksum(chk_sum);
+        }
     }
 }
 
@@ -848,9 +858,14 @@ mod test {
         let len = udp.header_len() + payload.len();
         let mut buffer = [0u8; 127];
         let mut packet = UdpNhcPacket::new_unchecked(&mut buffer[..len]);
-        udp.emit(&mut packet, &src_addr, &dst_addr, payload.len(), |buf| {
-            buf.copy_from_slice(&payload[..])
-        });
+        udp.emit(
+            &mut packet,
+            &src_addr,
+            &dst_addr,
+            payload.len(),
+            |buf| buf.copy_from_slice(&payload[..]),
+            &ChecksumCapabilities::default(),
+        );
 
         assert_eq!(packet.dispatch_field(), DISPATCH_UDP_HEADER);
         assert_eq!(packet.src_port(), 0xf0b1);