Skip to main content

leodos_protocols/transport/srspp/
packet.rs

1use crate::network::cfe::tc::TelecommandSecondaryHeader;
2use crate::network::isl::address::Address;
3use crate::network::isl::address::RawAddress;
4use crate::network::isl::routing::packet::IslRoutingTelecommandHeader;
5use crate::network::spp::Apid;
6use crate::network::spp::PacketType;
7use crate::network::spp::PacketVersion;
8use crate::network::spp::PrimaryHeader;
9use crate::network::spp::SecondaryHeaderFlag;
10use crate::network::spp::SequenceCount;
11use crate::network::spp::SequenceFlag;
12use crate::utils::checksum_u8;
13use crate::utils::get_bits_u8;
14use crate::utils::set_bits_u8;
15use crate::utils::validate_checksum_u8;
16use bon::bon;
17use core::mem::size_of;
18use zerocopy::FromBytes;
19use zerocopy::Immutable;
20use zerocopy::IntoBytes;
21use zerocopy::KnownLayout;
22use zerocopy::Unaligned;
23use zerocopy::byteorder::network_endian;
24
25/// SRSPP packet type discriminator.
26/// Current SRSPP protocol version.
27pub const SRSPP_VERSION: u8 = 0;
28
29/// SRSPP packet type discriminator (2 bits).
30#[derive(Copy, Clone, Eq, PartialEq, Debug)]
31#[repr(u8)]
32pub enum SrsppType {
33    /// Data packet carrying application payload.
34    Data = 0,
35    /// Acknowledgment packet.
36    Ack = 1,
37    /// End-of-stream signal (no payload).
38    Eos = 2,
39}
40
41impl TryFrom<u8> for SrsppType {
42    type Error = SrsppPacketError;
43
44    fn try_from(value: u8) -> Result<Self, Self::Error> {
45        match value {
46            0 => Ok(Self::Data),
47            1 => Ok(Self::Ack),
48            2 => Ok(Self::Eos),
49            _ => Err(SrsppPacketError::InvalidPacketType { value }),
50        }
51    }
52}
53
54/// Wire-format SRSPP header following the ISL routing header.
55///
56/// Byte layout (3 bytes):
57///   - source_address: 2 bytes (RawAddress)
58///   - version_type:   1 byte  (version[7:6] | type[5:4] | spare[3:0])
59#[repr(C, packed)]
60#[derive(FromBytes, IntoBytes, KnownLayout, Unaligned, Immutable, Copy, Clone, Debug)]
61pub(crate) struct SrsppHeader {
62    /// Source address of the sender.
63    source_address: RawAddress,
64    /// Packed version (2 bits), type (2 bits), spare (4 bits).
65    version_type: u8,
66}
67
68#[rustfmt::skip]
69mod bitmask {
70    /// Bitmask for the 2-bit protocol version field.
71    pub const VERSION_MASK: u8 = 0b_1100_0000;
72    /// Bitmask for the 2-bit packet type field.
73    pub const TYPE_MASK: u8 =    0b_0011_0000;
74}
75
76impl SrsppHeader {
77    /// Parse the packet type field into an `SrsppType`.
78    pub(crate) fn srspp_type(&self) -> Result<SrsppType, SrsppPacketError> {
79        SrsppType::try_from(get_bits_u8(self.version_type, bitmask::TYPE_MASK))
80    }
81
82    /// Returns the protocol version (2 bits).
83    #[allow(unused)]
84    pub(crate) fn version(&self) -> u8 {
85        get_bits_u8(self.version_type, bitmask::VERSION_MASK)
86    }
87
88    /// Returns the parsed source address.
89    pub(crate) fn source_address(&self) -> Address {
90        self.source_address.parse()
91    }
92
93    /// Sets the source address field.
94    pub(crate) fn set_source_address(&mut self, address: Address) {
95        self.source_address = RawAddress::from(address);
96    }
97
98    /// Sets the version and packet type fields.
99    pub(crate) fn set_srspp_type(&mut self, srspp_type: SrsppType) {
100        set_bits_u8(&mut self.version_type, bitmask::VERSION_MASK, SRSPP_VERSION);
101        set_bits_u8(&mut self.version_type, bitmask::TYPE_MASK, srspp_type as u8);
102    }
103}
104
105/// Payload of an SRSPP acknowledgment packet.
106#[repr(C, packed)]
107#[derive(FromBytes, IntoBytes, KnownLayout, Unaligned, Immutable, Copy, Clone, Debug)]
108pub struct AckPayload {
109    /// Sequence number up to which all packets are acknowledged.
110    cumulative_ack: network_endian::U16,
111    /// Bitmap of selectively acknowledged packets beyond the cumulative ack.
112    selective_ack_bitmap: network_endian::U16,
113}
114
115impl AckPayload {
116    /// Creates a new ACK payload with the given cumulative ack and bitmap.
117    pub(crate) fn new(cumulative_ack: u16, bitmap: u16) -> Self {
118        Self {
119            cumulative_ack: network_endian::U16::new(cumulative_ack),
120            selective_ack_bitmap: network_endian::U16::new(bitmap),
121        }
122    }
123
124    /// Returns the cumulative acknowledgment sequence number.
125    ///
126    /// Note: Used by CFS and Tokio
127    #[allow(dead_code)]
128    pub(crate) fn cumulative_ack(&self) -> SequenceCount {
129        SequenceCount::from(self.cumulative_ack.get())
130    }
131
132    /// Returns the selective acknowledgment bitmap.
133    ///
134    /// Note: Used by CFS and Tokio
135    #[allow(dead_code)]
136    pub(crate) fn selective_ack_bitmap(&self) -> u16 {
137        self.selective_ack_bitmap.get()
138    }
139}
140
141/// An SRSPP packet of unknown type (data or ack).
142#[repr(C, packed)]
143#[derive(FromBytes, IntoBytes, KnownLayout, Unaligned, Immutable)]
144pub struct SrsppPacket {
145    /// Space Packet primary header.
146    pub primary: PrimaryHeader,
147    /// cFE telecommand secondary header.
148    pub secondary: TelecommandSecondaryHeader,
149    /// ISL routing header for inter-satellite addressing.
150    pub(crate) isl_header: IslRoutingTelecommandHeader,
151    /// SRSPP protocol header.
152    pub(crate) srspp_header: SrsppHeader,
153    /// Remaining bytes (payload for data, ack fields for ack).
154    pub rest: [u8],
155}
156
157impl SrsppPacket {
158    /// Parse an SRSPP packet from a raw byte buffer.
159    pub fn parse(bytes: &[u8]) -> Result<&Self, SrsppPacketError> {
160        Self::ref_from_bytes(bytes).map_err(|_| SrsppPacketError::BufferTooSmall {
161            required: SrsppDataPacket::HEADER_SIZE,
162            provided: bytes.len(),
163        })
164    }
165
166    /// Returns the SRSPP packet type.
167    pub fn srspp_type(&self) -> Result<SrsppType, SrsppPacketError> {
168        self.srspp_header.srspp_type()
169    }
170
171    /// Returns the source address from the SRSPP header.
172    pub fn source_address(&self) -> Address {
173        self.srspp_header.source_address()
174    }
175}
176
177/// ```text
178/// +------------------------------------+---------+
179/// | Space Packet Primary Header        | 6 bytes |
180/// | cFE Telecommand Secondary Header   | 2 bytes |
181/// | ISL Routing Header                 | 2 bytes |
182/// | SRSPP Header                       | 3 bytes |
183/// | Payload                            | N bytes |
184/// +------------------------------------+---------+
185/// ```
186#[repr(C, packed)]
187#[derive(FromBytes, IntoBytes, KnownLayout, Unaligned, Immutable)]
188pub struct SrsppDataPacket {
189    /// Space Packet primary header.
190    pub primary: PrimaryHeader,
191    /// cFE telecommand secondary header.
192    pub secondary: TelecommandSecondaryHeader,
193    /// ISL routing header for inter-satellite addressing.
194    pub(crate) isl_header: IslRoutingTelecommandHeader,
195    /// SRSPP protocol header.
196    pub(crate) srspp_header: SrsppHeader,
197    /// Variable-length application payload.
198    pub payload: [u8],
199}
200
201impl SrsppDataPacket {
202    /// Total header size in bytes (SPP + cFE + ISL + SRSPP).
203    pub const HEADER_SIZE: usize = size_of::<PrimaryHeader>()
204        + size_of::<TelecommandSecondaryHeader>()
205        + size_of::<IslRoutingTelecommandHeader>()
206        + size_of::<SrsppHeader>();
207
208    /// Parse a data packet reference from a raw byte buffer.
209    pub fn parse(bytes: &[u8]) -> Result<&Self, SrsppPacketError> {
210        Self::ref_from_bytes(bytes).map_err(|_| SrsppPacketError::BufferTooSmall {
211            required: Self::HEADER_SIZE,
212            provided: bytes.len(),
213        })
214    }
215
216    /// Maximum payload bytes that fit within the given MTU.
217    pub const fn max_payload_size(mtu: usize) -> usize {
218        mtu.saturating_sub(Self::HEADER_SIZE)
219    }
220
221    /// Compute and set the cFE checksum over the entire packet.
222    pub fn set_cfe_checksum(&mut self) {
223        self.secondary.set_checksum(0);
224        self.secondary.set_checksum(checksum_u8(self.as_bytes()));
225    }
226
227    /// Validate the cFE checksum of this packet.
228    pub fn validate_cfe_checksum(&self) -> bool {
229        validate_checksum_u8(self.as_bytes())
230    }
231}
232
233/// ```text
234/// +------------------------------------+---------+
235/// | Space Packet Primary Header        | 6 bytes |
236/// | cFE Telecommand Secondary Header   | 2 bytes |
237/// | ISL Routing Header                 | 2 bytes |
238/// | SRSPP Header                       | 3 bytes |
239/// | ACK Payload                        | 4 bytes |
240/// +------------------------------------+---------+
241/// ```
242#[repr(C, packed)]
243#[derive(FromBytes, IntoBytes, KnownLayout, Unaligned, Immutable, Copy, Clone, Debug)]
244pub struct SrsppAckPacket {
245    /// Space Packet primary header.
246    pub primary: PrimaryHeader,
247    /// cFE telecommand secondary header.
248    pub secondary: TelecommandSecondaryHeader,
249    /// ISL routing header for inter-satellite addressing.
250    pub(crate) isl_header: IslRoutingTelecommandHeader,
251    /// SRSPP protocol header.
252    pub(crate) srspp_header: SrsppHeader,
253    /// Acknowledgment payload with cumulative ack and selective bitmap.
254    pub(crate) ack_payload: AckPayload,
255}
256
257impl SrsppAckPacket {
258    /// Parse an ACK packet reference from a raw byte buffer.
259    pub fn parse(bytes: &[u8]) -> Result<&Self, SrsppPacketError> {
260        if bytes.len() < size_of::<Self>() {
261            return Err(SrsppPacketError::BufferTooSmall {
262                required: size_of::<Self>(),
263                provided: bytes.len(),
264            });
265        }
266        Self::ref_from_bytes(&bytes[..size_of::<Self>()]).map_err(|_| {
267            SrsppPacketError::BufferTooSmall {
268                required: size_of::<Self>(),
269                provided: bytes.len(),
270            }
271        })
272    }
273
274    /// Compute and set the cFE checksum over the entire packet.
275    pub fn set_cfe_checksum(&mut self) {
276        self.secondary.set_checksum(0);
277        self.secondary.set_checksum(checksum_u8(self.as_bytes()));
278    }
279
280    /// Validate the cFE checksum of this packet.
281    pub fn validate_cfe_checksum(&self) -> bool {
282        validate_checksum_u8(self.as_bytes())
283    }
284}
285
286/// Errors that can occur when constructing or parsing SRSPP packets.
287#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)]
288pub enum SrsppPacketError {
289    /// Buffer is too small for the packet.
290    #[error("buffer too small: required {required} bytes, provided {provided} bytes")]
291    BufferTooSmall {
292        /// Minimum number of bytes needed.
293        required: usize,
294        /// Actual number of bytes provided.
295        provided: usize,
296    },
297    /// Packet type byte is not a valid SRSPP type.
298    #[error("invalid SRSPP packet type: {value:#04x}")]
299    InvalidPacketType {
300        /// The unrecognised packet-type byte.
301        value: u8,
302    },
303    /// Payload exceeds the maximum allowed size.
304    #[error("payload too large: maximum {max} bytes, provided {provided} bytes")]
305    PayloadTooLarge {
306        /// Maximum allowed payload size in bytes.
307        max: usize,
308        /// Actual payload size in bytes.
309        provided: usize,
310    },
311}
312
313#[bon]
314impl SrsppDataPacket {
315    /// Build a new SRSPP data packet in the provided buffer.
316    #[builder]
317    pub fn new<'a>(
318        buffer: &'a mut [u8],
319        source_address: Address,
320        target: Address,
321        apid: Apid,
322        function_code: u8,
323        sequence_count: SequenceCount,
324        sequence_flag: SequenceFlag,
325        payload_len: usize,
326    ) -> Result<&'a mut SrsppDataPacket, SrsppPacketError> {
327        let required_len = Self::HEADER_SIZE + payload_len;
328        let provided_len = buffer.len();
329
330        let (packet, _) = SrsppDataPacket::mut_from_prefix_with_elems(buffer, payload_len)
331            .map_err(|_| SrsppPacketError::BufferTooSmall {
332                required: required_len,
333                provided: provided_len,
334            })?;
335
336        packet.primary.set_version(PacketVersion::VERSION_1);
337        packet.primary.set_packet_type(PacketType::Telecommand);
338        packet
339            .primary
340            .set_secondary_header_flag(SecondaryHeaderFlag::Present);
341        packet.primary.set_apid(apid);
342        packet.primary.set_sequence_count(sequence_count);
343        packet.primary.set_sequence_flag(sequence_flag);
344
345        let data_field_len = size_of::<TelecommandSecondaryHeader>()
346            + size_of::<IslRoutingTelecommandHeader>()
347            + size_of::<SrsppHeader>()
348            + payload_len;
349        packet.primary.set_data_field_len(data_field_len as u16);
350
351        packet.secondary.set_function_code(function_code);
352        packet.secondary.set_checksum(0);
353
354        packet.isl_header.set_target(target);
355
356        packet.srspp_header.set_source_address(source_address);
357        packet.srspp_header.set_srspp_type(SrsppType::Data);
358
359        packet.set_cfe_checksum();
360
361        Ok(packet)
362    }
363}
364
365#[bon]
366impl SrsppAckPacket {
367    /// Build a new SRSPP acknowledgment packet in the provided buffer.
368    #[builder]
369    pub fn new<'a>(
370        buffer: &'a mut [u8],
371        source_address: Address,
372        target: Address,
373        apid: Apid,
374        function_code: u8,
375        sequence_count: SequenceCount,
376        cumulative_ack: SequenceCount,
377        selective_bitmap: u16,
378    ) -> Result<&'a mut SrsppAckPacket, SrsppPacketError> {
379        let provided_len = buffer.len();
380        let (packet, _) = SrsppAckPacket::mut_from_prefix(buffer).map_err(|_| {
381            SrsppPacketError::BufferTooSmall {
382                required: size_of::<SrsppAckPacket>(),
383                provided: provided_len,
384            }
385        })?;
386
387        packet.primary.set_version(PacketVersion::VERSION_1);
388        packet.primary.set_packet_type(PacketType::Telecommand);
389        packet
390            .primary
391            .set_secondary_header_flag(SecondaryHeaderFlag::Present);
392        packet.primary.set_apid(apid);
393        packet.primary.set_sequence_count(sequence_count);
394        packet.primary.set_sequence_flag(SequenceFlag::Unsegmented);
395
396        let data_field_len = size_of::<TelecommandSecondaryHeader>()
397            + size_of::<IslRoutingTelecommandHeader>()
398            + size_of::<SrsppHeader>()
399            + size_of::<AckPayload>();
400        packet.primary.set_data_field_len(data_field_len as u16);
401
402        packet.secondary.set_function_code(function_code);
403        packet.secondary.set_checksum(0);
404
405        packet.isl_header.set_target(target);
406
407        packet.srspp_header.set_source_address(source_address);
408        packet.srspp_header.set_srspp_type(SrsppType::Ack);
409
410        packet.ack_payload = AckPayload::new(cumulative_ack.value(), selective_bitmap);
411
412        packet.set_cfe_checksum();
413
414        Ok(packet)
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421
422    fn source_address() -> Address {
423        Address::satellite(1, 5)
424    }
425
426    fn target_address() -> Address {
427        Address::satellite(2, 3)
428    }
429
430    #[test]
431    fn test_data_roundtrip() {
432        let mut buffer = [0u8; 256];
433        let apid = Apid::new(0x42).unwrap();
434        let payload_data = b"hello SRSPP";
435
436        let packet = SrsppDataPacket::builder()
437            .buffer(&mut buffer)
438            .source_address(source_address())
439            .target(target_address())
440            .apid(apid)
441            .function_code(0x10)
442            .sequence_count(SequenceCount::from(7))
443            .sequence_flag(SequenceFlag::Unsegmented)
444            .payload_len(payload_data.len())
445            .build()
446            .unwrap();
447
448        packet.payload.copy_from_slice(payload_data);
449        packet.set_cfe_checksum();
450
451        let bytes = packet.as_bytes();
452
453        let header = SrsppPacket::parse(bytes).unwrap();
454        assert_eq!(header.srspp_type().unwrap(), SrsppType::Data);
455
456        let parsed = SrsppDataPacket::parse(bytes).unwrap();
457        assert_eq!(parsed.primary.apid(), apid);
458        assert_eq!(parsed.primary.sequence_count().value(), 7);
459        assert_eq!(parsed.primary.packet_type(), PacketType::Telecommand);
460        assert_eq!(
461            parsed.primary.secondary_header_flag(),
462            SecondaryHeaderFlag::Present
463        );
464        assert_eq!(parsed.isl_header.target(), target_address());
465        assert_eq!(parsed.secondary.function_code(), 0x10);
466        assert_eq!(parsed.srspp_header.source_address(), source_address());
467        assert_eq!(&parsed.payload, payload_data);
468        assert!(parsed.validate_cfe_checksum());
469    }
470
471    #[test]
472    fn test_ack_roundtrip() {
473        let mut buffer = [0u8; 64];
474        let apid = Apid::new(0x42).unwrap();
475
476        let packet = SrsppAckPacket::builder()
477            .buffer(&mut buffer)
478            .source_address(source_address())
479            .target(target_address())
480            .apid(apid)
481            .function_code(0x10)
482            .sequence_count(SequenceCount::from(3))
483            .cumulative_ack(SequenceCount::from(15))
484            .selective_bitmap(0b1100)
485            .build()
486            .unwrap();
487
488        let bytes = packet.as_bytes();
489
490        let header = SrsppPacket::parse(bytes).unwrap();
491        assert_eq!(header.srspp_type().unwrap(), SrsppType::Ack);
492
493        let parsed = SrsppAckPacket::parse(bytes).unwrap();
494        assert_eq!(parsed.primary.apid(), apid);
495        assert_eq!(parsed.isl_header.target(), target_address());
496        assert_eq!(parsed.srspp_header.source_address(), source_address());
497        assert_eq!(parsed.ack_payload.cumulative_ack().value(), 15);
498        assert_eq!(parsed.ack_payload.selective_ack_bitmap(), 0b1100);
499        assert!(parsed.validate_cfe_checksum());
500    }
501
502    #[test]
503    fn test_parse_srspp_type() {
504        let mut buffer = [0u8; 128];
505        let apid = Apid::new(0x42).unwrap();
506
507        SrsppDataPacket::builder()
508            .buffer(&mut buffer)
509            .source_address(source_address())
510            .target(target_address())
511            .apid(apid)
512            .function_code(0)
513            .sequence_count(SequenceCount::from(0))
514            .sequence_flag(SequenceFlag::Unsegmented)
515            .payload_len(10)
516            .build()
517            .unwrap();
518
519        assert_eq!(
520            SrsppPacket::parse(&buffer).unwrap().srspp_type().unwrap(),
521            SrsppType::Data,
522        );
523
524        SrsppAckPacket::builder()
525            .buffer(&mut buffer)
526            .source_address(source_address())
527            .target(target_address())
528            .apid(apid)
529            .function_code(0)
530            .sequence_count(SequenceCount::from(0))
531            .cumulative_ack(SequenceCount::from(0))
532            .selective_bitmap(0)
533            .build()
534            .unwrap();
535
536        assert_eq!(
537            SrsppPacket::parse(&buffer).unwrap().srspp_type().unwrap(),
538            SrsppType::Ack,
539        );
540    }
541}