Skip to main content

net_wire/ipv6/
builder.rs

1use core::fmt;
2
3use super::address::Ipv6Address;
4use super::next_header::Ipv6NextHeader;
5use super::packet::{HEADER_LENGTH, Ipv6PacketMut};
6
7/// Failure while validating an IPv6 builder request.
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub enum Ipv6PacketBuildError {
10    /// No Next Header value was supplied.
11    MissingNextHeader,
12    /// No hop limit was supplied.
13    MissingHopLimit,
14    /// No source address was supplied.
15    MissingSource,
16    /// No destination address was supplied.
17    MissingDestination,
18    /// The flow label exceeds its 20-bit field.
19    FlowLabelTooLarge,
20    /// The payload exceeds the base header's 16-bit Payload Length field.
21    PayloadLengthTooLarge,
22    /// The caller buffer cannot hold the base header and declared payload.
23    BufferTooShort {
24        /// Required packet length.
25        required: usize,
26        /// Available buffer length.
27        available: usize,
28    },
29}
30
31impl fmt::Display for Ipv6PacketBuildError {
32    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Self::MissingNextHeader => formatter.write_str("missing IPv6 Next Header value"),
35            Self::MissingHopLimit => formatter.write_str("missing IPv6 hop limit"),
36            Self::MissingSource => formatter.write_str("missing IPv6 source address"),
37            Self::MissingDestination => formatter.write_str("missing IPv6 destination address"),
38            Self::FlowLabelTooLarge => formatter.write_str("IPv6 flow label exceeds 20 bits"),
39            Self::PayloadLengthTooLarge => {
40                formatter.write_str("IPv6 payload length exceeds 65535 octets")
41            }
42            Self::BufferTooShort {
43                required,
44                available,
45            } => write!(
46                formatter,
47                "IPv6 buffer is too short: need {required} bytes, have {available}"
48            ),
49        }
50    }
51}
52
53/// Builds an RFC 8200 base header in a caller-provided packet buffer.
54///
55/// The builder validates every field and capacity before writing. It writes only the
56/// 40-octet base header; declared payload bytes and trailing capacity remain unchanged.
57pub struct Ipv6PacketBuilder<'buffer> {
58    buffer: &'buffer mut [u8],
59    payload_length: usize,
60    next_header: Option<Ipv6NextHeader>,
61    hop_limit: Option<u8>,
62    source: Option<Ipv6Address>,
63    destination: Option<Ipv6Address>,
64    traffic_class: u8,
65    flow_label: u32,
66}
67
68impl<'buffer> Ipv6PacketBuilder<'buffer> {
69    /// Starts a builder for an ordinary payload of `payload_length` octets.
70    ///
71    /// A zero length produces an ordinary zero Payload Length field and no Jumbo Payload option.
72    #[inline]
73    pub fn new(buffer: &'buffer mut [u8], payload_length: usize) -> Self {
74        Self {
75            buffer,
76            payload_length,
77            next_header: None,
78            hop_limit: None,
79            source: None,
80            destination: None,
81            traffic_class: 0,
82            flow_label: 0,
83        }
84    }
85
86    /// Supplies the Next Header field.
87    #[inline]
88    pub fn next_header(mut self, value: Ipv6NextHeader) -> Self {
89        self.next_header = Some(value);
90        self
91    }
92
93    /// Supplies the hop limit.
94    #[inline]
95    pub fn hop_limit(mut self, value: u8) -> Self {
96        self.hop_limit = Some(value);
97        self
98    }
99
100    /// Supplies the source address.
101    #[inline]
102    pub fn source(mut self, value: Ipv6Address) -> Self {
103        self.source = Some(value);
104        self
105    }
106
107    /// Supplies the destination address.
108    #[inline]
109    pub fn destination(mut self, value: Ipv6Address) -> Self {
110        self.destination = Some(value);
111        self
112    }
113
114    /// Supplies the traffic class.
115    #[inline]
116    pub fn traffic_class(mut self, value: u8) -> Self {
117        self.traffic_class = value;
118        self
119    }
120
121    /// Supplies the 20-bit flow label.
122    #[inline]
123    pub fn flow_label(mut self, value: u32) -> Self {
124        self.flow_label = value;
125        self
126    }
127
128    /// Validates the request and writes the RFC 8200 base header.
129    #[inline]
130    pub fn build(self) -> Result<Ipv6PacketMut<'buffer>, Ipv6PacketBuildError> {
131        let next_header = self
132            .next_header
133            .ok_or(Ipv6PacketBuildError::MissingNextHeader)?;
134        let hop_limit = self
135            .hop_limit
136            .ok_or(Ipv6PacketBuildError::MissingHopLimit)?;
137        let source = self.source.ok_or(Ipv6PacketBuildError::MissingSource)?;
138        let destination = self
139            .destination
140            .ok_or(Ipv6PacketBuildError::MissingDestination)?;
141        if self.flow_label > 0x000f_ffff {
142            return Err(Ipv6PacketBuildError::FlowLabelTooLarge);
143        }
144        if self.payload_length > u16::MAX as usize {
145            return Err(Ipv6PacketBuildError::PayloadLengthTooLarge);
146        }
147        let packet_length = HEADER_LENGTH + self.payload_length;
148        if self.buffer.len() < packet_length {
149            return Err(Ipv6PacketBuildError::BufferTooShort {
150                required: packet_length,
151                available: self.buffer.len(),
152            });
153        }
154
155        let bytes = &mut self.buffer[..packet_length];
156        bytes[0] = 0x60 | (self.traffic_class >> 4);
157        bytes[1] = (self.traffic_class << 4) | ((self.flow_label >> 16) as u8);
158        bytes[2] = (self.flow_label >> 8) as u8;
159        bytes[3] = self.flow_label as u8;
160        bytes[4..6].copy_from_slice(&(self.payload_length as u16).to_be_bytes());
161        bytes[6] = next_header.raw();
162        bytes[7] = hop_limit;
163        bytes[8..24].copy_from_slice(&source.octets());
164        bytes[24..40].copy_from_slice(&destination.octets());
165
166        Ok(Ipv6PacketMut::from_validated(bytes))
167    }
168}