From b134e145f93d634dff7eb9f2a01559273c687365 Mon Sep 17 00:00:00 2001 From: James Lee Date: Thu, 21 May 2026 14:47:09 +0800 Subject: [PATCH] mctp-estack: Consider MCTP transport header separately from the mtu in the Fragmenter The minimum payload for a non-eom MCTP packet is 64 bytes. Currently the Fragmenter considers MCTP transport headers as part of the mtu. This means the minimum mtu creates packets with 60 bytes of payload, less than the minimum. To fix this, the mtu now sets the maximum payload bytes, and the header is prepended. All packets returned by the fragmenter will now be four bytes longer. This needs to be accommodated by the size of input buffers and the expected length of packets. Signed-off-by: James Lee --- mctp-estack/src/fragment.rs | 44 +++++++++++++++++++++++++++++++------ standalone/src/serial.rs | 3 ++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/mctp-estack/src/fragment.rs b/mctp-estack/src/fragment.rs index 1c20b54..0272879 100644 --- a/mctp-estack/src/fragment.rs +++ b/mctp-estack/src/fragment.rs @@ -94,7 +94,7 @@ impl Fragmenter { /// (If you do, the vector has to hold exactly one buffer that is /// equal to the one passed to `fragment()`.) /// - /// `out` must be at least as large as the specified `mtu`. + /// `out` must be at least as large as the specified `mtu` plus the MCTP transport header (four bytes). pub fn fragment<'f>( &mut self, payload: &[u8], @@ -113,7 +113,7 @@ impl Fragmenter { /// (If you do, the vector has to hold exactly one buffer that is /// equal to the one passed to `fragment()`.) /// - /// `out` must be at least as large as the specified `mtu`. + /// `out` must be at least as large as the specified `mtu` plus the MCTP transport header (four bytes). pub fn fragment_vectored<'f>( &mut self, payload: &[&[u8]], @@ -129,17 +129,19 @@ impl Fragmenter { return SendOutput::failure(Error::BadArgument, self); } - // Require at least MTU buffer size, to ensure that all non-end + let fragment_length = self.mtu + MctpHeader::LEN; + + // Require at least fragment_length buffer size, to ensure that all non-end // fragments are the same size per the spec. - if out.len() < self.mtu { + if out.len() < fragment_length { debug!("small out buffer"); return SendOutput::failure(Error::BadArgument, self); } // Reserve header space, the remaining buffer keeps being // updated in `rest` - let max_total = out.len().min(self.mtu); - let (h, mut rest) = out[..max_total].split_at_mut(MctpHeader::LEN); + let (h, mut rest) = + out[..fragment_length].split_at_mut(MctpHeader::LEN); // Append type byte if self.header.som { @@ -162,7 +164,7 @@ impl Fragmenter { self.header.som = false; self.header.seq = (self.header.seq + 1) & mctp::MCTP_SEQ_MASK; - let used = max_total - rest.len(); + let used = fragment_length - rest.len(); SendOutput::Packet(&mut out[..used]) } } @@ -215,3 +217,31 @@ impl SendOutput<'_> { Self::Error { err, cookie: None } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_minimum_mtu() { + let mut fragmenter = Fragmenter::new( + mctp::MCTP_TYPE_CONTROL, + Eid(8), + Eid(9), + Tag::Owned(mctp::TagValue(0)), + mctp::MCTP_MIN_MTU, + None, + MsgIC(false), + 0, + ) + .unwrap(); + + let dummy_in = vec![1u8; mctp::MCTP_MIN_MTU * 2]; + let mut dummy_out = vec![0u8; mctp::MCTP_MIN_MTU * 2]; + let fragment = fragmenter.fragment(&dummy_in, &mut dummy_out); + let expected_length = mctp::MCTP_MIN_MTU + crate::MctpHeader::LEN; + assert!( + matches!(fragment, SendOutput::Packet(packet) if packet.len() == expected_length) + ); + } +} diff --git a/standalone/src/serial.rs b/standalone/src/serial.rs index 11b6d6d..c12478a 100644 --- a/standalone/src/serial.rs +++ b/standalone/src/serial.rs @@ -70,7 +70,8 @@ impl Inner { } loop { - let mut tx_pkt = [0u8; mctp_estack::serial::MTU_MAX]; + // Add for the four byte transport header + let mut tx_pkt = [0u8; mctp_estack::serial::MTU_MAX + 4]; let r = fragmenter.fragment(&tx_msg, &mut tx_pkt); match r { SendOutput::Packet(p) => {