Skip to main content

net_wire/ipv4/
builder.rs

1use super::address::Ipv4Address;
2use super::packet::{HEADER_LENGTH, Ipv4PacketMut};
3use super::protocol::Ipv4Protocol;
4use crate::internet_checksum;
5use core::fmt;
6/// IPv4 builder validation failure.
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum Ipv4PacketBuildError {
9    /// Source is missing.
10    MissingSource,
11    /// Destination is missing.
12    MissingDestination,
13    /// Protocol is missing.
14    MissingProtocol,
15    /// TTL is missing.
16    MissingTtl,
17    /// Options are invalid.
18    InvalidOptionsLength,
19    /// Total length exceeds 65535.
20    TotalLengthTooLarge,
21    /// Reserved flag is set.
22    ReservedFlagSet,
23    /// Buffer capacity is insufficient.
24    BufferTooShort {
25        /// Required bytes.
26        required: usize,
27        /// Available bytes.
28        available: usize,
29    },
30}
31impl fmt::Display for Ipv4PacketBuildError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Self::MissingSource => f.write_str("missing IPv4 source address"),
35            Self::MissingDestination => f.write_str("missing IPv4 destination address"),
36            Self::MissingProtocol => f.write_str("missing IPv4 protocol"),
37            Self::MissingTtl => f.write_str("missing IPv4 TTL"),
38            Self::InvalidOptionsLength => {
39                f.write_str("IPv4 options length must be a multiple of four and at most 40")
40            }
41            Self::TotalLengthTooLarge => f.write_str("IPv4 total length exceeds 65535"),
42            Self::ReservedFlagSet => f.write_str("IPv4 reserved flag bit is set"),
43            Self::BufferTooShort {
44                required,
45                available,
46            } => write!(
47                f,
48                "IPv4 buffer is too short: need {required} bytes, have {available}"
49            ),
50        }
51    }
52}
53/// Caller-buffer IPv4 builder.
54pub struct Ipv4PacketBuilder<'buffer, 'input> {
55    buffer: &'buffer mut [u8],
56    payload: usize,
57    source: Option<Ipv4Address>,
58    destination: Option<Ipv4Address>,
59    protocol: Option<Ipv4Protocol>,
60    ttl: Option<u8>,
61    identification: u16,
62    flags: u16,
63    dscp: u8,
64    options: &'input [u8],
65}
66impl<'buffer, 'input> Ipv4PacketBuilder<'buffer, 'input> {
67    /// Starts a builder with untouched payload bytes.
68    #[inline]
69    pub fn new(buffer: &'buffer mut [u8], payload: usize) -> Self {
70        Self {
71            buffer,
72            payload,
73            source: None,
74            destination: None,
75            protocol: None,
76            ttl: None,
77            identification: 0,
78            flags: 0,
79            dscp: 0,
80            options: &[],
81        }
82    }
83    /// Supplies source.
84    #[inline]
85    pub fn source(mut self, v: Ipv4Address) -> Self {
86        self.source = Some(v);
87        self
88    }
89    /// Supplies destination.
90    #[inline]
91    pub fn destination(mut self, v: Ipv4Address) -> Self {
92        self.destination = Some(v);
93        self
94    }
95    /// Supplies protocol.
96    #[inline]
97    pub fn protocol(mut self, v: Ipv4Protocol) -> Self {
98        self.protocol = Some(v);
99        self
100    }
101    /// Supplies TTL.
102    #[inline]
103    pub fn ttl(mut self, v: u8) -> Self {
104        self.ttl = Some(v);
105        self
106    }
107    /// Supplies identification.
108    #[inline]
109    pub fn identification(mut self, v: u16) -> Self {
110        self.identification = v;
111        self
112    }
113    /// Supplies flags and offset.
114    #[inline]
115    pub fn flags_fragment_offset(mut self, v: u16) -> Self {
116        self.flags = v;
117        self
118    }
119    /// Supplies DSCP/ECN.
120    #[inline]
121    pub fn dscp_ecn(mut self, v: u8) -> Self {
122        self.dscp = v;
123        self
124    }
125    /// Supplies copied options.
126    #[inline]
127    pub fn options(mut self, v: &'input [u8]) -> Self {
128        self.options = v;
129        self
130    }
131    /// Validates before all writes, writes/header-checksums only the header, and preserves payload and trailing capacity.
132    #[inline]
133    pub fn build(self) -> Result<Ipv4PacketMut<'buffer>, Ipv4PacketBuildError> {
134        let source = self.source.ok_or(Ipv4PacketBuildError::MissingSource)?;
135        let destination = self
136            .destination
137            .ok_or(Ipv4PacketBuildError::MissingDestination)?;
138        let protocol = self.protocol.ok_or(Ipv4PacketBuildError::MissingProtocol)?;
139        let ttl = self.ttl.ok_or(Ipv4PacketBuildError::MissingTtl)?;
140        if self.flags & 0x8000 != 0 {
141            return Err(Ipv4PacketBuildError::ReservedFlagSet);
142        }
143        if self.options.len() > 40 || !self.options.len().is_multiple_of(4) {
144            return Err(Ipv4PacketBuildError::InvalidOptionsLength);
145        }
146        let header = HEADER_LENGTH + self.options.len();
147        let total = header
148            .checked_add(self.payload)
149            .ok_or(Ipv4PacketBuildError::TotalLengthTooLarge)?;
150        if total > u16::MAX as usize {
151            return Err(Ipv4PacketBuildError::TotalLengthTooLarge);
152        }
153        if self.buffer.len() < total {
154            return Err(Ipv4PacketBuildError::BufferTooShort {
155                required: total,
156                available: self.buffer.len(),
157            });
158        }
159        let b = &mut self.buffer[..total];
160        b[0] = 0x40 | ((header / 4) as u8);
161        b[1] = self.dscp;
162        b[2..4].copy_from_slice(&(total as u16).to_be_bytes());
163        b[4..6].copy_from_slice(&self.identification.to_be_bytes());
164        b[6..8].copy_from_slice(&self.flags.to_be_bytes());
165        b[8] = ttl;
166        b[9] = protocol.raw();
167        b[10] = 0;
168        b[11] = 0;
169        b[12..16].copy_from_slice(&source.octets());
170        b[16..20].copy_from_slice(&destination.octets());
171        b[HEADER_LENGTH..header].copy_from_slice(self.options);
172        let checksum = !internet_checksum::sum(&b[..header]);
173        b[10..12].copy_from_slice(&checksum.to_be_bytes());
174        Ok(Ipv4PacketMut::from_validated(b, header))
175    }
176}