From f3310e63f4b014cc7f5f04ad9393e13602e7c163 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 30 May 2022 16:11:45 +0200 Subject: [PATCH] refactor 6LoWPAN fragmentation --- examples/sixlowpan.rs | 14 +- examples/sixlowpan_benchmark.rs | 10 +- src/iface/interface.rs | 526 +++++++++++++++++++++----------- src/phy/raw_socket.rs | 6 + 4 files changed, 360 insertions(+), 196 deletions(-) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index d7d975a1..ef33ecfd 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -51,7 +51,6 @@ use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::socket::udp; -use smoltcp::storage::RingBuffer; use smoltcp::time::Instant; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; @@ -70,8 +69,8 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 64]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 128]); + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); @@ -88,12 +87,7 @@ fn main() { 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 out_packet_buffer = [0u8; 1280]; let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) @@ -102,7 +96,7 @@ fn main() { .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache) .sixlowpan_fragments_cache(cache) - .out_fragments_cache(out_fragments_cache); + .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); let mut iface = builder.finalize(); let udp_handle = iface.add_socket(udp_socket); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 6de9df62..2fa39366 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -51,7 +51,6 @@ use std::str; use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; -use smoltcp::storage::RingBuffer; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; //For benchmark @@ -168,13 +167,6 @@ fn main() { 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; 1_000_000_000]))) - .collect(); - - let out_fragments_cache = RingBuffer::new(buffer); - let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); @@ -182,7 +174,7 @@ fn main() { .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache) .sixlowpan_fragments_cache(cache) - .out_fragments_cache(out_fragments_cache); + .sixlowpan_out_packet_cache(vec![]); let mut iface = builder.finalize(); let tcp1_handle = iface.add_socket(tcp1_socket); diff --git a/src/iface/interface.rs b/src/iface/interface.rs index a1edf673..d4fd3f6a 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -19,15 +19,62 @@ 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>)>; +pub(crate) struct FragmentsBuffer<'a> { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData<&'a ()>, +} + +pub(crate) struct OutPackets<'a> { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_out_packet: SixlowpanOutPacket<'a>, + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData<&'a ()>, +} #[cfg(feature = "proto-sixlowpan")] -type SixlowpanFragmentsBuffer<'a> = PacketAssemblerSet<'a, SixlowpanFragKey>; +pub(crate) struct SixlowpanOutPacket<'a> { + /// The buffer that holds the unfragmented 6LoWPAN packet. + buffer: ManagedSlice<'a, u8>, + /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. + packet_len: usize, + /// The amount of bytes that already have been transmitted. + sent_bytes: usize, + + /// The datagram size that is used for the fragmentation headers. + datagram_size: u16, + /// The datagram tag that is used for the fragmentation headers. + datagram_tag: u16, + + /// The size of the FRAG_N packets. + fragn_size: usize, + + /// The link layer IEEE802.15.4 source address. + ll_dst_addr: Ieee802154Address, + /// The link layer IEEE802.15.4 source address. + ll_src_addr: Ieee802154Address, +} + +#[cfg(feature = "proto-sixlowpan")] +impl<'a> SixlowpanOutPacket<'a> { + pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { + Self { + buffer, + packet_len: 0, + datagram_size: 0, + datagram_tag: 0, + sent_bytes: 0, + fragn_size: 0, + ll_dst_addr: Ieee802154Address::Absent, + ll_src_addr: Ieee802154Address::Absent, + } + } +} macro_rules! check { ($e:expr) => { @@ -54,11 +101,8 @@ 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>, + fragments: FragmentsBuffer<'a>, + out_packets: OutPackets<'a>, } /// The device independent part of an Ethernet network interface. @@ -113,9 +157,9 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, random_seed: u64, #[cfg(feature = "proto-sixlowpan")] - sixlowpan_fragments: Option>, + sixlowpan_fragments: Option>, #[cfg(feature = "proto-sixlowpan")] - out_fragments: Option>, + sixlowpan_out_buffer: Option>, } impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> @@ -178,7 +222,7 @@ let iface = InterfaceBuilder::new(device, vec![]) #[cfg(feature = "proto-sixlowpan")] sixlowpan_fragments: None, #[cfg(feature = "proto-sixlowpan")] - out_fragments: None, + sixlowpan_out_buffer: None, } } @@ -291,14 +335,20 @@ let iface = InterfaceBuilder::new(device, vec![]) } #[cfg(feature = "proto-sixlowpan")] - pub fn sixlowpan_fragments_cache(mut self, storage: SixlowpanFragmentsBuffer<'a>) -> Self { + pub fn sixlowpan_fragments_cache( + mut self, + storage: PacketAssemblerSet<'a, SixlowpanFragKey>, + ) -> 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); + pub fn sixlowpan_out_packet_cache(mut self, storage: T) -> Self + where + T: Into>, + { + self.sixlowpan_out_buffer = Some(storage.into()); self } @@ -385,14 +435,25 @@ let iface = InterfaceBuilder::new(device, vec![]) 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"), + fragments: FragmentsBuffer { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: self + .sixlowpan_fragments + .expect("Cache for incoming 6LoWPAN fragments is required"), + + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData, + }, + out_packets: OutPackets { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_out_packet: SixlowpanOutPacket::new( + self.sixlowpan_out_buffer + .expect("Cache for outgoing fragments is required"), + ), + + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData, + }, inner: InterfaceInner { now: Instant::from_secs(0), caps, @@ -827,6 +888,33 @@ where let mut readiness_may_have_changed = false; + #[cfg(feature = "proto-sixlowpan")] + { + let SixlowpanOutPacket { + packet_len, + sent_bytes, + .. + } = &self.out_packets.sixlowpan_out_packet; + + if *packet_len > *sent_bytes { + match self.device.transmit().ok_or(Error::Exhausted) { + Ok(tx_token) => { + if let Err(e) = self.inner.dispatch_ieee802154_out_packet( + tx_token, + &mut self.out_packets.sixlowpan_out_packet, + ) { + net_debug!("failed to transmit: {}", e); + } + } + Err(e) => { + net_debug!("failed to transmit: {}", e); + } + } + + return Ok(true); + } + } + loop { let processed_any = self.socket_ingress(); let emitted_any = self.socket_egress(); @@ -841,18 +929,6 @@ where } } - #[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) } @@ -907,10 +983,8 @@ where device, inner, sockets, - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_fragments, - #[cfg(feature = "proto-sixlowpan")] - out_fragments, + fragments: _fragments, + out_packets: _out_packets, } = self; while let Some((rx_token, tx_token)) = device.receive() { let res = rx_token.consume(inner.now, |frame| { @@ -933,11 +1007,13 @@ where } #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { - if let Some(packet) = - inner.process_ieee802154(sockets, &frame, sixlowpan_fragments) - { + if let Some(packet) = inner.process_ieee802154( + sockets, + &frame, + &mut _fragments.sixlowpan_fragments, + ) { if let Err(err) = - inner.dispatch_ip(tx_token, packet, Some(out_fragments)) + inner.dispatch_ip(tx_token, packet, Some(_out_packets)) { net_debug!("Failed to send response: {}", err); } @@ -961,8 +1037,7 @@ where device, inner, sockets, - #[cfg(feature = "proto-sixlowpan")] - out_fragments, + out_packets: _out_packets, .. } = self; let _caps = device.capabilities(); @@ -982,7 +1057,7 @@ where match device.transmit().ok_or(Error::Exhausted) { Ok(t) => { #[cfg(feature = "proto-sixlowpan")] - if let Err(_e) = inner.dispatch_ip(t, response, Some(out_fragments)) { + if let Err(_e) = inner.dispatch_ip(t, response, Some(_out_packets)) { net_debug!("failed to dispatch IP: {}", _e); } @@ -1406,7 +1481,7 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, sixlowpan_payload: &'payload T, - fragments: &'output mut SixlowpanFragmentsBuffer<'a>, + fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, ) -> Option> { let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); @@ -1441,7 +1516,7 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - fragments: &'output mut SixlowpanFragmentsBuffer<'a>, + fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, ) -> Option> { check!(fragments.remove_when(|frag| Ok( self.now - frag.start_time().unwrap() > Duration::from_secs(60) @@ -2649,7 +2724,7 @@ impl<'a> InterfaceInner<'a> { &mut self, tx_token: Tx, packet: IpPacket, - _out_fragments: Option<&mut OutBuffer<'_>>, + _out_packet: Option<&mut OutPackets<'_>>, ) -> Result<()> { let ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); @@ -2708,13 +2783,7 @@ impl<'a> InterfaceInner<'a> { _ => unreachable!(), }; - self.dispatch_ieee802154( - dst_hardware_addr, - &ip_repr, - tx_token, - packet, - _out_fragments, - ) + self.dispatch_ieee802154(dst_hardware_addr, &ip_repr, tx_token, packet, _out_packet) } } } @@ -2722,16 +2791,16 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] fn dispatch_ieee802154( &mut self, - ll_dst_addr: Ieee802154Address, + ll_dst_a: Ieee802154Address, ip_repr: &IpRepr, tx_token: Tx, packet: IpPacket, - out_fragments: Option<&mut OutBuffer<'_>>, + out_packet: Option<&mut OutPackets>, ) -> 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( + let ll_src_a = self.hardware_addr.map_or_else( || Err(Error::Malformed), |addr| match addr { HardwareAddress::Ieee802154(addr) => Ok(addr), @@ -2746,7 +2815,7 @@ impl<'a> InterfaceInner<'a> { }; // Create the IEEE802.15.4 header. - let mut ieee_repr = Ieee802154Repr { + let ieee_repr = Ieee802154Repr { frame_type: Ieee802154FrameType::Data, security_enabled: false, frame_pending: false, @@ -2755,17 +2824,17 @@ impl<'a> InterfaceInner<'a> { pan_id_compression: true, frame_version: Ieee802154FrameVersion::Ieee802154_2003, dst_pan_id: self.pan_id, - dst_addr: Some(ll_dst_addr), + dst_addr: Some(ll_dst_a), src_pan_id: self.pan_id, - src_addr: Some(ll_src_addr), + src_addr: Some(ll_src_a), }; // Create the 6LoWPAN IPHC header. let iphc_repr = SixlowpanIphcRepr { src_addr, - ll_src_addr: Some(ll_src_addr), + ll_src_addr: Some(ll_src_a), dst_addr, - ll_dst_addr: Some(ll_dst_addr), + ll_dst_addr: Some(ll_dst_a), next_header: match &packet { IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), #[cfg(feature = "socket-tcp")] @@ -2781,21 +2850,10 @@ impl<'a> InterfaceInner<'a> { flow_label: None, }; - // 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]; + // 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; - - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut buffer[..]); - ieee_repr.emit(&mut ieee_packet); - total_size += ieee_repr.buffer_len(); - - let mut iphc_packet = SixlowpanIphcPacket::new_unchecked(&mut buffer[total_size..]); - iphc_repr.emit(&mut iphc_packet); total_size += iphc_repr.buffer_len(); - let mut compressed_headers_len = iphc_repr.buffer_len(); #[allow(unreachable_patterns)] @@ -2803,69 +2861,113 @@ impl<'a> InterfaceInner<'a> { #[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), - ); - 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, - ); - 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, - ); - total_size += icmp_repr.buffer_len(); } _ => return Err(Error::Unrecognized), } - 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 ieee_len = ieee_repr.buffer_len(); - let out_fragments = out_fragments.unwrap(); + if total_size + ieee_len > 125 { + // The packet does not fit in one Ieee802154 frame, so we need fragmentation. + // We do this by emitting everything in the `out_packet.buffer` from the interface. + // After emitting everything into that buffer, we send the first fragment heere. + // When `poll` is called again, we check if out_packet was fully sent, otherwise we + // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. + + // `dispatch_ieee802154_out_packet` requires some information about the total packet size, + // the link local source and destination address... + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + .. + } = &mut out_packet.unwrap().sixlowpan_out_packet; + + *ll_dst_addr = ll_dst_a; + *ll_src_addr = ll_src_a; + + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); + iphc_repr.emit(&mut iphc_packet); + + let b = &mut buffer[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 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")] + IpPacket::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")] + IpPacket::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, + ); + } + _ => return Err(Error::Unrecognized), + } + + *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. - let datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + *datagram_size = (packet.ip_repr().payload_len() + 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. + *datagram_tag = tag; - let ieee_len = ieee_repr.buffer_len(); + let frag1 = SixlowpanFragRepr::FirstFragment { + size: *datagram_size, + tag, + }; + let fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, + tag, + offset: 0, + }; // 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 @@ -2873,28 +2975,20 @@ impl<'a> InterfaceInner<'a> { // 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, - }; - 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; + *fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; + + *sent_bytes = frag1_size; 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]); + // Add the IEEE header. + 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 first fragment header @@ -2903,59 +2997,137 @@ impl<'a> InterfaceInner<'a> { tx_buf = &mut tx_buf[frag1.buffer_len()..]; // Add the buffer part. - tx_buf[..frag1_size].copy_from_slice(&buffer[ieee_len..][..frag1_size]); + tx_buf[..frag1_size].copy_from_slice(&buffer[..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(); - } - - 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]); + // We don't need fragmentation, so we emit everything to the TX token. + tx_token.consume(self.now, total_size + ieee_len, |mut tx_buf| { + 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..]; + + 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()..]; + + #[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 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")] + IpPacket::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")] + IpPacket::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, + ); + } + _ => return Err(Error::Unrecognized), + } Ok(()) }) } } + #[cfg(feature = "medium-ieee802154")] + fn dispatch_ieee802154_out_packet( + &mut self, + tx_token: Tx, + out_packet: &mut SixlowpanOutPacket, + ) -> Result<()> { + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + .. + } = out_packet; + + // Create the IEEE802.15.4 header. + let 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), + }; + + // Create the FRAG_N header. + let mut fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, + tag: *datagram_tag, + offset: 0, + }; + + let ieee_len = ieee_repr.buffer_len(); + let frag_size = (*packet_len - *sent_bytes).min(*fragn_size); + + tx_token.consume( + self.now, + ieee_repr.buffer_len() + fragn.buffer_len() + frag_size, + |mut tx_buf| { + 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 + *sent_bytes) / 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 + tx_buf[..frag_size].copy_from_slice(&buffer[*sent_bytes..][..frag_size]); + + *sent_bytes += frag_size; + + Ok(()) + }, + ) + } + #[cfg(feature = "proto-igmp")] fn igmp_report_packet<'any>( &self, @@ -3069,7 +3241,7 @@ mod test { .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![])) + .sixlowpan_out_packet_cache(vec![]) .ip_addrs(ip_addrs); #[cfg(not(feature = "proto-sixlowpan"))] diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index b266156a..b760983a 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -33,6 +33,12 @@ impl RawSocket { let mut mtu = lower.interface_mtu()?; + // FIXME(thvdveld): this is a workaround for https://github.com/smoltcp-rs/smoltcp/issues/622 + #[cfg(feature = "medium-ieee802154")] + if medium == Medium::Ieee802154 { + mtu += 2; + } + #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)