瀏覽代碼

rewrite IpPacket

Thibaut Vandervelden 1 年之前
父節點
當前提交
618d7e33a7

+ 4 - 1
Cargo.toml

@@ -50,7 +50,10 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ]
 "proto-igmp" = ["proto-ipv4"]
 "proto-dhcpv4" = ["proto-ipv4"]
 "proto-ipv6" = []
-"proto-rpl" = []
+"proto-ipv6-hbh" = ["proto-ipv6"]
+"proto-ipv6-fragmentation" = ["proto-ipv6", "_proto-fragmentation"]
+"proto-ipv6-routing" = ["proto-ipv6"]
+"proto-rpl" = ["proto-ipv6-hbh", "proto-ipv6-routing"]
 "proto-sixlowpan" = ["proto-ipv6"]
 "proto-sixlowpan-fragmentation" = ["proto-sixlowpan", "_proto-fragmentation"]
 "proto-dns" = []

+ 17 - 11
src/iface/interface/ipv4.rs

@@ -8,14 +8,14 @@ use crate::socket::AnySocket;
 
 use crate::phy::{Medium, TxToken};
 use crate::time::Instant;
-use crate::wire::*;
+use crate::wire::{Ipv4Packet as Ipv4PacketWire, *};
 
 impl InterfaceInner {
     pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
         meta: PacketMeta,
-        ipv4_packet: &Ipv4Packet<&'a T>,
+        ipv4_packet: &Ipv4PacketWire<&'a T>,
         frag: &'a mut FragmentsBuffer,
     ) -> Option<IpPacket<'a>> {
         let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
@@ -306,7 +306,10 @@ impl InterfaceInner {
                 payload_len: icmp_repr.buffer_len(),
                 hop_limit: 64,
             };
-            Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr)))
+            Some(IpPacket::new_ipv4(
+                ipv4_reply_repr,
+                IpPayload::Icmpv4(icmp_repr),
+            ))
         } else if self.is_broadcast_v4(ipv4_repr.dst_addr) {
             // Only reply to broadcasts for echo replies and not other ICMP messages
             match icmp_repr {
@@ -319,7 +322,10 @@ impl InterfaceInner {
                             payload_len: icmp_repr.buffer_len(),
                             hop_limit: 64,
                         };
-                        Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr)))
+                        Some(IpPacket::new_ipv4(
+                            ipv4_reply_repr,
+                            IpPayload::Icmpv4(icmp_repr),
+                        ))
                     }
                     None => None,
                 },
@@ -373,7 +379,7 @@ impl InterfaceInner {
             }
 
             let mut packet =
-                Ipv4Packet::new_unchecked(&mut tx_buffer[..frag.ipv4.repr.buffer_len()]);
+                Ipv4PacketWire::new_unchecked(&mut tx_buffer[..frag.ipv4.repr.buffer_len()]);
             frag.ipv4.repr.emit(&mut packet, &caps.checksum);
             packet.set_ident(frag.ipv4.ident);
             packet.set_more_frags(more_frags);
@@ -405,7 +411,7 @@ impl InterfaceInner {
             group_addr,
             version,
         };
-        let pkt = IpPacket::Igmp((
+        let pkt = IpPacket::new_ipv4(
             Ipv4Repr {
                 src_addr: iface_addr,
                 // Send to the group being reported
@@ -415,8 +421,8 @@ impl InterfaceInner {
                 hop_limit: 1,
                 // [#183](https://github.com/m-labs/smoltcp/issues/183).
             },
-            igmp_repr,
-        ));
+            IpPayload::Igmp(igmp_repr),
+        );
         Some(pkt)
     }
 
@@ -427,7 +433,7 @@ impl InterfaceInner {
     ) -> Option<IpPacket<'any>> {
         self.ipv4_addr().map(|iface_addr| {
             let igmp_repr = IgmpRepr::LeaveGroup { group_addr };
-            IpPacket::Igmp((
+            IpPacket::new_ipv4(
                 Ipv4Repr {
                     src_addr: iface_addr,
                     dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS,
@@ -435,8 +441,8 @@ impl InterfaceInner {
                     payload_len: igmp_repr.buffer_len(),
                     hop_limit: 1,
                 },
-                igmp_repr,
-            ))
+                IpPayload::Igmp(igmp_repr),
+            )
         })
     }
 }

+ 6 - 3
src/iface/interface/ipv6.rs

@@ -1,8 +1,8 @@
 use super::check;
 use super::icmp_reply_payload_len;
 use super::InterfaceInner;
-use super::IpPacket;
 use super::SocketSet;
+use super::{IpPacket, IpPayload};
 
 #[cfg(feature = "socket-icmp")]
 use crate::socket::icmp;
@@ -232,7 +232,7 @@ impl InterfaceInner {
                         hop_limit: 0xff,
                         payload_len: advert.buffer_len(),
                     };
-                    Some(IpPacket::Icmpv6((ip_repr, advert)))
+                    Some(IpPacket::new_ipv6(ip_repr, IpPayload::Icmpv6(advert)))
                 } else {
                     None
                 }
@@ -300,7 +300,10 @@ impl InterfaceInner {
                 payload_len: icmp_repr.buffer_len(),
                 hop_limit: 64,
             };
-            Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr)))
+            Some(IpPacket::new_ipv6(
+                ipv6_reply_repr,
+                IpPayload::Icmpv6(icmp_repr),
+            ))
         } else {
             // Do not send any ICMP replies to a broadcast destination address.
             None

+ 47 - 177
src/iface/interface/mod.rs

@@ -23,7 +23,8 @@ mod igmp;
 #[cfg(feature = "proto-igmp")]
 pub use igmp::MulticastError;
 
-use core::cmp;
+use super::ip_packet::*;
+
 use core::result::Result;
 use heapless::{LinearMap, Vec};
 
@@ -44,6 +45,12 @@ use crate::rand::Rand;
 use crate::socket::dns;
 use crate::socket::*;
 use crate::time::{Duration, Instant};
+
+#[cfg(feature = "proto-ipv4")]
+use crate::wire::Ipv4Packet as Ipv4PacketWire;
+#[cfg(feature = "proto-ipv6")]
+use crate::wire::Ipv6Packet as Ipv6PacketWire;
+
 use crate::wire::*;
 
 #[cfg(feature = "_proto-fragmentation")]
@@ -300,158 +307,6 @@ impl Config {
     }
 }
 
-#[derive(Debug, PartialEq)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-#[cfg(feature = "medium-ethernet")]
-enum EthernetPacket<'a> {
-    #[cfg(feature = "proto-ipv4")]
-    Arp(ArpRepr),
-    Ip(IpPacket<'a>),
-}
-
-#[derive(Debug, PartialEq)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-pub(crate) enum IpPacket<'a> {
-    #[cfg(feature = "proto-ipv4")]
-    Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)),
-    #[cfg(feature = "proto-igmp")]
-    Igmp((Ipv4Repr, IgmpRepr)),
-    #[cfg(feature = "proto-ipv6")]
-    Icmpv6((Ipv6Repr, Icmpv6Repr<'a>)),
-    #[cfg(feature = "socket-raw")]
-    Raw((IpRepr, &'a [u8])),
-    #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
-    Udp((IpRepr, UdpRepr, &'a [u8])),
-    #[cfg(feature = "socket-tcp")]
-    Tcp((IpRepr, TcpRepr<'a>)),
-    #[cfg(feature = "socket-dhcpv4")]
-    Dhcpv4((Ipv4Repr, UdpRepr, DhcpRepr<'a>)),
-}
-
-impl<'a> IpPacket<'a> {
-    pub(crate) fn ip_repr(&self) -> IpRepr {
-        match self {
-            #[cfg(feature = "proto-ipv4")]
-            IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr),
-            #[cfg(feature = "proto-igmp")]
-            IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr),
-            #[cfg(feature = "proto-ipv6")]
-            IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(*ipv6_repr),
-            #[cfg(feature = "socket-raw")]
-            IpPacket::Raw((ip_repr, _)) => ip_repr.clone(),
-            #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
-            IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(),
-            #[cfg(feature = "socket-tcp")]
-            IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(),
-            #[cfg(feature = "socket-dhcpv4")]
-            IpPacket::Dhcpv4((ipv4_repr, _, _)) => IpRepr::Ipv4(*ipv4_repr),
-        }
-    }
-
-    pub(crate) fn emit_payload(
-        &self,
-        _ip_repr: &IpRepr,
-        payload: &mut [u8],
-        caps: &DeviceCapabilities,
-    ) {
-        match self {
-            #[cfg(feature = "proto-ipv4")]
-            IpPacket::Icmpv4((_, icmpv4_repr)) => {
-                icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum)
-            }
-            #[cfg(feature = "proto-igmp")]
-            IpPacket::Igmp((_, igmp_repr)) => {
-                igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload))
-            }
-            #[cfg(feature = "proto-ipv6")]
-            IpPacket::Icmpv6((_, icmpv6_repr)) => icmpv6_repr.emit(
-                &_ip_repr.src_addr(),
-                &_ip_repr.dst_addr(),
-                &mut Icmpv6Packet::new_unchecked(payload),
-                &caps.checksum,
-            ),
-            #[cfg(feature = "socket-raw")]
-            IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet),
-            #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
-            IpPacket::Udp((_, udp_repr, inner_payload)) => {
-                udp_repr.emit(
-                    &mut UdpPacket::new_unchecked(payload),
-                    &_ip_repr.src_addr(),
-                    &_ip_repr.dst_addr(),
-                    inner_payload.len(),
-                    |buf| buf.copy_from_slice(inner_payload),
-                    &caps.checksum,
-                );
-            }
-            #[cfg(feature = "socket-tcp")]
-            IpPacket::Tcp((_, mut tcp_repr)) => {
-                // This is a terrible hack to make TCP performance more acceptable on systems
-                // where the TCP buffers are significantly larger than network buffers,
-                // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window)
-                // together with four 1500 B Ethernet receive buffers. If left untreated,
-                // this would result in our peer pushing our window and sever packet loss.
-                //
-                // I'm really not happy about this "solution" but I don't know what else to do.
-                if let Some(max_burst_size) = caps.max_burst_size {
-                    let mut max_segment_size = caps.max_transmission_unit;
-                    max_segment_size -= _ip_repr.header_len();
-                    max_segment_size -= tcp_repr.header_len();
-
-                    let max_window_size = max_burst_size * max_segment_size;
-                    if tcp_repr.window_len as usize > max_window_size {
-                        tcp_repr.window_len = max_window_size as u16;
-                    }
-                }
-
-                tcp_repr.emit(
-                    &mut TcpPacket::new_unchecked(payload),
-                    &_ip_repr.src_addr(),
-                    &_ip_repr.dst_addr(),
-                    &caps.checksum,
-                );
-            }
-            #[cfg(feature = "socket-dhcpv4")]
-            IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => udp_repr.emit(
-                &mut UdpPacket::new_unchecked(payload),
-                &_ip_repr.src_addr(),
-                &_ip_repr.dst_addr(),
-                dhcp_repr.buffer_len(),
-                |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
-                &caps.checksum,
-            ),
-        };
-    }
-}
-
-#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
-fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
-    // Send back as much of the original payload as will fit within
-    // the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for
-    // more details.
-    //
-    // Since the entire network layer packet must fit within the minimum
-    // MTU supported, the payload must not exceed the following:
-    //
-    // <min mtu> - IP Header Size * 2 - ICMPv4 DstUnreachable hdr size
-    cmp::min(len, mtu - header_len * 2 - 8)
-}
-
-#[cfg(feature = "proto-igmp")]
-enum IgmpReportState {
-    Inactive,
-    ToGeneralQuery {
-        version: IgmpVersion,
-        timeout: Instant,
-        interval: Duration,
-        next_index: usize,
-    },
-    ToSpecificQuery {
-        version: IgmpVersion,
-        timeout: Instant,
-        group: Ipv4Address,
-    },
-}
-
 impl Interface {
     /// Create a network interface using the previously provided configuration.
     ///
@@ -905,8 +760,12 @@ impl Interface {
 
             let result = match &mut item.socket {
                 #[cfg(feature = "socket-raw")]
-                Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, response| {
-                    respond(inner, PacketMeta::default(), IpPacket::Raw(response))
+                Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, (ip, raw)| {
+                    respond(
+                        inner,
+                        PacketMeta::default(),
+                        IpPacket::new(ip, IpPayload::Raw(raw)),
+                    )
                 }),
                 #[cfg(feature = "socket-icmp")]
                 Socket::Icmp(socket) => {
@@ -915,40 +774,50 @@ impl Interface {
                         (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond(
                             inner,
                             PacketMeta::default(),
-                            IpPacket::Icmpv4((ipv4_repr, icmpv4_repr)),
+                            IpPacket::new_ipv4(ipv4_repr, IpPayload::Icmpv4(icmpv4_repr)),
                         ),
                         #[cfg(feature = "proto-ipv6")]
                         (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond(
                             inner,
                             PacketMeta::default(),
-                            IpPacket::Icmpv6((ipv6_repr, icmpv6_repr)),
+                            IpPacket::new_ipv6(ipv6_repr, IpPayload::Icmpv6(icmpv6_repr)),
                         ),
                         #[allow(unreachable_patterns)]
                         _ => unreachable!(),
                     })
                 }
                 #[cfg(feature = "socket-udp")]
-                Socket::Udp(socket) => socket.dispatch(&mut self.inner, |inner, meta, response| {
-                    respond(inner, meta, IpPacket::Udp(response))
-                }),
+                Socket::Udp(socket) => {
+                    socket.dispatch(&mut self.inner, |inner, meta, (ip, udp, payload)| {
+                        respond(inner, meta, IpPacket::new(ip, IpPayload::Udp(udp, payload)))
+                    })
+                }
                 #[cfg(feature = "socket-tcp")]
-                Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, response| {
-                    respond(inner, PacketMeta::default(), IpPacket::Tcp(response))
+                Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, (ip, tcp)| {
+                    respond(
+                        inner,
+                        PacketMeta::default(),
+                        IpPacket::new(ip, IpPayload::Tcp(tcp)),
+                    )
                 }),
                 #[cfg(feature = "socket-dhcpv4")]
-                Socket::Dhcpv4(socket) => socket.dispatch(&mut self.inner, |inner, response| {
-                    respond(inner, PacketMeta::default(), IpPacket::Dhcpv4(response))
-                }),
-                #[cfg(feature = "socket-dns")]
-                Socket::Dns(socket) => {
-                    socket.dispatch(&mut self.inner, |inner, (ip, udp, payload)| {
+                Socket::Dhcpv4(socket) => {
+                    socket.dispatch(&mut self.inner, |inner, (ip, udp, dhcp)| {
                         respond(
                             inner,
                             PacketMeta::default(),
-                            IpPacket::Udp((ip, udp, payload)),
+                            IpPacket::new_ipv4(ip, IpPayload::Dhcpv4(udp, dhcp)),
                         )
                     })
                 }
+                #[cfg(feature = "socket-dns")]
+                Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, (ip, udp, dns)| {
+                    respond(
+                        inner,
+                        PacketMeta::default(),
+                        IpPacket::new(ip, IpPayload::Udp(udp, dns)),
+                    )
+                }),
             };
 
             match result {
@@ -1293,13 +1162,13 @@ impl InterfaceInner {
         match IpVersion::of_packet(ip_payload.as_ref()) {
             #[cfg(feature = "proto-ipv4")]
             Ok(IpVersion::Ipv4) => {
-                let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
+                let ipv4_packet = check!(Ipv4PacketWire::new_checked(ip_payload));
 
                 self.process_ipv4(sockets, meta, &ipv4_packet, frag)
             }
             #[cfg(feature = "proto-ipv6")]
             Ok(IpVersion::Ipv6) => {
-                let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
+                let ipv6_packet = check!(Ipv6PacketWire::new_checked(ip_payload));
                 self.process_ipv6(sockets, meta, &ipv6_packet)
             }
             // Drop all other traffic.
@@ -1452,7 +1321,7 @@ impl InterfaceInner {
             if tcp_socket.accepts(self, &ip_repr, &tcp_repr) {
                 return tcp_socket
                     .process(self, &ip_repr, &tcp_repr)
-                    .map(IpPacket::Tcp);
+                    .map(|(ip, tcp)| IpPacket::new(ip, IpPayload::Tcp(tcp)));
             }
         }
 
@@ -1461,7 +1330,8 @@ impl InterfaceInner {
             None
         } else {
             // The packet wasn't handled by a socket, send a TCP RST packet.
-            Some(IpPacket::Tcp(tcp::Socket::rst_reply(&ip_repr, &tcp_repr)))
+            let (ip, tcp) = tcp::Socket::rst_reply(&ip_repr, &tcp_repr);
+            Some(IpPacket::new(ip, IpPayload::Tcp(tcp)))
         }
     }
 
@@ -1638,7 +1508,7 @@ impl InterfaceInner {
                     lladdr: Some(self.hardware_addr.into()),
                 });
 
-                let packet = IpPacket::Icmpv6((
+                let packet = IpPacket::new_ipv6(
                     Ipv6Repr {
                         src_addr,
                         dst_addr: dst_addr.solicited_node(),
@@ -1646,8 +1516,8 @@ impl InterfaceInner {
                         payload_len: solicit.buffer_len(),
                         hop_limit: 0xff,
                     },
-                    solicit,
-                ));
+                    IpPayload::Icmpv6(solicit),
+                );
 
                 if let Err(e) =
                     self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter)
@@ -1805,7 +1675,7 @@ impl InterfaceInner {
                         // Emit the IP header to the buffer.
                         emit_ip(&ip_repr, &mut frag.buffer);
 
-                        let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
+                        let mut ipv4_packet = Ipv4PacketWire::new_unchecked(&mut frag.buffer[..]);
                         frag.ipv4.ident = ipv4_id;
                         ipv4_packet.set_ident(ipv4_id);
                         ipv4_packet.set_more_frags(true);

+ 22 - 22
src/iface/interface/sixlowpan.rs

@@ -1,7 +1,7 @@
 use super::*;
 
 use crate::phy::ChecksumCapabilities;
-use crate::wire::*;
+use crate::wire::{Ipv6Packet as Ipv6PacketWire, *};
 
 // Max len of non-fragmented packets after decompression (including ipv6 header and payload)
 // TODO: lower. Should be (6lowpan mtu) - (min 6lowpan header size) + (max ipv6 header size)
@@ -48,7 +48,7 @@ impl InterfaceInner {
             }
         };
 
-        self.process_ipv6(sockets, meta, &check!(Ipv6Packet::new_checked(payload)))
+        self.process_ipv6(sockets, meta, &check!(Ipv6PacketWire::new_checked(payload)))
     }
 
     #[cfg(feature = "proto-sixlowpan-fragmentation")]
@@ -187,7 +187,7 @@ impl InterfaceInner {
         };
 
         // Emit the decompressed IPHC header (decompressed to an IPv6 header).
-        let mut ipv6_packet = Ipv6Packet::new_unchecked(&mut buffer[..ipv6_repr.buffer_len()]);
+        let mut ipv6_packet = Ipv6PacketWire::new_unchecked(&mut buffer[..ipv6_repr.buffer_len()]);
         ipv6_repr.emit(&mut ipv6_packet);
         let buffer = &mut buffer[ipv6_repr.buffer_len()..];
 
@@ -250,12 +250,12 @@ impl InterfaceInner {
             ll_src_addr: ieee_repr.src_addr,
             dst_addr,
             ll_dst_addr: ieee_repr.dst_addr,
-            next_header: match &packet {
-                IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
+            next_header: match &packet.payload() {
+                IpPayload::Icmpv6(..) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
                 #[cfg(feature = "socket-tcp")]
-                IpPacket::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
+                IpPayload::Tcp(..) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
                 #[cfg(feature = "socket-udp")]
-                IpPacket::Udp(_) => SixlowpanNextHeader::Compressed,
+                IpPayload::Udp(..) => SixlowpanNextHeader::Compressed,
                 #[allow(unreachable_patterns)]
                 _ => {
                     net_debug!("dispatch_ieee802154: dropping, unhandled protocol.");
@@ -275,20 +275,20 @@ impl InterfaceInner {
         let mut _compressed_headers_len = iphc_repr.buffer_len();
         let mut _uncompressed_headers_len = ip_repr.header_len();
 
-        match packet {
+        match packet.payload() {
             #[cfg(feature = "socket-udp")]
-            IpPacket::Udp((_, udpv6_repr, payload)) => {
-                let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr);
+            IpPayload::Udp(udpv6_repr, payload) => {
+                let udp_repr = SixlowpanUdpNhcRepr(*udpv6_repr);
                 _compressed_headers_len += udp_repr.header_len();
                 _uncompressed_headers_len += udpv6_repr.header_len();
                 total_size += udp_repr.header_len() + payload.len();
             }
             #[cfg(feature = "socket-tcp")]
-            IpPacket::Tcp((_, tcp_repr)) => {
+            IpPayload::Tcp(tcp_repr) => {
                 total_size += tcp_repr.buffer_len();
             }
             #[cfg(feature = "proto-ipv6")]
-            IpPacket::Icmpv6((_, icmp_repr)) => {
+            IpPayload::Icmpv6(icmp_repr) => {
                 total_size += icmp_repr.buffer_len();
             }
             #[allow(unreachable_patterns)]
@@ -328,10 +328,10 @@ impl InterfaceInner {
 
                 let b = &mut pkt.buffer[iphc_repr.buffer_len()..];
 
-                match packet {
+                match packet.payload() {
                     #[cfg(feature = "socket-udp")]
-                    IpPacket::Udp((_, udpv6_repr, payload)) => {
-                        let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr);
+                    IpPayload::Udp(udpv6_repr, payload) => {
+                        let udp_repr = SixlowpanUdpNhcRepr(*udpv6_repr);
                         let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked(
                             &mut b[..udp_repr.header_len() + payload.len()],
                         );
@@ -344,7 +344,7 @@ impl InterfaceInner {
                         );
                     }
                     #[cfg(feature = "socket-tcp")]
-                    IpPacket::Tcp((_, tcp_repr)) => {
+                    IpPayload::Tcp(tcp_repr) => {
                         let mut tcp_packet =
                             TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]);
                         tcp_repr.emit(
@@ -355,7 +355,7 @@ impl InterfaceInner {
                         );
                     }
                     #[cfg(feature = "proto-ipv6")]
-                    IpPacket::Icmpv6((_, icmp_repr)) => {
+                    IpPayload::Icmpv6(icmp_repr) => {
                         let mut icmp_packet =
                             Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]);
                         icmp_repr.emit(
@@ -444,10 +444,10 @@ impl InterfaceInner {
                 iphc_repr.emit(&mut iphc_packet);
                 tx_buf = &mut tx_buf[iphc_repr.buffer_len()..];
 
-                match packet {
+                match packet.payload() {
                     #[cfg(feature = "socket-udp")]
-                    IpPacket::Udp((_, udpv6_repr, payload)) => {
-                        let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr);
+                    IpPayload::Udp(udpv6_repr, payload) => {
+                        let udp_repr = SixlowpanUdpNhcRepr(*udpv6_repr);
                         let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked(
                             &mut tx_buf[..udp_repr.header_len() + payload.len()],
                         );
@@ -460,7 +460,7 @@ impl InterfaceInner {
                         );
                     }
                     #[cfg(feature = "socket-tcp")]
-                    IpPacket::Tcp((_, tcp_repr)) => {
+                    IpPayload::Tcp(tcp_repr) => {
                         let mut tcp_packet =
                             TcpPacket::new_unchecked(&mut tx_buf[..tcp_repr.buffer_len()]);
                         tcp_repr.emit(
@@ -471,7 +471,7 @@ impl InterfaceInner {
                         );
                     }
                     #[cfg(feature = "proto-ipv6")]
-                    IpPacket::Icmpv6((_, icmp_repr)) => {
+                    IpPayload::Icmpv6(icmp_repr) => {
                         let mut icmp_packet =
                             Icmpv6Packet::new_unchecked(&mut tx_buf[..icmp_repr.buffer_len()]);
                         icmp_repr.emit(

+ 29 - 22
src/iface/interface/tests/ipv4.rs

@@ -23,7 +23,7 @@ fn test_no_icmp_no_unicast(#[case] medium: Medium) {
 
     let mut bytes = vec![0u8; 54];
     repr.emit(&mut bytes, &ChecksumCapabilities::default());
-    let frame = Ipv4Packet::new_unchecked(&bytes);
+    let frame = Ipv4PacketWire::new_unchecked(&bytes);
 
     // Ensure that the unknown protocol frame does not trigger an
     // ICMP error response when the destination address is a
@@ -60,7 +60,7 @@ fn test_icmp_error_no_payload(#[case] medium: Medium) {
 
     let mut bytes = vec![0u8; 34];
     repr.emit(&mut bytes, &ChecksumCapabilities::default());
-    let frame = Ipv4Packet::new_unchecked(&bytes);
+    let frame = Ipv4PacketWire::new_unchecked(&bytes);
 
     // The expected Destination Unreachable response due to the
     // unknown protocol
@@ -76,7 +76,7 @@ fn test_icmp_error_no_payload(#[case] medium: Medium) {
         data: &NO_BYTES,
     };
 
-    let expected_repr = IpPacket::Icmpv4((
+    let expected_repr = IpPacket::new_ipv4(
         Ipv4Repr {
             src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
             dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
@@ -84,8 +84,8 @@ fn test_icmp_error_no_payload(#[case] medium: Medium) {
             payload_len: icmp_repr.buffer_len(),
             hop_limit: 64,
         },
-        icmp_repr,
-    ));
+        IpPayload::Icmpv4(icmp_repr),
+    );
 
     // Ensure that the unknown protocol triggers an error response.
     // And we correctly handle no payload.
@@ -222,7 +222,7 @@ fn test_icmp_error_port_unreachable(#[case] medium: Medium) {
         },
         data,
     };
-    let expected_repr = IpPacket::Icmpv4((
+    let expected_repr = IpPacket::new_ipv4(
         Ipv4Repr {
             src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
             dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
@@ -230,8 +230,8 @@ fn test_icmp_error_port_unreachable(#[case] medium: Medium) {
             payload_len: icmp_repr.buffer_len(),
             hop_limit: 64,
         },
-        icmp_repr,
-    ));
+        IpPayload::Icmpv4(icmp_repr),
+    );
 
     // Ensure that the unknown protocol triggers an error response.
     // And we correctly handle no payload.
@@ -289,7 +289,7 @@ fn test_icmp_error_port_unreachable(#[case] medium: Medium) {
 #[case(Medium::Ethernet)]
 #[cfg(feature = "medium-ethernet")]
 fn test_handle_ipv4_broadcast(#[case] medium: Medium) {
-    use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet};
+    use crate::wire::{Icmpv4Packet, Icmpv4Repr};
 
     let (mut iface, mut sockets, _device) = setup(medium);
 
@@ -317,14 +317,14 @@ fn test_handle_ipv4_broadcast(#[case] medium: Medium) {
     let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()];
     let frame = {
         ipv4_repr.emit(
-            &mut Ipv4Packet::new_unchecked(&mut bytes),
+            &mut Ipv4PacketWire::new_unchecked(&mut bytes),
             &ChecksumCapabilities::default(),
         );
         icmpv4_repr.emit(
             &mut Icmpv4Packet::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]),
             &ChecksumCapabilities::default(),
         );
-        Ipv4Packet::new_unchecked(&bytes)
+        Ipv4PacketWire::new_unchecked(&bytes)
     };
 
     // Expected ICMPv4 echo reply
@@ -340,7 +340,8 @@ fn test_handle_ipv4_broadcast(#[case] medium: Medium) {
         hop_limit: 64,
         payload_len: expected_icmpv4_repr.buffer_len(),
     };
-    let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr));
+    let expected_packet =
+        IpPacket::new_ipv4(expected_ipv4_repr, IpPayload::Icmpv4(expected_icmpv4_repr));
 
     assert_eq!(
         iface.inner.process_ipv4(
@@ -591,7 +592,10 @@ fn test_icmpv4_socket(#[case] medium: Medium) {
     };
     assert_eq!(
         iface.inner.process_icmpv4(&mut sockets, ip_repr, icmp_data),
-        Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))
+        Some(IpPacket::new_ipv4(
+            ipv4_reply,
+            IpPayload::Icmpv4(echo_reply)
+        ))
     );
 
     let socket = sockets.get_mut::<icmp::Socket>(socket_handle);
@@ -621,10 +625,10 @@ fn test_handle_igmp(#[case] medium: Medium) {
                     #[cfg(feature = "medium-ethernet")]
                     Medium::Ethernet => {
                         let eth_frame = EthernetFrame::new_checked(frame).ok()?;
-                        Ipv4Packet::new_checked(eth_frame.payload()).ok()?
+                        Ipv4PacketWire::new_checked(eth_frame.payload()).ok()?
                     }
                     #[cfg(feature = "medium-ip")]
-                    Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?,
+                    Medium::Ip => Ipv4PacketWire::new_checked(&frame[..]).ok()?,
                     #[cfg(feature = "medium-ieee802154")]
                     Medium::Ieee802154 => todo!(),
                 };
@@ -710,7 +714,7 @@ fn test_handle_igmp(#[case] medium: Medium) {
 #[case(Medium::Ethernet)]
 #[cfg(all(feature = "socket-raw", feature = "medium-ethernet"))]
 fn test_raw_socket_no_reply(#[case] medium: Medium) {
-    use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr};
+    use crate::wire::{IpVersion, UdpPacket, UdpRepr};
 
     let (mut iface, mut sockets, _) = setup(medium);
 
@@ -755,7 +759,7 @@ fn test_raw_socket_no_reply(#[case] medium: Medium) {
     let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN];
     let frame = {
         ipv4_repr.emit(
-            &mut Ipv4Packet::new_unchecked(&mut bytes),
+            &mut Ipv4PacketWire::new_unchecked(&mut bytes),
             &ChecksumCapabilities::default(),
         );
         udp_repr.emit(
@@ -766,7 +770,7 @@ fn test_raw_socket_no_reply(#[case] medium: Medium) {
             |buf| fill_slice(buf, 0x2a),
             &ChecksumCapabilities::default(),
         );
-        Ipv4Packet::new_unchecked(&bytes)
+        Ipv4PacketWire::new_unchecked(&bytes)
     };
 
     assert_eq!(
@@ -790,7 +794,7 @@ fn test_raw_socket_no_reply(#[case] medium: Medium) {
     feature = "medium-ethernet"
 ))]
 fn test_raw_socket_with_udp_socket(#[case] medium: Medium) {
-    use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr};
+    use crate::wire::{IpEndpoint, IpVersion, UdpPacket, UdpRepr};
 
     static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f];
 
@@ -851,7 +855,7 @@ fn test_raw_socket_with_udp_socket(#[case] medium: Medium) {
     let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len()];
     let frame = {
         ipv4_repr.emit(
-            &mut Ipv4Packet::new_unchecked(&mut bytes),
+            &mut Ipv4PacketWire::new_unchecked(&mut bytes),
             &ChecksumCapabilities::default(),
         );
         udp_repr.emit(
@@ -862,7 +866,7 @@ fn test_raw_socket_with_udp_socket(#[case] medium: Medium) {
             |buf| buf.copy_from_slice(&UDP_PAYLOAD),
             &ChecksumCapabilities::default(),
         );
-        Ipv4Packet::new_unchecked(&bytes)
+        Ipv4PacketWire::new_unchecked(&bytes)
     };
 
     assert_eq!(
@@ -956,6 +960,9 @@ fn test_icmp_reply_size(#[case] medium: Medium) {
             &vec![0x2a; MAX_PAYLOAD_LEN],
             payload,
         ),
-        Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr)))
+        Some(IpPacket::new_ipv4(
+            expected_ip_repr,
+            IpPayload::Icmpv4(expected_icmp_repr)
+        ))
     );
 }

+ 60 - 57
src/iface/interface/tests/ipv6.rs

@@ -1,7 +1,7 @@
 use super::*;
 
 fn parse_ipv6(data: &[u8]) -> crate::wire::Result<IpPacket<'_>> {
-    let ipv6_header = Ipv6Packet::new_checked(data)?;
+    let ipv6_header = Ipv6PacketWire::new_checked(data)?;
     let ipv6 = Ipv6Repr::parse(&ipv6_header)?;
 
     match ipv6.next_header {
@@ -19,7 +19,7 @@ fn parse_ipv6(data: &[u8]) -> crate::wire::Result<IpPacket<'_>> {
                 &Icmpv6Packet::new_checked(ipv6_header.payload())?,
                 &Default::default(),
             )?;
-            Ok(IpPacket::Icmpv6((ipv6, icmp)))
+            Ok(IpPacket::new_ipv6(ipv6, IpPayload::Icmpv6(icmp)))
         }
         IpProtocol::Ipv6NoNxt => todo!(),
         IpProtocol::Ipv6Opts => todo!(),
@@ -49,7 +49,7 @@ fn multicast_source_address(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -76,7 +76,7 @@ fn hop_by_hop_skip_with_icmp(#[case] medium: Medium) {
         0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
     ];
 
-    let response = Some(IpPacket::Icmpv6((
+    let response = Some(IpPacket::new_ipv6(
         Ipv6Repr {
             src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
             dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
@@ -84,12 +84,12 @@ fn hop_by_hop_skip_with_icmp(#[case] medium: Medium) {
             next_header: IpProtocol::Icmpv6,
             payload_len: 19,
         },
-        Icmpv6Repr::EchoReply {
+        IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
             ident: 42,
             seq_no: 420,
             data: b"Lorem Ipsum",
-        },
-    )));
+        }),
+    ));
 
     let (mut iface, mut sockets, _device) = setup(medium);
 
@@ -97,7 +97,7 @@ fn hop_by_hop_skip_with_icmp(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -132,7 +132,7 @@ fn hop_by_hop_discard_with_icmp(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -154,7 +154,7 @@ fn imcp_empty_echo_request(#[case] medium: Medium) {
 
     assert_eq!(
         parse_ipv6(&data),
-        Ok(IpPacket::Icmpv6((
+        Ok(IpPacket::new_ipv6(
             Ipv6Repr {
                 src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
                 dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
@@ -162,15 +162,15 @@ fn imcp_empty_echo_request(#[case] medium: Medium) {
                 next_header: IpProtocol::Icmpv6,
                 payload_len: 8,
             },
-            Icmpv6Repr::EchoRequest {
+            IpPayload::Icmpv6(Icmpv6Repr::EchoRequest {
                 ident: 0,
                 seq_no: 0,
                 data: b"",
-            }
-        )))
+            })
+        ))
     );
 
-    let response = Some(IpPacket::Icmpv6((
+    let response = Some(IpPacket::new_ipv6(
         Ipv6Repr {
             src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
             dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
@@ -178,12 +178,12 @@ fn imcp_empty_echo_request(#[case] medium: Medium) {
             next_header: IpProtocol::Icmpv6,
             payload_len: 8,
         },
-        Icmpv6Repr::EchoReply {
+        IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
             ident: 0,
             seq_no: 0,
             data: b"",
-        },
-    )));
+        }),
+    ));
 
     let (mut iface, mut sockets, _device) = setup(medium);
 
@@ -191,7 +191,7 @@ fn imcp_empty_echo_request(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -214,7 +214,7 @@ fn icmp_echo_request(#[case] medium: Medium) {
 
     assert_eq!(
         parse_ipv6(&data),
-        Ok(IpPacket::Icmpv6((
+        Ok(IpPacket::new_ipv6(
             Ipv6Repr {
                 src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
                 dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
@@ -222,15 +222,15 @@ fn icmp_echo_request(#[case] medium: Medium) {
                 next_header: IpProtocol::Icmpv6,
                 payload_len: 19,
             },
-            Icmpv6Repr::EchoRequest {
+            IpPayload::Icmpv6(Icmpv6Repr::EchoRequest {
                 ident: 42,
                 seq_no: 420,
                 data: b"Lorem Ipsum",
-            }
-        )))
+            })
+        ))
     );
 
-    let response = Some(IpPacket::Icmpv6((
+    let response = Some(IpPacket::new_ipv6(
         Ipv6Repr {
             src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
             dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
@@ -238,12 +238,12 @@ fn icmp_echo_request(#[case] medium: Medium) {
             next_header: IpProtocol::Icmpv6,
             payload_len: 19,
         },
-        Icmpv6Repr::EchoReply {
+        IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
             ident: 42,
             seq_no: 420,
             data: b"Lorem Ipsum",
-        },
-    )));
+        }),
+    ));
 
     let (mut iface, mut sockets, _device) = setup(medium);
 
@@ -251,7 +251,7 @@ fn icmp_echo_request(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -274,7 +274,7 @@ fn icmp_echo_reply_as_input(#[case] medium: Medium) {
 
     assert_eq!(
         parse_ipv6(&data),
-        Ok(IpPacket::Icmpv6((
+        Ok(IpPacket::new_ipv6(
             Ipv6Repr {
                 src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
                 dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
@@ -282,12 +282,12 @@ fn icmp_echo_reply_as_input(#[case] medium: Medium) {
                 next_header: IpProtocol::Icmpv6,
                 payload_len: 19,
             },
-            Icmpv6Repr::EchoReply {
+            IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
                 ident: 0,
                 seq_no: 0,
                 data: b"Lorem Ipsum",
-            }
-        )))
+            })
+        ))
     );
 
     let response = None;
@@ -298,7 +298,7 @@ fn icmp_echo_reply_as_input(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -327,7 +327,7 @@ fn unknown_proto_with_multicast_dst_address(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -348,7 +348,7 @@ fn unknown_proto(#[case] medium: Medium) {
         0x0, 0x0, 0x0, 0x0, 0x1,
     ];
 
-    let response = Some(IpPacket::Icmpv6((
+    let response = Some(IpPacket::new_ipv6(
         Ipv6Repr {
             src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
             dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
@@ -356,7 +356,7 @@ fn unknown_proto(#[case] medium: Medium) {
             next_header: IpProtocol::Icmpv6,
             payload_len: 48,
         },
-        Icmpv6Repr::ParamProblem {
+        IpPayload::Icmpv6(Icmpv6Repr::ParamProblem {
             reason: Icmpv6ParamProblem::UnrecognizedNxtHdr,
             pointer: 40,
             header: Ipv6Repr {
@@ -367,8 +367,8 @@ fn unknown_proto(#[case] medium: Medium) {
                 payload_len: 0,
             },
             data: &[],
-        },
-    )));
+        }),
+    ));
 
     let (mut iface, mut sockets, _device) = setup(medium);
 
@@ -376,7 +376,7 @@ fn unknown_proto(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -396,7 +396,7 @@ fn ndsic_neighbor_advertisement_ethernet(#[case] medium: Medium) {
 
     assert_eq!(
         parse_ipv6(&data),
-        Ok(IpPacket::Icmpv6((
+        Ok(IpPacket::new_ipv6(
             Ipv6Repr {
                 src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
                 dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
@@ -404,12 +404,12 @@ fn ndsic_neighbor_advertisement_ethernet(#[case] medium: Medium) {
                 next_header: IpProtocol::Icmpv6,
                 payload_len: 32,
             },
-            Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
+            IpPayload::Icmpv6(Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
                 flags: NdiscNeighborFlags::SOLICITED,
                 target_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 0x0002]),
                 lladdr: Some(RawHardwareAddress::from_bytes(&[0, 0, 0, 0, 0, 1])),
-            })
-        )))
+            }))
+        ))
     );
 
     let response = None;
@@ -420,7 +420,7 @@ fn ndsic_neighbor_advertisement_ethernet(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -450,7 +450,7 @@ fn ndsic_neighbor_advertisement_ethernet_multicast_addr(#[case] medium: Medium)
 
     assert_eq!(
         parse_ipv6(&data),
-        Ok(IpPacket::Icmpv6((
+        Ok(IpPacket::new_ipv6(
             Ipv6Repr {
                 src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
                 dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
@@ -458,14 +458,14 @@ fn ndsic_neighbor_advertisement_ethernet_multicast_addr(#[case] medium: Medium)
                 next_header: IpProtocol::Icmpv6,
                 payload_len: 32,
             },
-            Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
+            IpPayload::Icmpv6(Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
                 flags: NdiscNeighborFlags::SOLICITED,
                 target_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 0x0002]),
                 lladdr: Some(RawHardwareAddress::from_bytes(&[
                     0xff, 0xff, 0xff, 0xff, 0xff, 0xff
                 ])),
-            })
-        )))
+            }))
+        ))
     );
 
     let response = None;
@@ -476,7 +476,7 @@ fn ndsic_neighbor_advertisement_ethernet_multicast_addr(#[case] medium: Medium)
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -504,7 +504,7 @@ fn ndsic_neighbor_advertisement_ieee802154(#[case] medium: Medium) {
 
     assert_eq!(
         parse_ipv6(&data),
-        Ok(IpPacket::Icmpv6((
+        Ok(IpPacket::new_ipv6(
             Ipv6Repr {
                 src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]),
                 dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]),
@@ -512,12 +512,12 @@ fn ndsic_neighbor_advertisement_ieee802154(#[case] medium: Medium) {
                 next_header: IpProtocol::Icmpv6,
                 payload_len: 40,
             },
-            Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
+            IpPayload::Icmpv6(Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
                 flags: NdiscNeighborFlags::SOLICITED,
                 target_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 0x0002]),
                 lladdr: Some(RawHardwareAddress::from_bytes(&[0, 0, 0, 0, 0, 0, 0, 1])),
-            })
-        )))
+            }))
+        ))
     );
 
     let response = None;
@@ -528,7 +528,7 @@ fn ndsic_neighbor_advertisement_ieee802154(#[case] medium: Medium) {
         iface.inner.process_ipv6(
             &mut sockets,
             PacketMeta::default(),
-            &Ipv6Packet::new_checked(&data).unwrap()
+            &Ipv6PacketWire::new_checked(&data).unwrap()
         ),
         response
     );
@@ -603,10 +603,10 @@ fn test_handle_valid_ndisc_request(#[case] medium: Medium) {
             frame.into_inner(),
             &mut iface.fragments
         ),
-        Some(EthernetPacket::Ip(IpPacket::Icmpv6((
+        Some(EthernetPacket::Ip(IpPacket::new_ipv6(
             ipv6_expected,
-            icmpv6_expected
-        ))))
+            IpPayload::Icmpv6(icmpv6_expected)
+        )))
     );
 
     // Ensure the address of the requestor was entered in the cache
@@ -727,6 +727,9 @@ fn test_icmp_reply_size(#[case] medium: Medium) {
             &vec![0x2a; MAX_PAYLOAD_LEN],
             payload,
         ),
-        Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr)))
+        Some(IpPacket::new_ipv6(
+            expected_ip_repr,
+            IpPayload::Icmpv6(expected_icmp_repr)
+        ))
     );
 }

+ 18 - 16
src/iface/interface/tests/sixlowpan.rs

@@ -38,7 +38,7 @@ fn icmp_echo_request(#[case] medium: Medium) {
         0x37,
     ];
 
-    let response = Some(IpPacket::Icmpv6((
+    let response = Some(IpPacket::new_ipv6(
         Ipv6Repr {
             src_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242]),
             dst_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0x241c, 0x2957, 0x34a6, 0x3a62]),
@@ -46,7 +46,7 @@ fn icmp_echo_request(#[case] medium: Medium) {
             next_header: IpProtocol::Icmpv6,
             payload_len: 64,
         },
-        Icmpv6Repr::EchoReply {
+        IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
             ident: 4,
             seq_no: 1,
             data: &[
@@ -55,8 +55,8 @@ fn icmp_echo_request(#[case] medium: Medium) {
                 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
                 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
             ],
-        },
-    )));
+        }),
+    ));
 
     let (mut iface, mut sockets, _device) = setup(medium);
 
@@ -190,7 +190,7 @@ fn test_echo_request_sixlowpan_128_bytes() {
 
     assert_eq!(
         result,
-        Some(IpPacket::Icmpv6((
+        Some(IpPacket::new_ipv6(
             Ipv6Repr {
                 src_addr: Ipv6Address([
                     0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41,
@@ -204,12 +204,12 @@ fn test_echo_request_sixlowpan_128_bytes() {
                 payload_len: 136,
                 hop_limit: 64,
             },
-            Icmpv6Repr::EchoReply {
+            IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
                 ident: 39,
                 seq_no: 2,
                 data,
-            }
-        )))
+            })
+        ))
     );
 
     iface.inner.neighbor_cache.fill(
@@ -362,20 +362,22 @@ In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo n
         Ieee802154Address::default(),
         tx_token,
         PacketMeta::default(),
-        IpPacket::Udp((
-            IpRepr::Ipv6(Ipv6Repr {
+        IpPacket::new_ipv6(
+            Ipv6Repr {
                 src_addr: Ipv6Address::default(),
                 dst_addr: Ipv6Address::default(),
                 next_header: IpProtocol::Udp,
                 payload_len: udp_data.len(),
                 hop_limit: 64,
-            }),
-            UdpRepr {
-                src_port: 1234,
-                dst_port: 1234,
             },
-            udp_data,
-        )),
+            IpPayload::Udp(
+                UdpRepr {
+                    src_port: 1234,
+                    dst_port: 1234,
+                },
+                udp_data,
+            ),
+        ),
         &mut iface.fragmenter,
     );
 

+ 212 - 0
src/iface/ip_packet.rs

@@ -0,0 +1,212 @@
+use crate::phy::DeviceCapabilities;
+use crate::wire::*;
+
+#[allow(clippy::large_enum_variant)]
+#[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[cfg(feature = "medium-ethernet")]
+pub(crate) enum EthernetPacket<'a> {
+    #[cfg(feature = "proto-ipv4")]
+    Arp(ArpRepr),
+    Ip(IpPacket<'a>),
+}
+
+#[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub(crate) enum IpPacket<'p> {
+    #[cfg(feature = "proto-ipv4")]
+    Ipv4(Ipv4Packet<'p>),
+    #[cfg(feature = "proto-ipv6")]
+    Ipv6(Ipv6Packet<'p>),
+}
+
+impl<'p> IpPacket<'p> {
+    pub(crate) fn new(ip_repr: IpRepr, payload: IpPayload<'p>) -> Self {
+        match ip_repr {
+            #[cfg(feature = "proto-ipv4")]
+            IpRepr::Ipv4(header) => Self::new_ipv4(header, payload),
+            #[cfg(feature = "proto-ipv6")]
+            IpRepr::Ipv6(header) => Self::new_ipv6(header, payload),
+        }
+    }
+
+    #[cfg(feature = "proto-ipv4")]
+    pub(crate) fn new_ipv4(ip_repr: Ipv4Repr, payload: IpPayload<'p>) -> Self {
+        Self::Ipv4(Ipv4Packet {
+            header: ip_repr,
+            payload,
+        })
+    }
+
+    #[cfg(feature = "proto-ipv6")]
+    pub(crate) fn new_ipv6(ip_repr: Ipv6Repr, payload: IpPayload<'p>) -> Self {
+        Self::Ipv6(Ipv6Packet {
+            header: ip_repr,
+            #[cfg(feature = "proto-ipv6-hbh")]
+            hop_by_hop: None,
+            #[cfg(feature = "proto-ipv6-fragmentation")]
+            fragment: None,
+            #[cfg(feature = "proto-ipv6-routing")]
+            routing: None,
+            payload,
+        })
+    }
+
+    pub(crate) fn ip_repr(&self) -> IpRepr {
+        match self {
+            #[cfg(feature = "proto-ipv4")]
+            IpPacket::Ipv4(p) => IpRepr::Ipv4(p.header),
+            #[cfg(feature = "proto-ipv6")]
+            IpPacket::Ipv6(p) => IpRepr::Ipv6(p.header),
+        }
+    }
+
+    pub(crate) fn payload(&self) -> &IpPayload<'p> {
+        match self {
+            #[cfg(feature = "proto-ipv4")]
+            IpPacket::Ipv4(p) => &p.payload,
+            #[cfg(feature = "proto-ipv6")]
+            IpPacket::Ipv6(p) => &p.payload,
+        }
+    }
+
+    pub(crate) fn emit_payload(
+        &self,
+        _ip_repr: &IpRepr,
+        payload: &mut [u8],
+        caps: &DeviceCapabilities,
+    ) {
+        match self.payload() {
+            #[cfg(feature = "proto-ipv4")]
+            IpPayload::Icmpv4(icmpv4_repr) => {
+                icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum)
+            }
+            #[cfg(feature = "proto-igmp")]
+            IpPayload::Igmp(igmp_repr) => igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)),
+            #[cfg(feature = "proto-ipv6")]
+            IpPayload::Icmpv6(icmpv6_repr) => icmpv6_repr.emit(
+                &_ip_repr.src_addr(),
+                &_ip_repr.dst_addr(),
+                &mut Icmpv6Packet::new_unchecked(payload),
+                &caps.checksum,
+            ),
+            #[cfg(feature = "socket-raw")]
+            IpPayload::Raw(raw_packet) => payload.copy_from_slice(raw_packet),
+            #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
+            IpPayload::Udp(udp_repr, inner_payload) => udp_repr.emit(
+                &mut UdpPacket::new_unchecked(payload),
+                &_ip_repr.src_addr(),
+                &_ip_repr.dst_addr(),
+                inner_payload.len(),
+                |buf| buf.copy_from_slice(inner_payload),
+                &caps.checksum,
+            ),
+            #[cfg(feature = "socket-tcp")]
+            IpPayload::Tcp(mut tcp_repr) => {
+                // This is a terrible hack to make TCP performance more acceptable on systems
+                // where the TCP buffers are significantly larger than network buffers,
+                // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window)
+                // together with four 1500 B Ethernet receive buffers. If left untreated,
+                // this would result in our peer pushing our window and sever packet loss.
+                //
+                // I'm really not happy about this "solution" but I don't know what else to do.
+                if let Some(max_burst_size) = caps.max_burst_size {
+                    let mut max_segment_size = caps.max_transmission_unit;
+                    max_segment_size -= _ip_repr.header_len();
+                    max_segment_size -= tcp_repr.header_len();
+
+                    let max_window_size = max_burst_size * max_segment_size;
+                    if tcp_repr.window_len as usize > max_window_size {
+                        tcp_repr.window_len = max_window_size as u16;
+                    }
+                }
+
+                tcp_repr.emit(
+                    &mut TcpPacket::new_unchecked(payload),
+                    &_ip_repr.src_addr(),
+                    &_ip_repr.dst_addr(),
+                    &caps.checksum,
+                );
+            }
+            #[cfg(feature = "socket-dhcpv4")]
+            IpPayload::Dhcpv4(udp_repr, dhcp_repr) => udp_repr.emit(
+                &mut UdpPacket::new_unchecked(payload),
+                &_ip_repr.src_addr(),
+                &_ip_repr.dst_addr(),
+                dhcp_repr.buffer_len(),
+                |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
+                &caps.checksum,
+            ),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[cfg(feature = "proto-ipv4")]
+pub(crate) struct Ipv4Packet<'p> {
+    header: Ipv4Repr,
+    payload: IpPayload<'p>,
+}
+
+#[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[cfg(feature = "proto-ipv6")]
+pub(crate) struct Ipv6Packet<'p> {
+    header: Ipv6Repr,
+    #[cfg(feature = "proto-ipv6-hbh")]
+    hop_by_hop: Option<Ipv6HopByHopRepr<'p>>,
+    #[cfg(feature = "proto-ipv6-fragmentation")]
+    fragment: Option<Ipv6FragmentRepr>,
+    #[cfg(feature = "proto-ipv6-routing")]
+    routing: Option<Ipv6RoutingRepr<'p>>,
+    payload: IpPayload<'p>,
+}
+
+#[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub(crate) enum IpPayload<'p> {
+    #[cfg(feature = "proto-ipv4")]
+    Icmpv4(Icmpv4Repr<'p>),
+    #[cfg(feature = "proto-igmp")]
+    Igmp(IgmpRepr),
+    #[cfg(feature = "proto-ipv6")]
+    Icmpv6(Icmpv6Repr<'p>),
+    #[cfg(feature = "socket-raw")]
+    Raw(&'p [u8]),
+    #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
+    Udp(UdpRepr, &'p [u8]),
+    #[cfg(feature = "socket-tcp")]
+    Tcp(TcpRepr<'p>),
+    #[cfg(feature = "socket-dhcpv4")]
+    Dhcpv4(UdpRepr, DhcpRepr<'p>),
+}
+
+#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
+pub(crate) fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
+    // Send back as much of the original payload as will fit within
+    // the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for
+    // more details.
+    //
+    // Since the entire network layer packet must fit within the minimum
+    // MTU supported, the payload must not exceed the following:
+    //
+    // <min mtu> - IP Header Size * 2 - ICMPv4 DstUnreachable hdr size
+    len.min(mtu - header_len * 2 - 8)
+}
+
+#[cfg(feature = "proto-igmp")]
+pub(crate) enum IgmpReportState {
+    Inactive,
+    ToGeneralQuery {
+        version: IgmpVersion,
+        timeout: crate::time::Instant,
+        interval: crate::time::Duration,
+        next_index: usize,
+    },
+    ToSpecificQuery {
+        version: IgmpVersion,
+        timeout: crate::time::Instant,
+        group: Ipv4Address,
+    },
+}

+ 2 - 0
src/iface/mod.rs

@@ -15,6 +15,8 @@ mod rpl;
 mod socket_meta;
 mod socket_set;
 
+mod ip_packet;
+
 #[cfg(feature = "proto-igmp")]
 pub use self::interface::MulticastError;
 pub use self::interface::{Config, Interface, InterfaceInner as Context};