mctp_estack/
fragment.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2/*
3 * Copyright (c) 2024 Code Construct
4 */
5
6//! Packet Fragmentation
7
8#[allow(unused)]
9use crate::fmt::{debug, error, info, trace, warn};
10
11use mctp::{Eid, Error, MsgIC, MsgType, Result, Tag, MCTP_HEADER_VERSION_1};
12
13use crate::{AppCookie, Header, HEADER_LEN, MAX_MTU};
14
15/// Fragments a MCTP message.
16///
17/// This is constructed from [`Stack::start_send()`](crate::Stack::start_send)
18#[derive(Debug)]
19pub struct Fragmenter {
20    src: Eid,
21    dest: Eid,
22    typ: MsgType,
23    tag: Tag,
24    ic: MsgIC,
25    seq: u8,
26    mtu: usize,
27
28    first: bool,
29    done: bool,
30    cookie: Option<AppCookie>,
31
32    // A count of how many bytes have already been sent.
33    payload_used: usize,
34}
35
36impl Fragmenter {
37    pub(crate) fn new(
38        typ: MsgType,
39        src: Eid,
40        dest: Eid,
41        tag: Tag,
42        mtu: usize,
43        cookie: Option<AppCookie>,
44        ic: MsgIC,
45        initial_seq: u8,
46    ) -> Result<Self> {
47        if tag.tag().0 > mctp::MCTP_TAG_MAX {
48            return Err(Error::InvalidInput);
49        }
50        debug_assert!(typ.0 & 0x80 == 0, "IC bit's set in typ");
51        debug_assert!(initial_seq & !mctp::MCTP_SEQ_MASK == 0);
52        if mtu < HEADER_LEN + 1 {
53            debug!("mtu too small");
54            return Err(Error::BadArgument);
55        }
56        if mtu > MAX_MTU {
57            debug!("mtu too large");
58            return Err(Error::BadArgument);
59        }
60        // TODO other validity checks
61
62        Ok(Self {
63            payload_used: 0,
64            src,
65            dest,
66            typ,
67            mtu,
68            first: true,
69            done: false,
70            seq: initial_seq,
71            tag,
72            cookie,
73            ic,
74        })
75    }
76
77    pub fn tag(&self) -> Tag {
78        self.tag
79    }
80
81    pub fn dest(&self) -> Eid {
82        self.dest
83    }
84
85    pub fn cookie(&self) -> Option<AppCookie> {
86        self.cookie
87    }
88
89    fn header(&self) -> Header {
90        let mut header = Header::new(MCTP_HEADER_VERSION_1);
91        header.set_dest_endpoint_id(self.dest.0);
92        header.set_source_endpoint_id(self.src.0);
93        header.set_pkt_seq(self.seq);
94        if self.first {
95            header.set_som(self.first as u8);
96        }
97        header.set_msg_tag(self.tag.tag().0);
98        header.set_to(self.tag.is_owner() as u8);
99        header
100    }
101
102    /// Returns fragments for the MCTP payload
103    ///
104    /// The same input message `payload` should be passed to each `fragment()` call.
105    /// In `SendOutput::Packet(buf)`, `out` is borrowed as the returned fragment, filled with packet contents.
106    pub fn fragment<'f>(
107        &mut self,
108        payload: &[u8],
109        out: &'f mut [u8],
110    ) -> SendOutput<'f> {
111        if self.done {
112            return SendOutput::success(self);
113        }
114
115        // first fragment needs type byte
116        let min = HEADER_LEN + self.first as usize;
117
118        if out.len() < min {
119            return SendOutput::failure(Error::NoSpace, self);
120        }
121
122        // Reserve header space, the remaining buffer keeps being
123        // updated in `rest`
124        let max_total = out.len().min(self.mtu);
125        // let out = &mut out[..max_total];
126        let (h, mut rest) = out[..max_total].split_at_mut(HEADER_LEN);
127
128        // Append type byte
129        if self.first {
130            rest[0] = mctp::encode_type_ic(self.typ, self.ic);
131            rest = &mut rest[1..];
132        }
133
134        if payload.len() < self.payload_used {
135            // Caller is passing varying payload buffers
136            return SendOutput::failure(Error::InvalidInput, self);
137        }
138
139        // Copy as much as is available in input or output
140        let p = &payload[self.payload_used..];
141        let l = p.len().min(rest.len());
142        let (d, rest) = rest.split_at_mut(l);
143        self.payload_used += l;
144        d.copy_from_slice(&p[..l]);
145
146        // Add the header
147        let mut header = self.header();
148        if self.payload_used == payload.len() {
149            header.set_eom(1);
150            self.done = true;
151        }
152        h.copy_from_slice(&header.0);
153
154        self.first = false;
155        self.seq = (self.seq + 1) & mctp::MCTP_SEQ_MASK;
156
157        let used = max_total - rest.len();
158        SendOutput::Packet(&mut out[..used])
159    }
160
161    pub fn is_done(&self) -> bool {
162        self.done
163    }
164}
165
166pub enum SendOutput<'p> {
167    Packet(&'p mut [u8]),
168    Complete {
169        tag: Tag,
170        cookie: Option<AppCookie>,
171    },
172    Error {
173        err: Error,
174        cookie: Option<AppCookie>,
175    },
176}
177
178impl SendOutput<'_> {
179    /// Returns an unborrowed copy for Complete or Error variants.
180    ///
181    /// Panics if called with a Packet variant (borrowed).
182    /// For avoiding borrow problems. Can be removed once Rust polonius merges.
183    pub(crate) fn unborrowed<'x>(self) -> Option<SendOutput<'x>> {
184        match self {
185            Self::Packet(_) => unreachable!(),
186            Self::Complete { tag, cookie } => {
187                Some(SendOutput::Complete { tag, cookie })
188            }
189            Self::Error { err, cookie } => {
190                Some(SendOutput::Error { err, cookie })
191            }
192        }
193    }
194
195    pub(crate) fn success(f: &Fragmenter) -> Self {
196        Self::Complete {
197            tag: f.tag,
198            cookie: f.cookie,
199        }
200    }
201
202    pub(crate) fn failure(err: Error, f: &Fragmenter) -> Self {
203        Self::Error {
204            err,
205            cookie: f.cookie,
206        }
207    }
208
209    /// Just an error, no fragmenter required
210    pub(crate) fn bare_failure(err: Error) -> Self {
211        Self::Error { err, cookie: None }
212    }
213}