Browse Source

iface: unify ipv4/6lowpan packet assemblers.

If you enable both ipv4 and 6lowpan on the same binary, an Interface
had twice the needed reassembly buffers, one copy for ipv4 and another
for 6lowpan. Only one of both was used at a time.

Now interfaces have a single assembler used for either medium.
Dario Nieuwenhuis 2 years ago
parent
commit
12fd8161c7

+ 8 - 2
Cargo.toml

@@ -45,12 +45,12 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ]
 "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
 
 "proto-ipv4" = []
-"proto-ipv4-fragmentation" = ["proto-ipv4"]
+"proto-ipv4-fragmentation" = ["proto-ipv4", "_proto-fragmentation"]
 "proto-igmp" = ["proto-ipv4"]
 "proto-dhcpv4" = ["proto-ipv4"]
 "proto-ipv6" = []
 "proto-sixlowpan" = ["proto-ipv6"]
-"proto-sixlowpan-fragmentation" = ["proto-sixlowpan"]
+"proto-sixlowpan-fragmentation" = ["proto-sixlowpan", "_proto-fragmentation"]
 "proto-dns" = []
 
 "socket" = []
@@ -74,6 +74,12 @@ default = [
   "async"
 ]
 
+# Private features
+# Features starting with "_" are considered private. They should not be enabled by 
+# other crates, and they are not considered semver-stable.
+
+"_proto-fragmentation" = []
+
 [[example]]
 name = "packet2pcap"
 path = "utils/packet2pcap.rs"

+ 8 - 12
src/iface/interface/ethernet.rs

@@ -15,7 +15,7 @@ impl InterfaceInner {
         &mut self,
         sockets: &mut SocketSet,
         frame: &'frame T,
-        _fragments: &'frame mut FragmentsBuffer,
+        fragments: &'frame mut FragmentsBuffer,
     ) -> Option<EthernetPacket<'frame>> {
         let eth_frame = check!(EthernetFrame::new_checked(frame));
 
@@ -34,17 +34,13 @@ impl InterfaceInner {
             EthernetProtocol::Ipv4 => {
                 let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload()));
 
-                #[cfg(feature = "proto-ipv4-fragmentation")]
-                {
-                    self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments))
-                        .map(EthernetPacket::Ip)
-                }
-
-                #[cfg(not(feature = "proto-ipv4-fragmentation"))]
-                {
-                    self.process_ipv4(sockets, &ipv4_packet, None)
-                        .map(EthernetPacket::Ip)
-                }
+                self.process_ipv4(
+                    sockets,
+                    &ipv4_packet,
+                    #[cfg(feature = "proto-ipv4-fragmentation")]
+                    &mut fragments.assembler,
+                )
+                .map(EthernetPacket::Ip)
             }
             #[cfg(feature = "proto-ipv6")]
             EthernetProtocol::Ipv6 => {

+ 7 - 20
src/iface/interface/ipv4.rs

@@ -1,15 +1,4 @@
-use super::check;
-use super::icmp_reply_payload_len;
-use super::InterfaceInner;
-use super::IpPacket;
-use super::PacketAssemblerSet;
-use super::SocketSet;
-
-#[cfg(feature = "medium-ethernet")]
-use super::EthernetPacket;
-
-#[cfg(feature = "proto-ipv4-fragmentation")]
-use super::Ipv4OutPacket;
+use super::*;
 
 #[cfg(feature = "socket-dhcpv4")]
 use crate::socket::dhcpv4;
@@ -22,12 +11,12 @@ use crate::time::{Duration, Instant};
 use crate::wire::*;
 
 impl InterfaceInner {
-    pub(super) fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>(
+    pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
-        ipv4_packet: &Ipv4Packet<&'payload T>,
-        _fragments: Option<&'output mut PacketAssemblerSet<Ipv4FragKey>>,
-    ) -> Option<IpPacket<'output>> {
+        ipv4_packet: &Ipv4Packet<&'a T>,
+        #[cfg(feature = "proto-ipv4-fragmentation")] assembler: &'a mut PacketAssemblerSet<FragKey>,
+    ) -> Option<IpPacket<'a>> {
         let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
         if !self.is_unicast_v4(ipv4_repr.src_addr) {
             // Discard packets with non-unicast source addresses.
@@ -39,12 +28,10 @@ impl InterfaceInner {
         let ip_payload = {
             const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(90);
 
-            let fragments = _fragments.unwrap();
-
             if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 {
-                let key = ipv4_packet.get_key();
+                let key = FragKey::Ipv4(ipv4_packet.get_key());
 
-                let f = match fragments.get(&key, self.now + REASSEMBLY_TIMEOUT) {
+                let f = match assembler.get(&key, self.now + REASSEMBLY_TIMEOUT) {
                     Ok(f) => f,
                     Err(_) => {
                         net_debug!("No available packet assembler for fragmented packet");

+ 25 - 23
src/iface/interface/mod.rs

@@ -43,13 +43,23 @@ const FRAGMENTATION_BUFFER_SIZE: usize = 1500;
 #[cfg(feature = "proto-sixlowpan")]
 const SIXLOWPAN_ADDRESS_CONTEXT_COUNT: usize = 4;
 
+#[cfg(feature = "_proto-fragmentation")]
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub(crate) enum FragKey {
+    #[cfg(feature = "proto-ipv4-fragmentation")]
+    Ipv4(Ipv4FragKey),
+    #[cfg(feature = "proto-sixlowpan-fragmentation")]
+    Sixlowpan(SixlowpanFragKey),
+}
+
 pub(crate) struct FragmentsBuffer {
     #[cfg(feature = "proto-sixlowpan")]
     decompress_buf: [u8; sixlowpan::MAX_DECOMPRESSED_LEN],
-    #[cfg(feature = "proto-ipv4-fragmentation")]
-    pub(crate) ipv4_fragments: PacketAssemblerSet<Ipv4FragKey>,
-    #[cfg(feature = "proto-sixlowpan-fragmentation")]
-    sixlowpan_fragments: PacketAssemblerSet<SixlowpanFragKey>,
+
+    #[cfg(feature = "_proto-fragmentation")]
+    pub(crate) assembler: PacketAssemblerSet<FragKey>,
+
     #[cfg(feature = "proto-sixlowpan-fragmentation")]
     sixlowpan_reassembly_timeout: Duration,
 }
@@ -558,10 +568,8 @@ impl Interface {
                 #[cfg(feature = "proto-sixlowpan")]
                 decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
 
-                #[cfg(feature = "proto-ipv4-fragmentation")]
-                ipv4_fragments: PacketAssemblerSet::new(),
-                #[cfg(feature = "proto-sixlowpan-fragmentation")]
-                sixlowpan_fragments: PacketAssemblerSet::new(),
+                #[cfg(feature = "_proto-fragmentation")]
+                assembler: PacketAssemblerSet::new(),
                 #[cfg(feature = "proto-sixlowpan-fragmentation")]
                 sixlowpan_reassembly_timeout: Duration::from_secs(60),
             },
@@ -767,11 +775,8 @@ impl Interface {
     {
         self.inner.now = timestamp;
 
-        #[cfg(feature = "proto-ipv4-fragmentation")]
-        self.fragments.ipv4_fragments.remove_expired(timestamp);
-
-        #[cfg(feature = "proto-sixlowpan-fragmentation")]
-        self.fragments.sixlowpan_fragments.remove_expired(timestamp);
+        #[cfg(feature = "_proto-fragmentation")]
+        self.fragments.assembler.remove_expired(timestamp);
 
         #[cfg(feature = "proto-ipv4-fragmentation")]
         if self.ipv4_egress(device) {
@@ -1328,22 +1333,19 @@ impl InterfaceInner {
         &mut self,
         sockets: &mut SocketSet,
         ip_payload: &'frame T,
-        _fragments: &'frame mut FragmentsBuffer,
+        fragments: &'frame mut FragmentsBuffer,
     ) -> Option<IpPacket<'frame>> {
         match IpVersion::of_packet(ip_payload.as_ref()) {
             #[cfg(feature = "proto-ipv4")]
             Ok(IpVersion::Ipv4) => {
                 let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
 
-                #[cfg(feature = "proto-ipv4-fragmentation")]
-                {
-                    self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments))
-                }
-
-                #[cfg(not(feature = "proto-ipv4-fragmentation"))]
-                {
-                    self.process_ipv4(sockets, &ipv4_packet, None)
-                }
+                self.process_ipv4(
+                    sockets,
+                    &ipv4_packet,
+                    #[cfg(feature = "proto-ipv4-fragmentation")]
+                    &mut fragments.assembler,
+                )
             }
             #[cfg(feature = "proto-ipv6")]
             Ok(IpVersion::Ipv6) => {

+ 3 - 2
src/iface/interface/sixlowpan.rs

@@ -98,6 +98,7 @@ impl InterfaceInner {
         f: &'output mut FragmentsBuffer,
     ) -> Option<&'output [u8]> {
         use crate::iface::fragmentation::{AssemblerError, AssemblerFullError};
+        use crate::iface::interface::FragKey;
 
         // We have a fragment header, which means we cannot process the 6LoWPAN packet,
         // unless we have a complete one after processing this fragment.
@@ -105,7 +106,7 @@ impl InterfaceInner {
 
         // 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);
+        let key = FragKey::Sixlowpan(frag.get_key(ieee802154_repr));
 
         // The offset of this fragment in increments of 8 octets.
         let offset = frag.datagram_offset() as usize * 8;
@@ -116,7 +117,7 @@ impl InterfaceInner {
         // We also pass the header size, since this is needed when other fragments
         // (other than the first one) are added.
         let frag_slot = match f
-            .sixlowpan_fragments
+            .assembler
             .get(&key, self.now + f.sixlowpan_reassembly_timeout)
         {
             Ok(frag) => frag,

+ 10 - 28
src/iface/interface/tests.rs

@@ -167,14 +167,12 @@ fn test_no_icmp_no_unicast_ipv4() {
     // ICMP error response when the destination address is a
     // broadcast address
 
-    #[cfg(not(feature = "proto-ipv4-fragmentation"))]
-    assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None);
-    #[cfg(feature = "proto-ipv4-fragmentation")]
     assert_eq!(
         iface.inner.process_ipv4(
             &mut sockets,
             &frame,
-            Some(&mut iface.fragments.ipv4_fragments)
+            #[cfg(feature = "proto-ipv4-fragmentation")]
+            &mut iface.fragments.assembler
         ),
         None
     );
@@ -255,18 +253,12 @@ fn test_icmp_error_no_payload() {
     // Ensure that the unknown protocol triggers an error response.
     // And we correctly handle no payload.
 
-    #[cfg(not(feature = "proto-ipv4-fragmentation"))]
-    assert_eq!(
-        iface.inner.process_ipv4(&mut sockets, &frame, None),
-        Some(expected_repr)
-    );
-
-    #[cfg(feature = "proto-ipv4-fragmentation")]
     assert_eq!(
         iface.inner.process_ipv4(
             &mut sockets,
             &frame,
-            Some(&mut iface.fragments.ipv4_fragments)
+            #[cfg(feature = "proto-ipv4-fragmentation")]
+            &mut iface.fragments.assembler
         ),
         Some(expected_repr)
     );
@@ -571,18 +563,12 @@ fn test_handle_ipv4_broadcast() {
     };
     let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr));
 
-    #[cfg(not(feature = "proto-ipv4-fragmentation"))]
-    assert_eq!(
-        iface.inner.process_ipv4(&mut sockets, &frame, None),
-        Some(expected_packet)
-    );
-
-    #[cfg(feature = "proto-ipv4-fragmentation")]
     assert_eq!(
         iface.inner.process_ipv4(
             &mut sockets,
             &frame,
-            Some(&mut iface.fragments.ipv4_fragments)
+            #[cfg(feature = "proto-ipv4-fragmentation")]
+            &mut iface.fragments.assembler
         ),
         Some(expected_packet)
     );
@@ -1277,14 +1263,12 @@ fn test_raw_socket_no_reply() {
         Ipv4Packet::new_unchecked(&bytes)
     };
 
-    #[cfg(not(feature = "proto-ipv4-fragmentation"))]
-    assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None);
-    #[cfg(feature = "proto-ipv4-fragmentation")]
     assert_eq!(
         iface.inner.process_ipv4(
             &mut sockets,
             &frame,
-            Some(&mut iface.fragments.ipv4_fragments)
+            #[cfg(feature = "proto-ipv4-fragmentation")]
+            &mut iface.fragments.assembler
         ),
         None
     );
@@ -1368,14 +1352,12 @@ fn test_raw_socket_with_udp_socket() {
         Ipv4Packet::new_unchecked(&bytes)
     };
 
-    #[cfg(not(feature = "proto-ipv4-fragmentation"))]
-    assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None);
-    #[cfg(feature = "proto-ipv4-fragmentation")]
     assert_eq!(
         iface.inner.process_ipv4(
             &mut sockets,
             &frame,
-            Some(&mut iface.fragments.ipv4_fragments)
+            #[cfg(feature = "proto-ipv4-fragmentation")]
+            &mut iface.fragments.assembler
         ),
         None
     );

+ 1 - 0
src/wire/ipv4.rs

@@ -27,6 +27,7 @@ pub const MIN_MTU: usize = 576;
 pub const ADDR_SIZE: usize = 4;
 
 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Key {
     id: u16,
     src_addr: Address,