Skip to main content

mcrx_core/
packet.rs

1use crate::subscription::SubscriptionId;
2use bytes::Bytes;
3use std::net::{IpAddr, SocketAddr};
4
5/// A received packet together with the metadata needed by the receiver core.
6#[derive(Debug, Clone)]
7pub struct Packet {
8    /// The subscription through which this packet was received.
9    pub subscription_id: SubscriptionId,
10    /// The remote sender's source address and source port.
11    pub source: SocketAddr,
12    /// The destination multicast group address.
13    pub group: IpAddr,
14    /// The destination UDP port on which the packet was received.
15    pub dst_port: u16,
16    /// The raw UDP payload bytes.
17    pub payload: Bytes,
18}
19
20/// Structured receive metadata that can grow as the platform layer learns more
21/// about the packet delivery context.
22#[derive(Debug, Clone, PartialEq, Eq)]
23#[non_exhaustive]
24pub struct ReceiveMetadata {
25    /// The local socket address currently bound by the receiving socket, if known.
26    pub socket_local_addr: Option<SocketAddr>,
27    /// The local interface requested by the subscription configuration, if any.
28    ///
29    /// This reflects configured intent, not pktinfo-derived ingress state.
30    pub configured_interface: Option<IpAddr>,
31    /// The local IPv6 interface index requested by the subscription configuration, if any.
32    pub configured_interface_index: Option<u32>,
33    /// The local destination IP address from pktinfo-style metadata, if available.
34    pub destination_local_ip: Option<IpAddr>,
35    /// The ingress interface index from pktinfo-style metadata, if available.
36    pub ingress_interface_index: Option<u32>,
37}
38
39impl ReceiveMetadata {
40    pub(crate) fn empty() -> Self {
41        Self {
42            socket_local_addr: None,
43            configured_interface: None,
44            configured_interface_index: None,
45            destination_local_ip: None,
46            ingress_interface_index: None,
47        }
48    }
49}
50
51/// A received packet together with richer receive metadata.
52#[derive(Debug, Clone)]
53#[non_exhaustive]
54pub struct PacketWithMetadata {
55    /// The packet payload and core addressing information.
56    pub packet: Packet,
57    /// Additional receive context supplied by the platform layer.
58    pub metadata: ReceiveMetadata,
59}
60
61impl Packet {
62    /// Returns the length of the payload in bytes.
63    pub fn payload_len(&self) -> usize {
64        self.payload.len()
65    }
66
67    pub fn payload(&self) -> &[u8] {
68        &self.payload
69    }
70}
71
72impl PacketWithMetadata {
73    /// Returns a read-only reference to the inner packet.
74    pub fn packet(&self) -> &Packet {
75        &self.packet
76    }
77
78    /// Returns a read-only reference to the receive metadata.
79    pub fn metadata(&self) -> &ReceiveMetadata {
80        &self.metadata
81    }
82
83    /// Discards the richer metadata and returns the inner packet.
84    pub fn into_packet(self) -> Packet {
85        self.packet
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use std::net::{IpAddr, Ipv4Addr, SocketAddrV4};
93
94    #[test]
95    fn packet_payload_len_returns_correct_length() {
96        let packet = Packet {
97            subscription_id: SubscriptionId(1),
98            source: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 12345)),
99            group: IpAddr::V4(Ipv4Addr::new(239, 1, 2, 3)),
100            dst_port: 5000,
101            payload: Bytes::from_static(&[1, 2, 3]),
102        };
103
104        assert_eq!(packet.payload_len(), 3);
105    }
106
107    #[test]
108    fn subscription_id_equality_works() {
109        let a = SubscriptionId(7);
110        let b = SubscriptionId(7);
111        let c = SubscriptionId(8);
112
113        assert_eq!(a, b);
114        assert_ne!(a, c);
115    }
116
117    #[test]
118    fn packet_with_metadata_into_packet_discards_metadata() {
119        let packet = Packet {
120            subscription_id: SubscriptionId(1),
121            source: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 12345)),
122            group: IpAddr::V4(Ipv4Addr::new(239, 1, 2, 3)),
123            dst_port: 5000,
124            payload: Bytes::from_static(&[1, 2, 3]),
125        };
126
127        let detailed = PacketWithMetadata {
128            packet: packet.clone(),
129            metadata: ReceiveMetadata {
130                socket_local_addr: Some(SocketAddr::V4(SocketAddrV4::new(
131                    Ipv4Addr::UNSPECIFIED,
132                    5000,
133                ))),
134                configured_interface: None,
135                configured_interface_index: None,
136                destination_local_ip: None,
137                ingress_interface_index: None,
138            },
139        };
140
141        let stripped = detailed.into_packet();
142
143        assert_eq!(stripped.subscription_id, packet.subscription_id);
144        assert_eq!(stripped.source, packet.source);
145        assert_eq!(stripped.group, packet.group);
146        assert_eq!(stripped.dst_port, packet.dst_port);
147        assert_eq!(stripped.payload, packet.payload);
148    }
149}