Skip to main content

net_wire/icmpv6/
builder.rs

1//! ICMPv6 caller-buffer builder.
2
3use core::fmt;
4
5use super::message::{HEADER_LENGTH, Icmpv6MessageMut};
6use super::types::Icmpv6Type;
7
8/// Failure to construct an ICMPv6 message in caller-provided storage.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum Icmpv6MessageBuildError {
11    /// The type was not supplied.
12    MissingType,
13    /// The code was not supplied.
14    MissingCode,
15    /// The requested common header and body length cannot be represented as a `usize`.
16    MessageLengthTooLarge,
17    /// The buffer cannot hold the requested message.
18    BufferTooShort {
19        /// Required bytes.
20        required: usize,
21        /// Available bytes.
22        available: usize,
23    },
24}
25
26impl fmt::Display for Icmpv6MessageBuildError {
27    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match self {
29            Self::MissingType => formatter.write_str("missing ICMPv6 type"),
30            Self::MissingCode => formatter.write_str("missing ICMPv6 code"),
31            Self::MessageLengthTooLarge => {
32                formatter.write_str("ICMPv6 message length exceeds the platform limit")
33            }
34            Self::BufferTooShort {
35                required,
36                available,
37            } => write!(
38                formatter,
39                "ICMPv6 buffer is too short: need {required} bytes, have {available}"
40            ),
41        }
42    }
43}
44
45/// Builds an ICMPv6 common header in caller-owned storage.
46pub struct Icmpv6MessageBuilder<'a> {
47    buffer: &'a mut [u8],
48    body_length: usize,
49    message_type: Option<Icmpv6Type>,
50    code: Option<u8>,
51    checksum: u16,
52}
53
54impl<'a> Icmpv6MessageBuilder<'a> {
55    /// Starts a builder for `body_length` bytes, which are left untouched.
56    #[inline]
57    pub fn new(buffer: &'a mut [u8], body_length: usize) -> Self {
58        Self {
59            buffer,
60            body_length,
61            message_type: None,
62            code: None,
63            checksum: 0,
64        }
65    }
66
67    /// Supplies the ICMPv6 message type.
68    #[inline]
69    pub fn message_type(mut self, value: Icmpv6Type) -> Self {
70        self.message_type = Some(value);
71        self
72    }
73
74    /// Supplies the ICMPv6 message code.
75    #[inline]
76    pub fn code(mut self, value: u8) -> Self {
77        self.code = Some(value);
78        self
79    }
80
81    /// Supplies the encoded checksum; it defaults to zero.
82    #[inline]
83    pub fn checksum(mut self, value: u16) -> Self {
84        self.checksum = value;
85        self
86    }
87
88    /// Validates all inputs, then writes only the common header.
89    ///
90    /// Body bytes and capacity beyond the requested message are preserved. On error, the whole
91    /// caller-provided buffer is unchanged.
92    #[inline]
93    pub fn build(self) -> Result<Icmpv6MessageMut<'a>, Icmpv6MessageBuildError> {
94        let message_type = self
95            .message_type
96            .ok_or(Icmpv6MessageBuildError::MissingType)?;
97        let code = self.code.ok_or(Icmpv6MessageBuildError::MissingCode)?;
98        let length = HEADER_LENGTH
99            .checked_add(self.body_length)
100            .ok_or(Icmpv6MessageBuildError::MessageLengthTooLarge)?;
101        if self.buffer.len() < length {
102            return Err(Icmpv6MessageBuildError::BufferTooShort {
103                required: length,
104                available: self.buffer.len(),
105            });
106        }
107
108        let bytes = &mut self.buffer[..length];
109        bytes[0] = message_type.raw();
110        bytes[1] = code;
111        bytes[2..4].copy_from_slice(&self.checksum.to_be_bytes());
112        Ok(Icmpv6MessageMut::from_validated(bytes))
113    }
114}