Skip to main content

net_wire/ipv6/
packet.rs

1use super::address::Ipv6Address;
2use super::builder::Ipv6PacketBuildError;
3use super::next_header::Ipv6NextHeader;
4use crate::error::ParseError;
5
6pub(super) const HEADER_LENGTH: usize = 40;
7
8/// Meaning of the IPv6 base header's 16-bit Payload Length field.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum Ipv6PayloadLength {
11    /// A nonzero payload length declared directly by the base header.
12    Declared(u16),
13    /// A zero field whose nonempty payload extent cannot be resolved from the base header alone.
14    ///
15    /// An ordinary empty payload also encodes zero. A nonempty tail may include a Jumbo Payload
16    /// option or lower-layer padding and cannot be resolved at this parsing layer.
17    Unspecified,
18}
19
20/// A structurally validated immutable IPv6 base-header view.
21///
22/// Parsing implements the fixed header from RFC 8200 section 3. It does not traverse extension
23/// headers. A nonzero Payload Length bounds the view exactly; a zero field retains every supplied
24/// trailing byte as an unresolved tail; zero with no trailing bytes is an ordinary empty
25/// payload.
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
27pub struct Ipv6Packet<'a> {
28    bytes: &'a [u8],
29}
30
31impl<'a> Ipv6Packet<'a> {
32    /// Validates the fixed header, version, and any nonzero declared payload extent.
33    #[inline]
34    pub fn parse(bytes: &'a [u8]) -> Result<Self, ParseError> {
35        let packet_length = validate(bytes)?;
36        Ok(Self {
37            bytes: &bytes[..packet_length],
38        })
39    }
40
41    /// Returns the eight-bit traffic class.
42    #[inline]
43    pub fn traffic_class(&self) -> u8 {
44        ((self.bytes[0] & 0x0f) << 4) | (self.bytes[1] >> 4)
45    }
46
47    /// Returns the 20-bit flow label.
48    #[inline]
49    pub fn flow_label(&self) -> u32 {
50        (u32::from(self.bytes[1] & 0x0f) << 16)
51            | (u32::from(self.bytes[2]) << 8)
52            | u32::from(self.bytes[3])
53    }
54
55    /// Interprets the base header's Payload Length field without resolving Jumbo Payload options.
56    #[inline]
57    pub fn payload_length(&self) -> Ipv6PayloadLength {
58        match self.raw_payload_length() {
59            0 => Ipv6PayloadLength::Unspecified,
60            value => Ipv6PayloadLength::Declared(value),
61        }
62    }
63
64    /// Returns the raw 16-bit Payload Length field.
65    #[inline]
66    pub fn raw_payload_length(&self) -> u16 {
67        u16::from_be_bytes(self.bytes[4..6].try_into().expect("validated IPv6 header"))
68    }
69
70    /// Returns the Next Header value, preserving unknown values.
71    #[inline]
72    pub fn next_header(&self) -> Ipv6NextHeader {
73        Ipv6NextHeader::new(self.bytes[6])
74    }
75
76    /// Returns the hop limit.
77    #[inline]
78    pub fn hop_limit(&self) -> u8 {
79        self.bytes[7]
80    }
81
82    /// Returns the source address.
83    #[inline]
84    pub fn source(&self) -> Ipv6Address {
85        Ipv6Address::new(self.bytes[8..24].try_into().expect("validated IPv6 header"))
86    }
87
88    /// Returns the destination address.
89    #[inline]
90    pub fn destination(&self) -> Ipv6Address {
91        Ipv6Address::new(
92            self.bytes[24..40]
93                .try_into()
94                .expect("validated IPv6 header"),
95        )
96    }
97
98    /// Returns bytes after the fixed header.
99    ///
100    /// For a nonzero Payload Length these are exactly the declared payload. For a zero field they
101    /// are an unresolved nonempty tail that may be lower-layer padding or Jumbo Payload content;
102    /// an empty tail is an ordinary empty payload.
103    #[inline]
104    pub fn payload(&self) -> &'a [u8] {
105        &self.bytes[HEADER_LENGTH..]
106    }
107
108    /// Returns all bytes represented by this base-header view.
109    #[inline]
110    pub fn as_bytes(&self) -> &'a [u8] {
111        self.bytes
112    }
113}
114
115/// A structurally validated mutable IPv6 base-header view.
116#[derive(Debug, Eq, PartialEq)]
117pub struct Ipv6PacketMut<'a> {
118    bytes: &'a mut [u8],
119}
120
121impl<'a> Ipv6PacketMut<'a> {
122    /// Validates the fixed header, version, and any nonzero declared payload extent.
123    #[inline]
124    pub fn parse(bytes: &'a mut [u8]) -> Result<Self, ParseError> {
125        let packet_length = validate(bytes)?;
126        Ok(Self::from_validated(&mut bytes[..packet_length]))
127    }
128
129    pub(super) fn from_validated(bytes: &'a mut [u8]) -> Self {
130        Self { bytes }
131    }
132
133    /// Returns the eight-bit traffic class.
134    #[inline]
135    pub fn traffic_class(&self) -> u8 {
136        ((self.bytes[0] & 0x0f) << 4) | (self.bytes[1] >> 4)
137    }
138
139    /// Returns the 20-bit flow label.
140    #[inline]
141    pub fn flow_label(&self) -> u32 {
142        (u32::from(self.bytes[1] & 0x0f) << 16)
143            | (u32::from(self.bytes[2]) << 8)
144            | u32::from(self.bytes[3])
145    }
146
147    /// Interprets the Payload Length field without resolving Jumbo Payload options.
148    #[inline]
149    pub fn payload_length(&self) -> Ipv6PayloadLength {
150        match self.raw_payload_length() {
151            0 => Ipv6PayloadLength::Unspecified,
152            value => Ipv6PayloadLength::Declared(value),
153        }
154    }
155
156    /// Returns the raw 16-bit Payload Length field.
157    #[inline]
158    pub fn raw_payload_length(&self) -> u16 {
159        u16::from_be_bytes(self.bytes[4..6].try_into().expect("validated IPv6 header"))
160    }
161
162    /// Returns the Next Header value, preserving unknown values.
163    #[inline]
164    pub fn next_header(&self) -> Ipv6NextHeader {
165        Ipv6NextHeader::new(self.bytes[6])
166    }
167
168    /// Returns the hop limit.
169    #[inline]
170    pub fn hop_limit(&self) -> u8 {
171        self.bytes[7]
172    }
173
174    /// Returns the source address.
175    #[inline]
176    pub fn source(&self) -> Ipv6Address {
177        Ipv6Address::new(self.bytes[8..24].try_into().expect("validated IPv6 header"))
178    }
179
180    /// Returns the destination address.
181    #[inline]
182    pub fn destination(&self) -> Ipv6Address {
183        Ipv6Address::new(
184            self.bytes[24..40]
185                .try_into()
186                .expect("validated IPv6 header"),
187        )
188    }
189
190    /// Returns bytes after the fixed header, with zero-field ambiguity as documented on `Ipv6Packet`.
191    #[inline]
192    pub fn payload(&self) -> &[u8] {
193        &self.bytes[HEADER_LENGTH..]
194    }
195
196    /// Returns bytes after the fixed header mutably.
197    #[inline]
198    pub fn payload_mut(&mut self) -> &mut [u8] {
199        &mut self.bytes[HEADER_LENGTH..]
200    }
201
202    /// Returns all represented bytes.
203    #[inline]
204    pub fn as_bytes(&self) -> &[u8] {
205        self.bytes
206    }
207
208    /// Returns all represented bytes mutably.
209    #[inline]
210    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
211        self.bytes
212    }
213
214    /// Replaces the traffic class while preserving version and flow-label bits.
215    #[inline]
216    pub fn set_traffic_class(&mut self, value: u8) {
217        self.bytes[0] = (self.bytes[0] & 0xf0) | (value >> 4);
218        self.bytes[1] = (self.bytes[1] & 0x0f) | (value << 4);
219    }
220
221    /// Replaces the flow label, or leaves the packet unchanged when it exceeds 20 bits.
222    #[inline]
223    pub fn set_flow_label(&mut self, value: u32) -> Result<(), Ipv6PacketBuildError> {
224        if value > 0x000f_ffff {
225            return Err(Ipv6PacketBuildError::FlowLabelTooLarge);
226        }
227        self.bytes[1] = (self.bytes[1] & 0xf0) | ((value >> 16) as u8);
228        self.bytes[2] = (value >> 8) as u8;
229        self.bytes[3] = value as u8;
230        Ok(())
231    }
232
233    /// Replaces the Next Header value.
234    #[inline]
235    pub fn set_next_header(&mut self, value: Ipv6NextHeader) {
236        self.bytes[6] = value.raw();
237    }
238
239    /// Replaces the hop limit.
240    #[inline]
241    pub fn set_hop_limit(&mut self, value: u8) {
242        self.bytes[7] = value;
243    }
244
245    /// Replaces the source address.
246    #[inline]
247    pub fn set_source(&mut self, value: Ipv6Address) {
248        self.bytes[8..24].copy_from_slice(&value.octets());
249    }
250
251    /// Replaces the destination address.
252    #[inline]
253    pub fn set_destination(&mut self, value: Ipv6Address) {
254        self.bytes[24..40].copy_from_slice(&value.octets());
255    }
256}
257
258#[inline]
259fn validate(bytes: &[u8]) -> Result<usize, ParseError> {
260    if bytes.len() < HEADER_LENGTH {
261        return Err(ParseError::Truncated {
262            minimum: HEADER_LENGTH,
263            available: bytes.len(),
264        });
265    }
266    let version = bytes[0] >> 4;
267    if version != 6 {
268        return Err(ParseError::InvalidVersion {
269            expected: 6,
270            actual: version,
271        });
272    }
273    let payload_length = usize::from(u16::from_be_bytes(
274        bytes[4..6].try_into().expect("validated IPv6 header"),
275    ));
276    if payload_length == 0 {
277        return Ok(bytes.len());
278    }
279    let packet_length = HEADER_LENGTH + payload_length;
280    if bytes.len() < packet_length {
281        return Err(ParseError::Truncated {
282            minimum: packet_length,
283            available: bytes.len(),
284        });
285    }
286    Ok(packet_length)
287}