Skip to main content

net_wire/ethernet/
builder.rs

1use core::fmt;
2
3use super::address::MacAddress;
4use super::ether_type::EtherType;
5use super::frame::{EthernetFrameMut, HEADER_LENGTH};
6
7const DESTINATION: core::ops::Range<usize> = 0..6;
8const SOURCE: core::ops::Range<usize> = 6..12;
9const ETHER_TYPE: core::ops::Range<usize> = 12..14;
10
11/// An allocation-free failure while constructing an Ethernet II frame.
12///
13/// These failures apply to the destination/source/type header prescribed by
14/// [RFC 894](https://www.rfc-editor.org/rfc/rfc894).
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum EthernetFrameBuildError {
17    /// No destination address was supplied.
18    MissingDestination,
19    /// No source address was supplied.
20    MissingSource,
21    /// No protocol type was supplied.
22    MissingEtherType,
23    /// Header length plus the requested payload length overflowed `usize`.
24    LengthOverflow {
25        /// Fixed RFC 894 header length used in the attempted addition.
26        header_length: usize,
27        /// Requested payload length used in the attempted addition.
28        payload_length: usize,
29    },
30    /// The caller buffer cannot hold the requested header and payload.
31    BufferTooShort {
32        /// Required header-plus-payload length.
33        required: usize,
34        /// Available caller-buffer length.
35        available: usize,
36    },
37}
38
39impl fmt::Display for EthernetFrameBuildError {
40    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            Self::MissingDestination => formatter.write_str("missing Ethernet destination address"),
43            Self::MissingSource => formatter.write_str("missing Ethernet source address"),
44            Self::MissingEtherType => formatter.write_str("missing Ethernet protocol type"),
45            Self::LengthOverflow {
46                header_length,
47                payload_length,
48            } => write!(
49                formatter,
50                "Ethernet frame length overflow: {header_length} + {payload_length}"
51            ),
52            Self::BufferTooShort {
53                required,
54                available,
55            } => write!(
56                formatter,
57                "Ethernet buffer is too short: need {required} bytes, have {available}"
58            ),
59        }
60    }
61}
62
63/// An allocation-free builder for an Ethernet II header in a caller-provided buffer.
64///
65/// It writes the destination, source, and protocol-type header specified by
66/// [RFC 894](https://www.rfc-editor.org/rfc/rfc894), never generates FCS bytes, and
67/// leaves the declared payload contents to the caller.
68pub struct EthernetFrameBuilder<'a> {
69    buffer: &'a mut [u8],
70    payload_length: usize,
71    destination: Option<MacAddress>,
72    source: Option<MacAddress>,
73    ether_type: Option<EtherType>,
74}
75
76impl<'a> EthernetFrameBuilder<'a> {
77    /// Starts construction in `buffer` with an explicit payload length.
78    ///
79    /// `build` will represent exactly the RFC 894 header followed by this many payload
80    /// octets; it does not write payload and never generates FCS bytes.
81    #[inline]
82    pub fn new(buffer: &'a mut [u8], payload_length: usize) -> Self {
83        Self {
84            buffer,
85            payload_length,
86            destination: None,
87            source: None,
88            ether_type: None,
89        }
90    }
91
92    /// Supplies the destination address for the Ethernet header in
93    /// [RFC 894](https://www.rfc-editor.org/rfc/rfc894).
94    #[inline]
95    pub fn destination(mut self, destination: MacAddress) -> Self {
96        self.destination = Some(destination);
97        self
98    }
99
100    /// Supplies the source address for the Ethernet header in
101    /// [RFC 894](https://www.rfc-editor.org/rfc/rfc894).
102    #[inline]
103    pub fn source(mut self, source: MacAddress) -> Self {
104        self.source = Some(source);
105        self
106    }
107
108    /// Supplies the protocol type for the Ethernet header in
109    /// [RFC 894](https://www.rfc-editor.org/rfc/rfc894).
110    #[inline]
111    pub fn ether_type(mut self, ether_type: EtherType) -> Self {
112        self.ether_type = Some(ether_type);
113        self
114    }
115
116    /// Validates required fields and capacity, then writes exactly the Ethernet II header.
117    ///
118    /// Validation completes before any write. On success, the returned view is restricted
119    /// to the RFC 894 header plus the declared payload; payload and trailing capacity are
120    /// unchanged, and FCS bytes are never generated.
121    #[inline]
122    pub fn build(self) -> Result<EthernetFrameMut<'a>, EthernetFrameBuildError> {
123        let destination = self
124            .destination
125            .ok_or(EthernetFrameBuildError::MissingDestination)?;
126        let source = self.source.ok_or(EthernetFrameBuildError::MissingSource)?;
127        let ether_type = self
128            .ether_type
129            .ok_or(EthernetFrameBuildError::MissingEtherType)?;
130        let frame_length = HEADER_LENGTH.checked_add(self.payload_length).ok_or(
131            EthernetFrameBuildError::LengthOverflow {
132                header_length: HEADER_LENGTH,
133                payload_length: self.payload_length,
134            },
135        )?;
136        if self.buffer.len() < frame_length {
137            return Err(EthernetFrameBuildError::BufferTooShort {
138                required: frame_length,
139                available: self.buffer.len(),
140            });
141        }
142
143        let bytes = &mut self.buffer[..frame_length];
144        bytes[DESTINATION].copy_from_slice(&destination.octets());
145        bytes[SOURCE].copy_from_slice(&source.octets());
146        bytes[ETHER_TYPE].copy_from_slice(&ether_type.raw().to_be_bytes());
147        Ok(EthernetFrameMut::from_validated(bytes))
148    }
149}