Browse Source

sixlowpan: add fragmentation logic in interface

Thibaut Vandervelden 3 years ago
parent
commit
adf56a1701

+ 74 - 11
examples/sixlowpan.rs

@@ -47,11 +47,13 @@ use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
 use std::str;
 
-use smoltcp::iface::{InterfaceBuilder, NeighborCache};
+use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache};
 use smoltcp::phy::{wait as phy_wait, Medium, RawSocket};
 use smoltcp::socket::udp;
 use smoltcp::time::Instant;
 use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr};
+use smoltcp::socket::tcp;
+use smoltcp::storage::RingBuffer;
 
 fn main() {
     utils::setup_logging("");
@@ -72,6 +74,10 @@ fn main() {
     let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 128]);
     let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer);
 
+    let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
+    let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
+    let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer);
+
     let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([
         0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
     ]);
@@ -80,22 +86,44 @@ fn main() {
         64,
     )];
 
+    let cache = FragmentsCache::new(vec![], BTreeMap::new());
+
+    let buffer: Vec<(usize, managed::ManagedSlice<'_, u8>)> = (0..12)
+        .into_iter()
+        .map(|_| (0_usize, managed::ManagedSlice::from(vec![0; 127])))
+        .collect();
+
+    let out_fragments_cache = RingBuffer::new(buffer);
+
     let mut builder = InterfaceBuilder::new(device, vec![])
         .ip_addrs(ip_addrs)
         .pan_id(Ieee802154Pan(0xbeef));
     builder = builder
         .hardware_addr(ieee802154_addr.into())
-        .neighbor_cache(neighbor_cache);
+        .neighbor_cache(neighbor_cache)
+        .sixlowpan_fragments_cache(cache)
+        .out_fragments_cache(out_fragments_cache);
     let mut iface = builder.finalize();
 
     let udp_handle = iface.add_socket(udp_socket);
+    let tcp_handle = iface.add_socket(tcp_socket);
+
+    let socket = iface.get_socket::<tcp::Socket>(tcp_handle);
+    socket.listen(50000).unwrap();
+
+    let mut tcp_active = false;
 
     loop {
         let timestamp = Instant::now();
-        match iface.poll(timestamp) {
-            Ok(_) => {}
-            Err(e) => {
-                debug!("poll error: {}", e);
+
+        let mut poll = true;
+        while poll {
+            match iface.poll(timestamp) {
+                Ok(r) => poll = r,
+                Err(e) => {
+                    debug!("poll error: {}", e);
+                    break;
+                }
             }
         }
 
@@ -105,6 +133,7 @@ fn main() {
             socket.bind(6969).unwrap()
         }
 
+        let mut buffer = vec![0; 1500];
         let client = match socket.recv() {
             Ok((data, endpoint)) => {
                 debug!(
@@ -112,17 +141,51 @@ fn main() {
                     str::from_utf8(data).unwrap(),
                     endpoint
                 );
-                Some(endpoint)
+                buffer[..data.len()].copy_from_slice(data);
+                Some((data.len(), endpoint))
             }
             Err(_) => None,
         };
-        if let Some(endpoint) = client {
-            let data = b"hello\n";
+        if let Some((len, endpoint)) = client {
             debug!(
                 "udp:6969 send data: {:?}",
-                str::from_utf8(data.as_ref()).unwrap()
+                str::from_utf8(&buffer[..len]).unwrap()
             );
-            socket.send_slice(data, endpoint).unwrap();
+            socket.send_slice(&buffer[..len], endpoint).unwrap();
+        }
+
+        let socket = iface.get_socket::<tcp::Socket>(tcp_handle);
+        if socket.is_active() && !tcp_active {
+            debug!("connected");
+        } else if !socket.is_active() && tcp_active {
+            debug!("disconnected");
+        }
+        tcp_active = socket.is_active();
+
+        if socket.may_recv() {
+            let data = socket
+                .recv(|data| {
+                    let data = data.to_owned();
+                    if !data.is_empty() {
+                        debug!(
+                            "recv data: {:?}",
+                            str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
+                        );
+                    }
+                    (data.len(), data)
+                })
+                .unwrap();
+
+            if socket.can_send() && !data.is_empty() {
+                debug!(
+                    "send data: {:?}",
+                    str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
+                );
+                socket.send_slice(&data[..]).unwrap();
+            }
+        } else if socket.may_send() {
+            debug!("close");
+            socket.close();
         }
 
         phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error");

+ 4 - 6
fuzz/fuzz_targets/sixlowpan_udp_header.rs

@@ -1,6 +1,6 @@
 #![no_main]
 use libfuzzer_sys::fuzz_target;
-use smoltcp::wire::{Ipv6Address, SixlowpanUdpPacket, SixlowpanUdpRepr};
+use smoltcp::wire::{Ipv6Address, SixlowpanUdpNhcPacket, SixlowpanUdpNhcRepr};
 
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)]
 pub struct AddressFuzzer(pub [u8; 16]);
@@ -16,21 +16,19 @@ struct SixlowpanUdpPacketFuzzer<'a> {
     data: &'a [u8],
     src_addr: AddressFuzzer,
     dst_addr: AddressFuzzer,
-    checksum: Option<u16>,
 }
 
 fuzz_target!(|fuzz: SixlowpanUdpPacketFuzzer| {
-    if let Ok(ref frame) = SixlowpanUdpPacket::new_checked(fuzz.data) {
-        if let Ok(repr) = SixlowpanUdpRepr::parse(
+    if let Ok(ref frame) = SixlowpanUdpNhcPacket::new_checked(fuzz.data) {
+        if let Ok(repr) = SixlowpanUdpNhcRepr::parse(
             frame,
             &fuzz.src_addr.into(),
             &fuzz.dst_addr.into(),
-            fuzz.checksum,
         ) {
             let payload = frame.payload();
             let mut buffer = vec![0; repr.header_len() + payload.len()];
 
-            let mut frame = SixlowpanUdpPacket::new_unchecked(&mut buffer[..]);
+            let mut frame = SixlowpanUdpNhcPacket::new_unchecked(&mut buffer[..]);
             repr.emit(
                 &mut frame,
                 &fuzz.src_addr.into(),

+ 19 - 8
src/iface/fragmentation.rs

@@ -25,6 +25,7 @@ enum AssemblerState {
         total_size: usize,
         last_updated: Instant,
         started_on: Instant,
+        offset_correction: isize,
     },
 }
 
@@ -48,7 +49,12 @@ impl<'a> PacketAssembler<'a> {
     ///
     /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when the buffer is too small for holding all the
     /// fragments of a packet.
-    pub(crate) fn start(&mut self, total_size: usize, start_time: Instant) -> Result<()> {
+    pub(crate) fn start(
+        &mut self,
+        total_size: usize,
+        start_time: Instant,
+        offset_correction: isize,
+    ) -> Result<()> {
         match &mut self.buffer {
             ManagedSlice::Borrowed(b) if b.len() < total_size => {
                 return Err(Error::PacketAssemblerBufferTooSmall);
@@ -65,6 +71,7 @@ impl<'a> PacketAssembler<'a> {
             total_size,
             last_updated: start_time,
             started_on: start_time,
+            offset_correction,
         };
 
         Ok(())
@@ -86,8 +93,12 @@ impl<'a> PacketAssembler<'a> {
                 ref mut assembler,
                 total_size,
                 ref mut last_updated,
+                offset_correction,
                 ..
             } => {
+                let offset = offset as isize + offset_correction;
+                let offset = if offset <= 0 { 0 } else { offset as usize };
+
                 if offset + data.len() > total_size {
                     return Err(Error::PacketAssemblerBufferTooSmall);
                 }
@@ -392,10 +403,10 @@ mod tests {
         let mut p_assembler = PacketAssembler::new(&mut storage[..]);
 
         assert_eq!(
-            p_assembler.start(2, Instant::now()),
+            p_assembler.start(2, Instant::now(), 0),
             Err(Error::PacketAssemblerBufferTooSmall)
         );
-        assert_eq!(p_assembler.start(1, Instant::now()), Ok(()));
+        assert_eq!(p_assembler.start(1, Instant::now(), 0), Ok(()));
 
         let data = b"Hello World!";
         assert_eq!(
@@ -409,7 +420,7 @@ mod tests {
         let mut storage = [0u8; 5];
         let mut p_assembler = PacketAssembler::new(&mut storage[..]);
 
-        p_assembler.start(5, Instant::now()).unwrap();
+        p_assembler.start(5, Instant::now(), 0).unwrap();
         let data = b"Rust";
 
         p_assembler.add(&data[..], 0, Instant::now()).unwrap();
@@ -424,7 +435,7 @@ mod tests {
 
         let data = b"Hello World!";
 
-        p_assembler.start(data.len(), Instant::now()).unwrap();
+        p_assembler.start(data.len(), Instant::now(), 0).unwrap();
 
         p_assembler.add(b"Hello ", 0, Instant::now()).unwrap();
         assert_eq!(
@@ -483,7 +494,7 @@ mod tests {
         set.reserve_with_key(&key).unwrap();
         set.get_packet_assembler_mut(&key)
             .unwrap()
-            .start(0, Instant::now())
+            .start(0, Instant::now(), 0)
             .unwrap();
         set.get_assembled_packet(&key).unwrap();
 
@@ -491,7 +502,7 @@ mod tests {
         set.reserve_with_key(&key).unwrap();
         set.get_packet_assembler_mut(&key)
             .unwrap()
-            .start(0, Instant::now())
+            .start(0, Instant::now(), 0)
             .unwrap();
         set.get_assembled_packet(&key).unwrap();
 
@@ -499,7 +510,7 @@ mod tests {
         set.reserve_with_key(&key).unwrap();
         set.get_packet_assembler_mut(&key)
             .unwrap()
-            .start(0, Instant::now())
+            .start(0, Instant::now(), 0)
             .unwrap();
         set.get_assembled_packet(&key).unwrap();
     }

+ 493 - 159
src/iface/interface.rs

@@ -5,7 +5,6 @@
 use core::cmp;
 use managed::{ManagedMap, ManagedSlice};
 
-#[allow(unused)]
 #[cfg(feature = "proto-sixlowpan")]
 use super::fragmentation::PacketAssemblerSet;
 use super::socket_set::SocketSet;
@@ -20,10 +19,16 @@ use crate::socket::dhcpv4;
 #[cfg(feature = "socket-dns")]
 use crate::socket::dns;
 use crate::socket::*;
+use crate::storage::RingBuffer;
 use crate::time::{Duration, Instant};
 use crate::wire::*;
 use crate::{Error, Result};
 
+type OutBuffer<'a> = RingBuffer<'a, (usize, ManagedSlice<'a, u8>)>;
+
+#[cfg(feature = "proto-sixlowpan")]
+type SixlowpanFragmentsBuffer<'a> = PacketAssemblerSet<'a, SixlowpanFragKey>;
+
 macro_rules! check {
     ($e:expr) => {
         match $e {
@@ -49,6 +54,11 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> {
     device: DeviceT,
     sockets: SocketSet<'a>,
     inner: InterfaceInner<'a>,
+    // Currently this is behind the sixlowpan feature. However, this buffer could also be used for other protocols.
+    #[cfg(feature = "proto-sixlowpan")]
+    out_fragments: OutBuffer<'a>,
+    #[cfg(feature = "proto-sixlowpan")]
+    sixlowpan_fragments: SixlowpanFragmentsBuffer<'a>,
 }
 
 /// The device independent part of an Ethernet network interface.
@@ -61,6 +71,7 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> {
 pub struct InterfaceInner<'a> {
     caps: DeviceCapabilities,
     now: Instant,
+
     #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
     neighbor_cache: Option<NeighborCache<'a>>,
     #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
@@ -69,6 +80,8 @@ pub struct InterfaceInner<'a> {
     sequence_no: u8,
     #[cfg(feature = "medium-ieee802154")]
     pan_id: Option<Ieee802154Pan>,
+    #[cfg(feature = "proto-sixlowpan")]
+    tag: u16,
     ip_addrs: ManagedSlice<'a, IpCidr>,
     #[cfg(feature = "proto-ipv4")]
     any_ip: bool,
@@ -99,6 +112,10 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> {
     #[cfg(feature = "proto-igmp")]
     ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>,
     random_seed: u64,
+    #[cfg(feature = "proto-sixlowpan")]
+    sixlowpan_fragments: Option<SixlowpanFragmentsBuffer<'a>>,
+    #[cfg(feature = "proto-sixlowpan")]
+    out_fragments: Option<OutBuffer<'a>>,
 }
 
 impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT>
@@ -108,7 +125,7 @@ where
     /// Create a builder used for creating a network interface using the
     /// given device and address.
     #[cfg_attr(
-        feature = "medium-ethernet",
+        all(feature = "medium-ethernet", not(feature = "proto-sixlowpan")),
         doc = r##"
 # Examples
 
@@ -139,7 +156,7 @@ let iface = InterfaceBuilder::new(device, vec![])
         SocketsT: Into<ManagedSlice<'a, SocketStorage<'a>>>,
     {
         InterfaceBuilder {
-            device: device,
+            device,
             sockets: SocketSet::new(sockets),
 
             #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
@@ -157,6 +174,11 @@ let iface = InterfaceBuilder::new(device, vec![])
             #[cfg(feature = "proto-igmp")]
             ipv4_multicast_groups: ManagedMap::Borrowed(&mut []),
             random_seed: 0,
+
+            #[cfg(feature = "proto-sixlowpan")]
+            sixlowpan_fragments: None,
+            #[cfg(feature = "proto-sixlowpan")]
+            out_fragments: None,
         }
     }
 
@@ -268,6 +290,18 @@ let iface = InterfaceBuilder::new(device, vec![])
         self
     }
 
+    #[cfg(feature = "proto-sixlowpan")]
+    pub fn sixlowpan_fragments_cache(mut self, storage: SixlowpanFragmentsBuffer<'a>) -> Self {
+        self.sixlowpan_fragments = Some(storage);
+        self
+    }
+
+    #[cfg(feature = "proto-sixlowpan")]
+    pub fn out_fragments_cache(mut self, storage: OutBuffer<'a>) -> Self {
+        self.out_fragments = Some(storage);
+        self
+    }
+
     /// Create a network interface using the previously provided configuration.
     ///
     /// # Panics
@@ -337,9 +371,28 @@ let iface = InterfaceBuilder::new(device, vec![])
             }
         }
 
+        #[cfg(feature = "proto-sixlowpan")]
+        let mut tag;
+
+        #[cfg(feature = "proto-sixlowpan")]
+        loop {
+            tag = (rand.rand_u32() & 0xffff) as u16;
+            if tag != 0 {
+                break;
+            }
+        }
+
         Interface {
             device: self.device,
             sockets: self.sockets,
+            #[cfg(feature = "proto-sixlowpan")]
+            sixlowpan_fragments: self
+                .sixlowpan_fragments
+                .expect("Cache for incoming 6LoWPAN fragments is required"),
+            #[cfg(feature = "proto-sixlowpan")]
+            out_fragments: self
+                .out_fragments
+                .expect("Cache for outgoing fragments is required"),
             inner: InterfaceInner {
                 now: Instant::from_secs(0),
                 caps,
@@ -359,6 +412,8 @@ let iface = InterfaceBuilder::new(device, vec![])
                 sequence_no,
                 #[cfg(feature = "medium-ieee802154")]
                 pan_id: self.pan_id,
+                #[cfg(feature = "proto-sixlowpan")]
+                tag,
                 rand,
             },
         }
@@ -654,7 +709,7 @@ where
                 {
                     // Send initial membership report
                     let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, pkt)?;
+                    self.inner.dispatch_ip(tx_token, pkt, None)?;
                     Ok(true)
                 } else {
                     Ok(false)
@@ -686,7 +741,7 @@ where
                 } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) {
                     // Send group leave packet
                     let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, pkt)?;
+                    self.inner.dispatch_ip(tx_token, pkt, None)?;
                     Ok(true)
                 } else {
                     Ok(false)
@@ -771,6 +826,7 @@ where
         self.inner.now = timestamp;
 
         let mut readiness_may_have_changed = false;
+
         loop {
             let processed_any = self.socket_ingress();
             let emitted_any = self.socket_egress();
@@ -784,6 +840,19 @@ where
                 break;
             }
         }
+
+        #[cfg(feature = "proto-sixlowpan")]
+        while !self.out_fragments.is_empty() {
+            let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
+
+            // FIXME(thvdveld): remove the unwrap
+            let (len, buffer) = self.out_fragments.dequeue_one().unwrap();
+            tx_token.consume(self.inner.now, *len, |tx_token| {
+                tx_token.copy_from_slice(&buffer[..*len]);
+                Ok(())
+            })?;
+        }
+
         Ok(readiness_may_have_changed)
     }
 
@@ -838,7 +907,10 @@ where
             device,
             inner,
             sockets,
-            ..
+            #[cfg(feature = "proto-sixlowpan")]
+            sixlowpan_fragments,
+            #[cfg(feature = "proto-sixlowpan")]
+            out_fragments,
         } = self;
         while let Some((rx_token, tx_token)) = device.receive() {
             let res = rx_token.consume(inner.now, |frame| {
@@ -854,15 +926,19 @@ where
                     #[cfg(feature = "medium-ip")]
                     Medium::Ip => {
                         if let Some(packet) = inner.process_ip(sockets, &frame) {
-                            if let Err(err) = inner.dispatch_ip(tx_token, packet) {
+                            if let Err(err) = inner.dispatch_ip(tx_token, packet, None) {
                                 net_debug!("Failed to send response: {}", err);
                             }
                         }
                     }
                     #[cfg(feature = "medium-ieee802154")]
                     Medium::Ieee802154 => {
-                        if let Some(packet) = inner.process_ieee802154(sockets, &frame) {
-                            if let Err(err) = inner.dispatch_ieee802154(tx_token, packet) {
+                        if let Some(packet) =
+                            inner.process_ieee802154(sockets, &frame, sixlowpan_fragments)
+                        {
+                            if let Err(err) =
+                                inner.dispatch_ip(tx_token, packet, Some(out_fragments))
+                            {
                                 net_debug!("Failed to send response: {}", err);
                             }
                         }
@@ -885,6 +961,8 @@ where
             device,
             inner,
             sockets,
+            #[cfg(feature = "proto-sixlowpan")]
+            out_fragments,
             ..
         } = self;
         let _caps = device.capabilities();
@@ -901,8 +979,16 @@ where
             let mut neighbor_addr = None;
             let mut respond = |inner: &mut InterfaceInner, response: IpPacket| {
                 neighbor_addr = Some(response.ip_repr().dst_addr());
-                let tx_token = device.transmit().ok_or(Error::Exhausted)?;
-                inner.dispatch_ip(tx_token, response)?;
+                // FIXME(thvdveld): remove unwrap
+                let tx_token = device.transmit().ok_or(Error::Exhausted).unwrap();
+                #[cfg(feature = "proto-sixlowpan")]
+                {
+                    inner.dispatch_ip(tx_token, response, Some(out_fragments)).unwrap();
+                }
+                #[cfg(not(feature = "proto-sixlowpan"))]
+                {
+                    check!(inner.dispatch_ip(tx_token, response, None));
+                }
                 emitted_any = true;
                 Ok(())
             };
@@ -982,7 +1068,7 @@ where
                 if let Some(pkt) = self.inner.igmp_report_packet(version, group) {
                     // Send initial membership report
                     let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, pkt)?;
+                    self.inner.dispatch_ip(tx_token, pkt, None)?;
                 }
 
                 self.inner.igmp_report_state = IgmpReportState::Inactive;
@@ -1006,7 +1092,7 @@ where
                         if let Some(pkt) = self.inner.igmp_report_packet(version, addr) {
                             // Send initial membership report
                             let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                            self.inner.dispatch_ip(tx_token, pkt)?;
+                            self.inner.dispatch_ip(tx_token, pkt, None)?;
                         }
 
                         let next_timeout = (timeout + interval).max(self.inner.now);
@@ -1142,7 +1228,10 @@ impl<'a> InterfaceInner<'a> {
             #[cfg(feature = "medium-ieee802154")]
             pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)),
             #[cfg(feature = "medium-ieee802154")]
-            sequence_no: 0,
+            sequence_no: 1,
+
+            #[cfg(feature = "proto-sixlowpan")]
+            tag: 1,
 
             #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
             hardware_addr: Some(crate::wire::HardwareAddress::Ethernet(
@@ -1186,6 +1275,13 @@ impl<'a> InterfaceInner<'a> {
         no
     }
 
+    #[cfg(feature = "proto-sixlowpan")]
+    fn get_sixlowpan_fragment_tag(&mut self) -> u16 {
+        let tag = self.tag;
+        self.tag = self.tag.wrapping_add(1);
+        tag
+    }
+
     /// Determine if the given `Ipv6Address` is the solicited node
     /// multicast address for a IPv6 addresses assigned to the interface.
     /// See [RFC 4291 § 2.7.1] for more details.
@@ -1299,11 +1395,12 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(feature = "medium-ieee802154")]
-    fn process_ieee802154<'frame, T: AsRef<[u8]> + ?Sized>(
+    fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
-        sixlowpan_payload: &'frame T,
-    ) -> Option<IpPacket<'frame>> {
+        sixlowpan_payload: &'payload T,
+        fragments: &'output mut SixlowpanFragmentsBuffer<'a>,
+    ) -> Option<IpPacket<'output>> {
         let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload));
         let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame));
 
@@ -1319,25 +1416,130 @@ impl<'a> InterfaceInner<'a> {
             && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST)
         {
             net_debug!(
-                "dropping {:?} because not our PAN id (or not broadcast)",
+                "IEEE802.15.4: dropping {:?} because not our PAN id (or not broadcast)",
                 ieee802154_repr
             );
             return None;
         }
 
         match ieee802154_frame.payload() {
-            Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload),
+            Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, fragments),
             None => None,
         }
     }
 
     #[cfg(feature = "proto-sixlowpan")]
-    fn process_sixlowpan<'frame, T: AsRef<[u8]> + ?Sized>(
+    fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
         ieee802154_repr: &Ieee802154Repr,
-        payload: &'frame T,
-    ) -> Option<IpPacket<'frame>> {
+        payload: &'payload T,
+        fragments: &'output mut SixlowpanFragmentsBuffer<'a>,
+    ) -> Option<IpPacket<'output>> {
+        check!(fragments.remove_when(|frag| Ok(
+            self.now - frag.start_time().unwrap() > Duration::from_secs(60)
+        )));
+        fragments.remove_discarded();
+
+        let payload = match check!(SixlowpanPacket::dispatch(payload)) {
+            SixlowpanPacket::FragmentHeader => {
+                // We have a fragment header, which means we cannot process the 6LoWPAN packet,
+                // unless we have a complete one after processing this fragment.
+                let frag = check!(SixlowpanFragPacket::new_checked(payload));
+
+                // The key specifies to which 6LoWPAN fragment it belongs too.
+                // It is based on the link layer addresses, the tag and the size.
+                let key = frag.get_key(ieee802154_repr);
+
+                // The offset of this fragment in increments of 8 octets.
+                let offset = frag.datagram_offset() as usize * 8;
+
+                if frag.is_first_fragment() {
+                    // The first fragment contains the total size of the IPv6 packet.
+                    // However, we received a packet that is compressed following the 6LoWPAN
+                    // standard. This means we need to convert the IPv6 packet size to a 6LoWPAN
+                    // packet size. The packet size can be different because of first the
+                    // compression of the IP header and when UDP is used (because the UDP header
+                    // can also be compressed). Other headers are not compressed by 6LoWPAN.
+
+                    let iphc = check!(SixlowpanIphcPacket::new_checked(frag.payload()));
+                    let iphc_repr = check!(SixlowpanIphcRepr::parse(
+                        &iphc,
+                        ieee802154_repr.src_addr,
+                        ieee802154_repr.dst_addr,
+                    ));
+
+                    // The uncompressed header size always starts with 40, since this is the size
+                    // of a IPv6 header.
+                    let mut uncompressed_header_size = 40;
+                    let mut compressed_header_size = iphc.header_len();
+
+                    // We need to check if we have an UDP packet, since this header can also be
+                    // compressed by 6LoWPAN. We currently don't support extension headers yet.
+                    match iphc_repr.next_header {
+                        SixlowpanNextHeader::Compressed => {
+                            match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) {
+                                SixlowpanNhcPacket::ExtHeader => {
+                                    net_debug!("6LoWPAN: extension headers not supported");
+                                    return None;
+                                }
+                                SixlowpanNhcPacket::UdpHeader => {
+                                    let udp_packet =
+                                        check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload()));
+
+                                    uncompressed_header_size += 8;
+                                    compressed_header_size +=
+                                        1 + udp_packet.ports_size() + udp_packet.checksum_size();
+                                }
+                            }
+                        }
+                        SixlowpanNextHeader::Uncompressed(_) => (),
+                    }
+
+                    // We reserve a spot in the packet assembler set and add the required
+                    // information to the packet assembler.
+                    // This information is the total size of the packet when it is fully assmbled.
+                    // We also pass the header size, since this is needed when other fragments
+                    // (other than the first one) are added.
+                    check!(check!(fragments.reserve_with_key(&key)).start(
+                        frag.datagram_size() as usize - uncompressed_header_size
+                            + compressed_header_size,
+                        self.now,
+                        -((uncompressed_header_size - compressed_header_size) as isize),
+                    ));
+                }
+
+                let frags = check!(fragments.get_packet_assembler_mut(&key));
+
+                // Check if 60 seconds have passed since the start of the first fragment.
+                if self.now - frags.start_time().unwrap() > Duration::from_secs(60) {
+                    frags.mark_discarded();
+                    return None;
+                }
+
+                net_trace!("6LoWPAN: received packet fragment");
+
+                // Add the fragment to the packet assembler.
+                match frags.add(frag.payload(), offset, self.now) {
+                    Ok(true) => {
+                        net_trace!("6LoWPAN: fragmented packet now complete");
+                        check!(fragments.get_assembled_packet(&key))
+                    }
+                    Ok(false) => {
+                        return None;
+                    }
+                    Err(Error::PacketAssemblerOverlap) => {
+                        net_trace!("6LoWPAN: overlap in packet");
+                        frags.mark_discarded();
+                        return None;
+                    }
+                    Err(_) => return None,
+                }
+            }
+            SixlowpanPacket::IphcHeader => payload.as_ref(),
+        };
+
+        // At this point we should have a valid 6LoWPAN packet.
         // The first header needs to be an IPHC header.
         let iphc_packet = check!(SixlowpanIphcPacket::new_checked(payload));
         let iphc_repr = check!(SixlowpanIphcRepr::parse(
@@ -1352,10 +1554,9 @@ impl<'a> InterfaceInner<'a> {
             dst_addr: iphc_repr.dst_addr,
             hop_limit: iphc_repr.hop_limit,
             next_header: IpProtocol::Unknown(0),
-            payload_len: iphc_repr.buffer_len(),
+            payload_len: 40,
         };
 
-        // Currently we assume the next header is a UDP, so we ignore everything else.
         match iphc_repr.next_header {
             SixlowpanNextHeader::Compressed => {
                 match check!(SixlowpanNhcPacket::dispatch(payload)) {
@@ -1370,9 +1571,10 @@ impl<'a> InterfaceInner<'a> {
                     }
                     #[cfg(feature = "socket-udp")]
                     SixlowpanNhcPacket::UdpHeader => {
-                        ipv6_repr.next_header = IpProtocol::Udp;
-                        // Handle the UDP
                         let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(payload));
+                        ipv6_repr.next_header = IpProtocol::Udp;
+                        ipv6_repr.payload_len += 8 + udp_packet.payload().len();
+
                         let udp_repr = check!(SixlowpanUdpNhcRepr::parse(
                             &udp_packet,
                             &iphc_repr.src_addr,
@@ -1381,6 +1583,10 @@ impl<'a> InterfaceInner<'a> {
 
                         // Look for UDP sockets that will accept the UDP packet.
                         // If it does not accept the packet, then send an ICMP message.
+                        //
+                        // NOTE(thvdveld): this is currently the same code as in self.process_udp.
+                        // However, we cannot use that one because the payload passed to it is a
+                        // normal IPv6 UDP payload, which is not what we have here.
                         for udp_socket in sockets
                             .iter_mut()
                             .filter_map(|i| udp::Socket::downcast(&mut i.socket))
@@ -1396,6 +1602,8 @@ impl<'a> InterfaceInner<'a> {
                             }
                         }
 
+                        // When we are here then then there was no UDP socket that accepted the UDP
+                        // message.
                         let payload_len = icmp_reply_payload_len(
                             payload.len(),
                             IPV6_MIN_MTU,
@@ -1415,8 +1623,14 @@ impl<'a> InterfaceInner<'a> {
                     ipv6_repr.next_header = IpProtocol::Icmpv6;
                     self.process_icmpv6(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload())
                 }
-                _ => {
-                    net_debug!("Headers other than ICMPv6 and compressed headers are currently not supported for 6LoWPAN");
+                #[cfg(feature = "socket-tcp")]
+                IpProtocol::Tcp => {
+                    ipv6_repr.next_header = nxt_hdr;
+                    ipv6_repr.payload_len += payload.len();
+                    self.process_tcp(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload())
+                }
+                proto => {
+                    net_debug!("6LoWPAN: {} currently not supported", proto);
                     None
                 }
             },
@@ -2046,7 +2260,7 @@ impl<'a> InterfaceInner<'a> {
                 Icmpv4Repr::EchoReply { .. } => match self.ipv4_address() {
                     Some(src_addr) => {
                         let ipv4_reply_repr = Ipv4Repr {
-                            src_addr: src_addr,
+                            src_addr,
                             dst_addr: ipv4_repr.src_addr,
                             next_header: IpProtocol::Icmp,
                             payload_len: icmp_repr.buffer_len(),
@@ -2214,7 +2428,7 @@ impl<'a> InterfaceInner<'a> {
                     arp_repr.emit(&mut packet);
                 })
             }
-            EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet),
+            EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, None),
         }
     }
 
@@ -2406,7 +2620,7 @@ impl<'a> InterfaceInner<'a> {
                     solicit,
                 ));
 
-                self.dispatch_ip(tx_token, packet)?;
+                self.dispatch_ip(tx_token, packet, None)?;
             }
 
             #[allow(unreachable_patterns)]
@@ -2424,7 +2638,12 @@ impl<'a> InterfaceInner<'a> {
         }
     }
 
-    fn dispatch_ip<Tx: TxToken>(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> {
+    fn dispatch_ip<Tx: TxToken>(
+        &mut self,
+        tx_token: Tx,
+        packet: IpPacket,
+        _out_fragments: Option<&mut OutBuffer<'_>>,
+    ) -> Result<()> {
         let ip_repr = packet.ip_repr();
         assert!(!ip_repr.dst_addr().is_unspecified());
 
@@ -2471,17 +2690,6 @@ impl<'a> InterfaceInner<'a> {
                     Ok(())
                 })
             }
-            #[cfg(feature = "medium-ieee802154")]
-            Medium::Ieee802154 => self.dispatch_ieee802154(tx_token, packet),
-        }
-    }
-
-    #[cfg(feature = "medium-ieee802154")]
-    fn dispatch_ieee802154<Tx: TxToken>(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> {
-        let ip_repr = packet.ip_repr();
-        assert!(!ip_repr.dst_addr().is_unspecified());
-
-        match self.caps.medium {
             #[cfg(feature = "medium-ieee802154")]
             Medium::Ieee802154 => {
                 let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr(
@@ -2493,134 +2701,246 @@ impl<'a> InterfaceInner<'a> {
                     _ => unreachable!(),
                 };
 
-                let ack_request = dst_hardware_addr.is_unicast();
+                self.dispatch_ieee802154(
+                    dst_hardware_addr,
+                    &ip_repr,
+                    tx_token,
+                    packet,
+                    _out_fragments,
+                )
+            }
+        }
+    }
+
+    #[cfg(feature = "medium-ieee802154")]
+    fn dispatch_ieee802154<Tx: TxToken>(
+        &mut self,
+        ll_dst_addr: Ieee802154Address,
+        ip_repr: &IpRepr,
+        tx_token: Tx,
+        packet: IpPacket,
+        out_fragments: Option<&mut OutBuffer<'_>>,
+    ) -> Result<()> {
+        // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet.
+        // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to
+        // fragment it.
+        let ll_src_addr = self.hardware_addr.map_or_else(
+            || Err(Error::Malformed),
+            |addr| match addr {
+                HardwareAddress::Ieee802154(addr) => Ok(addr),
+                _ => Err(Error::Malformed),
+            },
+        )?;
 
-                let ack_request = match packet {
-                    IpPacket::Icmpv6(_) => false,
-                    _ => ack_request,
-                };
+        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),
+            _ => return Err(Error::Unaddressable),
+        };
 
-                let mut tx_len = 0;
+        // Create the IEEE802.15.4 header.
+        let mut ieee_repr = Ieee802154Repr {
+            frame_type: Ieee802154FrameType::Data,
+            security_enabled: false,
+            frame_pending: false,
+            ack_request: false,
+            sequence_number: Some(self.get_sequence_number()),
+            pan_id_compression: true,
+            frame_version: Ieee802154FrameVersion::Ieee802154_2003,
+            dst_pan_id: self.pan_id,
+            dst_addr: Some(ll_dst_addr),
+            src_pan_id: self.pan_id,
+            src_addr: Some(ll_src_addr),
+        };
 
-                let ll_src_addr =
-                    if let Some(HardwareAddress::Ieee802154(addr)) = self.hardware_addr {
-                        Some(addr)
-                    } else {
-                        return Err(Error::Malformed);
-                    };
+        // Create the 6LoWPAN IPHC header.
+        let iphc_repr = SixlowpanIphcRepr {
+            src_addr,
+            ll_src_addr: Some(ll_src_addr),
+            dst_addr,
+            ll_dst_addr: Some(ll_dst_addr),
+            next_header: match &packet {
+                IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
+                #[cfg(feature = "socket-tcp")]
+                IpPacket::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
+                #[cfg(feature = "socket-udp")]
+                IpPacket::Udp(_) => SixlowpanNextHeader::Compressed,
+                #[allow(unreachable_patterns)]
+                _ => return Err(Error::Unrecognized),
+            },
+            hop_limit: ip_repr.hop_limit(),
+            ecn: None,
+            dscp: None,
+            flow_label: None,
+        };
 
-                let ieee_repr = Ieee802154Repr {
-                    frame_type: Ieee802154FrameType::Data,
-                    security_enabled: false,
-                    frame_pending: false,
-                    ack_request,
-                    sequence_number: Some(self.get_sequence_number()),
-                    pan_id_compression: true,
-                    frame_version: Ieee802154FrameVersion::Ieee802154_2003,
-                    dst_pan_id: self.pan_id,
-                    dst_addr: Some(dst_hardware_addr),
-                    src_pan_id: self.pan_id,
-                    src_addr: ll_src_addr,
-                };
+        // We will first emit every packet into this buffer.
+        // When we emitted everything we know if we need fragmentation or not.
+        // This method is not ideal, but emitting UDP frames require that the full buffer is passed
+        // to the `emit` function. However, the buffer for IEEE802.15.4 is usually 125 bytes.
+        let mut buffer = [0; 4096];
+        let mut total_size = 0;
 
-                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)]
-                    _ => return Err(Error::Unaddressable),
-                };
+        let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut buffer[..]);
+        ieee_repr.emit(&mut ieee_packet);
+        total_size += ieee_repr.buffer_len();
 
-                #[allow(unreachable_patterns)]
-                let (next_header, hop_limit) = match &packet {
-                    #[cfg(feature = "socket-udp")]
-                    IpPacket::Udp(_) => (SixlowpanNextHeader::Compressed, 64),
-                    IpPacket::Icmpv6((_, repr)) => (
-                        SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
-                        match repr {
-                            Icmpv6Repr::Ndisc(_) => 255,
-                            _ => 64,
-                        },
-                    ),
-                    _ => return Err(Error::Unrecognized),
-                };
+        let mut iphc_packet = SixlowpanIphcPacket::new_unchecked(&mut buffer[total_size..]);
+        iphc_repr.emit(&mut iphc_packet);
+        total_size += iphc_repr.buffer_len();
 
-                let iphc_repr = SixlowpanIphcRepr {
-                    src_addr,
-                    ll_src_addr,
-                    dst_addr,
-                    ll_dst_addr: Some(dst_hardware_addr),
-                    next_header,
-                    hop_limit,
-                    ecn: None,
-                    dscp: None,
-                    flow_label: None,
-                };
+        let mut compressed_headers_len = iphc_repr.buffer_len();
 
-                tx_len += ieee_repr.buffer_len();
-                tx_len += iphc_repr.buffer_len();
+        #[allow(unreachable_patterns)]
+        match packet {
+            #[cfg(feature = "socket-udp")]
+            IpPacket::Udp((_, udpv6_repr, payload)) => {
+                let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr);
+                let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked(
+                    &mut buffer[total_size..][..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),
+                );
 
-                #[allow(unreachable_patterns)]
-                match &packet {
-                    #[cfg(feature = "socket-udp")]
-                    IpPacket::Udp((_, udp_repr, payload)) => {
-                        let udp_repr = SixlowpanUdpNhcRepr(*udp_repr);
-                        tx_len += udp_repr.header_len() + payload.len();
-                    }
-                    IpPacket::Icmpv6((_, icmp)) => {
-                        tx_len += icmp.buffer_len();
-                    }
-                    _ => return Err(Error::Unrecognized),
-                }
+                compressed_headers_len += udp_repr.header_len();
+                total_size += udp_repr.header_len() + payload.len();
+            }
+            #[cfg(feature = "socket-tcp")]
+            IpPacket::Tcp((_, tcp_repr)) => {
+                let mut tcp_packet =
+                    TcpPacket::new_unchecked(&mut buffer[total_size..][..tcp_repr.buffer_len()]);
+                tcp_repr.emit(
+                    &mut tcp_packet,
+                    &iphc_repr.src_addr.into(),
+                    &iphc_repr.dst_addr.into(),
+                    &self.caps.checksum,
+                );
 
-                tx_token.consume(self.now, tx_len, |mut tx_buffer| {
-                    // 1. Create the header of 802.15.4
-                    let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buffer);
-                    ieee_repr.emit(&mut ieee_packet);
+                total_size += tcp_repr.buffer_len();
+            }
+            #[cfg(feature = "proto-ipv6")]
+            IpPacket::Icmpv6((_, icmp_repr)) => {
+                let mut icmp_packet = Icmpv6Packet::new_unchecked(
+                    &mut buffer[total_size..][..icmp_repr.buffer_len()],
+                );
+                icmp_repr.emit(
+                    &iphc_repr.src_addr.into(),
+                    &iphc_repr.dst_addr.into(),
+                    &mut icmp_packet,
+                    &self.caps.checksum,
+                );
 
-                    let mut start = ieee_repr.buffer_len();
+                total_size += icmp_repr.buffer_len();
+            }
+            _ => return Err(Error::Unrecognized),
+        }
 
-                    // 2. Create the header for 6LoWPAN IPHC
-                    let mut iphc_packet =
-                        SixlowpanIphcPacket::new_unchecked(&mut tx_buffer[start..tx_len]);
-                    iphc_repr.emit(&mut iphc_packet);
-                    start += iphc_repr.buffer_len();
+        if total_size > 125 {
+            // Fragmentation is required because the data does not fit into a IEEE802.15.4 packet.
+            // Everything has already been emitted in the buffer, thus we only need to insert the
+            // fragmentation headers in the correct places.
+            //
+            // The first fragment header is right after the first IEEE802.15.4 header.
+            //
+            // Since we can only use `tx_token` once we need to store other packets into the
+            // out_fragments buffer.
+
+            let out_fragments = out_fragments.unwrap();
+
+            // The datagram size that we need to set in the first fragment header is equal to the
+            // IPv6 payload length + 40.
+            let datagram_size = (packet.ip_repr().payload_len() + 40) as u16;
+            // We generate a random tag.
+            let tag = self.get_sixlowpan_fragment_tag();
+
+            let ieee_len = ieee_repr.buffer_len();
+
+            // We calculate how much data we can send in the first fragment and the other
+            // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight
+            // (except for the last fragment) since the offset field in the fragment is an offset
+            // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3].
+            //
+            // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3
+
+            let frag1 = SixlowpanFragRepr::FirstFragment {
+                size: datagram_size,
+                tag,
+            };
+            let mut fragn = SixlowpanFragRepr::Fragment {
+                size: datagram_size,
+                tag,
+                offset: 0,
+            };
 
-                    #[allow(unreachable_patterns)]
-                    match packet {
-                        #[cfg(feature = "socket-udp")]
-                        IpPacket::Udp((_, udp_repr, payload)) => {
-                            // 3. Create the header for 6LoWPAN UDP
-                            let mut udp_packet =
-                                SixlowpanUdpNhcPacket::new_unchecked(&mut tx_buffer[start..tx_len]);
-
-                            SixlowpanUdpNhcRepr(udp_repr).emit(
-                                &mut udp_packet,
-                                &iphc_repr.src_addr,
-                                &iphc_repr.dst_addr,
-                                payload.len(),
-                                |buf| buf.copy_from_slice(payload),
-                            );
-                        }
-                        #[cfg(feature = "proto-ipv6")]
-                        IpPacket::Icmpv6((_, icmp_repr)) => {
-                            // 3. Create the header for ICMPv6
-                            let mut icmp_packet =
-                                Icmpv6Packet::new_unchecked(&mut tx_buffer[start..tx_len]);
-
-                            icmp_repr.emit(
-                                &iphc_repr.src_addr.into(),
-                                &iphc_repr.dst_addr.into(),
-                                &mut icmp_packet,
-                                &self.caps.checksum,
-                            );
-                        }
-                        _ => return Err(Error::Unrecognized),
-                    }
+            let frag1_size = ((125 - ieee_len - frag1.buffer_len() - compressed_headers_len)
+                & 0xffff_fff8)
+                + compressed_headers_len;
+            let fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8;
+
+            tx_token.consume(
+                self.now,
+                ieee_len + frag1.buffer_len() + frag1_size,
+                |mut tx_buf| {
+                    // Copy the IEEE header
+                    tx_buf[..ieee_len].copy_from_slice(&buffer[..ieee_len]);
+                    tx_buf = &mut tx_buf[ieee_len..];
+
+                    // Add the first fragment header
+                    let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf);
+                    frag1.emit(&mut frag1_packet);
+                    tx_buf = &mut tx_buf[frag1.buffer_len()..];
+
+                    // Add the buffer part.
+                    tx_buf[..frag1_size].copy_from_slice(&buffer[ieee_len..][..frag1_size]);
 
                     Ok(())
-                })
+                },
+            )?;
+
+            let mut written = ieee_len + frag1_size;
+
+            // We need to save the the rest of the packets into the `out_fragments` buffer.
+            while written < total_size {
+                out_fragments.enqueue_one_with::<'_, (), Error, _>(|(len, tx_buf)| {
+                    let mut tx_buf = &mut tx_buf[..];
+
+                    // Modify the sequence number of the IEEE header
+                    ieee_repr.sequence_number = Some(self.get_sequence_number());
+                    let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
+                    ieee_repr.emit(&mut ieee_packet);
+                    tx_buf = &mut tx_buf[ieee_len..];
+
+                    // Add the next fragment header
+                    let datagram_offset = ((40 + written - ieee_len) / 8) as u8;
+                    fragn.set_offset(datagram_offset);
+                    let mut frag_packet =
+                        SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]);
+                    fragn.emit(&mut frag_packet);
+                    tx_buf = &mut tx_buf[fragn.buffer_len()..];
+
+                    // Add the buffer part
+                    let frag_size = (total_size - written).min(fragn_size);
+                    tx_buf[..frag_size].copy_from_slice(&buffer[written..][..frag_size]);
+                    written += frag_size;
+
+                    // Save the lenght of this packet.
+                    *len = ieee_len + fragn.buffer_len() + frag_size;
+                    Ok(())
+                }).unwrap().unwrap();
             }
-            #[allow(unreachable_patterns)]
-            _ => Err(Error::NotSupported),
+
+            Ok(())
+        } else {
+            // No fragmentation is needed thus we can copy everything over that already has been
+            // emitted in the buffer.
+            tx_token.consume(self.now, total_size, |tx_buffer| {
+                tx_buffer.copy_from_slice(&buffer[..total_size]);
+                Ok(())
+            })
         }
     }
 
@@ -2732,10 +3052,20 @@ mod test {
             IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64),
         ];
 
+        #[cfg(feature = "proto-sixlowpan")]
+        let iface_builder = InterfaceBuilder::new(device, vec![])
+            .hardware_addr(EthernetAddress::default().into())
+            .neighbor_cache(NeighborCache::new(BTreeMap::new()))
+            .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new()))
+            .out_fragments_cache(RingBuffer::new(vec![]))
+            .ip_addrs(ip_addrs);
+
+        #[cfg(not(feature = "proto-sixlowpan"))]
         let iface_builder = InterfaceBuilder::new(device, vec![])
             .hardware_addr(EthernetAddress::default().into())
             .neighbor_cache(NeighborCache::new(BTreeMap::new()))
             .ip_addrs(ip_addrs);
+
         #[cfg(feature = "proto-igmp")]
         let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new());
         iface_builder.finalize()
@@ -2985,7 +3315,7 @@ mod test {
                 payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
                 hop_limit: 64,
             },
-            data: data,
+            data,
         };
         let expected_repr = IpPacket::Icmpv4((
             Ipv4Repr {
@@ -3232,8 +3562,8 @@ mod test {
         };
         #[cfg(feature = "proto-ipv6")]
         let ip_repr = Ipv6Repr {
-            src_addr: src_addr,
-            dst_addr: dst_addr,
+            src_addr,
+            dst_addr,
             next_header: IpProtocol::Udp,
             hop_limit: 64,
             payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN,
@@ -3468,7 +3798,11 @@ mod test {
     }
 
     #[test]
-    #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
+    #[cfg(all(
+        feature = "medium-ethernet",
+        feature = "proto-ipv4",
+        not(feature = "medium-ieee802154")
+    ))]
     fn test_arp_flush_after_update_ip() {
         let mut iface = create_loopback_ethernet();
 

+ 2 - 2
src/wire/sixlowpan.rs

@@ -2059,7 +2059,7 @@ mod test {
 
     #[test]
     fn sixlowpan_three_fragments() {
-        use crate::iface::{FragmentsCache, SixlowpanAssemblerInfo};
+        use crate::iface::FragmentsCache;
         use crate::time::Instant;
         use crate::wire::ieee802154::Frame as Ieee802154Frame;
         use crate::wire::ieee802154::Repr as Ieee802154Repr;
@@ -2106,8 +2106,8 @@ mod test {
             .unwrap()
             .start(
                 frag.datagram_size() as usize - uncompressed + compressed,
-                SixlowpanAssemblerInfo::new(uncompressed - compressed),
                 Instant::now(),
+                -((uncompressed - compressed) as isize),
             )
             .unwrap();
         frags_cache