瀏覽代碼

Device-level packet metadata identifiers

datdenkikniet 1 年之前
父節點
當前提交
78b4f39a10

+ 3 - 1
Cargo.toml

@@ -63,6 +63,8 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ]
 "socket-dns" = ["socket", "proto-dns"]
 "socket-mdns" = ["socket-dns"]
 
+"packet-id" = []
+
 "async" = []
 
 default = [
@@ -72,7 +74,7 @@ default = [
   "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-dns",
   "proto-ipv4-fragmentation", "proto-sixlowpan-fragmentation",
   "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "socket-mdns",
-  "async"
+  "packet-id", "async"
 ]
 
 # Private features

+ 1 - 1
examples/server.rs

@@ -111,7 +111,7 @@ fn main() {
         };
         if let Some((endpoint, data)) = client {
             debug!("udp:6969 send data: {:?} to {}", data, endpoint,);
-            socket.send_slice(&data, endpoint).unwrap();
+            socket.send_slice(&data, endpoint.endpoint()).unwrap();
         }
 
         // tcp:6969: respond "hello"

+ 3 - 1
examples/sixlowpan.rs

@@ -135,7 +135,9 @@ fn main() {
                 "udp:6969 send data: {:?}",
                 str::from_utf8(&buffer[..len]).unwrap()
             );
-            socket.send_slice(&buffer[..len], endpoint).unwrap();
+            socket
+                .send_slice(&buffer[..len], endpoint.endpoint())
+                .unwrap();
         }
 
         let socket = sockets.get_mut::<tcp::Socket>(tcp_handle);

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

@@ -14,6 +14,7 @@ impl InterfaceInner {
     pub(super) fn process_ethernet<'frame, T: AsRef<[u8]>>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: crate::phy::PacketId,
         frame: &'frame T,
         fragments: &'frame mut FragmentsBuffer,
     ) -> Option<EthernetPacket<'frame>> {
@@ -34,13 +35,13 @@ impl InterfaceInner {
             EthernetProtocol::Ipv4 => {
                 let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload()));
 
-                self.process_ipv4(sockets, &ipv4_packet, fragments)
+                self.process_ipv4(sockets, packet_id, &ipv4_packet, fragments)
                     .map(EthernetPacket::Ip)
             }
             #[cfg(feature = "proto-ipv6")]
             EthernetProtocol::Ipv6 => {
                 let ipv6_packet = check!(Ipv6Packet::new_checked(eth_frame.payload()));
-                self.process_ipv6(sockets, &ipv6_packet)
+                self.process_ipv6(sockets, packet_id, &ipv6_packet)
                     .map(EthernetPacket::Ip)
             }
             // Drop all other traffic.

+ 4 - 1
src/iface/interface/ieee802154.rs

@@ -7,6 +7,7 @@ impl InterfaceInner {
     pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         sixlowpan_payload: &'payload T,
         _fragments: &'output mut FragmentsBuffer,
     ) -> Option<IpPacket<'output>> {
@@ -32,7 +33,9 @@ impl InterfaceInner {
         }
 
         match ieee802154_frame.payload() {
-            Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, _fragments),
+            Some(payload) => {
+                self.process_sixlowpan(sockets, packet_id, &ieee802154_repr, payload, _fragments)
+            }
             None => None,
         }
     }

+ 2 - 0
src/iface/interface/ipv4.rs

@@ -14,6 +14,7 @@ impl InterfaceInner {
     pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         ipv4_packet: &Ipv4Packet<&'a T>,
         frag: &'a mut FragmentsBuffer,
     ) -> Option<IpPacket<'a>> {
@@ -138,6 +139,7 @@ impl InterfaceInner {
 
                 self.process_udp(
                     sockets,
+                    packet_id,
                     ip_repr,
                     udp_repr,
                     handled_by_raw_socket,

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

@@ -8,6 +8,7 @@ use super::SocketSet;
 use crate::socket::icmp;
 use crate::socket::AnySocket;
 
+use crate::phy::PacketId;
 use crate::wire::*;
 
 impl InterfaceInner {
@@ -15,6 +16,7 @@ impl InterfaceInner {
     pub(super) fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         ipv6_packet: &Ipv6Packet<&'frame T>,
     ) -> Option<IpPacket<'frame>> {
         let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet));
@@ -34,6 +36,7 @@ impl InterfaceInner {
 
         self.process_nxt_hdr(
             sockets,
+            packet_id,
             ipv6_repr,
             ipv6_repr.next_header,
             handled_by_raw_socket,
@@ -47,6 +50,7 @@ impl InterfaceInner {
     pub(super) fn process_nxt_hdr<'frame>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         ipv6_repr: Ipv6Repr,
         nxt_hdr: IpProtocol,
         handled_by_raw_socket: bool,
@@ -67,6 +71,7 @@ impl InterfaceInner {
 
                 self.process_udp(
                     sockets,
+                    packet_id,
                     ipv6_repr.into(),
                     udp_repr,
                     handled_by_raw_socket,
@@ -78,9 +83,13 @@ impl InterfaceInner {
             #[cfg(feature = "socket-tcp")]
             IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload),
 
-            IpProtocol::HopByHop => {
-                self.process_hopbyhop(sockets, ipv6_repr, handled_by_raw_socket, ip_payload)
-            }
+            IpProtocol::HopByHop => self.process_hopbyhop(
+                sockets,
+                packet_id,
+                ipv6_repr,
+                handled_by_raw_socket,
+                ip_payload,
+            ),
 
             #[cfg(feature = "socket-raw")]
             _ if handled_by_raw_socket => None,
@@ -240,6 +249,7 @@ impl InterfaceInner {
     pub(super) fn process_hopbyhop<'frame>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         ipv6_repr: Ipv6Repr,
         handled_by_raw_socket: bool,
         ip_payload: &'frame [u8],
@@ -272,6 +282,7 @@ impl InterfaceInner {
         }
         self.process_nxt_hdr(
             sockets,
+            packet_id,
             ipv6_repr,
             hbh_repr.next_header,
             handled_by_raw_socket,

+ 60 - 31
src/iface/interface/mod.rs

@@ -37,6 +37,7 @@ use crate::config::{
     IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT,
 };
 use crate::iface::Routes;
+use crate::phy::PacketId;
 use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
 use crate::rand::Rand;
 #[cfg(feature = "socket-dns")]
@@ -320,7 +321,7 @@ pub(crate) enum IpPacket<'a> {
     #[cfg(feature = "socket-raw")]
     Raw((IpRepr, &'a [u8])),
     #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
-    Udp((IpRepr, UdpRepr, &'a [u8])),
+    Udp((IpRepr, UdpRepr, &'a [u8], PacketId)),
     #[cfg(feature = "socket-tcp")]
     Tcp((IpRepr, TcpRepr<'a>)),
     #[cfg(feature = "socket-dhcpv4")]
@@ -339,7 +340,7 @@ impl<'a> IpPacket<'a> {
             #[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(),
+            IpPacket::Udp((ip_repr, _, _, _)) => ip_repr.clone(),
             #[cfg(feature = "socket-tcp")]
             IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(),
             #[cfg(feature = "socket-dhcpv4")]
@@ -347,6 +348,14 @@ impl<'a> IpPacket<'a> {
         }
     }
 
+    pub(crate) fn packet_id(&self) -> PacketId {
+        match self {
+            #[cfg(feature = "socket-udp")]
+            IpPacket::Udp((_, _, _, packet_id)) => *packet_id,
+            _ => PacketId::empty(),
+        }
+    }
+
     pub(crate) fn emit_payload(
         &self,
         _ip_repr: &IpRepr,
@@ -372,14 +381,16 @@ impl<'a> IpPacket<'a> {
             #[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,
-            ),
+            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
@@ -416,7 +427,7 @@ impl<'a> IpPacket<'a> {
                 |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
                 &caps.checksum,
             ),
-        }
+        };
     }
 }
 
@@ -803,14 +814,17 @@ impl Interface {
         let mut processed_any = false;
 
         while let Some((rx_token, tx_token)) = device.receive(self.inner.now) {
+            let rx_packet_id = rx_token.packet_id();
             rx_token.consume(|frame| {
                 match self.inner.caps.medium {
                     #[cfg(feature = "medium-ethernet")]
                     Medium::Ethernet => {
-                        if let Some(packet) =
-                            self.inner
-                                .process_ethernet(sockets, &frame, &mut self.fragments)
-                        {
+                        if let Some(packet) = self.inner.process_ethernet(
+                            sockets,
+                            rx_packet_id,
+                            &frame,
+                            &mut self.fragments,
+                        ) {
                             if let Err(err) =
                                 self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
                             {
@@ -820,9 +834,12 @@ impl Interface {
                     }
                     #[cfg(feature = "medium-ip")]
                     Medium::Ip => {
-                        if let Some(packet) =
-                            self.inner.process_ip(sockets, &frame, &mut self.fragments)
-                        {
+                        if let Some(packet) = self.inner.process_ip(
+                            sockets,
+                            rx_packet_id,
+                            &frame,
+                            &mut self.fragments,
+                        ) {
                             if let Err(err) =
                                 self.inner
                                     .dispatch_ip(tx_token, packet, &mut self.fragmenter)
@@ -833,10 +850,12 @@ impl Interface {
                     }
                     #[cfg(feature = "medium-ieee802154")]
                     Medium::Ieee802154 => {
-                        if let Some(packet) =
-                            self.inner
-                                .process_ieee802154(sockets, &frame, &mut self.fragments)
-                        {
+                        if let Some(packet) = self.inner.process_ieee802154(
+                            sockets,
+                            rx_packet_id,
+                            &frame,
+                            &mut self.fragments,
+                        ) {
                             if let Err(err) =
                                 self.inner
                                     .dispatch_ip(tx_token, packet, &mut self.fragmenter)
@@ -923,9 +942,11 @@ impl Interface {
                     respond(inner, IpPacket::Dhcpv4(response))
                 }),
                 #[cfg(feature = "socket-dns")]
-                Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, response| {
-                    respond(inner, IpPacket::Udp(response))
-                }),
+                Socket::Dns(socket) => {
+                    socket.dispatch(&mut self.inner, |inner, (ip, udp, payload)| {
+                        respond(inner, IpPacket::Udp((ip, udp, payload, PacketId::empty())))
+                    })
+                }
             };
 
             match result {
@@ -1263,6 +1284,7 @@ impl InterfaceInner {
     fn process_ip<'frame, T: AsRef<[u8]>>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         ip_payload: &'frame T,
         frag: &'frame mut FragmentsBuffer,
     ) -> Option<IpPacket<'frame>> {
@@ -1271,12 +1293,12 @@ impl InterfaceInner {
             Ok(IpVersion::Ipv4) => {
                 let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
 
-                self.process_ipv4(sockets, &ipv4_packet, frag)
+                self.process_ipv4(sockets, packet_id, &ipv4_packet, frag)
             }
             #[cfg(feature = "proto-ipv6")]
             Ok(IpVersion::Ipv6) => {
                 let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
-                self.process_ipv6(sockets, &ipv6_packet)
+                self.process_ipv6(sockets, packet_id, &ipv6_packet)
             }
             // Drop all other traffic.
             _ => None,
@@ -1341,9 +1363,11 @@ impl InterfaceInner {
     }
 
     #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
+    #[allow(clippy::too_many_arguments)]
     fn process_udp<'frame>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         ip_repr: IpRepr,
         udp_repr: UdpRepr,
         handled_by_raw_socket: bool,
@@ -1356,7 +1380,7 @@ impl InterfaceInner {
             .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket))
         {
             if udp_socket.accepts(self, &ip_repr, &udp_repr) {
-                udp_socket.process(self, &ip_repr, &udp_repr, udp_payload);
+                udp_socket.process(self, packet_id, &ip_repr, &udp_repr, udp_payload);
                 return None;
             }
         }
@@ -1643,7 +1667,9 @@ impl InterfaceInner {
 
     fn dispatch_ip<Tx: TxToken>(
         &mut self,
-        tx_token: Tx,
+        // NOTE(unused_mut): tx_token isn't always mutated, depending on
+        // the feature set that is used.
+        #[allow(unused_mut)] mut tx_token: Tx,
         packet: IpPacket,
         frag: &mut Fragmenter,
     ) -> Result<(), DispatchError> {
@@ -1684,7 +1710,7 @@ impl InterfaceInner {
 
         // If the medium is Ethernet, then we need to retrieve the destination hardware address.
         #[cfg(feature = "medium-ethernet")]
-        let (dst_hardware_addr, tx_token) = match self.caps.medium {
+        let (dst_hardware_addr, mut tx_token) = match self.caps.medium {
             Medium::Ethernet => {
                 match self.lookup_hardware_addr(
                     tx_token,
@@ -1723,7 +1749,7 @@ impl InterfaceInner {
             repr.emit(&mut tx_buffer, &self.caps.checksum);
 
             let payload = &mut tx_buffer[repr.header_len()..];
-            packet.emit_payload(repr, payload, &caps);
+            packet.emit_payload(repr, payload, &caps)
         };
 
         let total_ip_len = ip_repr.buffer_len();
@@ -1771,6 +1797,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[..]);
                         frag.ipv4.ident = ipv4_id;
                         ipv4_packet.set_ident(ipv4_id);
@@ -1807,6 +1834,8 @@ impl InterfaceInner {
                         Ok(())
                     }
                 } else {
+                    tx_token.set_packet_id(packet.packet_id());
+
                     // No fragmentation is required.
                     tx_token.consume(total_len, |mut tx_buffer| {
                         #[cfg(feature = "medium-ethernet")]

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

@@ -11,6 +11,7 @@ impl InterfaceInner {
     pub(super) fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>(
         &mut self,
         sockets: &mut SocketSet,
+        packet_id: PacketId,
         ieee802154_repr: &Ieee802154Repr,
         payload: &'payload T,
         f: &'output mut FragmentsBuffer,
@@ -47,7 +48,11 @@ impl InterfaceInner {
             }
         };
 
-        self.process_ipv6(sockets, &check!(Ipv6Packet::new_checked(payload)))
+        self.process_ipv6(
+            sockets,
+            packet_id,
+            &check!(Ipv6Packet::new_checked(payload)),
+        )
     }
 
     #[cfg(feature = "proto-sixlowpan-fragmentation")]
@@ -227,7 +232,7 @@ impl InterfaceInner {
 
     pub(super) fn dispatch_sixlowpan<Tx: TxToken>(
         &mut self,
-        tx_token: Tx,
+        mut tx_token: Tx,
         packet: IpPacket,
         ieee_repr: Ieee802154Repr,
         frag: &mut Fragmenter,
@@ -275,7 +280,7 @@ impl InterfaceInner {
 
         match packet {
             #[cfg(feature = "socket-udp")]
-            IpPacket::Udp((_, udpv6_repr, payload)) => {
+            IpPacket::Udp((_, udpv6_repr, payload, _)) => {
                 let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr);
                 _compressed_headers_len += udp_repr.header_len();
                 _uncompressed_headers_len += udpv6_repr.header_len();
@@ -328,7 +333,7 @@ impl InterfaceInner {
 
                 match packet {
                     #[cfg(feature = "socket-udp")]
-                    IpPacket::Udp((_, udpv6_repr, payload)) => {
+                    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()],
@@ -429,6 +434,8 @@ impl InterfaceInner {
                 return;
             }
         } else {
+            tx_token.set_packet_id(packet.packet_id());
+
             // We don't need fragmentation, so we emit everything to the TX token.
             tx_token.consume(total_size + ieee_len, |mut tx_buf| {
                 let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
@@ -442,7 +449,7 @@ impl InterfaceInner {
 
                 match packet {
                     #[cfg(feature = "socket-udp")]
-                    IpPacket::Udp((_, udpv6_repr, payload)) => {
+                    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()],

+ 96 - 37
src/iface/interface/tests.rs

@@ -166,9 +166,12 @@ fn test_no_icmp_no_unicast_ipv4() {
     // broadcast address
 
     assert_eq!(
-        iface
-            .inner
-            .process_ipv4(&mut sockets, &frame, &mut iface.fragments),
+        iface.inner.process_ipv4(
+            &mut sockets,
+            PacketId::empty(),
+            &frame,
+            &mut iface.fragments
+        ),
         None
     );
 }
@@ -198,7 +201,12 @@ fn test_no_icmp_no_unicast_ipv6() {
     // Ensure that the unknown protocol frame does not trigger an
     // ICMP error response when the destination address is a
     // broadcast address
-    assert_eq!(iface.inner.process_ipv6(&mut sockets, &frame), None);
+    assert_eq!(
+        iface
+            .inner
+            .process_ipv6(&mut sockets, PacketId::empty(), &frame),
+        None
+    );
 }
 
 #[test]
@@ -249,9 +257,12 @@ fn test_icmp_error_no_payload() {
     // And we correctly handle no payload.
 
     assert_eq!(
-        iface
-            .inner
-            .process_ipv4(&mut sockets, &frame, &mut iface.fragments),
+        iface.inner.process_ipv4(
+            &mut sockets,
+            PacketId::empty(),
+            &frame,
+            &mut iface.fragments
+        ),
         Some(expected_repr)
     );
 }
@@ -385,9 +396,15 @@ fn test_icmp_error_port_unreachable() {
     // Ensure that the unknown protocol triggers an error response.
     // And we correctly handle no payload.
     assert_eq!(
-        iface
-            .inner
-            .process_udp(&mut sockets, ip_repr, udp_repr, false, &UDP_PAYLOAD, data),
+        iface.inner.process_udp(
+            &mut sockets,
+            PacketId::empty(),
+            ip_repr,
+            udp_repr,
+            false,
+            &UDP_PAYLOAD,
+            data
+        ),
         Some(expected_repr)
     );
 
@@ -415,6 +432,7 @@ fn test_icmp_error_port_unreachable() {
     assert_eq!(
         iface.inner.process_udp(
             &mut sockets,
+            PacketId::empty(),
             ip_repr,
             udp_repr,
             false,
@@ -428,7 +446,7 @@ fn test_icmp_error_port_unreachable() {
 #[test]
 #[cfg(feature = "socket-udp")]
 fn test_handle_udp_broadcast() {
-    use crate::wire::IpEndpoint;
+    use crate::{socket::udp::UdpMetadata, wire::IpEndpoint};
 
     static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f];
 
@@ -490,6 +508,7 @@ fn test_handle_udp_broadcast() {
     assert_eq!(
         iface.inner.process_udp(
             &mut sockets,
+            PacketId::empty(),
             ip_repr,
             udp_repr,
             false,
@@ -505,7 +524,10 @@ fn test_handle_udp_broadcast() {
     assert!(socket.can_recv());
     assert_eq!(
         socket.recv(),
-        Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67)))
+        Ok((
+            &UDP_PAYLOAD[..],
+            UdpMetadata::new(IpEndpoint::new(src_ip.into(), 67), PacketId::empty(),)
+        ))
     );
 }
 
@@ -566,9 +588,12 @@ fn test_handle_ipv4_broadcast() {
     let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr));
 
     assert_eq!(
-        iface
-            .inner
-            .process_ipv4(&mut sockets, &frame, &mut iface.fragments),
+        iface.inner.process_ipv4(
+            &mut sockets,
+            PacketId::empty(),
+            &frame,
+            &mut iface.fragments
+        ),
         Some(expected_packet)
     );
 }
@@ -680,6 +705,7 @@ fn test_icmp_reply_size() {
     assert_eq!(
         iface.inner.process_udp(
             &mut sockets,
+            PacketId::empty(),
             ip_repr.into(),
             udp_repr,
             false,
@@ -692,6 +718,7 @@ fn test_icmp_reply_size() {
     assert_eq!(
         iface.inner.process_udp(
             &mut sockets,
+            PacketId::empty(),
             ip_repr.into(),
             udp_repr,
             false,
@@ -731,9 +758,12 @@ fn test_handle_valid_arp_request() {
 
     // Ensure an ARP Request for us triggers an ARP Reply
     assert_eq!(
-        iface
-            .inner
-            .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
+        iface.inner.process_ethernet(
+            &mut sockets,
+            PacketId::empty(),
+            frame.into_inner(),
+            &mut iface.fragments
+        ),
         Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
             operation: ArpOperation::Reply,
             source_hardware_addr: local_hw_addr,
@@ -807,9 +837,12 @@ fn test_handle_valid_ndisc_request() {
 
     // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement
     assert_eq!(
-        iface
-            .inner
-            .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
+        iface.inner.process_ethernet(
+            &mut sockets,
+            PacketId::empty(),
+            frame.into_inner(),
+            &mut iface.fragments
+        ),
         Some(EthernetPacket::Ip(IpPacket::Icmpv6((
             ipv6_expected,
             icmpv6_expected
@@ -855,9 +888,12 @@ fn test_handle_other_arp_request() {
 
     // Ensure an ARP Request for someone else does not trigger an ARP Reply
     assert_eq!(
-        iface
-            .inner
-            .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
+        iface.inner.process_ethernet(
+            &mut sockets,
+            PacketId::empty(),
+            frame.into_inner(),
+            &mut iface.fragments
+        ),
         None
     );
 
@@ -908,9 +944,12 @@ fn test_arp_flush_after_update_ip() {
 
     // Ensure an ARP Request for us triggers an ARP Reply
     assert_eq!(
-        iface
-            .inner
-            .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
+        iface.inner.process_ethernet(
+            &mut sockets,
+            PacketId::empty(),
+            frame.into_inner(),
+            &mut iface.fragments
+        ),
         Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
             operation: ArpOperation::Reply,
             source_hardware_addr: local_hw_addr,
@@ -1103,7 +1142,9 @@ fn test_icmpv6_nxthdr_unknown() {
     // Ensure the unknown next header causes a ICMPv6 Parameter Problem
     // error message to be sent to the sender.
     assert_eq!(
-        iface.inner.process_ipv6(&mut sockets, &frame),
+        iface
+            .inner
+            .process_ipv6(&mut sockets, PacketId::empty(), &frame),
         Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr)))
     );
 }
@@ -1267,9 +1308,12 @@ fn test_raw_socket_no_reply() {
     };
 
     assert_eq!(
-        iface
-            .inner
-            .process_ipv4(&mut sockets, &frame, &mut iface.fragments),
+        iface.inner.process_ipv4(
+            &mut sockets,
+            PacketId::empty(),
+            &frame,
+            &mut iface.fragments
+        ),
         None
     );
 }
@@ -1277,7 +1321,10 @@ fn test_raw_socket_no_reply() {
 #[test]
 #[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))]
 fn test_raw_socket_with_udp_socket() {
-    use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr};
+    use crate::{
+        socket::udp::UdpMetadata,
+        wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr},
+    };
 
     static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f];
 
@@ -1353,9 +1400,12 @@ fn test_raw_socket_with_udp_socket() {
     };
 
     assert_eq!(
-        iface
-            .inner
-            .process_ipv4(&mut sockets, &frame, &mut iface.fragments),
+        iface.inner.process_ipv4(
+            &mut sockets,
+            PacketId::empty(),
+            &frame,
+            &mut iface.fragments
+        ),
         None
     );
 
@@ -1364,7 +1414,10 @@ fn test_raw_socket_with_udp_socket() {
     assert!(socket.can_recv());
     assert_eq!(
         socket.recv(),
-        Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67)))
+        Ok((
+            &UDP_PAYLOAD[..],
+            UdpMetadata::new(IpEndpoint::new(src_addr.into(), 67), PacketId::empty(),)
+        ))
     );
 }
 
@@ -1458,6 +1511,7 @@ fn test_echo_request_sixlowpan_128_bytes() {
     assert_eq!(
         iface.inner.process_sixlowpan(
             &mut sockets,
+            PacketId::empty(),
             &ieee802154_repr,
             &request_first_part_packet.into_inner(),
             &mut iface.fragments
@@ -1482,6 +1536,7 @@ fn test_echo_request_sixlowpan_128_bytes() {
 
     let result = iface.inner.process_sixlowpan(
         &mut sockets,
+        PacketId::empty(),
         &ieee802154_repr,
         &request_second_part,
         &mut iface.fragments,
@@ -1612,6 +1667,7 @@ fn test_sixlowpan_udp_with_fragmentation() {
     assert_eq!(
         iface.inner.process_sixlowpan(
             &mut sockets,
+            PacketId::empty(),
             &ieee802154_repr,
             udp_first_part,
             &mut iface.fragments
@@ -1631,6 +1687,7 @@ fn test_sixlowpan_udp_with_fragmentation() {
     assert_eq!(
         iface.inner.process_sixlowpan(
             &mut sockets,
+            PacketId::empty(),
             &ieee802154_repr,
             udp_second_part,
             &mut iface.fragments
@@ -1642,8 +1699,9 @@ fn test_sixlowpan_udp_with_fragmentation() {
 
     let udp_data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
                          In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo nec.";
+
     assert_eq!(
-        socket.recv(),
+        socket.recv().map(|(data, meta)| (data, meta.endpoint())),
         Ok((
             &udp_data[..],
             IpEndpoint {
@@ -1673,6 +1731,7 @@ fn test_sixlowpan_udp_with_fragmentation() {
                 dst_port: 1234,
             },
             udp_data,
+            PacketId::empty(),
         )),
         &mut iface.fragmenter,
     );

+ 16 - 1
src/phy/fault_injector.rs

@@ -1,6 +1,8 @@
 use crate::phy::{self, Device, DeviceCapabilities};
 use crate::time::{Duration, Instant};
 
+use super::PacketId;
+
 // We use our own RNG to stay compatible with #![no_std].
 // The use of the RNG below has a slight bias, but it doesn't matter.
 fn xorshift32(state: &mut u32) -> u32 {
@@ -211,6 +213,7 @@ impl<D: Device> Device for FaultInjector<D> {
 
     fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
         let (rx_token, tx_token) = self.inner.receive(timestamp)?;
+        let rx_packet_id = <D::RxToken<'_> as phy::RxToken>::packet_id(&rx_token);
 
         let len = super::RxToken::consume(rx_token, |buffer| {
             if (self.config.max_size > 0 && buffer.len() > self.config.max_size)
@@ -240,7 +243,10 @@ impl<D: Device> Device for FaultInjector<D> {
             self.state.corrupt(&mut buf[..]);
         }
 
-        let rx = RxToken { buf };
+        let rx = RxToken {
+            buf,
+            packet_id: rx_packet_id,
+        };
         let tx = TxToken {
             state: &mut self.state,
             config: self.config,
@@ -265,6 +271,7 @@ impl<D: Device> Device for FaultInjector<D> {
 #[doc(hidden)]
 pub struct RxToken<'a> {
     buf: &'a mut [u8],
+    packet_id: PacketId,
 }
 
 impl<'a> phy::RxToken for RxToken<'a> {
@@ -274,6 +281,10 @@ impl<'a> phy::RxToken for RxToken<'a> {
     {
         f(self.buf)
     }
+
+    fn packet_id(&self) -> phy::PacketId {
+        self.packet_id
+    }
 }
 
 #[doc(hidden)]
@@ -315,4 +326,8 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
             f(buf)
         })
     }
+
+    fn set_packet_id(&mut self, packet_id: PacketId) {
+        self.token.set_packet_id(packet_id);
+    }
 }

+ 8 - 0
src/phy/fuzz_injector.rs

@@ -99,6 +99,10 @@ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> {
             f(buffer)
         })
     }
+
+    fn packet_id(&self) -> phy::PacketId {
+        self.token.packet_id()
+    }
 }
 
 #[doc(hidden)]
@@ -118,4 +122,8 @@ impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> {
             result
         })
     }
+
+    fn set_packet_id(&mut self, packet_id: phy::PacketId) {
+        self.token.set_packet_id(packet_id)
+    }
 }

+ 46 - 0
src/phy/mod.rs

@@ -130,6 +130,43 @@ pub use self::tracer::Tracer;
 ))]
 pub use self::tuntap_interface::TunTapInterface;
 
+/// An ID that can be used to uniquely identify a packet to a [`Device`],
+/// sent or received by that same [`Device`]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
+pub struct PacketId {
+    #[cfg(feature = "packet-id")]
+    id: Option<u32>,
+}
+
+impl PacketId {
+    pub fn empty() -> Self {
+        #[cfg(feature = "packet-id")]
+        {
+            Self { id: None }
+        }
+        #[cfg(not(feature = "packet-id"))]
+        {
+            Self {}
+        }
+    }
+}
+
+#[cfg(feature = "packet-id")]
+impl PacketId {
+    pub fn id(&self) -> Option<u32> {
+        self.id
+    }
+
+    /// Create a new packet ID.
+    ///
+    /// A caller of this function should know the context in which
+    /// this ID is relevant.
+    pub fn new(id: u32) -> Self {
+        Self { id: Some(id) }
+    }
+}
+
 /// A description of checksum behavior for a particular protocol.
 #[derive(Debug, Clone, Copy, Default)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -339,6 +376,11 @@ pub trait RxToken {
     fn consume<R, F>(self, f: F) -> R
     where
         F: FnOnce(&mut [u8]) -> R;
+
+    /// The Packet ID associated with the frame received by this [`RxToken`]
+    fn packet_id(&self) -> PacketId {
+        PacketId::empty()
+    }
 }
 
 /// A token to transmit a single network packet.
@@ -352,4 +394,8 @@ pub trait TxToken {
     fn consume<R, F>(self, len: usize, f: F) -> R
     where
         F: FnOnce(&mut [u8]) -> R;
+
+    /// The Packet ID to be associated with the frame to be transmitted by this [`TxToken`].
+    #[allow(unused_variables)]
+    fn set_packet_id(&mut self, packet_id: PacketId) {}
 }

+ 8 - 0
src/phy/pcap_writer.rs

@@ -231,6 +231,10 @@ impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> {
             f(buffer)
         })
     }
+
+    fn packet_id(&self) -> phy::PacketId {
+        self.token.packet_id()
+    }
 }
 
 #[doc(hidden)]
@@ -257,4 +261,8 @@ impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> {
             result
         })
     }
+
+    fn set_packet_id(&mut self, packet_id: phy::PacketId) {
+        self.token.set_packet_id(packet_id)
+    }
 }

+ 8 - 0
src/phy/tracer.rs

@@ -108,6 +108,10 @@ impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
             f(buffer)
         })
     }
+
+    fn packet_id(&self) -> phy::PacketId {
+        self.token.packet_id()
+    }
 }
 
 #[doc(hidden)]
@@ -136,6 +140,10 @@ impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
             result
         })
     }
+
+    fn set_packet_id(&mut self, packet_id: phy::PacketId) {
+        self.token.set_packet_id(packet_id)
+    }
 }
 
 pub struct Packet<'a> {

+ 196 - 38
src/socket/udp.rs

@@ -3,17 +3,59 @@ use core::cmp::min;
 use core::task::Waker;
 
 use crate::iface::Context;
+use crate::phy::PacketId;
 use crate::socket::PollAt;
 #[cfg(feature = "async")]
 use crate::socket::WakerRegistration;
 use crate::storage::Empty;
 use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr};
 
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct UdpMetadata {
+    endpoint: IpEndpoint,
+    packet_id: PacketId,
+}
+
+impl UdpMetadata {
+    /// The endpoint of this metadata
+    pub fn endpoint(&self) -> IpEndpoint {
+        self.endpoint
+    }
+
+    /// The packet ID of this metadata
+    pub fn packet_id(self) -> PacketId {
+        self.packet_id
+    }
+
+    /// Create a new metadata instance.
+    ///
+    /// If `packet_id` is `Some`, it can be used to track a datagram
+    /// as it is handled by the networking stack, or other elements of `smoltcp`
+    /// that interact with the specific datagram.
+    pub(crate) fn new(endpoint: IpEndpoint, packet_id: PacketId) -> Self {
+        Self {
+            endpoint,
+            packet_id,
+        }
+    }
+}
+
+impl core::fmt::Display for UdpMetadata {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        #[cfg(feature = "packet-id")]
+        return write!(f, "{}, PacketID: {:?}", self.endpoint, self.packet_id);
+
+        #[cfg(not(feature = "packet-id"))]
+        write!(f, "{}", self.endpoint)
+    }
+}
+
 /// A UDP packet metadata.
-pub type PacketMetadata = crate::storage::PacketMetadata<IpEndpoint>;
+pub type PacketMetadata = crate::storage::PacketMetadata<UdpMetadata>;
 
 /// A UDP packet ring buffer.
-pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, IpEndpoint>;
+pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, UdpMetadata>;
 
 /// Error returned by [`Socket::bind`]
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -281,7 +323,7 @@ impl<'a> Socket<'a> {
 
         let payload_buf = self
             .tx_buffer
-            .enqueue(size, remote_endpoint)
+            .enqueue(size, UdpMetadata::new(remote_endpoint, PacketId::empty()))
             .map_err(|_| SendError::BufferFull)?;
 
         net_trace!(
@@ -293,6 +335,44 @@ impl<'a> Socket<'a> {
         Ok(payload_buf)
     }
 
+    /// Send a packet, but marked, so that possible metadata about the packet
+    /// may be retrieved from the sending device
+    #[cfg(feature = "packet-id")]
+    pub fn send_marked(
+        &mut self,
+        size: usize,
+        remote_endpoint: IpEndpoint,
+        packet_id: u32,
+    ) -> Result<&mut [u8], SendError> {
+        if self.endpoint.port == 0 {
+            return Err(SendError::Unaddressable);
+        }
+        if remote_endpoint.addr.is_unspecified() {
+            return Err(SendError::Unaddressable);
+        }
+        if remote_endpoint.port == 0 {
+            return Err(SendError::Unaddressable);
+        }
+
+        let payload_buf = self
+            .tx_buffer
+            .enqueue(
+                size,
+                UdpMetadata::new(remote_endpoint, PacketId::new(packet_id)),
+            )
+            .map_err(|_| SendError::BufferFull)?;
+
+        net_trace!(
+            "udp:{}:{}: buffer to send {} octets (marked with ID {})",
+            self.endpoint,
+            remote_endpoint,
+            size,
+            packet_id
+        );
+
+        Ok(payload_buf)
+    }
+
     /// Enqueue a packet to be send to a given remote endpoint and pass the buffer
     /// to the provided closure. The closure then returns the size of the data written
     /// into the buffer.
@@ -319,7 +399,11 @@ impl<'a> Socket<'a> {
 
         let size = self
             .tx_buffer
-            .enqueue_with_infallible(max_size, remote_endpoint, f)
+            .enqueue_with_infallible(
+                max_size,
+                UdpMetadata::new(remote_endpoint, PacketId::empty()),
+                f,
+            )
             .map_err(|_| SendError::BufferFull)?;
 
         net_trace!(
@@ -348,14 +432,14 @@ impl<'a> Socket<'a> {
     /// as a pointer to the payload.
     ///
     /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty.
-    pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint), RecvError> {
+    pub fn recv(&mut self) -> Result<(&[u8], UdpMetadata), RecvError> {
         let (remote_endpoint, payload_buf) =
             self.rx_buffer.dequeue().map_err(|_| RecvError::Exhausted)?;
 
         net_trace!(
             "udp:{}:{}: receive {} buffered octets",
             self.endpoint,
-            remote_endpoint,
+            remote_endpoint.endpoint,
             payload_buf.len()
         );
         Ok((payload_buf, remote_endpoint))
@@ -365,7 +449,7 @@ impl<'a> Socket<'a> {
     /// and return the amount of octets copied as well as the endpoint.
     ///
     /// See also [recv](#method.recv).
-    pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpEndpoint), RecvError> {
+    pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, UdpMetadata), RecvError> {
         let (buffer, endpoint) = self.recv().map_err(|_| RecvError::Exhausted)?;
         let length = min(data.len(), buffer.len());
         data[..length].copy_from_slice(&buffer[..length]);
@@ -377,14 +461,14 @@ impl<'a> Socket<'a> {
     /// This function otherwise behaves identically to [recv](#method.recv).
     ///
     /// It returns `Err(Error::Exhausted)` if the receive buffer is empty.
-    pub fn peek(&mut self) -> Result<(&[u8], &IpEndpoint), RecvError> {
+    pub fn peek(&mut self) -> Result<(&[u8], &UdpMetadata), RecvError> {
         let endpoint = self.endpoint;
         self.rx_buffer.peek().map_err(|_| RecvError::Exhausted).map(
             |(remote_endpoint, payload_buf)| {
                 net_trace!(
                     "udp:{}:{}: peek {} buffered octets",
                     endpoint,
-                    remote_endpoint,
+                    remote_endpoint.endpoint,
                     payload_buf.len()
                 );
                 (payload_buf, remote_endpoint)
@@ -398,7 +482,7 @@ impl<'a> Socket<'a> {
     /// This function otherwise behaves identically to [recv_slice](#method.recv_slice).
     ///
     /// See also [peek](#method.peek).
-    pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &IpEndpoint), RecvError> {
+    pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &UdpMetadata), RecvError> {
         let (buffer, endpoint) = self.peek()?;
         let length = min(data.len(), buffer.len());
         data[..length].copy_from_slice(&buffer[..length]);
@@ -423,6 +507,7 @@ impl<'a> Socket<'a> {
     pub(crate) fn process(
         &mut self,
         cx: &mut Context,
+        packet_id: PacketId,
         ip_repr: &IpRepr,
         repr: &UdpRepr,
         payload: &[u8],
@@ -443,7 +528,12 @@ impl<'a> Socket<'a> {
             size
         );
 
-        match self.rx_buffer.enqueue(size, remote_endpoint) {
+        let metadata = UdpMetadata {
+            endpoint: remote_endpoint,
+            packet_id,
+        };
+
+        match self.rx_buffer.enqueue(size, metadata) {
             Ok(buf) => buf.copy_from_slice(payload),
             Err(_) => net_trace!(
                 "udp:{}:{}: buffer full, dropped incoming packet",
@@ -458,21 +548,21 @@ impl<'a> Socket<'a> {
 
     pub(crate) fn dispatch<F, E>(&mut self, cx: &mut Context, emit: F) -> Result<(), E>
     where
-        F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), E>,
+        F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8], PacketId)) -> Result<(), E>,
     {
         let endpoint = self.endpoint;
         let hop_limit = self.hop_limit.unwrap_or(64);
 
-        let res = self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| {
+        let res = self.tx_buffer.dequeue_with(|packet_meta, payload_buf| {
             let src_addr = match endpoint.addr {
                 Some(addr) => addr,
-                None => match cx.get_source_address(remote_endpoint.addr) {
+                None => match cx.get_source_address(packet_meta.endpoint.addr) {
                     Some(addr) => addr,
                     None => {
                         net_trace!(
                             "udp:{}:{}: cannot find suitable source address, dropping.",
                             endpoint,
-                            remote_endpoint
+                            packet_meta.endpoint
                         );
                         return Ok(());
                     }
@@ -482,22 +572,23 @@ impl<'a> Socket<'a> {
             net_trace!(
                 "udp:{}:{}: sending {} octets",
                 endpoint,
-                remote_endpoint,
+                packet_meta.endpoint,
                 payload_buf.len()
             );
 
             let repr = UdpRepr {
                 src_port: endpoint.port,
-                dst_port: remote_endpoint.port,
+                dst_port: packet_meta.endpoint.port,
             };
             let ip_repr = IpRepr::new(
                 src_addr,
-                remote_endpoint.addr,
+                packet_meta.endpoint.addr,
                 IpProtocol::Udp,
                 repr.header_len() + payload_buf.len(),
                 hop_limit,
             );
-            emit(cx, (ip_repr, repr, payload_buf))
+
+            emit(cx, (ip_repr, repr, payload_buf, packet_meta.packet_id))
         });
         match res {
             Err(Empty) => Ok(()),
@@ -525,7 +616,12 @@ mod test {
     use crate::wire::{IpRepr, UdpRepr};
 
     fn buffer(packets: usize) -> PacketBuffer<'static> {
-        PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 16 * packets])
+        PacketBuffer::new(
+            (0..packets)
+                .map(|_| PacketMetadata::EMPTY)
+                .collect::<Vec<_>>(),
+            vec![0; 16 * packets],
+        )
     }
 
     fn socket(
@@ -682,7 +778,7 @@ mod test {
         assert!(!socket.can_send());
 
         assert_eq!(
-            socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| {
+            socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| {
                 assert_eq!(ip_repr, LOCAL_IP_REPR);
                 assert_eq!(udp_repr, LOCAL_UDP_REPR);
                 assert_eq!(payload, PAYLOAD);
@@ -693,7 +789,7 @@ mod test {
         assert!(!socket.can_send());
 
         assert_eq!(
-            socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| {
+            socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| {
                 assert_eq!(ip_repr, LOCAL_IP_REPR);
                 assert_eq!(udp_repr, LOCAL_UDP_REPR);
                 assert_eq!(payload, PAYLOAD);
@@ -715,13 +811,31 @@ mod test {
         assert_eq!(socket.recv(), Err(RecvError::Exhausted));
 
         assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
-        socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
+        socket.process(
+            &mut cx,
+            PacketId::empty(),
+            &REMOTE_IP_REPR,
+            &REMOTE_UDP_REPR,
+            PAYLOAD,
+        );
         assert!(socket.can_recv());
 
         assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
-        socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
+        socket.process(
+            &mut cx,
+            PacketId::empty(),
+            &REMOTE_IP_REPR,
+            &REMOTE_UDP_REPR,
+            PAYLOAD,
+        );
 
-        assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
+        assert_eq!(
+            socket.recv(),
+            Ok((
+                &b"abcdef"[..],
+                UdpMetadata::new(REMOTE_END, PacketId::empty())
+            ))
+        );
         assert!(!socket.can_recv());
     }
 
@@ -734,9 +848,27 @@ mod test {
 
         assert_eq!(socket.peek(), Err(RecvError::Exhausted));
 
-        socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
-        assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END)));
-        assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
+        socket.process(
+            &mut cx,
+            PacketId::empty(),
+            &REMOTE_IP_REPR,
+            &REMOTE_UDP_REPR,
+            PAYLOAD,
+        );
+        assert_eq!(
+            socket.peek(),
+            Ok((
+                &b"abcdef"[..],
+                &UdpMetadata::new(REMOTE_END, PacketId::empty())
+            ))
+        );
+        assert_eq!(
+            socket.recv(),
+            Ok((
+                &b"abcdef"[..],
+                UdpMetadata::new(REMOTE_END, PacketId::empty())
+            ))
+        );
         assert_eq!(socket.peek(), Err(RecvError::Exhausted));
     }
 
@@ -748,10 +880,19 @@ mod test {
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
         assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
-        socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
+        socket.process(
+            &mut cx,
+            PacketId::empty(),
+            &REMOTE_IP_REPR,
+            &REMOTE_UDP_REPR,
+            PAYLOAD,
+        );
 
         let mut slice = [0; 4];
-        assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END)));
+        assert_eq!(
+            socket.recv_slice(&mut slice[..]),
+            Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty())))
+        );
         assert_eq!(&slice, b"abcd");
     }
 
@@ -762,12 +903,24 @@ mod test {
 
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
-        socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
+        socket.process(
+            &mut cx,
+            PacketId::empty(),
+            &REMOTE_IP_REPR,
+            &REMOTE_UDP_REPR,
+            PAYLOAD,
+        );
 
         let mut slice = [0; 4];
-        assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END)));
+        assert_eq!(
+            socket.peek_slice(&mut slice[..]),
+            Ok((4, &UdpMetadata::new(REMOTE_END, PacketId::empty())))
+        );
         assert_eq!(&slice, b"abcd");
-        assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END)));
+        assert_eq!(
+            socket.recv_slice(&mut slice[..]),
+            Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty())))
+        );
         assert_eq!(&slice, b"abcd");
         assert_eq!(socket.peek_slice(&mut slice[..]), Err(RecvError::Exhausted));
     }
@@ -782,7 +935,7 @@ mod test {
         s.set_hop_limit(Some(0x2a));
         assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
         assert_eq!(
-            s.dispatch(&mut cx, |_, (ip_repr, _, _)| {
+            s.dispatch(&mut cx, |_, (ip_repr, _, _, _)| {
                 assert_eq!(
                     ip_repr,
                     IpReprIpvX(IpvXRepr {
@@ -841,7 +994,8 @@ mod test {
 
     #[test]
     fn test_process_empty_payload() {
-        let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]);
+        let meta = Box::leak(Box::new([PacketMetadata::EMPTY]));
+        let recv_buffer = PacketBuffer::new(&mut meta[..], vec![]);
         let mut socket = socket(recv_buffer, buffer(0));
         let mut cx = Context::mock();
 
@@ -851,13 +1005,17 @@ mod test {
             src_port: REMOTE_PORT,
             dst_port: LOCAL_PORT,
         };
-        socket.process(&mut cx, &REMOTE_IP_REPR, &repr, &[]);
-        assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END)));
+        socket.process(&mut cx, PacketId::empty(), &REMOTE_IP_REPR, &repr, &[]);
+        assert_eq!(
+            socket.recv(),
+            Ok((&[][..], UdpMetadata::new(REMOTE_END, PacketId::empty())))
+        );
     }
 
     #[test]
     fn test_closing() {
-        let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]);
+        let meta = Box::leak(Box::new([PacketMetadata::EMPTY]));
+        let recv_buffer = PacketBuffer::new(&mut meta[..], vec![]);
         let mut socket = socket(recv_buffer, buffer(0));
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));