Skip to main content

pim_protocol/
data_frame.rs

1//! Data-plane frame that carries mesh-routed IP payloads.
2
3use bitflags::bitflags;
4use bytes::{Buf, BufMut, BytesMut};
5
6use pim_core::{FrameCodec, NodeId, PimError};
7
8bitflags! {
9    /// Flags on a mesh data frame.
10    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
11    pub struct DataFlags: u8 {
12        /// Payload contains a [`FragmentFrame`](crate::FragmentFrame) body.
13        const IS_FRAGMENT      = 0b0000_0001;
14        /// Fragment reaches the end of the original packet.
15        const IS_LAST_FRAGMENT = 0b0000_0010;
16        /// Receiver should acknowledge delivery.
17        const REQUIRES_ACK     = 0b0000_0100;
18        /// Payload is an acknowledgement rather than ordinary data.
19        const IS_ACK           = 0b0000_1000;
20        /// Payload is destined for internet egress via a gateway.
21        const IS_INTERNET      = 0b0001_0000;
22        /// Payload is ECIES-encrypted (E2E) to the final destination gateway.
23        /// Relay nodes must forward as-is; only the gateway decrypts.
24        const IS_E2E           = 0b0010_0000;
25        /// Payload carries a mesh-routed control-plane message.
26        const IS_CONTROL       = 0b0100_0000;
27    }
28}
29
30/// Carries an IP packet (or fragment) through the mesh.
31///
32/// Header: src_id(16) + dst_id(16) + session_id(4) + ttl(1) + flags(1) + payload_len(2) = 40 bytes
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct MeshDataFrame {
35    /// Original source node for the payload.
36    pub src_id: NodeId,
37    /// Final destination node for the payload.
38    pub dst_id: NodeId,
39    /// Session identifier chosen by the sender.
40    pub session_id: u32,
41    /// Remaining hop budget.
42    pub ttl: u8,
43    /// Delivery and fragmentation flags.
44    pub flags: DataFlags,
45    /// Encapsulated IP packet bytes or fragment payload.
46    pub payload: bytes::Bytes,
47}
48
49const HEADER_SIZE: usize = 40;
50
51impl FrameCodec for MeshDataFrame {
52    fn encode(&self, buf: &mut BytesMut) {
53        buf.put_slice(self.src_id.as_bytes());
54        buf.put_slice(self.dst_id.as_bytes());
55        buf.put_u32(self.session_id);
56        buf.put_u8(self.ttl);
57        buf.put_u8(self.flags.bits());
58        buf.put_u16(self.payload.len() as u16);
59        buf.put_slice(&self.payload);
60    }
61
62    fn decode(buf: &mut BytesMut) -> Result<Self, PimError> {
63        if buf.len() < HEADER_SIZE {
64            return Err(PimError::Protocol(format!(
65                "data frame too short: need {HEADER_SIZE} bytes, have {}",
66                buf.len()
67            )));
68        }
69
70        let mut src_bytes = [0u8; 16];
71        src_bytes.copy_from_slice(&buf[0..16]);
72        let src_id = NodeId::from_bytes(src_bytes);
73
74        let mut dst_bytes = [0u8; 16];
75        dst_bytes.copy_from_slice(&buf[16..32]);
76        let dst_id = NodeId::from_bytes(dst_bytes);
77
78        let session_id = (&buf[32..36]).get_u32();
79        let ttl = buf[36];
80        let flags = DataFlags::from_bits_truncate(buf[37]);
81        let payload_len = (&buf[38..40]).get_u16() as usize;
82
83        let total = HEADER_SIZE + payload_len;
84        if buf.len() < total {
85            return Err(PimError::Protocol(format!(
86                "data frame truncated: need {total} bytes, have {}",
87                buf.len()
88            )));
89        }
90
91        buf.advance(HEADER_SIZE);
92        let payload = buf.split_to(payload_len).freeze();
93
94        Ok(MeshDataFrame {
95            src_id,
96            dst_id,
97            session_id,
98            ttl,
99            flags,
100            payload,
101        })
102    }
103}
104
105#[cfg(test)]
106mod tests;