Skip to main content

packet_formats/ipv6/
ext_hdrs.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Parsing and serialization of IPv6 extension headers.
6//!
7//! The IPv6 extension header format is defined in [RFC 8200 Section 4].
8//!
9//! [RFC 8200 Section 4]: https://datatracker.ietf.org/doc/html/rfc8200#section-4
10
11use core::convert::Infallible as Never;
12use core::marker::PhantomData;
13
14use byteorder::{ByteOrder, NetworkEndian};
15use packet::records::options::{
16    AlignedOptionBuilder, LengthEncoding, OptionBuilder, OptionLayout, OptionParseErr,
17    OptionParseLayout,
18};
19use packet::records::{
20    ParsedRecord, RecordParseResult, Records, RecordsContext, RecordsImpl, RecordsImplLayout,
21    RecordsRawImpl,
22};
23use packet::{BufferView, BufferViewMut};
24use zerocopy::byteorder::network_endian::U16;
25
26use crate::ip::{FragmentOffset, IpProto, Ipv6ExtHdrType, Ipv6Proto};
27
28/// The length of an IPv6 Fragment Extension Header.
29pub(crate) const IPV6_FRAGMENT_EXT_HDR_LEN: usize = 8;
30
31/// An IPv6 Extension Header.
32#[derive(Debug)]
33pub struct Ipv6ExtensionHeader<'a> {
34    // Marked as `pub(super)` because it is only used in tests within
35    // the `crate::ipv6` (`super`) module.
36    pub(super) next_header: u8,
37    data: Ipv6ExtensionHeaderData<'a>,
38}
39
40impl<'a> Ipv6ExtensionHeader<'a> {
41    /// Returns the extension header-specific data.
42    pub fn data(&self) -> &Ipv6ExtensionHeaderData<'a> {
43        &self.data
44    }
45
46    /// Consumes `self` returning only the containing data.
47    pub fn into_data(self) -> Ipv6ExtensionHeaderData<'a> {
48        self.data
49    }
50}
51
52/// The data associated with an IPv6 Extension Header.
53#[allow(missing_docs)]
54#[derive(Debug)]
55pub enum Ipv6ExtensionHeaderData<'a> {
56    HopByHopOptions { options: HopByHopOptionsData<'a> },
57    Routing { routing_data: RoutingData<'a> },
58    Fragment { fragment_data: FragmentData },
59    DestinationOptions { options: DestinationOptionsData<'a> },
60}
61
62//
63// Records parsing for IPv6 Extension Header
64//
65
66/// Possible errors that can happen when parsing IPv6 Extension Headers.
67#[allow(missing_docs)]
68#[derive(Debug, PartialEq, Eq)]
69pub(super) enum Ipv6ExtensionHeaderParsingError {
70    // `pointer` is the offset from the beginning of the first extension header
71    // to the point of error. `must_send_icmp` is a flag that requires us to send
72    // an ICMP response if true. `header_len` is the size of extension headers before
73    // encountering an error (number of bytes from successfully parsed
74    // extension headers).
75    ErroneousHeaderField { pointer: u32, must_send_icmp: bool },
76    UnrecognizedNextHeader { pointer: u32, must_send_icmp: bool },
77    UnrecognizedOption { pointer: u32, must_send_icmp: bool, action: ExtensionHeaderOptionAction },
78    BufferExhausted,
79    MalformedData,
80}
81
82impl From<Never> for Ipv6ExtensionHeaderParsingError {
83    fn from(err: Never) -> Ipv6ExtensionHeaderParsingError {
84        match err {}
85    }
86}
87
88/// Context that gets passed around when parsing IPv6 Extension Headers.
89#[derive(Debug, Clone)]
90pub(super) struct Ipv6ExtensionHeaderParsingContext {
91    // Next expected header.
92    // Marked as `pub(super)` because it is inly used in tests within
93    // the `crate::ipv6` (`super`) module.
94    pub(super) next_header: u8,
95
96    // Whether context is being used for iteration or not.
97    iter: bool,
98
99    // Counter for number of extension headers parsed.
100    headers_parsed: usize,
101
102    // Byte count of successfully parsed extension headers.
103    pub(super) bytes_parsed: usize,
104}
105
106impl Ipv6ExtensionHeaderParsingContext {
107    /// Returns a new `Ipv6ExtensionHeaderParsingContext` which expects the
108    /// first header to have the ID specified by `next_header`.
109    pub(super) fn new(next_header: u8) -> Ipv6ExtensionHeaderParsingContext {
110        Ipv6ExtensionHeaderParsingContext {
111            iter: false,
112            headers_parsed: 0,
113            next_header,
114            bytes_parsed: 0,
115        }
116    }
117}
118
119impl RecordsContext for Ipv6ExtensionHeaderParsingContext {
120    type Counter = ();
121
122    fn clone_for_iter(&self) -> Self {
123        let mut ret = self.clone();
124        ret.iter = true;
125        ret
126    }
127
128    fn counter_mut(&mut self) -> &mut () {
129        get_empty_tuple_mut_ref()
130    }
131}
132
133/// Implement the actual parsing of IPv6 Extension Headers.
134#[derive(Debug)]
135pub(super) struct Ipv6ExtensionHeaderImpl;
136
137impl Ipv6ExtensionHeaderImpl {
138    /// Make sure a Next Header value in an extension header is valid.
139    fn valid_next_header(next_header: u8) -> bool {
140        // Passing false to `is_valid_next_header`'s `for_fixed_header` parameter because
141        // this function will never be called when checking the Next Header field
142        // of the fixed header (which would be the first Next Header).
143        is_valid_next_header(next_header, false)
144    }
145
146    /// Get the first two bytes if possible and return them.
147    ///
148    /// `get_next_hdr_and_len` takes the first two bytes from `data` and
149    /// treats them as the Next Header and Hdr Ext Len fields. With the
150    /// Next Header field, `get_next_hdr_and_len` makes sure it is a valid
151    /// value before returning the Next Header and Hdr Ext Len fields.
152    fn get_next_hdr_and_len<'a, BV: BufferView<&'a [u8]>>(
153        data: &mut BV,
154        context: &Ipv6ExtensionHeaderParsingContext,
155    ) -> Result<(u8, u8), Ipv6ExtensionHeaderParsingError> {
156        let next_header =
157            data.take_byte_front().ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
158
159        // Make sure we recognize the next header.
160        // When parsing headers, if we encounter a next header value we don't
161        // recognize, we SHOULD send back an ICMP response. Since we only SHOULD,
162        // we set `must_send_icmp` to `false`.
163        if !Self::valid_next_header(next_header) {
164            return Err(Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
165                pointer: context.bytes_parsed as u32,
166                must_send_icmp: false,
167            });
168        }
169
170        let hdr_ext_len =
171            data.take_byte_front().ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
172
173        Ok((next_header, hdr_ext_len))
174    }
175
176    /// Parse Hop By Hop Options Extension Header.
177    // TODO(ghanan): Look into implementing the IPv6 Jumbo Payload option
178    //               (https://tools.ietf.org/html/rfc2675) and the router
179    //               alert option (https://tools.ietf.org/html/rfc2711).
180    fn parse_hop_by_hop_options<'a, BV: BufferView<&'a [u8]>>(
181        data: &mut BV,
182        context: &mut Ipv6ExtensionHeaderParsingContext,
183    ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
184        let (next_header, hdr_ext_len) = Self::get_next_hdr_and_len(data, context)?;
185
186        // As per RFC 8200 section 4.3, Hdr Ext Len is the length of this extension
187        // header in  8-octect units, not including the first 8 octets (where 2 of
188        // them are the Next Header and the Hdr Ext Len fields). Since we already
189        // 'took' the Next Header and Hdr Ext Len octets, we need to make sure
190        // we have (Hdr Ext Len) * 8 + 6 bytes bytes in `data`.
191        let expected_len = (hdr_ext_len as usize) * 8 + 6;
192
193        let options = data
194            .take_front(expected_len)
195            .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
196
197        let options_context = ExtensionHeaderOptionContext::new();
198        let options = Records::parse_with_context(options, options_context).map_err(|e| {
199            // We know the below `try_from` call will not result in a `None` value because
200            // the maximum size of an IPv6 packet's payload (extension headers + body) is
201            // `core::u32::MAX`. This maximum size is only possible when using IPv6
202            // jumbograms as defined by RFC 2675, which uses a 32 bit field for the payload
203            // length. If we receive such a hypothetical packet with the maximum possible
204            // payload length which only contains extension headers, we know that the offset
205            // of any location within the payload must fit within an `u32`. If the packet is
206            // a normal IPv6 packet (not a jumbogram), the maximum size of the payload is
207            // `core::u16::MAX` (as the normal payload length field is only 16 bits), which
208            // is significantly less than the maximum possible size of a jumbogram.
209            ext_hdr_opt_err_to_ext_hdr_err(u32::try_from(context.bytes_parsed + 2).unwrap(), e)
210        })?;
211        let options = HopByHopOptionsData::new(options);
212
213        // Update context
214        context.next_header = next_header;
215        context.headers_parsed += 1;
216        context.bytes_parsed += 2 + expected_len;
217
218        Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader {
219            next_header,
220            data: Ipv6ExtensionHeaderData::HopByHopOptions { options },
221        }))
222    }
223
224    /// Parse Routing Extension Header.
225    fn parse_routing<'a, BV: BufferView<&'a [u8]>>(
226        data: &mut BV,
227        context: &mut Ipv6ExtensionHeaderParsingContext,
228    ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
229        let (next_header, hdr_ext_len) = Self::get_next_hdr_and_len(data, context)?;
230
231        // As per RFC 8200 section 4.4, Hdr Ext Len is the length of this extension
232        // header in  8-octect units, not including the first 8 octets (where 2 of
233        // them are the Next Header and the Hdr Ext Len fields). Since we already
234        // 'took' the Next Header and Hdr Ext Len octets, we need to make sure
235        // we have (Hdr Ext Len) * 8 + 6 bytes bytes in `data`.
236        let expected_len = (hdr_ext_len as usize) * 8 + 6;
237        let bytes = data
238            .take_front(expected_len)
239            .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
240        let routing_data = RoutingData { bytes };
241
242        let segments_left = routing_data.segments_left();
243
244        // Currently we do not support any routing type.
245        //
246        // Note, this includes routing type 0 which is defined in RFC 2460 as it has been
247        // deprecated as of RFC 5095 for security reasons.
248
249        // If we receive a routing header with an unrecognized routing type,
250        // what we do depends on the segments left. If segments left is 0, we
251        // must ignore the routing header and continue processing other headers
252        // (note that we still return a record here to support operations that
253        // depend on the packet structure; any consumers are expected to ignore
254        // it). If segments left is not 0, we need to discard this packet and
255        // send an ICMP Parameter Problem, Code 0 with a pointer to this
256        // unrecognized routing type.
257        if segments_left == 0 {
258            // Update context
259            context.next_header = next_header;
260            context.headers_parsed += 1;
261            context.bytes_parsed += 2 + expected_len;
262
263            Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader {
264                next_header,
265                data: Ipv6ExtensionHeaderData::Routing { routing_data },
266            }))
267        } else {
268            // As per RFC 8200, if we encounter a routing header with an unrecognized
269            // routing type, and segments left is non-zero, we MUST discard the packet
270            // and send and ICMP Parameter Problem response.
271            Err(Ipv6ExtensionHeaderParsingError::ErroneousHeaderField {
272                pointer: (context.bytes_parsed as u32) + 2,
273                must_send_icmp: true,
274            })
275        }
276    }
277
278    /// Parse Fragment Extension Header.
279    fn parse_fragment<'a, BV: BufferView<&'a [u8]>>(
280        data: &mut BV,
281        context: &mut Ipv6ExtensionHeaderParsingContext,
282    ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
283        // Fragment Extension Header requires exactly 8 bytes so make sure
284        // `data` has at least 8 bytes left. If `data` has at least 8 bytes left,
285        // we are guaranteed that all `take_front` calls done by this
286        // method will succeed since we will never attempt to call `take_front`
287        // with more than 8 bytes total.
288        if data.len() < 8 {
289            return Err(Ipv6ExtensionHeaderParsingError::BufferExhausted);
290        }
291
292        // For Fragment headers, we do not actually have a HdrExtLen field. Instead,
293        // the second byte in the header (where HdrExtLen would normally exist), is
294        // a reserved field, so we can simply ignore it for now.
295        let (next_header, _) = Self::get_next_hdr_and_len(data, context)?;
296
297        // Update context
298        context.next_header = next_header;
299        context.headers_parsed += 1;
300        context.bytes_parsed += 8;
301
302        Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader {
303            next_header,
304            data: Ipv6ExtensionHeaderData::Fragment {
305                // First unwrap is safe because we already know data is at least
306                // 8 bytes long and we've consumed 2 bytes.
307                //
308                // Second unwrap is safe because we're converting from a slice
309                // of length 6 to an array of length 6.
310                fragment_data: FragmentData {
311                    bytes: data.take_front(6).unwrap().try_into().unwrap(),
312                },
313            },
314        }))
315    }
316
317    /// Parse Destination Options Extension Header.
318    fn parse_destination_options<'a, BV: BufferView<&'a [u8]>>(
319        data: &mut BV,
320        context: &mut Ipv6ExtensionHeaderParsingContext,
321    ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
322        let (next_header, hdr_ext_len) = Self::get_next_hdr_and_len(data, context)?;
323
324        // As per RFC 8200 section 4.6, Hdr Ext Len is the length of this extension
325        // header in  8-octet units, not including the first 8 octets (where 2 of
326        // them are the Next Header and the Hdr Ext Len fields).
327        let expected_len = (hdr_ext_len as usize) * 8 + 6;
328
329        let options = data
330            .take_front(expected_len)
331            .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
332
333        let options_context = ExtensionHeaderOptionContext::new();
334        let options = Records::parse_with_context(options, options_context).map_err(|e| {
335            // We know the below `try_from` call will not result in a `None` value because
336            // the maximum size of an IPv6 packet's payload (extension headers + body) is
337            // `core::u32::MAX`. This maximum size is only possible when using IPv6
338            // jumbograms as defined by RFC 2675, which uses a 32 bit field for the payload
339            // length. If we receive such a hypothetical packet with the maximum possible
340            // payload length which only contains extension headers, we know that the offset
341            // of any location within the payload must fit within an `u32`. If the packet is
342            // a normal IPv6 packet (not a jumbogram), the maximum size of the payload is
343            // `core::u16::MAX` (as the normal payload length field is only 16 bits), which
344            // is significantly less than the maximum possible size of a jumbogram.
345            ext_hdr_opt_err_to_ext_hdr_err(u32::try_from(context.bytes_parsed + 2).unwrap(), e)
346        })?;
347        let options = DestinationOptionsData::new(options);
348
349        // Update context
350        context.next_header = next_header;
351        context.headers_parsed += 1;
352        context.bytes_parsed += 2 + expected_len;
353
354        Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader {
355            next_header,
356            data: Ipv6ExtensionHeaderData::DestinationOptions { options },
357        }))
358    }
359}
360
361impl RecordsImplLayout for Ipv6ExtensionHeaderImpl {
362    type Context = Ipv6ExtensionHeaderParsingContext;
363    type Error = Ipv6ExtensionHeaderParsingError;
364}
365
366impl RecordsImpl for Ipv6ExtensionHeaderImpl {
367    type Record<'a> = Ipv6ExtensionHeader<'a>;
368
369    fn parse_with_context<'a, BV: BufferView<&'a [u8]>>(
370        data: &mut BV,
371        context: &mut Self::Context,
372    ) -> RecordParseResult<Self::Record<'a>, Self::Error> {
373        let expected_hdr = context.next_header;
374
375        match Ipv6ExtHdrType::from(expected_hdr) {
376            Ipv6ExtHdrType::HopByHopOptions => Self::parse_hop_by_hop_options(data, context),
377            Ipv6ExtHdrType::Routing => Self::parse_routing(data, context),
378            Ipv6ExtHdrType::Fragment => Self::parse_fragment(data, context),
379            Ipv6ExtHdrType::DestinationOptions => Self::parse_destination_options(data, context),
380            Ipv6ExtHdrType::EncapsulatingSecurityPayload | Ipv6ExtHdrType::Authentication => {
381                // We don't implement these extension header types.
382                //
383                // Per RFC 2460:
384                //   If, as a result of processing a header, a node is required to
385                //   proceed to the next header but the Next Header value in the
386                //   current header is unrecognized by the node, it should discard
387                //   the packet and send an ICMP Parameter Problem message to the
388                //   source of the packet, with an ICMP Code value of 1
389                //   ("unrecognized Next Header type encountered") and the ICMP
390                //   Pointer field containing the offset of the unrecognized value
391                //   within the original packet.
392                Err(Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
393                    // TODO(https://fxbug.dev/42158223): When overhauling packet
394                    // validation, return the right value for `pointer`.
395                    pointer: u32::MAX,
396                    // This is false because of the "should" in the quoted RFC
397                    // text.
398                    must_send_icmp: false,
399                })
400            }
401            Ipv6ExtHdrType::Other(_) => {
402                if is_valid_next_header_upper_layer(expected_hdr) {
403                    // Stop parsing extension headers when we find a Next Header value
404                    // for a higher level protocol.
405                    Ok(ParsedRecord::Done)
406                } else {
407                    // Should never end up here because we guarantee that if we hit an
408                    // invalid Next Header field while parsing extension headers, we will
409                    // return an error when we see it right away. Since the only other time
410                    // `context.next_header` can get an invalid value assigned is when we parse
411                    // the fixed IPv6 header, but we check if the next header is valid before
412                    // parsing extension headers.
413
414                    unreachable!(
415                        "Should never try parsing an extension header with an unrecognized type"
416                    );
417                }
418            }
419        }
420    }
421}
422
423impl<'a> RecordsRawImpl<'a> for Ipv6ExtensionHeaderImpl {
424    fn parse_raw_with_context<BV: BufferView<&'a [u8]>>(
425        data: &mut BV,
426        context: &mut Self::Context,
427    ) -> Result<bool, Self::Error> {
428        if is_valid_next_header_upper_layer(context.next_header) {
429            Ok(false)
430        } else {
431            let (next, skip) = match Ipv6ExtHdrType::from(context.next_header) {
432                Ipv6ExtHdrType::HopByHopOptions
433                | Ipv6ExtHdrType::Routing
434                | Ipv6ExtHdrType::DestinationOptions
435                | Ipv6ExtHdrType::Other(_) => {
436                    // take next header and header len, and skip the next 6
437                    // octets + the number of 64 bit words in header len.
438                    // NOTE: we can assume that Other will be parsed
439                    //  as such based on the extensibility note in
440                    //  RFC 8200 Section-4.8
441                    data.take_front(2)
442                        .map(|x| (x[0], (x[1] as usize) * 8 + 6))
443                        .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?
444                }
445                Ipv6ExtHdrType::Fragment => {
446                    // take next header from first, then skip next 7
447                    (
448                        data.take_byte_front()
449                            .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?,
450                        7,
451                    )
452                }
453                Ipv6ExtHdrType::EncapsulatingSecurityPayload => {
454                    // TODO(brunodalbo): We don't support ESP yet, so return
455                    //  an error instead of panicking "unimplemented" to avoid
456                    //  having a panic-path that can be remotely triggered.
457                    return debug_err!(
458                        Err(Ipv6ExtensionHeaderParsingError::MalformedData),
459                        "ESP extension header not supported"
460                    );
461                }
462                Ipv6ExtHdrType::Authentication => {
463                    // take next header and payload len, and skip the next
464                    // (payload_len + 2) 32 bit words, minus the 2 octets
465                    // already consumed.
466                    data.take_front(2)
467                        .map(|x| (x[0], (x[1] as usize + 2) * 4 - 2))
468                        .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?
469                }
470            };
471            let _: &[u8] =
472                data.take_front(skip).ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
473            context.next_header = next;
474            Ok(true)
475        }
476    }
477}
478
479//
480// Hop-By-Hop Options
481//
482
483/// Hop By Hop Options extension header data.
484#[derive(Debug)]
485pub struct HopByHopOptionsData<'a> {
486    options: Records<&'a [u8], HopByHopOptionsImpl>,
487}
488
489impl<'a> HopByHopOptionsData<'a> {
490    /// Returns a new `HopByHopOptionsData` with `options`.
491    fn new(options: Records<&'a [u8], HopByHopOptionsImpl>) -> HopByHopOptionsData<'a> {
492        HopByHopOptionsData { options }
493    }
494
495    /// Returns an iterator over the [`HopByHopOptions`] in this
496    /// `HopByHopOptionsData`.
497    pub fn iter(&'a self) -> impl Iterator<Item = HopByHopOption<'a>> {
498        self.options.iter()
499    }
500}
501
502/// An option found in a Hop By Hop Options extension header.
503pub type HopByHopOption<'a> = ExtensionHeaderOption<HopByHopOptionData<'a>>;
504
505/// An implementation of [`OptionsImpl`] for options found in a Hop By Hop Options
506/// extension header.
507pub(super) type HopByHopOptionsImpl = ExtensionHeaderOptionImpl<HopByHopOptionDataImpl>;
508
509/// Hop-By-Hop Option Type number as per [RFC 2711 section-2.1]
510///
511/// [RFC 2711 section-2.1]: https://tools.ietf.org/html/rfc2711#section-2.1
512const HBH_OPTION_KIND_RTRALRT: u8 = 5;
513
514/// Length for RouterAlert as per [RFC 2711 section-2.1]
515///
516/// [RFC 2711 section-2.1]: https://tools.ietf.org/html/rfc2711#section-2.1
517const HBH_OPTION_RTRALRT_LEN: usize = 2;
518
519/// HopByHop Options Extension header data.
520#[allow(missing_docs)]
521#[derive(Debug, PartialEq, Eq, Clone)]
522pub enum HopByHopOptionData<'a> {
523    Unrecognized { kind: u8, len: u8, data: &'a [u8] },
524    RouterAlert { data: u16 },
525}
526
527/// Impl for Hop By Hop Options parsing.
528#[derive(Debug)]
529pub(super) struct HopByHopOptionDataImpl;
530
531impl ExtensionHeaderOptionDataImplLayout for HopByHopOptionDataImpl {
532    type Context = ();
533}
534
535impl ExtensionHeaderOptionDataImpl for HopByHopOptionDataImpl {
536    type OptionData<'a> = HopByHopOptionData<'a>;
537
538    fn parse_option<'a>(
539        kind: u8,
540        data: &'a [u8],
541        _context: &mut Self::Context,
542        allow_unrecognized: bool,
543    ) -> ExtensionHeaderOptionDataParseResult<Self::OptionData<'a>> {
544        match kind {
545            HBH_OPTION_KIND_RTRALRT => {
546                if data.len() == HBH_OPTION_RTRALRT_LEN {
547                    ExtensionHeaderOptionDataParseResult::Ok(HopByHopOptionData::RouterAlert {
548                        data: NetworkEndian::read_u16(data),
549                    })
550                } else {
551                    // Since the length is wrong, and the length is indicated at the second byte within
552                    // the option itself. We count from 0 of course.
553                    ExtensionHeaderOptionDataParseResult::ErrorAt(1)
554                }
555            }
556            _ => {
557                if allow_unrecognized {
558                    ExtensionHeaderOptionDataParseResult::Ok(HopByHopOptionData::Unrecognized {
559                        kind,
560                        len: data.len() as u8,
561                        data,
562                    })
563                } else {
564                    ExtensionHeaderOptionDataParseResult::UnrecognizedKind
565                }
566            }
567        }
568    }
569}
570
571impl OptionLayout for HopByHopOptionsImpl {
572    type KindLenField = u8;
573    const LENGTH_ENCODING: LengthEncoding = LengthEncoding::ValueOnly;
574}
575
576impl OptionParseLayout for HopByHopOptionsImpl {
577    type Error = OptionParseErr;
578    const END_OF_OPTIONS: Option<u8> = Some(0);
579    const NOP: Option<u8> = Some(1);
580}
581
582/// Provides an implementation of `OptionLayout` for Hop-by-Hop options.
583///
584/// Use this instead of `HopByHopOptionsImpl` for `<HopByHopOption as
585/// OptionBuilder>::Layout` in order to avoid having to make a ton of other
586/// things `pub` which are reachable from `HopByHopOptionsImpl`.
587#[doc(hidden)]
588pub enum HopByHopOptionLayout {}
589
590impl OptionLayout for HopByHopOptionLayout {
591    type KindLenField = u8;
592    const LENGTH_ENCODING: LengthEncoding = LengthEncoding::ValueOnly;
593}
594
595impl<'a> OptionBuilder for HopByHopOption<'a> {
596    type Layout = HopByHopOptionLayout;
597    fn serialized_len(&self) -> usize {
598        match self.data {
599            HopByHopOptionData::RouterAlert { .. } => HBH_OPTION_RTRALRT_LEN,
600            HopByHopOptionData::Unrecognized { len, .. } => len as usize,
601        }
602    }
603
604    fn option_kind(&self) -> u8 {
605        let action: u8 = self.action.into();
606        let mutable = self.mutable as u8;
607        let type_number = match self.data {
608            HopByHopOptionData::Unrecognized { kind, .. } => kind,
609            HopByHopOptionData::RouterAlert { .. } => HBH_OPTION_KIND_RTRALRT,
610        };
611        (action << 6) | (mutable << 5) | type_number
612    }
613
614    fn serialize_into(&self, mut buffer: &mut [u8]) {
615        match self.data {
616            HopByHopOptionData::Unrecognized { data, .. } => buffer.copy_from_slice(data),
617            HopByHopOptionData::RouterAlert { data } => {
618                // If the buffer doesn't contain enough space, it is a
619                // contract violation, panic here.
620                (&mut buffer).write_obj_front(&U16::new(data)).unwrap()
621            }
622        }
623    }
624}
625
626impl<'a> AlignedOptionBuilder for HopByHopOption<'a> {
627    fn alignment_requirement(&self) -> (usize, usize) {
628        match self.data {
629            // RouterAlert must be aligned at 2 * n + 0 bytes.
630            // See: https://tools.ietf.org/html/rfc2711#section-2.1
631            HopByHopOptionData::RouterAlert { .. } => (2, 0),
632            _ => (1, 0),
633        }
634    }
635
636    fn serialize_padding(buf: &mut [u8], length: usize) {
637        assert!(length <= buf.len());
638        assert!(length <= (core::u8::MAX as usize) + 2);
639
640        #[allow(clippy::comparison_chain)]
641        if length == 1 {
642            // Use Pad1
643            buf[0] = 0
644        } else if length > 1 {
645            // Use PadN
646            buf[0] = 1;
647            buf[1] = (length - 2) as u8;
648            #[allow(clippy::needless_range_loop)]
649            for i in 2..length {
650                buf[i] = 0
651            }
652        }
653    }
654}
655
656//
657// Routing
658//
659
660/// Routing Extension header data.
661///
662/// As per RFC 8200, section 4.4 the Routing header is structured as:
663/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
664/// |  Next Header  |  Hdr Ext Len  |  Routing Type | Segments Left |
665/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
666/// |                                                               |
667/// .                                                               .
668/// .                       type-specific data                      .
669/// .                                                               .
670/// |                                                               |
671/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
672///
673/// where the format of the type-specific data is determined by the Routing
674/// Type.
675#[derive(Debug)]
676pub struct RoutingData<'a> {
677    bytes: &'a [u8],
678}
679
680/// Supported Routing Types.
681#[derive(Debug, PartialEq, Eq)]
682pub enum RoutingType {}
683
684/// Error returned when the routing type failed to parse.
685#[derive(Debug, PartialEq, Eq)]
686pub enum RoutingTypeParseError {
687    /// The Routing header has an unknown routing type and must be ignored per
688    /// RFC 8200 section 4.4.
689    UnsupportedType(u8),
690}
691
692impl TryFrom<u8> for RoutingType {
693    type Error = RoutingTypeParseError;
694
695    fn try_from(value: u8) -> Result<Self, Self::Error> {
696        Err(RoutingTypeParseError::UnsupportedType(value))
697    }
698}
699
700impl<'a> RoutingData<'a> {
701    /// Returns the routing type.
702    pub fn routing_type(&self) -> Result<RoutingType, RoutingTypeParseError> {
703        debug_assert!(self.bytes.len() >= 6);
704        RoutingType::try_from(self.bytes[0])
705    }
706
707    /// Returns the number of segments left.
708    pub fn segments_left(&self) -> u8 {
709        debug_assert!(self.bytes.len() >= 6);
710        self.bytes[1]
711    }
712}
713
714//
715// Fragment
716//
717
718/// Fragment Extension header data.
719///
720/// As per RFC 8200, section 4.5 the fragment header is structured as:
721/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
722/// |  Next Header  |   Reserved    |      Fragment Offset    |Res|M|
723/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
724/// |                         Identification                        |
725/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
726///
727/// where Fragment Offset is 13 bits, Res is a reserved 2 bits and M
728/// is a 1 bit flag. Identification is a 32bit value.
729#[derive(Debug, Copy, Clone)]
730pub struct FragmentData {
731    bytes: [u8; 6],
732}
733
734impl FragmentData {
735    /// Returns the fragment offset.
736    pub fn fragment_offset(&self) -> FragmentOffset {
737        FragmentOffset::new_with_msb(U16::from_bytes([self.bytes[0], self.bytes[1]]).get())
738    }
739
740    /// Returns the more fragments flags.
741    pub fn m_flag(&self) -> bool {
742        (self.bytes[1] & 0x1) == 0x01
743    }
744
745    /// Returns the identification value.
746    pub fn identification(&self) -> u32 {
747        NetworkEndian::read_u32(&self.bytes[2..6])
748    }
749}
750
751//
752// Destination Options
753//
754
755/// Destination Options extension header data.
756#[derive(Debug)]
757pub struct DestinationOptionsData<'a> {
758    options: Records<&'a [u8], DestinationOptionsImpl>,
759}
760
761impl<'a> DestinationOptionsData<'a> {
762    /// Returns a new `DestinationOptionsData` with `options`.
763    fn new(options: Records<&'a [u8], DestinationOptionsImpl>) -> DestinationOptionsData<'a> {
764        DestinationOptionsData { options }
765    }
766
767    /// Returns an iterator over the [`DestinationOptions`] in this
768    /// `DestinationOptionsData`.
769    pub fn iter(&'a self) -> impl Iterator<Item = DestinationOption<'a>> {
770        self.options.iter()
771    }
772}
773
774/// An option found in a Destination Options extension header.
775pub type DestinationOption<'a> = ExtensionHeaderOption<DestinationOptionData<'a>>;
776
777/// An implementation of [`OptionsImpl`] for options found in a Destination Options
778/// extension header.
779pub(super) type DestinationOptionsImpl = ExtensionHeaderOptionImpl<DestinationOptionDataImpl>;
780
781/// Destination Options extension header data.
782#[allow(missing_docs)]
783#[derive(Debug)]
784pub enum DestinationOptionData<'a> {
785    Unrecognized { kind: u8, len: u8, data: &'a [u8] },
786}
787
788/// Impl for Destination Options parsing.
789#[derive(Debug)]
790pub(super) struct DestinationOptionDataImpl;
791
792impl ExtensionHeaderOptionDataImplLayout for DestinationOptionDataImpl {
793    type Context = ();
794}
795
796impl ExtensionHeaderOptionDataImpl for DestinationOptionDataImpl {
797    type OptionData<'a> = DestinationOptionData<'a>;
798
799    fn parse_option<'a>(
800        kind: u8,
801        data: &'a [u8],
802        _context: &mut Self::Context,
803        allow_unrecognized: bool,
804    ) -> ExtensionHeaderOptionDataParseResult<Self::OptionData<'a>> {
805        if allow_unrecognized {
806            ExtensionHeaderOptionDataParseResult::Ok(DestinationOptionData::Unrecognized {
807                kind,
808                len: data.len() as u8,
809                data,
810            })
811        } else {
812            ExtensionHeaderOptionDataParseResult::UnrecognizedKind
813        }
814    }
815}
816
817//
818// Generic Extension Header who's data are options.
819//
820
821/// Context that gets passed around when parsing IPv6 Extension Header options.
822#[derive(Debug, Clone)]
823pub(super) struct ExtensionHeaderOptionContext<C: Sized + Clone> {
824    // Counter for number of options parsed.
825    options_parsed: usize,
826
827    // Byte count of successfully parsed options.
828    bytes_parsed: usize,
829
830    // Extension header specific context data.
831    specific_context: C,
832}
833
834impl<C: Sized + Clone + Default> ExtensionHeaderOptionContext<C> {
835    fn new() -> Self {
836        ExtensionHeaderOptionContext {
837            options_parsed: 0,
838            bytes_parsed: 0,
839            specific_context: C::default(),
840        }
841    }
842}
843
844impl<C: Sized + Clone> RecordsContext for ExtensionHeaderOptionContext<C> {
845    type Counter = ();
846
847    fn counter_mut(&mut self) -> &mut () {
848        get_empty_tuple_mut_ref()
849    }
850}
851
852/// Basic associated types required by `ExtensionHeaderOptionDataImpl`.
853pub(super) trait ExtensionHeaderOptionDataImplLayout {
854    /// A context type that can be used to maintain state while parsing multiple
855    /// records.
856    type Context: RecordsContext;
857}
858
859/// The result of parsing an extension header option data.
860#[derive(PartialEq, Eq, Debug)]
861pub enum ExtensionHeaderOptionDataParseResult<D> {
862    /// Successfully parsed data.
863    Ok(D),
864
865    /// An error occurred at the indicated offset within the option.
866    ///
867    /// For example, if the data length goes wrong, you should probably
868    /// make the offset to be 1 because in most (almost all) cases, the
869    /// length is at the second byte of the option.
870    ErrorAt(u32),
871
872    /// The option kind is not recognized.
873    UnrecognizedKind,
874}
875
876/// An implementation of an extension header specific option data parser.
877pub(super) trait ExtensionHeaderOptionDataImpl: ExtensionHeaderOptionDataImplLayout {
878    /// Extension header specific option data.
879    ///
880    /// Note, `OptionData` does not need to hold general option data as defined by
881    /// RFC 8200 section 4.2. It should only hold extension header specific option
882    /// data.
883    type OptionData<'a>: Sized;
884
885    /// Parse an option of a given `kind` from `data`.
886    ///
887    /// When `kind` is recognized returns `Ok(o)` where `o` is a successfully parsed
888    /// option. When `kind` is not recognized, returns `UnrecognizedKind` if `allow_unrecognized`
889    /// is `false`. If `kind` is not recognized but `allow_unrecognized` is `true`,
890    /// returns an `Ok(o)` where `o` holds option data without actually parsing it
891    /// (i.e. an unrecognized type that simply keeps track of the `kind` and `data`
892    /// that was passed to `parse_option`). A recognized option `kind` with incorrect
893    /// `data` must return `ErrorAt(offset)`, where the offset indicates where the
894    /// erroneous field is within the option data buffer.
895    fn parse_option<'a>(
896        kind: u8,
897        data: &'a [u8],
898        context: &mut Self::Context,
899        allow_unrecognized: bool,
900    ) -> ExtensionHeaderOptionDataParseResult<Self::OptionData<'a>>;
901}
902
903/// Generic implementation of extension header options parsing.
904///
905/// `ExtensionHeaderOptionImpl` handles the common implementation details
906/// of extension header options and lets `O` (which implements
907/// `ExtensionHeaderOptionDataImpl`) handle the extension header specific
908/// option parsing.
909#[derive(Debug)]
910pub(super) struct ExtensionHeaderOptionImpl<O>(PhantomData<O>);
911
912impl<O> ExtensionHeaderOptionImpl<O> {
913    const PAD1: u8 = 0;
914    const PADN: u8 = 1;
915}
916
917impl<O> RecordsImplLayout for ExtensionHeaderOptionImpl<O>
918where
919    O: ExtensionHeaderOptionDataImplLayout,
920{
921    type Error = ExtensionHeaderOptionParsingError;
922    type Context = ExtensionHeaderOptionContext<O::Context>;
923}
924
925impl<O> RecordsImpl for ExtensionHeaderOptionImpl<O>
926where
927    O: ExtensionHeaderOptionDataImpl,
928{
929    type Record<'a> = ExtensionHeaderOption<O::OptionData<'a>>;
930
931    fn parse_with_context<'a, BV: BufferView<&'a [u8]>>(
932        data: &mut BV,
933        context: &mut Self::Context,
934    ) -> RecordParseResult<Self::Record<'a>, Self::Error> {
935        // If we have no more bytes left, we are done.
936        let kind = match data.take_byte_front() {
937            None => return Ok(ParsedRecord::Done),
938            Some(k) => k,
939        };
940
941        // Will never get an error because we only use the 2 least significant bits which
942        // can only have a max value of 3 and all values in [0, 3] are valid values of
943        // `ExtensionHeaderOptionAction`.
944        let action =
945            ExtensionHeaderOptionAction::try_from((kind >> 6) & 0x3).expect("Unexpected error");
946        let mutable = ((kind >> 5) & 0x1) == 0x1;
947        // Note that `kind` remains unmodified here: per RFC 8200 section 4.2,
948        // the three high-order bits parsed above are to be treated as part of
949        // the Option Type.
950
951        // If our kind is a PAD1, consider it a NOP.
952        if kind == Self::PAD1 {
953            // Update context.
954            context.options_parsed += 1;
955            context.bytes_parsed += 1;
956
957            return Ok(ParsedRecord::Skipped);
958        }
959
960        let len =
961            data.take_byte_front().ok_or(ExtensionHeaderOptionParsingError::BufferExhausted)?;
962
963        let data = data
964            .take_front(len as usize)
965            .ok_or(ExtensionHeaderOptionParsingError::BufferExhausted)?;
966
967        // If our kind is a PADN, consider it a NOP as well.
968        if kind == Self::PADN {
969            // Update context.
970            context.options_parsed += 1;
971            context.bytes_parsed += 2 + (len as usize);
972
973            return Ok(ParsedRecord::Skipped);
974        }
975
976        // Parse the actual option data.
977        match O::parse_option(
978            kind,
979            data,
980            &mut context.specific_context,
981            action == ExtensionHeaderOptionAction::SkipAndContinue,
982        ) {
983            ExtensionHeaderOptionDataParseResult::Ok(o) => {
984                // Update context.
985                context.options_parsed += 1;
986                context.bytes_parsed += 2 + (len as usize);
987
988                Ok(ParsedRecord::Parsed(ExtensionHeaderOption { action, mutable, data: o }))
989            }
990            ExtensionHeaderOptionDataParseResult::ErrorAt(offset) => {
991                // The precondition here is that `bytes_parsed + offset` must point inside the
992                // packet. So as reasoned in the next match arm, it is not possible to exceed
993                // `core::u32::max`. Given this reasoning, we know the call to `unwrap` should not
994                // panic.
995                Err(ExtensionHeaderOptionParsingError::ErroneousOptionField {
996                    pointer: u32::try_from(context.bytes_parsed + offset as usize).unwrap(),
997                })
998            }
999            ExtensionHeaderOptionDataParseResult::UnrecognizedKind => {
1000                // Unrecognized option type.
1001                match action {
1002                    // `O::parse_option` should never return
1003                    // `ExtensionHeaderOptionDataParseResult::UnrecognizedKind` when the
1004                    // action is `ExtensionHeaderOptionAction::SkipAndContinue` because
1005                    // we expect `O::parse_option` to return something that holds the
1006                    // option data without actually parsing it since we pass `true` for its
1007                    // `allow_unrecognized` parameter.
1008                    ExtensionHeaderOptionAction::SkipAndContinue => unreachable!(
1009                        "Should never end up here since action was set to skip and continue"
1010                    ),
1011                    // We know the below `try_from` call will not result in a `None` value because
1012                    // the maximum size of an IPv6 packet's payload (extension headers + body) is
1013                    // `core::u32::MAX`. This maximum size is only possible when using IPv6
1014                    // jumbograms as defined by RFC 2675, which uses a 32 bit field for the payload
1015                    // length. If we receive such a hypothetical packet with the maximum possible
1016                    // payload length which only contains extension headers, we know that the offset
1017                    // of any location within the payload must fit within an `u32`. If the packet is
1018                    // a normal IPv6 packet (not a jumbogram), the maximum size of the payload is
1019                    // `core::u16::MAX` (as the normal payload length field is only 16 bits), which
1020                    // is significantly less than the maximum possible size of a jumbogram.
1021                    _ => Err(ExtensionHeaderOptionParsingError::UnrecognizedOption {
1022                        pointer: u32::try_from(context.bytes_parsed).unwrap(),
1023                        action,
1024                    }),
1025                }
1026            }
1027        }
1028    }
1029}
1030
1031/// Possible errors when parsing extension header options.
1032#[allow(missing_docs)]
1033#[derive(Debug, PartialEq, Eq)]
1034pub(crate) enum ExtensionHeaderOptionParsingError {
1035    ErroneousOptionField { pointer: u32 },
1036    UnrecognizedOption { pointer: u32, action: ExtensionHeaderOptionAction },
1037    BufferExhausted,
1038}
1039
1040impl From<Never> for ExtensionHeaderOptionParsingError {
1041    fn from(err: Never) -> ExtensionHeaderOptionParsingError {
1042        match err {}
1043    }
1044}
1045
1046/// Action to take when an unrecognized option type is encountered.
1047///
1048/// `ExtensionHeaderOptionAction` is an action that MUST be taken (according
1049/// to RFC 8200 section 4.2) when an IPv6 processing node does not
1050/// recognize an option's type.
1051#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1052pub enum ExtensionHeaderOptionAction {
1053    /// Skip over the option and continue processing the header.
1054    /// value = 0.
1055    SkipAndContinue,
1056
1057    /// Just discard the packet.
1058    /// value = 1.
1059    DiscardPacket,
1060
1061    /// Discard the packet and, regardless of whether or not the packet's
1062    /// destination address was a multicast address, send an ICMP parameter
1063    /// problem, code 2 (unrecognized option), message to the packet's source
1064    /// address, pointing to the unrecognized type.
1065    /// value = 2.
1066    DiscardPacketSendIcmp,
1067
1068    /// Discard the packet and, and only if the packet's destination address
1069    /// was not a multicast address, send an ICMP parameter problem, code 2
1070    /// (unrecognized option), message to the packet's source address, pointing
1071    /// to the unrecognized type.
1072    /// value = 3.
1073    DiscardPacketSendIcmpNoMulticast,
1074}
1075
1076impl TryFrom<u8> for ExtensionHeaderOptionAction {
1077    type Error = ();
1078
1079    fn try_from(value: u8) -> Result<Self, ()> {
1080        match value {
1081            0 => Ok(ExtensionHeaderOptionAction::SkipAndContinue),
1082            1 => Ok(ExtensionHeaderOptionAction::DiscardPacket),
1083            2 => Ok(ExtensionHeaderOptionAction::DiscardPacketSendIcmp),
1084            3 => Ok(ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast),
1085            _ => Err(()),
1086        }
1087    }
1088}
1089
1090impl From<ExtensionHeaderOptionAction> for u8 {
1091    fn from(a: ExtensionHeaderOptionAction) -> u8 {
1092        match a {
1093            ExtensionHeaderOptionAction::SkipAndContinue => 0,
1094            ExtensionHeaderOptionAction::DiscardPacket => 1,
1095            ExtensionHeaderOptionAction::DiscardPacketSendIcmp => 2,
1096            ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast => 3,
1097        }
1098    }
1099}
1100
1101/// Extension header option.
1102///
1103/// Generic Extension header option type that has extension header specific
1104/// option data (`data`) defined by an `O`. The common option format is defined in
1105/// section 4.2 of RFC 8200, outlining actions and mutability for option types.
1106#[derive(PartialEq, Eq, Debug, Clone)]
1107pub struct ExtensionHeaderOption<O> {
1108    /// Action to take if the option type is unrecognized.
1109    pub action: ExtensionHeaderOptionAction,
1110
1111    /// Whether or not the option data of the option can change en route to the
1112    /// packet's final destination. When an Authentication header is present in
1113    /// the packet, the option data must be treated as 0s when computing or
1114    /// verifying the packet's authenticating value when the option data can change
1115    /// en route.
1116    pub mutable: bool,
1117
1118    /// Option data associated with a specific extension header.
1119    pub data: O,
1120}
1121
1122//
1123// Helper functions
1124//
1125
1126/// Make sure a Next Header is valid.
1127///
1128/// Check if the provided `next_header` is a valid Next Header value. Note,
1129/// we are intentionally not allowing HopByHopOptions after the first Next
1130/// Header as per section 4.1 of RFC 8200 which restricts the HopByHop extension
1131/// header to only appear as the very first extension header. `is_valid_next_header`.
1132/// If a caller specifies `for_fixed_header` as true, then it is assumed `next_header` is
1133/// the Next Header value in the fixed header, where a HopbyHopOptions extension
1134/// header number is allowed.
1135pub(super) fn is_valid_next_header(next_header: u8, for_fixed_header: bool) -> bool {
1136    // Make sure the Next Header in the fixed header is a valid extension
1137    // header or a valid upper layer protocol.
1138
1139    match Ipv6ExtHdrType::from(next_header) {
1140        // HopByHop Options Extension header as a next header value
1141        // is only valid if it is in the fixed header.
1142        Ipv6ExtHdrType::HopByHopOptions => for_fixed_header,
1143
1144        // Not an IPv6 Extension header number, so make sure it is
1145        // a valid upper layer protocol.
1146        Ipv6ExtHdrType::Other(next_header) => is_valid_next_header_upper_layer(next_header),
1147
1148        // All valid Extension Header numbers
1149        _ => true,
1150    }
1151}
1152
1153/// Make sure a Next Header is a valid upper layer protocol.
1154///
1155/// Make sure a Next Header is a valid upper layer protocol in an IPv6 packet. Note,
1156/// we intentionally are not allowing ICMP(v4) since we are working on IPv6 packets.
1157pub(super) fn is_valid_next_header_upper_layer(next_header: u8) -> bool {
1158    match Ipv6Proto::from(next_header) {
1159        Ipv6Proto::Proto(IpProto::Tcp)
1160        | Ipv6Proto::Proto(IpProto::Udp)
1161        | Ipv6Proto::Icmpv6
1162        | Ipv6Proto::NoNextHeader => true,
1163        Ipv6Proto::Proto(IpProto::Reserved) | Ipv6Proto::Other(_) => false,
1164    }
1165}
1166
1167/// Convert an `ExtensionHeaderOptionParsingError` to an
1168/// `Ipv6ExtensionHeaderParsingError`.
1169///
1170/// `offset` is the offset of the start of the options containing the error, `err`,
1171/// from the end of the fixed header in an IPv6 packet.
1172fn ext_hdr_opt_err_to_ext_hdr_err(
1173    offset: u32,
1174    err: ExtensionHeaderOptionParsingError,
1175) -> Ipv6ExtensionHeaderParsingError {
1176    match err {
1177        ExtensionHeaderOptionParsingError::ErroneousOptionField { pointer } => {
1178            Ipv6ExtensionHeaderParsingError::ErroneousHeaderField {
1179                pointer: offset + pointer,
1180                // TODO: RFC only suggests we SHOULD generate an ICMP message,
1181                // and ideally, we should generate ICMP messages only when the problem
1182                // is severe enough, we do not want to flood the network. So we
1183                // should investigate the criteria for this field to become true.
1184                must_send_icmp: false,
1185            }
1186        }
1187        ExtensionHeaderOptionParsingError::UnrecognizedOption { pointer, action } => {
1188            Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1189                pointer: offset + pointer,
1190                must_send_icmp: true,
1191                action,
1192            }
1193        }
1194        ExtensionHeaderOptionParsingError::BufferExhausted => {
1195            Ipv6ExtensionHeaderParsingError::BufferExhausted
1196        }
1197    }
1198}
1199
1200fn get_empty_tuple_mut_ref<'a>() -> &'a mut () {
1201    // This is a hack since `&mut ()` is invalid.
1202    let bytes: &mut [u8] = &mut [];
1203    zerocopy::Ref::into_mut(zerocopy::Ref::<_, ()>::from_bytes(bytes).unwrap())
1204}
1205
1206#[cfg(test)]
1207mod tests {
1208    use packet::records::{AlignedRecordSequenceBuilder, RecordBuilder};
1209
1210    use crate::ip::Ipv4Proto;
1211
1212    use super::*;
1213
1214    #[test]
1215    fn test_is_valid_next_header_upper_layer() {
1216        // Make sure upper layer protocols like TCP are valid
1217        assert!(is_valid_next_header_upper_layer(IpProto::Tcp.into()));
1218        assert!(is_valid_next_header_upper_layer(IpProto::Tcp.into()));
1219
1220        // Make sure upper layer protocol ICMP(v4) is not valid
1221        assert!(!is_valid_next_header_upper_layer(Ipv4Proto::Icmp.into()));
1222        assert!(!is_valid_next_header_upper_layer(Ipv4Proto::Icmp.into()));
1223
1224        // Make sure any other value is not valid.
1225        // Note, if 255 becomes a valid value, we should fix this test
1226        assert!(!is_valid_next_header(255, true));
1227        assert!(!is_valid_next_header(255, false));
1228    }
1229
1230    #[test]
1231    fn test_is_valid_next_header() {
1232        // Make sure HopByHop Options is only valid if it is in the first Next Header
1233        // (In the fixed header).
1234        assert!(is_valid_next_header(Ipv6ExtHdrType::HopByHopOptions.into(), true));
1235        assert!(!is_valid_next_header(Ipv6ExtHdrType::HopByHopOptions.into(), false));
1236
1237        // Make sure other extension headers (like routing) can be in any
1238        // Next Header
1239        assert!(is_valid_next_header(Ipv6ExtHdrType::Routing.into(), true));
1240        assert!(is_valid_next_header(Ipv6ExtHdrType::Routing.into(), false));
1241
1242        // Make sure upper layer protocols like TCP can be in any Next Header
1243        assert!(is_valid_next_header(IpProto::Tcp.into(), true));
1244        assert!(is_valid_next_header(IpProto::Tcp.into(), false));
1245
1246        // Make sure upper layer protocol ICMP(v4) cannot be in any Next Header
1247        assert!(!is_valid_next_header(Ipv4Proto::Icmp.into(), true));
1248        assert!(!is_valid_next_header(Ipv4Proto::Icmp.into(), false));
1249
1250        // Make sure any other value is not valid.
1251        // Note, if 255 becomes a valid value, we should fix this test
1252        assert!(!is_valid_next_header(255, true));
1253        assert!(!is_valid_next_header(255, false));
1254    }
1255
1256    #[test]
1257    fn test_hop_by_hop_options() {
1258        // Test parsing of Pad1 (marked as NOP)
1259        let buffer = [0; 10];
1260        let mut context = ExtensionHeaderOptionContext::new();
1261        let options =
1262            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1263                .unwrap();
1264        assert_eq!(options.iter().count(), 0);
1265        assert_eq!(context.bytes_parsed, 10);
1266        assert_eq!(context.options_parsed, 10);
1267
1268        // Test parsing of Pad1 w/ PadN (treated as NOP)
1269        #[rustfmt::skip]
1270        let buffer = [
1271            0,                            // Pad1
1272            1, 0,                         // Pad2
1273            1, 8, 0, 0, 0, 0, 0, 0, 0, 0, // Pad10
1274        ];
1275        let mut context = ExtensionHeaderOptionContext::new();
1276        let options =
1277            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1278                .unwrap();
1279        assert_eq!(options.iter().count(), 0);
1280        assert_eq!(context.bytes_parsed, 13);
1281        assert_eq!(context.options_parsed, 3);
1282
1283        // Test parsing with an unknown option type but its action is
1284        // skip/continue
1285        #[rustfmt::skip]
1286        let buffer = [
1287            0,                            // Pad1
1288            63, 1, 0,                     // Unrecognized Option Type but can skip/continue
1289            1,  6, 0, 0, 0, 0, 0, 0,      // Pad8
1290        ];
1291        let mut context = ExtensionHeaderOptionContext::new();
1292        let options =
1293            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1294                .unwrap();
1295        let options: Vec<HopByHopOption<'_>> = options.iter().collect();
1296        assert_eq!(options.len(), 1);
1297        assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1298        assert_eq!(context.bytes_parsed, 12);
1299        assert_eq!(context.options_parsed, 3);
1300    }
1301
1302    #[test]
1303    fn test_hop_by_hop_options_err() {
1304        // Test parsing but missing last 2 bytes
1305        #[rustfmt::skip]
1306        let buffer = [
1307            0,                            // Pad1
1308            1, 0,                         // Pad2
1309            1, 8, 0, 0, 0, 0, 0, 0,       // Pad10 (but missing 2 bytes)
1310        ];
1311        let mut context = ExtensionHeaderOptionContext::new();
1312        assert_eq!(
1313            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1314                .expect_err("Parsed successfully when we were short 2 bytes"),
1315            ExtensionHeaderOptionParsingError::BufferExhausted
1316        );
1317        assert_eq!(context.bytes_parsed, 3);
1318        assert_eq!(context.options_parsed, 2);
1319
1320        // Test parsing with unknown option type but action set to discard
1321        #[rustfmt::skip]
1322        let buffer = [
1323            1,   1, 0,                    // Pad3
1324            127, 0,                       // Unrecognized Option Type w/ action to discard
1325            1,   6, 0, 0, 0, 0, 0, 0,     // Pad8
1326        ];
1327        let mut context = ExtensionHeaderOptionContext::new();
1328        assert_eq!(
1329            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1330                .expect_err("Parsed successfully when we had an unrecognized option type"),
1331            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1332                pointer: 3,
1333                action: ExtensionHeaderOptionAction::DiscardPacket,
1334            }
1335        );
1336        assert_eq!(context.bytes_parsed, 3);
1337        assert_eq!(context.options_parsed, 1);
1338
1339        // Test parsing with unknown option type but action set to discard and
1340        // send ICMP.
1341        #[rustfmt::skip]
1342        let buffer = [
1343            1,   1, 0,                    // Pad3
1344            191, 0,                       // Unrecognized Option Type w/ action to discard
1345                                          // & send icmp
1346            1,   6, 0, 0, 0, 0, 0, 0,     // Pad8
1347        ];
1348        let mut context = ExtensionHeaderOptionContext::new();
1349        assert_eq!(
1350            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1351                .expect_err("Parsed successfully when we had an unrecognized option type"),
1352            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1353                pointer: 3,
1354                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmp,
1355            }
1356        );
1357        assert_eq!(context.bytes_parsed, 3);
1358        assert_eq!(context.options_parsed, 1);
1359
1360        // Test parsing with unknown option type but action set to discard and
1361        // send ICMP if not sending to a multicast address
1362        #[rustfmt::skip]
1363        let buffer = [
1364            1,   1, 0,                    // Pad3
1365            255, 0,                       // Unrecognized Option Type w/ action to discard
1366                                          // & send icmp if no multicast
1367            1,   6, 0, 0, 0, 0, 0, 0,     // Pad8
1368        ];
1369        let mut context = ExtensionHeaderOptionContext::new();
1370        assert_eq!(
1371            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1372                .expect_err("Parsed successfully when we had an unrecognized option type"),
1373            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1374                pointer: 3,
1375                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1376            }
1377        );
1378        assert_eq!(context.bytes_parsed, 3);
1379        assert_eq!(context.options_parsed, 1);
1380
1381        // Test parsing Pad1 but with upper bits set.
1382        #[rustfmt::skip]
1383        let buffer = [
1384            // 0b11000000 -> action = 0b11, mutable = 0b0, option type = 0b00000
1385            // (matching lower-order bits of Pad1).
1386            0xC0,
1387            1, 0,                         // Pad2
1388            1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Pad10
1389        ];
1390        let mut context = ExtensionHeaderOptionContext::new();
1391        assert_eq!(
1392            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1393                .expect_err("Parsed successfully when we had Pad1 with upper bits set"),
1394            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1395                pointer: 0,
1396                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1397            }
1398        );
1399        assert_eq!(context.bytes_parsed, 0);
1400        assert_eq!(context.options_parsed, 0);
1401
1402        // Test parsing Pad2 but with upper bits set.
1403        #[rustfmt::skip]
1404        let buffer = [
1405            0,                            // Pad1
1406            // 0b11000001 -> action = 0b11, mutable = 0b0, option type = 0b00001
1407            // (matching lower-order bits of Pad2).
1408            0xC1, 0,
1409            1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Pad10
1410        ];
1411        let mut context = ExtensionHeaderOptionContext::new();
1412        assert_eq!(
1413            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1414                .expect_err("Parsed successfully when we had Pad2 with upper bits set"),
1415            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1416                pointer: 1,
1417                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1418            }
1419        );
1420        assert_eq!(context.bytes_parsed, 1);
1421        assert_eq!(context.options_parsed, 1);
1422
1423        // Test parsing PadN but with upper bits set.
1424        #[rustfmt::skip]
1425        let buffer = [
1426            0,                               // Pad1
1427            1, 0,                            // Pad2
1428            // 0b11000001 -> action = 0b11, mutable = 0b0, option type = 0b00001
1429            // (matching lower-order bits of PadN).
1430            0xC1, 8, 0, 0, 0, 0, 0, 0, 0, 0,
1431        ];
1432        let mut context = ExtensionHeaderOptionContext::new();
1433        assert_eq!(
1434            Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1435                .expect_err("Parsed successfully when we had PadN with upper bits set"),
1436            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1437                pointer: 3,
1438                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1439            }
1440        );
1441        assert_eq!(context.bytes_parsed, 3);
1442        assert_eq!(context.options_parsed, 2);
1443    }
1444
1445    #[test]
1446    fn test_destination_options() {
1447        // Test parsing of Pad1 (marked as NOP)
1448        let buffer = [0; 10];
1449        let mut context = ExtensionHeaderOptionContext::new();
1450        let options =
1451            Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1452                .unwrap();
1453        assert_eq!(options.iter().count(), 0);
1454        assert_eq!(context.bytes_parsed, 10);
1455        assert_eq!(context.options_parsed, 10);
1456
1457        // Test parsing of Pad1 w/ PadN (treated as NOP)
1458        #[rustfmt::skip]
1459        let buffer = [
1460            0,                            // Pad1
1461            1, 0,                         // Pad2
1462            1, 8, 0, 0, 0, 0, 0, 0, 0, 0, // Pad10
1463        ];
1464        let mut context = ExtensionHeaderOptionContext::new();
1465        let options =
1466            Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1467                .unwrap();
1468        assert_eq!(options.iter().count(), 0);
1469        assert_eq!(context.bytes_parsed, 13);
1470        assert_eq!(context.options_parsed, 3);
1471
1472        // Test parsing with an unknown option type but its action is
1473        // skip/continue
1474        #[rustfmt::skip]
1475        let buffer = [
1476            0,                            // Pad1
1477            63, 1, 0,                     // Unrecognized Option Type but can skip/continue
1478            1,  6, 0, 0, 0, 0, 0, 0,      // Pad8
1479        ];
1480        let mut context = ExtensionHeaderOptionContext::new();
1481        let options =
1482            Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1483                .unwrap();
1484        let options: Vec<DestinationOption<'_>> = options.iter().collect();
1485        assert_eq!(options.len(), 1);
1486        assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1487        assert_eq!(context.bytes_parsed, 12);
1488        assert_eq!(context.options_parsed, 3);
1489    }
1490
1491    #[test]
1492    fn test_destination_options_err() {
1493        // Test parsing but missing last 2 bytes
1494        #[rustfmt::skip]
1495        let buffer = [
1496            0,                            // Pad1
1497            1, 0,                         // Pad2
1498            1, 8, 0, 0, 0, 0, 0, 0,       // Pad10 (but missing 2 bytes)
1499        ];
1500        let mut context = ExtensionHeaderOptionContext::new();
1501        assert_eq!(
1502            Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1503                .expect_err("Parsed successfully when we were short 2 bytes"),
1504            ExtensionHeaderOptionParsingError::BufferExhausted
1505        );
1506        assert_eq!(context.bytes_parsed, 3);
1507        assert_eq!(context.options_parsed, 2);
1508
1509        // Test parsing with unknown option type but action set to discard
1510        #[rustfmt::skip]
1511        let buffer = [
1512            1,   1, 0,                    // Pad3
1513            127, 0,                       // Unrecognized Option Type w/ action to discard
1514            1,   6, 0, 0, 0, 0, 0, 0,     // Pad8
1515        ];
1516        let mut context = ExtensionHeaderOptionContext::new();
1517        assert_eq!(
1518            Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1519                .expect_err("Parsed successfully when we had an unrecognized option type"),
1520            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1521                pointer: 3,
1522                action: ExtensionHeaderOptionAction::DiscardPacket,
1523            }
1524        );
1525        assert_eq!(context.bytes_parsed, 3);
1526        assert_eq!(context.options_parsed, 1);
1527
1528        // Test parsing with unknown option type but action set to discard and
1529        // send ICMP.
1530        #[rustfmt::skip]
1531        let buffer = [
1532            1,   1, 0,                    // Pad3
1533            191, 0,                       // Unrecognized Option Type w/ action to discard
1534                                          // & send icmp
1535            1,   6, 0, 0, 0, 0, 0, 0,     // Pad8
1536        ];
1537        let mut context = ExtensionHeaderOptionContext::new();
1538        assert_eq!(
1539            Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1540                .expect_err("Parsed successfully when we had an unrecognized option type"),
1541            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1542                pointer: 3,
1543                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmp,
1544            }
1545        );
1546        assert_eq!(context.bytes_parsed, 3);
1547        assert_eq!(context.options_parsed, 1);
1548
1549        // Test parsing with unknown option type but action set to discard and
1550        // send ICMP if not sending to a multicast address
1551        #[rustfmt::skip]
1552        let buffer = [
1553            1,   1, 0,                    // Pad3
1554            255, 0,                       // Unrecognized Option Type w/ action to discard
1555                                          // & send icmp if no multicast
1556            1,   6, 0, 0, 0, 0, 0, 0,     // Pad8
1557        ];
1558        let mut context = ExtensionHeaderOptionContext::new();
1559        assert_eq!(
1560            Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1561                .expect_err("Parsed successfully when we had an unrecognized option type"),
1562            ExtensionHeaderOptionParsingError::UnrecognizedOption {
1563                pointer: 3,
1564                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1565            }
1566        );
1567        assert_eq!(context.bytes_parsed, 3);
1568        assert_eq!(context.options_parsed, 1);
1569    }
1570
1571    #[test]
1572    fn test_hop_by_hop_options_ext_hdr() {
1573        // Test parsing of just a single Hop By Hop Extension Header.
1574        // The hop by hop options will only be pad options.
1575        let context =
1576            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1577        #[rustfmt::skip]
1578        let buffer = [
1579            IpProto::Tcp.into(),     // Next Header
1580            1,                       // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1581            1,  4, 0, 0, 0, 0,       // Pad6
1582            63, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action set to skip/continue
1583        ];
1584        let ext_hdrs =
1585            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1586                .unwrap();
1587        let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
1588        assert_eq!(ext_hdrs.len(), 1);
1589        assert_eq!(ext_hdrs[0].next_header, IpProto::Tcp.into());
1590        if let Ipv6ExtensionHeaderData::HopByHopOptions { options } = ext_hdrs[0].data() {
1591            // Everything should have been a NOP/ignore except for the unrecognized type
1592            let options: Vec<HopByHopOption<'_>> = options.iter().collect();
1593            assert_eq!(options.len(), 1);
1594            assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1595        } else {
1596            panic!("Should have matched HopByHopOptions {:?}", ext_hdrs[0].data());
1597        }
1598    }
1599
1600    #[test]
1601    fn test_hop_by_hop_options_ext_hdr_err() {
1602        // Test parsing of just a single Hop By Hop Extension Header with errors.
1603
1604        // Test with invalid Next Header
1605        let context =
1606            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1607        #[rustfmt::skip]
1608        let buffer = [
1609            255,                  // Next Header (Invalid)
1610            0,                    // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1611            1, 4, 0, 0, 0, 0,     // Pad6
1612        ];
1613        let error =
1614            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1615                .expect_err("Parsed successfully when the next header was invalid");
1616        if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
1617            error
1618        {
1619            assert_eq!(pointer, 0);
1620            assert!(!must_send_icmp);
1621        } else {
1622            panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
1623        }
1624
1625        // Test with invalid option type w/ action = discard.
1626        let context =
1627            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1628        #[rustfmt::skip]
1629        let buffer = [
1630            IpProto::Tcp.into(),      // Next Header
1631            1,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1632            1,   4, 0, 0, 0, 0,       // Pad6
1633            127, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action = discard
1634        ];
1635        let error =
1636            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1637                .expect_err("Parsed successfully with an unrecognized option type");
1638        if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1639            pointer,
1640            must_send_icmp,
1641            action,
1642        } = error
1643        {
1644            assert_eq!(pointer, 8);
1645            assert!(must_send_icmp);
1646            assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacket);
1647        } else {
1648            panic!("Should have matched with UnrecognizedOption: {:?}", error);
1649        }
1650
1651        // Test with invalid option type w/ action = discard & send icmp
1652        let context =
1653            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1654        #[rustfmt::skip]
1655        let buffer = [
1656            IpProto::Tcp.into(),      // Next Header
1657            1,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1658            1,   4, 0, 0, 0, 0,       // Pad6
1659            191, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action = discard & send icmp
1660        ];
1661        let error =
1662            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1663                .expect_err("Parsed successfully with an unrecognized option type");
1664        if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1665            pointer,
1666            must_send_icmp,
1667            action,
1668        } = error
1669        {
1670            assert_eq!(pointer, 8);
1671            assert!(must_send_icmp);
1672            assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmp);
1673        } else {
1674            panic!("Should have matched with UnrecognizedOption: {:?}", error);
1675        }
1676
1677        // Test with invalid option type w/ action = discard & send icmp if not multicast
1678        let context =
1679            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1680        #[rustfmt::skip]
1681        let buffer = [
1682            IpProto::Tcp.into(),      // Next Header
1683            1,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1684            1,   4, 0, 0, 0, 0,       // Pad6
1685            255, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action = discard & send icmp
1686                                      // if destination address is not a multicast
1687        ];
1688        let error =
1689            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1690                .expect_err("Parsed successfully with an unrecognized option type");
1691        if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1692            pointer,
1693            must_send_icmp,
1694            action,
1695        } = error
1696        {
1697            assert_eq!(pointer, 8);
1698            assert!(must_send_icmp);
1699            assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast);
1700        } else {
1701            panic!("Should have matched with UnrecognizedOption: {:?}", error);
1702        }
1703
1704        // Test with valid option type and invalid data w/ action = skip & continue
1705        let context =
1706            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1707        #[rustfmt::skip]
1708            let buffer = [
1709            IpProto::Tcp.into(),      // Next Header
1710            0,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1711            5,   3, 0, 0, 0,          // RouterAlert, but with a wrong data length.
1712            0,                        // Pad1
1713        ];
1714        let error =
1715            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1716                .expect_err(
1717                    "Should fail to parse the header because one of the option is malformed",
1718                );
1719        if let Ipv6ExtensionHeaderParsingError::ErroneousHeaderField { pointer, .. } = error {
1720            assert_eq!(pointer, 3);
1721        } else {
1722            panic!("Should have matched with UnrecognizedOption: {:?}", error);
1723        }
1724    }
1725
1726    #[test]
1727    fn test_routing_ext_hdr() {
1728        // Test parsing of just a single Routing Extension Header.
1729        let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1730        #[rustfmt::skip]
1731        let buffer = [
1732            IpProto::Tcp.into(), // Next Header
1733            4,                   // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1734            0,                   // Routing Type
1735            0,                   // Segments Left (0 so no error)
1736            0, 0, 0, 0,          // Reserved
1737            // Addresses for Routing Header w/ Type 0
1738            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
1739            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1740
1741        ];
1742        let ext_hdrs =
1743            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1744                .unwrap();
1745        let results: Vec<_> = ext_hdrs.iter().collect();
1746        assert_eq!(results.len(), 1);
1747        assert_eq!(results[0].next_header, IpProto::Tcp.into());
1748        if let Ipv6ExtensionHeaderData::Routing { routing_data } = results[0].data() {
1749            assert_eq!(routing_data.routing_type(), Err(RoutingTypeParseError::UnsupportedType(0)));
1750            assert_eq!(routing_data.segments_left(), 0);
1751        } else {
1752            panic!("Should have matched with RoutingExtensionHeader");
1753        }
1754    }
1755
1756    #[test]
1757    fn test_routing_ext_hdr_err() {
1758        // Test parsing of just a single Routing Extension Header with errors.
1759
1760        // Explicitly test to make sure we do not support routing type 0 as per RFC 5095
1761        let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1762        #[rustfmt::skip]
1763        let buffer = [
1764            IpProto::Tcp.into(), // Next Header
1765            4,                   // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1766            0,                   // Routing Type (0 which we should not support)
1767            1,                   // Segments Left
1768            0, 0, 0, 0,          // Reserved
1769            // Addresses for Routing Header w/ Type 0
1770            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
1771            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1772        ];
1773        let error =
1774            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1775                .expect_err("Parsed successfully when the routing type was set to 0");
1776        if let Ipv6ExtensionHeaderParsingError::ErroneousHeaderField { pointer, must_send_icmp } =
1777            error
1778        {
1779            assert_eq!(pointer, 2);
1780            assert!(must_send_icmp);
1781        } else {
1782            panic!("Should have matched with ErroneousHeaderField: {:?}", error);
1783        }
1784
1785        // Test Invalid Next Header
1786        let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1787        #[rustfmt::skip]
1788        let buffer = [
1789            255,                 // Next Header (Invalid)
1790            4,                   // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1791            0,                   // Routing Type
1792            1,                   // Segments Left
1793            0, 0, 0, 0,          // Reserved
1794            // Addresses for Routing Header w/ Type 0
1795            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
1796            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1797
1798        ];
1799        let error =
1800            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1801                .expect_err("Parsed successfully when the next header was invalid");
1802        if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
1803            error
1804        {
1805            assert_eq!(pointer, 0);
1806            assert!(!must_send_icmp);
1807        } else {
1808            panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
1809        }
1810
1811        // Test Unrecognized Routing Type
1812        let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1813        #[rustfmt::skip]
1814        let buffer = [
1815            IpProto::Tcp.into(), // Next Header
1816            4,                   // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1817            255,                 // Routing Type (Invalid)
1818            1,                   // Segments Left
1819            0, 0, 0, 0,          // Reserved
1820            // Addresses for Routing Header w/ Type 0
1821            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
1822            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1823
1824        ];
1825        let error =
1826            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1827                .expect_err("Parsed successfully with an unrecognized routing type");
1828        if let Ipv6ExtensionHeaderParsingError::ErroneousHeaderField { pointer, must_send_icmp } =
1829            error
1830        {
1831            // Should point to the location of the routing type.
1832            assert_eq!(pointer, 2);
1833            assert!(must_send_icmp);
1834        } else {
1835            panic!("Should have matched with ErroneousHeaderField: {:?}", error);
1836        }
1837    }
1838
1839    #[test]
1840    fn test_fragment_ext_hdr() {
1841        // Test parsing of just a single Fragment Extension Header.
1842        let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Fragment.into());
1843        let frag_offset_res_m_flag: u16 = (5063 << 3) | 1;
1844        let identification: u32 = 3266246449;
1845        #[rustfmt::skip]
1846        let buffer = [
1847            IpProto::Tcp.into(),                   // Next Header
1848            0,                                     // Reserved
1849            (frag_offset_res_m_flag >> 8) as u8,   // Fragment Offset MSB
1850            (frag_offset_res_m_flag & 0xFF) as u8, // Fragment Offset LS5bits w/ Res w/ M Flag
1851            // Identification
1852            (identification >> 24) as u8,
1853            ((identification >> 16) & 0xFF) as u8,
1854            ((identification >> 8) & 0xFF) as u8,
1855            (identification & 0xFF) as u8,
1856        ];
1857        let ext_hdrs =
1858            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1859                .unwrap();
1860        let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
1861        assert_eq!(ext_hdrs.len(), 1);
1862        assert_eq!(ext_hdrs[0].next_header, IpProto::Tcp.into());
1863
1864        if let Ipv6ExtensionHeaderData::Fragment { fragment_data } = ext_hdrs[0].data() {
1865            assert_eq!(fragment_data.fragment_offset().into_raw(), 5063);
1866            assert_eq!(fragment_data.m_flag(), true);
1867            assert_eq!(fragment_data.identification(), 3266246449);
1868        } else {
1869            panic!("Should have matched Fragment: {:?}", ext_hdrs[0].data());
1870        }
1871    }
1872
1873    #[test]
1874    fn test_fragment_ext_hdr_err() {
1875        // Test parsing of just a single Fragment Extension Header with errors.
1876
1877        // Test invalid Next Header
1878        let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Fragment.into());
1879        let frag_offset_res_m_flag: u16 = (5063 << 3) | 1;
1880        let identification: u32 = 3266246449;
1881        #[rustfmt::skip]
1882        let buffer = [
1883            255,                                   // Next Header (Invalid)
1884            0,                                     // Reserved
1885            (frag_offset_res_m_flag >> 8) as u8,   // Fragment Offset MSB
1886            (frag_offset_res_m_flag & 0xFF) as u8, // Fragment Offset LS5bits w/ Res w/ M Flag
1887            // Identification
1888            (identification >> 24) as u8,
1889            ((identification >> 16) & 0xFF) as u8,
1890            ((identification >> 8) & 0xFF) as u8,
1891            (identification & 0xFF) as u8,
1892        ];
1893        let error =
1894            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1895                .expect_err("Parsed successfully when the next header was invalid");
1896        if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
1897            error
1898        {
1899            assert_eq!(pointer, 0);
1900            assert!(!must_send_icmp);
1901        } else {
1902            panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
1903        }
1904    }
1905
1906    #[test]
1907    fn test_no_next_header_ext_hdr() {
1908        // Test parsing of just a single NoNextHeader Extension Header.
1909        let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6Proto::NoNextHeader.into());
1910        #[rustfmt::skip]
1911        let buffer = [0, 0, 0, 0,];
1912        let ext_hdrs =
1913            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1914                .unwrap();
1915        assert_eq!(ext_hdrs.iter().count(), 0);
1916    }
1917
1918    #[test]
1919    fn test_destination_options_ext_hdr() {
1920        // Test parsing of just a single Destination options Extension Header.
1921        // The destination options will only be pad options.
1922        let context =
1923            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1924        #[rustfmt::skip]
1925        let buffer = [
1926            IpProto::Tcp.into(),     // Next Header
1927            1,                       // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1928            1, 4, 0, 0, 0, 0,        // Pad6
1929            63, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action set to skip/continue
1930        ];
1931        let ext_hdrs =
1932            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1933                .unwrap();
1934        let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
1935        assert_eq!(ext_hdrs.len(), 1);
1936        assert_eq!(ext_hdrs[0].next_header, IpProto::Tcp.into());
1937        if let Ipv6ExtensionHeaderData::DestinationOptions { options } = ext_hdrs[0].data() {
1938            // Everything should have been a NOP/ignore except for the unrecognized type
1939            let options: Vec<DestinationOption<'_>> = options.iter().collect();
1940            assert_eq!(options.len(), 1);
1941            assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1942        } else {
1943            panic!("Should have matched DestinationOptions: {:?}", ext_hdrs[0].data());
1944        }
1945    }
1946
1947    #[test]
1948    fn test_destination_options_ext_hdr_err() {
1949        // Test parsing of just a single Destination Options Extension Header with errors.
1950        let context =
1951            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1952
1953        // Test with invalid Next Header
1954        #[rustfmt::skip]
1955        let buffer = [
1956            255,                  // Next Header (Invalid)
1957            0,                    // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1958            1, 4, 0, 0, 0, 0,     // Pad6
1959        ];
1960        let error =
1961            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1962                .expect_err("Parsed successfully when the next header was invalid");
1963        if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
1964            error
1965        {
1966            assert_eq!(pointer, 0);
1967            assert!(!must_send_icmp);
1968        } else {
1969            panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
1970        }
1971
1972        // Test with invalid option type w/ action = discard.
1973        let context =
1974            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1975        #[rustfmt::skip]
1976        let buffer = [
1977            IpProto::Tcp.into(),      // Next Header
1978            1,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
1979            1,   4, 0, 0, 0, 0,       // Pad6
1980            127, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action = discard
1981        ];
1982        let error =
1983            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1984                .expect_err("Parsed successfully with an unrecognized option type");
1985        if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1986            pointer,
1987            must_send_icmp,
1988            action,
1989        } = error
1990        {
1991            assert_eq!(pointer, 8);
1992            assert!(must_send_icmp);
1993            assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacket);
1994        } else {
1995            panic!("Should have matched with UnrecognizedOption: {:?}", error);
1996        }
1997
1998        // Test with invalid option type w/ action = discard & send icmp
1999        let context =
2000            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
2001        #[rustfmt::skip]
2002        let buffer = [
2003            IpProto::Tcp.into(),      // Next Header
2004            1,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2005            1,   4, 0, 0, 0, 0,       // Pad6
2006            191, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action = discard & send icmp
2007        ];
2008        let error =
2009            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2010                .expect_err("Parsed successfully with an unrecognized option type");
2011        if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
2012            pointer,
2013            must_send_icmp,
2014            action,
2015        } = error
2016        {
2017            assert_eq!(pointer, 8);
2018            assert!(must_send_icmp);
2019            assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmp);
2020        } else {
2021            panic!("Should have matched with UnrecognizedOption: {:?}", error);
2022        }
2023
2024        // Test with invalid option type w/ action = discard & send icmp if not multicast
2025        let context =
2026            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
2027        #[rustfmt::skip]
2028        let buffer = [
2029            IpProto::Tcp.into(),      // Next Header
2030            1,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2031            1,   4, 0, 0, 0, 0,       // Pad6
2032            255, 6, 0, 0, 0, 0, 0, 0, // Unrecognized option type w/ action = discard & send icmp
2033                                      // if destination address is not a multicast
2034        ];
2035        let error =
2036            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2037                .expect_err("Parsed successfully with an unrecognized option type");
2038        if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
2039            pointer,
2040            must_send_icmp,
2041            action,
2042        } = error
2043        {
2044            assert_eq!(pointer, 8);
2045            assert!(must_send_icmp);
2046            assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast);
2047        } else {
2048            panic!("Should have matched with UnrecognizedOption: {:?}", error);
2049        }
2050    }
2051
2052    #[test]
2053    fn test_multiple_ext_hdrs() {
2054        // Test parsing of multiple extension headers.
2055        let context =
2056            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2057        #[rustfmt::skip]
2058        let buffer = [
2059            // HopByHop Options Extension Header
2060            Ipv6ExtHdrType::Routing.into(), // Next Header
2061            0,                       // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2062            0,                       // Pad1
2063            1, 0,                    // Pad2
2064            1, 1, 0,                 // Pad3
2065
2066            // Routing Extension Header
2067            Ipv6ExtHdrType::DestinationOptions.into(), // Next Header
2068            4,                                  // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2069            0,                                  // Routing Type
2070            0,                                  // Segments Left
2071            0, 0, 0, 0,                         // Reserved
2072            // Addresses for Routing Header w/ Type 0
2073            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
2074            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
2075
2076            // Destination Options Extension Header
2077            IpProto::Tcp.into(),     // Next Header
2078            1,                       // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2079            0,                       // Pad1
2080            1,  0,                   // Pad2
2081            1,  1, 0,                // Pad3
2082            63, 6, 0, 0, 0, 0, 0, 0, // Unrecognized type w/ action = discard
2083        ];
2084        let ext_hdrs =
2085            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2086                .unwrap();
2087
2088        let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
2089        assert_eq!(ext_hdrs.len(), 3);
2090
2091        // Check first extension header (hop-by-hop options)
2092        assert_eq!(ext_hdrs[0].next_header, Ipv6ExtHdrType::Routing.into());
2093        if let Ipv6ExtensionHeaderData::HopByHopOptions { options } = ext_hdrs[0].data() {
2094            // Everything should have been a NOP/ignore
2095            assert_eq!(options.iter().count(), 0);
2096        } else {
2097            panic!("Should have matched HopByHopOptions: {:?}", ext_hdrs[0].data());
2098        }
2099
2100        // Check second extension header (routing)
2101        assert_eq!(ext_hdrs[1].next_header, Ipv6ExtHdrType::DestinationOptions.into());
2102        if let Ipv6ExtensionHeaderData::Routing { routing_data } = ext_hdrs[1].data() {
2103            assert_eq!(routing_data.routing_type(), Err(RoutingTypeParseError::UnsupportedType(0)));
2104            assert_eq!(routing_data.segments_left(), 0);
2105        } else {
2106            panic!("Should have matched RoutingExtensionHeader: {:?}", ext_hdrs[1].data());
2107        }
2108
2109        // Check the third extension header (destination options)
2110        assert_eq!(ext_hdrs[2].next_header, IpProto::Tcp.into());
2111        if let Ipv6ExtensionHeaderData::DestinationOptions { options } = ext_hdrs[2].data() {
2112            // Everything should have been a NOP/ignore except for the unrecognized type
2113            let options: Vec<DestinationOption<'_>> = options.iter().collect();
2114            assert_eq!(options.len(), 1);
2115            assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
2116        } else {
2117            panic!("Should have matched DestinationOptions: {:?}", ext_hdrs[2].data());
2118        }
2119    }
2120
2121    #[test]
2122    fn test_multiple_ext_hdrs_errs() {
2123        // Test parsing of multiple extension headers with erros.
2124
2125        // Test Invalid next header in the second extension header.
2126        let context =
2127            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2128        #[rustfmt::skip]
2129        let buffer = [
2130            // HopByHop Options Extension Header
2131            Ipv6ExtHdrType::Routing.into(), // Next Header
2132            0,                       // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2133            0,                       // Pad1
2134            1, 0,                    // Pad2
2135            1, 1, 0,                 // Pad3
2136
2137            // Routing Extension Header
2138            255,                                // Next Header (Invalid)
2139            4,                                  // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2140            0,                                  // Routing Type
2141            1,                                  // Segments Left
2142            0, 0, 0, 0,                         // Reserved
2143            // Addresses for Routing Header w/ Type 0
2144            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
2145            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
2146
2147            // Destination Options Extension Header
2148            IpProto::Tcp.into(),    // Next Header
2149            1,                      // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2150            0,                      // Pad1
2151            1, 0,                   // Pad2
2152            1, 1, 0,                // Pad3
2153            1, 6, 0, 0, 0, 0, 0, 0, // Pad8
2154        ];
2155        let error =
2156            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2157                .expect_err("Parsed successfully when the next header was invalid");
2158        if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
2159            error
2160        {
2161            assert_eq!(pointer, 8);
2162            assert!(!must_send_icmp);
2163        } else {
2164            panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
2165        }
2166
2167        // Test HopByHop extension header not being the very first extension header
2168        let context =
2169            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2170        #[rustfmt::skip]
2171        let buffer = [
2172            // Routing Extension Header
2173            Ipv6ExtHdrType::HopByHopOptions.into(),    // Next Header (Valid but HopByHop restricted to first extension header)
2174            4,                                  // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2175            0,                                  // Routing Type
2176            1,                                  // Segments Left
2177            0, 0, 0, 0,                         // Reserved
2178            // Addresses for Routing Header w/ Type 0
2179            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
2180            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
2181
2182            // HopByHop Options Extension Header
2183            Ipv6ExtHdrType::DestinationOptions.into(), // Next Header
2184            0,                                  // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2185            0,                                  // Pad1
2186            1, 0,                               // Pad2
2187            1, 1, 0,                            // Pad3
2188
2189            // Destination Options Extension Header
2190            IpProto::Tcp.into(),    // Next Header
2191            1,                      // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2192            0,                      // Pad1
2193            1, 0,                   // Pad2
2194            1, 1, 0,                // Pad3
2195            1, 6, 0, 0, 0, 0, 0, 0, // Pad8
2196        ];
2197        let error =
2198            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2199                .expect_err("Parsed successfully when a hop by hop extension header was not the fist extension header");
2200        if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
2201            error
2202        {
2203            assert_eq!(pointer, 0);
2204            assert!(!must_send_icmp);
2205        } else {
2206            panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
2207        }
2208
2209        // Test parsing of destination options with an unrecognized option type w/ action
2210        // set to discard and send icmp
2211        let context =
2212            Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2213        #[rustfmt::skip]
2214        let buffer = [
2215            // HopByHop Options Extension Header
2216            Ipv6ExtHdrType::DestinationOptions.into(), // Next Header
2217            0,                       // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2218            0,                       // Pad1
2219            1, 0,                    // Pad2
2220            1, 1, 0,                 // Pad3
2221
2222            // Destination Options Extension Header
2223            IpProto::Tcp.into(),      // Next Header
2224            1,                        // Hdr Ext Len (In 8-octet units, not including first 8 octets)
2225            0,                        // Pad1
2226            1,   0,                   // Pad2
2227            1,   1, 0,                // Pad3
2228            191, 6, 0, 0, 0, 0, 0, 0, // Unrecognized type w/ action = discard
2229        ];
2230        let error =
2231            Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2232                .expect_err("Parsed successfully with an unrecognized destination option type");
2233        if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
2234            pointer,
2235            must_send_icmp,
2236            action,
2237        } = error
2238        {
2239            assert_eq!(pointer, 16);
2240            assert!(must_send_icmp);
2241            assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmp);
2242        } else {
2243            panic!("Should have matched with UnrecognizedOption: {:?}", error);
2244        }
2245    }
2246
2247    #[test]
2248    fn test_serialize_hbh_router_alert() {
2249        let mut buffer = [0u8; 4];
2250        let option = HopByHopOption {
2251            action: ExtensionHeaderOptionAction::SkipAndContinue,
2252            mutable: false,
2253            data: HopByHopOptionData::RouterAlert { data: 0 },
2254        };
2255        <HopByHopOption<'_> as RecordBuilder>::serialize_into(&option, &mut buffer);
2256        assert_eq!(&buffer[..], &[5, 2, 0, 0]);
2257    }
2258
2259    #[test]
2260    fn test_parse_hbh_router_alert() {
2261        // Test RouterAlert with correct data length.
2262        let context = ExtensionHeaderOptionContext::new();
2263        let buffer = [5, 2, 0, 0];
2264
2265        let options =
2266            Records::<_, HopByHopOptionsImpl>::parse_with_context(&buffer[..], context).unwrap();
2267        let rtralrt = options.iter().next().unwrap();
2268        assert!(!rtralrt.mutable);
2269        assert_eq!(rtralrt.action, ExtensionHeaderOptionAction::SkipAndContinue);
2270        assert_eq!(rtralrt.data, HopByHopOptionData::RouterAlert { data: 0 });
2271
2272        // Test that the three higher-order bits are considered part of the
2273        // Option Type when parsing options.
2274        let context = ExtensionHeaderOptionContext::new();
2275        // 0b11000101 -> action = 0b11, mutable = 0b0, option type = 0b00101
2276        // (matching lower-order bits of RouterAlert).
2277        let buffer = [0xC5, 2, 0, 0];
2278
2279        let error = Records::<_, HopByHopOptionsImpl>::parse_with_context(&buffer[..], context)
2280            .expect_err("UnrecognizedOption should have been returned");
2281        assert_eq!(
2282            error,
2283            ExtensionHeaderOptionParsingError::UnrecognizedOption {
2284                pointer: 0,
2285                action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast
2286            }
2287        );
2288
2289        // Test RouterAlert with wrong data length.
2290        let result = <HopByHopOptionDataImpl as ExtensionHeaderOptionDataImpl>::parse_option(
2291            5,
2292            &buffer[1..],
2293            &mut (),
2294            false,
2295        );
2296        assert_eq!(result, ExtensionHeaderOptionDataParseResult::ErrorAt(1));
2297
2298        let context = ExtensionHeaderOptionContext::new();
2299        let buffer = [5, 3, 0, 0, 0];
2300
2301        let error = Records::<_, HopByHopOptionsImpl>::parse_with_context(&buffer[..], context)
2302            .expect_err(
2303                "Parsing a malformed option with recognized kind but with wrong data should fail",
2304            );
2305        assert_eq!(error, ExtensionHeaderOptionParsingError::ErroneousOptionField { pointer: 1 });
2306    }
2307
2308    // Construct a bunch of `HopByHopOption`s according to lengths:
2309    // if `length` is
2310    //   - `None`: RouterAlert is generated.
2311    //   - `Some(l)`: the Unrecognized option with length `l - 2` is constructed.
2312    //     It is `l - 2` so that the whole record has size l.
2313    // This function is used so that the alignment of RouterAlert can be tested.
2314    fn trivial_hbh_options(lengths: &[Option<usize>]) -> Vec<HopByHopOption<'static>> {
2315        static ZEROES: [u8; 16] = [0u8; 16];
2316        lengths
2317            .iter()
2318            .map(|l| HopByHopOption {
2319                mutable: false,
2320                action: ExtensionHeaderOptionAction::SkipAndContinue,
2321                data: match l {
2322                    Some(l) => HopByHopOptionData::Unrecognized {
2323                        kind: 1,
2324                        len: (*l - 2) as u8,
2325                        data: &ZEROES[0..*l - 2],
2326                    },
2327                    None => HopByHopOptionData::RouterAlert { data: 0 },
2328                },
2329            })
2330            .collect()
2331    }
2332
2333    #[test]
2334    fn test_aligned_records_serializer() {
2335        // Test whether we can serialize our RouterAlert at 2-byte boundary
2336        for i in 2..12 {
2337            let options = trivial_hbh_options(&[Some(i), None]);
2338            let ser = AlignedRecordSequenceBuilder::<
2339                ExtensionHeaderOption<HopByHopOptionData<'_>>,
2340                _,
2341            >::new(2, options.iter());
2342            let mut buf = [0u8; 16];
2343            ser.serialize_into(&mut buf[0..16]);
2344            let base = (i + 1) & !1;
2345            // we want to make sure that our RouterAlert is aligned at 2-byte boundary.
2346            assert_eq!(&buf[base..base + 4], &[5, 2, 0, 0]);
2347        }
2348    }
2349}