Forráskód Böngészése

add icmp tests for 6LoWPAN (also with frags)

Thibaut Vandervelden 2 éve
szülő
commit
50842508bb

+ 1 - 0
.github/workflows/test.yml

@@ -41,6 +41,7 @@ jobs:
           - std medium-ethernet proto-ipv6 socket-tcp
           - std medium-ethernet medium-ip proto-ipv4 socket-icmp socket-tcp
           - std medium-ip proto-ipv6 socket-icmp socket-tcp
+          - std medium-ieee802154 proto-sixlowpan proto-sixlowpan-fragmentation socket-udp
           - std medium-ip proto-ipv4 proto-ipv6 socket-tcp socket-udp
 
           # Test features chosen to be as aggressive as possible.

+ 13 - 9
src/iface/interface/mod.rs

@@ -1529,8 +1529,11 @@ impl<'a> InterfaceInner<'a> {
             caps: DeviceCapabilities {
                 #[cfg(feature = "medium-ethernet")]
                 medium: crate::phy::Medium::Ethernet,
-                #[cfg(not(feature = "medium-ethernet"))]
+                #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ip"))]
                 medium: crate::phy::Medium::Ip,
+                #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
+                medium: crate::phy::Medium::Ieee802154,
+
                 checksum: crate::phy::ChecksumCapabilities {
                     #[cfg(feature = "proto-ipv4")]
                     icmpv4: crate::phy::Checksum::Both,
@@ -1577,10 +1580,17 @@ impl<'a> InterfaceInner<'a> {
             #[cfg(feature = "proto-ipv4-fragmentation")]
             ipv4_id: 1,
 
-            #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+            #[cfg(feature = "medium-ethernet")]
             hardware_addr: Some(crate::wire::HardwareAddress::Ethernet(
                 crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]),
             )),
+            #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
+            hardware_addr: Some(crate::wire::HardwareAddress::Ieee802154(
+                crate::wire::Ieee802154Address::Extended([
+                    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x2, 0x2,
+                ]),
+            )),
+
             #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
             neighbor_cache: None,
 
@@ -2098,13 +2108,7 @@ impl<'a> InterfaceInner<'a> {
                 _ => unreachable!(),
             };
 
-            return self.dispatch_ieee802154(
-                dst_hardware_addr,
-                &ip_repr,
-                tx_token,
-                packet,
-                _out_packet,
-            );
+            return self.dispatch_ieee802154(dst_hardware_addr, tx_token, packet, _out_packet);
         }
 
         // Dispatch IP/Ethernet:

+ 13 - 5
src/iface/interface/sixlowpan.rs

@@ -134,7 +134,8 @@ impl<'a> InterfaceInner<'a> {
                         let udp_repr = check!(SixlowpanUdpNhcRepr::parse(
                             &udp_packet,
                             &iphc_repr.src_addr,
-                            &iphc_repr.dst_addr
+                            &iphc_repr.dst_addr,
+                            &self.checksum_caps(),
                         ));
 
                         self.process_udp(
@@ -284,7 +285,6 @@ impl<'a> InterfaceInner<'a> {
     pub(super) fn dispatch_ieee802154<Tx: TxToken>(
         &mut self,
         ll_dst_a: Ieee802154Address,
-        ip_repr: &IpRepr,
         tx_token: Tx,
         packet: IpPacket,
         _out_packet: Option<&mut OutPackets>,
@@ -300,6 +300,8 @@ impl<'a> InterfaceInner<'a> {
             },
         )?;
 
+        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)]
@@ -395,9 +397,15 @@ impl<'a> InterfaceInner<'a> {
                     ..
                 } = &mut _out_packet.unwrap().sixlowpan_out_packet;
 
-                if buffer.len() < total_size {
-                    net_debug!("6LoWPAN: Fragmentation buffer is too small");
-                    return Err(Error::Exhausted);
+                match buffer {
+                    managed::ManagedSlice::Borrowed(buffer) => {
+                        if buffer.len() < total_size {
+                            net_debug!("6LoWPAN: Fragmentation buffer is too small");
+                            return Err(Error::Exhausted);
+                        }
+                    }
+                    #[cfg(any(feature = "std", feature = "alloc"))]
+                    managed::ManagedSlice::Owned(buffer) => buffer.resize(total_size, 0),
                 }
 
                 *ll_dst_addr = ll_dst_a;

+ 416 - 21
src/iface/interface/tests.rs

@@ -19,14 +19,25 @@ fn fill_slice(s: &mut [u8], val: u8) {
     }
 }
 
-fn create<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
-    #[cfg(feature = "medium-ethernet")]
-    return create_ethernet();
-    #[cfg(not(feature = "medium-ethernet"))]
-    return create_ip();
+#[cfg(feature = "medium-ethernet")]
+const MEDIUM: Medium = Medium::Ethernet;
+#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ip"))]
+const MEDIUM: Medium = Medium::Ip;
+#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
+const MEDIUM: Medium = Medium::Ieee802154;
+
+fn create<'a>(medium: Medium) -> (Interface<'a>, SocketSet<'a>, Loopback) {
+    match medium {
+        #[cfg(feature = "medium-ethernet")]
+        Medium::Ethernet => create_ethernet(),
+        #[cfg(feature = "medium-ip")]
+        Medium::Ip => create_ip(),
+        #[cfg(feature = "medium-ieee802154")]
+        Medium::Ieee802154 => create_ieee802154(),
+    }
 }
 
-#[cfg(all(feature = "medium-ip"))]
+#[cfg(feature = "medium-ip")]
 #[allow(unused)]
 fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
     // Create a basic device
@@ -54,7 +65,7 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
     (iface, SocketSet::new(vec![]), device)
 }
 
-#[cfg(all(feature = "medium-ethernet"))]
+#[cfg(feature = "medium-ethernet")]
 fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
     // Create a basic device
     let mut device = Loopback::new(Medium::Ethernet);
@@ -89,6 +100,32 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
     (iface, SocketSet::new(vec![]), device)
 }
 
+#[cfg(feature = "medium-ieee802154")]
+fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
+    // Create a basic device
+    let mut device = Loopback::new(Medium::Ieee802154);
+    let ip_addrs = [
+        #[cfg(feature = "proto-ipv6")]
+        IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128),
+        #[cfg(feature = "proto-ipv6")]
+        IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64),
+    ];
+
+    let iface_builder = InterfaceBuilder::new()
+        .hardware_addr(Ieee802154Address::default().into())
+        .neighbor_cache(NeighborCache::new(BTreeMap::new()))
+        .ip_addrs(ip_addrs);
+
+    #[cfg(feature = "proto-sixlowpan-fragmentation")]
+    let iface_builder = iface_builder
+        .sixlowpan_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new()))
+        .sixlowpan_fragmentation_buffer(vec![]);
+
+    let iface = iface_builder.finalize(&mut device);
+
+    (iface, SocketSet::new(vec![]), device)
+}
+
 #[cfg(feature = "proto-igmp")]
 fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec<Vec<u8>> {
     let mut pkts = Vec::new();
@@ -126,7 +163,7 @@ fn test_builder_initialization_panic() {
 #[test]
 #[cfg(feature = "proto-ipv4")]
 fn test_no_icmp_no_unicast_ipv4() {
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     // Unknown Ipv4 Protocol
     //
@@ -165,7 +202,7 @@ fn test_no_icmp_no_unicast_ipv4() {
 #[test]
 #[cfg(feature = "proto-ipv6")]
 fn test_no_icmp_no_unicast_ipv6() {
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     // Unknown Ipv6 Protocol
     //
@@ -194,7 +231,7 @@ fn test_no_icmp_no_unicast_ipv6() {
 #[cfg(feature = "proto-ipv4")]
 fn test_icmp_error_no_payload() {
     static NO_BYTES: [u8; 0] = [];
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     // Unknown Ipv4 Protocol with no payload
     let repr = IpRepr::Ipv4(Ipv4Repr {
@@ -257,7 +294,7 @@ fn test_icmp_error_no_payload() {
 #[test]
 #[cfg(feature = "proto-ipv4")]
 fn test_local_subnet_broadcasts() {
-    let (mut iface, _, _device) = create();
+    let (mut iface, _, _device) = create(MEDIUM);
     iface.update_ip_addrs(|addrs| {
         addrs.iter_mut().next().map(|addr| {
             *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24));
@@ -314,7 +351,7 @@ fn test_icmp_error_port_unreachable() {
     static UDP_PAYLOAD: [u8; 12] = [
         0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21,
     ];
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     let mut udp_bytes_unicast = vec![0u8; 20];
     let mut udp_bytes_broadcast = vec![0u8; 20];
@@ -420,7 +457,7 @@ fn test_handle_udp_broadcast() {
 
     static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f];
 
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]);
     let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]);
@@ -502,7 +539,7 @@ fn test_handle_udp_broadcast() {
 fn test_handle_ipv4_broadcast() {
     use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet};
 
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     let our_ipv4_addr = iface.ipv4_address().unwrap();
     let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]);
@@ -585,7 +622,7 @@ fn test_icmp_reply_size() {
     #[cfg(feature = "proto-ipv6")]
     const MAX_PAYLOAD_LEN: usize = 1192;
 
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
     let src_addr = Ipv4Address([192, 168, 1, 1]);
@@ -941,7 +978,7 @@ fn test_arp_flush_after_update_ip() {
 fn test_icmpv4_socket() {
     use crate::wire::Icmpv4Packet;
 
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]);
     let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]);
@@ -1012,7 +1049,7 @@ fn test_icmpv4_socket() {
 #[test]
 #[cfg(feature = "proto-ipv6")]
 fn test_solicited_node_addrs() {
-    let (mut iface, _, _device) = create();
+    let (mut iface, _, _device) = create(MEDIUM);
     let mut new_addrs = vec![
         IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64),
         IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64),
@@ -1035,7 +1072,7 @@ fn test_solicited_node_addrs() {
 #[test]
 #[cfg(feature = "proto-ipv6")]
 fn test_icmpv6_nxthdr_unknown() {
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
 
@@ -1129,7 +1166,7 @@ fn test_handle_igmp() {
         Ipv4Address::new(224, 0, 0, 56),
     ];
 
-    let (mut iface, mut sockets, mut device) = create();
+    let (mut iface, mut sockets, mut device) = create(MEDIUM);
 
     // Join multicast groups
     let timestamp = Instant::now();
@@ -1199,7 +1236,7 @@ fn test_handle_igmp() {
 fn test_raw_socket_no_reply() {
     use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr};
 
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     let packets = 1;
     let rx_buffer =
@@ -1276,7 +1313,7 @@ fn test_raw_socket_with_udp_socket() {
 
     static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f];
 
-    let (mut iface, mut sockets, _device) = create();
+    let (mut iface, mut sockets, _device) = create(MEDIUM);
 
     let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]);
     let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]);
@@ -1367,3 +1404,361 @@ fn test_raw_socket_with_udp_socket() {
         Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67)))
     );
 }
+
+#[cfg(all(
+    not(feature = "medium-ethernet"),
+    feature = "proto-sixlowpan",
+    feature = "proto-sixlowpan-fragmentation"
+))]
+#[test]
+fn test_echo_request_sixlowpan_128_bytes() {
+    use crate::phy::Checksum;
+
+    let (mut iface, mut sockets, mut device) = create(Medium::Ieee802154);
+    // TODO: modify the example, such that we can also test if the checksum is correctly
+    // computed.
+    iface.inner.caps.checksum.icmpv6 = Checksum::None;
+
+    assert_eq!(iface.inner.caps.medium, Medium::Ieee802154);
+    let now = iface.inner.now();
+
+    iface.inner.neighbor_cache.as_mut().unwrap().fill(
+        Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0]).into(),
+        HardwareAddress::Ieee802154(Ieee802154Address::default()),
+        now,
+    );
+
+    let mut ieee802154_repr = Ieee802154Repr {
+        frame_type: Ieee802154FrameType::Data,
+        security_enabled: false,
+        frame_pending: false,
+        ack_request: false,
+        sequence_number: Some(5),
+        pan_id_compression: true,
+        frame_version: Ieee802154FrameVersion::Ieee802154_2003,
+        dst_pan_id: Some(Ieee802154Pan(0xbeef)),
+        dst_addr: Some(Ieee802154Address::Extended([
+            0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76,
+        ])),
+        src_pan_id: Some(Ieee802154Pan(0xbeef)),
+        src_addr: Some(Ieee802154Address::Extended([
+            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a,
+        ])),
+    };
+
+    // NOTE: this data is retrieved from tests with Contiki-NG
+
+    let request_first_part_packet = SixlowpanFragPacket::new_checked(&[
+        0xc0, 0xb0, 0x00, 0x8e, 0x6a, 0x33, 0x05, 0x25, 0x2c, 0x3a, 0x80, 0x00, 0xe0, 0x71, 0x00,
+        0x27, 0x00, 0x02, 0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
+        0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+        0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+        0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+        0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+    ])
+    .unwrap();
+
+    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();
+
+    assert_eq!(
+        request_first_part_iphc_repr.src_addr,
+        Ipv6Address([
+            0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb,
+            0x1a,
+        ]),
+    );
+    assert_eq!(
+        request_first_part_iphc_repr.dst_addr,
+        Ipv6Address([
+            0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc,
+            0x76,
+        ]),
+    );
+
+    let request_second_part = [
+        0xe0, 0xb0, 0x00, 0x8e, 0x10, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+        0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+        0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+        0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+    ];
+
+    assert_eq!(
+        iface.inner.process_sixlowpan(
+            &mut sockets,
+            &ieee802154_repr,
+            &request_first_part_packet.into_inner(),
+            Some((
+                &mut iface.fragments.sixlowpan_fragments,
+                iface.fragments.sixlowpan_fragments_cache_timeout,
+            )),
+        ),
+        None
+    );
+
+    ieee802154_repr.sequence_number = Some(6);
+
+    // data that was generated when using `ping -s 128`
+    let data = &[
+        0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+        0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+        0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+        0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+        0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+        0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+        0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+        0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+    ];
+
+    let result = iface.inner.process_sixlowpan(
+        &mut sockets,
+        &ieee802154_repr,
+        &request_second_part,
+        Some((
+            &mut iface.fragments.sixlowpan_fragments,
+            iface.fragments.sixlowpan_fragments_cache_timeout,
+        )),
+    );
+
+    assert_eq!(
+        result,
+        Some(IpPacket::Icmpv6((
+            Ipv6Repr {
+                src_addr: Ipv6Address([
+                    0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41,
+                    0xfc, 0x76,
+                ]),
+                dst_addr: Ipv6Address([
+                    0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42,
+                    0xb, 0x1a,
+                ]),
+                next_header: IpProtocol::Icmpv6,
+                payload_len: 136,
+                hop_limit: 64,
+            },
+            Icmpv6Repr::EchoReply {
+                ident: 39,
+                seq_no: 2,
+                data,
+            }
+        )))
+    );
+
+    iface.inner.neighbor_cache.as_mut().unwrap().fill(
+        IpAddress::Ipv6(Ipv6Address([
+            0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a,
+        ])),
+        HardwareAddress::Ieee802154(Ieee802154Address::default()),
+        Instant::now(),
+    );
+
+    let tx_token = device.transmit().unwrap();
+    iface
+        .inner
+        .dispatch_ieee802154(
+            Ieee802154Address::default(),
+            tx_token,
+            result.unwrap(),
+            Some(&mut iface.out_packets),
+        )
+        .unwrap();
+
+    assert_eq!(
+        device.queue[0],
+        &[
+            0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0xc0, 0xb0, 0x5, 0x4e, 0x7a, 0x11, 0x3a, 0x92, 0xfc, 0x48, 0xc2,
+            0xa4, 0x41, 0xfc, 0x76, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, 0x81, 0x0, 0x0,
+            0x0, 0x0, 0x27, 0x0, 0x2, 0xa2, 0xc2, 0x2d, 0x63, 0x0, 0x0, 0x0, 0x0, 0xd9, 0x5e, 0xc,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+            0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+            0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+            0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
+            0x44, 0x45, 0x46, 0x47,
+        ]
+    );
+
+    iface.poll(Instant::now(), &mut device, &mut sockets);
+
+    assert_eq!(
+        device.queue[1],
+        &[
+            0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0xe0, 0xb0, 0x5, 0x4e, 0xf, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
+            0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
+            0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+            0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+            0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+        ]
+    );
+}
+
+#[cfg(all(
+    not(feature = "medium-ethernet"),
+    feature = "proto-sixlowpan",
+    feature = "proto-sixlowpan-fragmentation"
+))]
+#[test]
+fn test_sixlowpan_udp_with_fragmentation() {
+    use crate::phy::Checksum;
+
+    let mut ieee802154_repr = Ieee802154Repr {
+        frame_type: Ieee802154FrameType::Data,
+        security_enabled: false,
+        frame_pending: false,
+        ack_request: false,
+        sequence_number: Some(5),
+        pan_id_compression: true,
+        frame_version: Ieee802154FrameVersion::Ieee802154_2003,
+        dst_pan_id: Some(Ieee802154Pan(0xbeef)),
+        dst_addr: Some(Ieee802154Address::Extended([
+            0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76,
+        ])),
+        src_pan_id: Some(Ieee802154Pan(0xbeef)),
+        src_addr: Some(Ieee802154Address::Extended([
+            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a,
+        ])),
+    };
+
+    let (mut iface, mut sockets, mut device) = create(Medium::Ieee802154);
+    iface.inner.caps.checksum.udp = Checksum::None;
+
+    let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]);
+    let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]);
+    let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer);
+    let udp_socket_handle = sockets.add(udp_socket);
+
+    {
+        let socket = sockets.get_mut::<udp::Socket>(udp_socket_handle);
+        assert_eq!(socket.bind(6969), Ok(()));
+        assert!(!socket.can_recv());
+        assert!(socket.can_send());
+    }
+
+    let udp_first_part = &[
+        0xc0, 0xbc, 0x00, 0x92, 0x6e, 0x33, 0x07, 0xe7, 0xdc, 0xf0, 0xd3, 0xc9, 0x1b, 0x39, 0xbf,
+        0xa0, 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, 0x6f, 0x72,
+        0x74, 0x6f, 0x72, 0x2e, 0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e,
+    ];
+
+    assert_eq!(
+        iface.inner.process_sixlowpan(
+            &mut sockets,
+            &ieee802154_repr,
+            udp_first_part,
+            Some((
+                &mut iface.fragments.sixlowpan_fragments,
+                iface.fragments.sixlowpan_fragments_cache_timeout
+            ))
+        ),
+        None
+    );
+
+    ieee802154_repr.sequence_number = Some(6);
+
+    let udp_second_part = &[
+        0xe0, 0xbc, 0x00, 0x92, 0x11, 0x64, 0x69, 0x74, 0x20, 0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73,
+        0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76, 0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76,
+        0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c, 0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20,
+        0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, 0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e,
+    ];
+
+    assert_eq!(
+        iface.inner.process_sixlowpan(
+            &mut sockets,
+            &ieee802154_repr,
+            udp_second_part,
+            Some((
+                &mut iface.fragments.sixlowpan_fragments,
+                iface.fragments.sixlowpan_fragments_cache_timeout
+            ))
+        ),
+        None
+    );
+
+    let socket = sockets.get_mut::<udp::Socket>(udp_socket_handle);
+
+    let udp_data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
+                         In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo nec.";
+    assert_eq!(
+        socket.recv(),
+        Ok((
+            &udp_data[..],
+            IpEndpoint {
+                addr: IpAddress::Ipv6(Ipv6Address([
+                    0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42,
+                    0xb, 0x1a,
+                ])),
+                port: 54217,
+            }
+        ))
+    );
+
+    let tx_token = device.transmit().unwrap();
+    iface
+        .inner
+        .dispatch_ieee802154(
+            Ieee802154Address::default(),
+            tx_token,
+            IpPacket::Udp((
+                IpRepr::Ipv6(Ipv6Repr {
+                    src_addr: Ipv6Address::default(),
+                    dst_addr: Ipv6Address::default(),
+                    next_header: IpProtocol::Udp,
+                    payload_len: udp_data.len(),
+                    hop_limit: 64,
+                }),
+                UdpRepr {
+                    src_port: 1234,
+                    dst_port: 1234,
+                },
+                udp_data,
+            )),
+            Some(&mut iface.out_packets),
+        )
+        .unwrap();
+
+    iface.poll(Instant::now(), &mut device, &mut sockets);
+
+    assert_eq!(
+        device.queue[0],
+        &[
+            0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 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,
+        ]
+    );
+
+    assert_eq!(
+        device.queue[1],
+        &[
+            0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0xe0, 0xb4, 0x5, 0x4e, 0xf, 0x6f, 0x72, 0x74, 0x6f, 0x72, 0x2e,
+            0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e, 0x64, 0x69, 0x74, 0x20,
+            0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73, 0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76,
+            0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76, 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c,
+            0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64,
+            0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e,
+        ]
+    );
+}

+ 1 - 0
src/iface/neighbor.rs

@@ -224,6 +224,7 @@ impl<'a> Cache<'a> {
     }
 }
 
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
 #[cfg(test)]
 mod test {
     use super::*;

+ 2 - 1
src/phy/loopback.rs

@@ -11,7 +11,7 @@ use crate::Result;
 /// A loopback device.
 #[derive(Debug)]
 pub struct Loopback {
-    queue: VecDeque<Vec<u8>>,
+    pub(crate) queue: VecDeque<Vec<u8>>,
     medium: Medium,
 }
 
@@ -73,6 +73,7 @@ impl phy::RxToken for RxToken {
 }
 
 #[doc(hidden)]
+#[derive(Debug)]
 pub struct TxToken<'a> {
     queue: &'a mut VecDeque<Vec<u8>>,
 }

+ 7 - 0
src/wire/ieee802154.rs

@@ -89,6 +89,13 @@ pub enum Address {
     Extended([u8; 8]),
 }
 
+#[cfg(test)]
+impl Default for Address {
+    fn default() -> Self {
+        Address::Extended([0u8; 8])
+    }
+}
+
 impl Address {
     /// The broadcast address.
     pub const BROADCAST: Address = Address::Short([0xff; 2]);

+ 1 - 0
src/wire/ndisc.rs

@@ -514,6 +514,7 @@ impl<'a> Repr<'a> {
     }
 }
 
+#[cfg(feature = "medium-ethernet")]
 #[cfg(test)]
 mod test {
     use super::*;

+ 1 - 0
src/wire/ndiscoption.rs

@@ -628,6 +628,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
     }
 }
 
+#[cfg(feature = "medium-ethernet")]
 #[cfg(test)]
 mod test {
     use super::Error;

+ 40 - 29
src/wire/sixlowpan.rs

@@ -227,7 +227,8 @@ pub mod frag {
     use byteorder::{ByteOrder, NetworkEndian};
 
     /// Key used for identifying all the link fragments that belong to the same packet.
-    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     pub struct Key {
         ll_src_addr: Ieee802154Address,
         ll_dst_addr: Ieee802154Address,
@@ -1412,11 +1413,14 @@ pub mod nhc {
     //!
     //! [RFC 6282 § 4]: https://datatracker.ietf.org/doc/html/rfc6282#section-4
     use super::{Error, NextHeader, Result, DISPATCH_EXT_HEADER, DISPATCH_UDP_HEADER};
-    use crate::wire::{
-        ip::{checksum, Address as IpAddress},
-        ipv6,
-        udp::Repr as UdpRepr,
-        IpProtocol,
+    use crate::{
+        phy::ChecksumCapabilities,
+        wire::{
+            ip::{checksum, Address as IpAddress},
+            ipv6,
+            udp::Repr as UdpRepr,
+            IpProtocol,
+        },
     };
     use byteorder::{ByteOrder, NetworkEndian};
     use ipv6::Address;
@@ -1967,6 +1971,7 @@ pub mod nhc {
             packet: &UdpNhcPacket<&'a T>,
             src_addr: &ipv6::Address,
             dst_addr: &ipv6::Address,
+            checksum_caps: &ChecksumCapabilities,
         ) -> Result<Self> {
             packet.check_len()?;
 
@@ -1974,28 +1979,27 @@ pub mod nhc {
                 return Err(Error);
             }
 
-            let payload_len = packet.payload().len();
-            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,
-                ),
-                packet.src_port(),
-                packet.dst_port(),
-                payload_len as u16 + 8,
-                checksum::data(packet.payload()),
-            ]);
-
-            if let Some(checksum) = packet.checksum() {
-                if chk_sum != checksum {
-                    return Err(Error);
+            if checksum_caps.udp.rx() {
+                let payload_len = packet.payload().len();
+                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,
+                    ),
+                    packet.src_port(),
+                    packet.dst_port(),
+                    payload_len as u16 + 8,
+                    checksum::data(packet.payload()),
+                ]);
+
+                if let Some(checksum) = packet.checksum() {
+                    if chk_sum != checksum {
+                        return Err(Error);
+                    }
                 }
-            } else {
-                net_trace!("Currently we do not support elided checksums.");
-                return Err(Error);
-            };
+            }
 
             Ok(Self(UdpRepr {
                 src_port: packet.src_port(),
@@ -2135,6 +2139,8 @@ pub mod nhc {
 
 #[cfg(test)]
 mod test {
+    use crate::phy::ChecksumCapabilities;
+
     use super::*;
 
     #[test]
@@ -2344,8 +2350,13 @@ mod test {
         let payload = udp_hdr.payload();
         assert_eq!(String::from_utf8_lossy(payload), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. \n");
 
-        let udp_repr =
-            nhc::UdpNhcRepr::parse(&udp_hdr, &iphc_repr.src_addr, &iphc_repr.dst_addr).unwrap();
+        let udp_repr = nhc::UdpNhcRepr::parse(
+            &udp_hdr,
+            &iphc_repr.src_addr,
+            &iphc_repr.dst_addr,
+            &ChecksumCapabilities::default(),
+        )
+        .unwrap();
 
         assert_eq!(udp_repr.src_port, 53855);
         assert_eq!(udp_repr.dst_port, 6969);