1use 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
28use crate::ipv6::{IPV6_FIXED_HDR_LEN, NEXT_HEADER_OFFSET};
29
30pub const IPV6_FRAGMENT_EXT_HDR_LEN: usize = 8;
32
33#[allow(missing_docs)]
35#[derive(Debug)]
36pub enum Ipv6ExtensionHeader<'a> {
37 HopByHopOptions { options: HopByHopOptionsData<'a> },
38 Routing { routing_data: RoutingData<'a> },
39 Fragment { fragment_data: FragmentData },
40 DestinationOptions { options: DestinationOptionsData<'a> },
41}
42
43#[allow(missing_docs)]
49#[derive(Debug, PartialEq, Eq)]
50pub(super) enum Ipv6ExtensionHeaderParsingError {
51 ErroneousHeaderField { pointer: u32, must_send_icmp: bool },
57 UnrecognizedNextHeader { pointer: u32, must_send_icmp: bool },
58 UnrecognizedOption { pointer: u32, must_send_icmp: bool, action: ExtensionHeaderOptionAction },
59 BufferExhausted,
60 MalformedData,
61}
62
63impl From<Never> for Ipv6ExtensionHeaderParsingError {
64 fn from(err: Never) -> Ipv6ExtensionHeaderParsingError {
65 match err {}
66 }
67}
68
69#[derive(Debug, Clone)]
71pub(super) struct Ipv6ExtensionHeaderParsingContext {
72 pub(super) next_header: u8,
76
77 iter: bool,
79
80 headers_parsed: usize,
82
83 pub(super) position: usize,
85
86 pub(super) next_header_offset: usize,
88
89 pub(super) saw_fragment: bool,
91}
92
93impl Ipv6ExtensionHeaderParsingContext {
94 pub(super) fn new(next_header: u8) -> Ipv6ExtensionHeaderParsingContext {
97 Ipv6ExtensionHeaderParsingContext {
98 iter: false,
99 headers_parsed: 0,
100 next_header,
101 next_header_offset: NEXT_HEADER_OFFSET.into(),
102 position: IPV6_FIXED_HDR_LEN,
103 saw_fragment: false,
104 }
105 }
106}
107
108impl RecordsContext for Ipv6ExtensionHeaderParsingContext {
109 fn clone_for_iter(&self) -> Self {
110 let mut ret = self.clone();
111 ret.iter = true;
112 ret
113 }
114}
115
116#[derive(Debug)]
118pub(super) struct Ipv6ExtensionHeaderImpl;
119
120impl Ipv6ExtensionHeaderImpl {
121 fn parse_next_hdr_and_len<'a, BV: BufferView<&'a [u8]>>(
127 data: &mut BV,
128 context: &mut Ipv6ExtensionHeaderParsingContext,
129 ) -> Result<u8, Ipv6ExtensionHeaderParsingError> {
130 let next_header =
131 data.take_byte_front().ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
132 let hdr_ext_len =
133 data.take_byte_front().ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
134
135 context.next_header = next_header;
136 context.next_header_offset = context.position;
137 context.position += 2;
138
139 Ok(hdr_ext_len)
140 }
141
142 fn parse_hop_by_hop_options<'a, BV: BufferView<&'a [u8]>>(
147 data: &mut BV,
148 context: &mut Ipv6ExtensionHeaderParsingContext,
149 ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
150 let hdr_ext_len = Self::parse_next_hdr_and_len(data, context)?;
151
152 let expected_len = (hdr_ext_len as usize) * 8 + 6;
158
159 let options = data
160 .take_front(expected_len)
161 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
162
163 let options_context = ExtensionHeaderOptionContext::new(context.position);
164 let options = Records::parse_with_context(options, options_context)
165 .map_err(ext_hdr_opt_err_to_ext_hdr_err)?;
166 let options = HopByHopOptionsData::new(options);
167
168 context.position += expected_len;
170 context.headers_parsed += 1;
171
172 Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader::HopByHopOptions { options }))
173 }
174
175 fn parse_routing<'a, BV: BufferView<&'a [u8]>>(
177 data: &mut BV,
178 context: &mut Ipv6ExtensionHeaderParsingContext,
179 ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
180 let hdr_ext_len = Self::parse_next_hdr_and_len(data, context)?;
181
182 let expected_len = (hdr_ext_len as usize) * 8 + 6;
188 let bytes = data
189 .take_front(expected_len)
190 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
191 let routing_data = RoutingData { bytes };
192
193 let segments_left = routing_data.segments_left();
194
195 if segments_left == 0 {
209 context.position += expected_len;
211 context.headers_parsed += 1;
212
213 Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader::Routing { routing_data }))
214 } else {
215 Err(Ipv6ExtensionHeaderParsingError::ErroneousHeaderField {
219 pointer: u32::try_from(context.position).unwrap(),
220 must_send_icmp: true,
221 })
222 }
223 }
224
225 fn parse_fragment<'a, BV: BufferView<&'a [u8]>>(
227 data: &mut BV,
228 context: &mut Ipv6ExtensionHeaderParsingContext,
229 ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
230 if data.len() < 8 {
236 return Err(Ipv6ExtensionHeaderParsingError::BufferExhausted);
237 }
238
239 let _ = Self::parse_next_hdr_and_len(data, context)?;
243
244 context.position += 6;
246 context.headers_parsed += 1;
247 context.saw_fragment = true;
248
249 Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader::Fragment {
250 fragment_data: FragmentData { bytes: data.take_front(6).unwrap().try_into().unwrap() },
256 }))
257 }
258
259 fn parse_destination_options<'a, BV: BufferView<&'a [u8]>>(
261 data: &mut BV,
262 context: &mut Ipv6ExtensionHeaderParsingContext,
263 ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
264 let hdr_ext_len = Self::parse_next_hdr_and_len(data, context)?;
265
266 let expected_len = (hdr_ext_len as usize) * 8 + 6;
270
271 let options = data
272 .take_front(expected_len)
273 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
274
275 let options_context = ExtensionHeaderOptionContext::new(context.position);
276 let options = Records::parse_with_context(options, options_context)
277 .map_err(ext_hdr_opt_err_to_ext_hdr_err)?;
278 let options = DestinationOptionsData::new(options);
279
280 context.position += expected_len;
282 context.headers_parsed += 1;
283
284 Ok(ParsedRecord::Parsed(Ipv6ExtensionHeader::DestinationOptions { options }))
285 }
286}
287
288impl RecordsImplLayout for Ipv6ExtensionHeaderImpl {
289 type Context = Ipv6ExtensionHeaderParsingContext;
290 type Error = Ipv6ExtensionHeaderParsingError;
291}
292
293impl RecordsImpl for Ipv6ExtensionHeaderImpl {
294 type Record<'a> = Ipv6ExtensionHeader<'a>;
295
296 fn parse_with_context<'a, BV: BufferView<&'a [u8]>>(
297 data: &mut BV,
298 context: &mut Self::Context,
299 ) -> RecordParseResult<Self::Record<'a>, Self::Error> {
300 let expected_hdr = context.next_header;
301 let next_header_type = Ipv6ExtHdrType::from(expected_hdr);
302
303 check_hop_by_hop_position(next_header_type, context)?;
304
305 if context.saw_fragment {
309 return Ok(ParsedRecord::Done);
310 }
311
312 match next_header_type {
313 Ipv6ExtHdrType::HopByHopOptions => Self::parse_hop_by_hop_options(data, context),
314 Ipv6ExtHdrType::Routing => Self::parse_routing(data, context),
315 Ipv6ExtHdrType::Fragment => Self::parse_fragment(data, context),
316 Ipv6ExtHdrType::DestinationOptions => Self::parse_destination_options(data, context),
317 Ipv6ExtHdrType::EncapsulatingSecurityPayload | Ipv6ExtHdrType::Authentication => {
318 Err(Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
330 pointer: context.next_header_offset as u32,
331 must_send_icmp: false,
334 })
335 }
336 Ipv6ExtHdrType::Other(_) if is_valid_next_header_upper_layer(expected_hdr) => {
337 Ok(ParsedRecord::Done)
340 }
341 Ipv6ExtHdrType::Other(_) => {
342 Err(Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
345 pointer: context.next_header_offset as u32,
346 must_send_icmp: false,
347 })
348 }
349 }
350 }
351}
352
353impl<'a> RecordsRawImpl<'a> for Ipv6ExtensionHeaderImpl {
354 fn parse_raw_with_context<BV: BufferView<&'a [u8]>>(
355 data: &mut BV,
356 context: &mut Self::Context,
357 ) -> Result<bool, Self::Error> {
358 let next_header_type = Ipv6ExtHdrType::from(context.next_header);
359
360 check_hop_by_hop_position(next_header_type, context)?;
361
362 if context.saw_fragment {
366 return Ok(false);
367 }
368
369 let (next, skip) = match next_header_type {
370 Ipv6ExtHdrType::HopByHopOptions => {
371 data.take_front(2)
374 .map(|x| (x[0], (x[1] as usize) * 8 + 6))
375 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?
376 }
377
378 Ipv6ExtHdrType::Routing | Ipv6ExtHdrType::DestinationOptions => {
379 data.take_front(2)
382 .map(|x| (x[0], (x[1] as usize) * 8 + 6))
383 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?
384 }
385 Ipv6ExtHdrType::Fragment => {
386 context.saw_fragment = true;
387 (
389 data.take_byte_front()
390 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?,
391 7,
392 )
393 }
394 Ipv6ExtHdrType::EncapsulatingSecurityPayload => {
395 return debug_err!(
399 Err(Ipv6ExtensionHeaderParsingError::MalformedData),
400 "ESP extension header not supported"
401 );
402 }
403 Ipv6ExtHdrType::Authentication => {
404 data.take_front(2)
408 .map(|x| (x[0], (x[1] as usize + 2) * 4 - 2))
409 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?
410 }
411 Ipv6ExtHdrType::Other(next_header) if is_valid_next_header_upper_layer(next_header) => {
412 return Ok(false);
413 }
414
415 Ipv6ExtHdrType::Other(_) => {
416 return Err(Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
417 pointer: context.next_header_offset as u32,
418 must_send_icmp: false,
419 });
420 }
421 };
422 let _: &[u8] =
423 data.take_front(skip).ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?;
424 context.next_header = next;
425 context.next_header_offset = context.position;
426 context.position += skip;
427 context.headers_parsed += 1;
428 Ok(true)
429 }
430}
431
432#[derive(Debug)]
438pub struct HopByHopOptionsData<'a> {
439 options: Records<&'a [u8], HopByHopOptionsImpl>,
440}
441
442impl<'a> HopByHopOptionsData<'a> {
443 fn new(options: Records<&'a [u8], HopByHopOptionsImpl>) -> HopByHopOptionsData<'a> {
445 HopByHopOptionsData { options }
446 }
447
448 pub fn iter(&'a self) -> impl Iterator<Item = HopByHopOption<'a>> {
451 self.options.iter()
452 }
453}
454
455pub type HopByHopOption<'a> = ExtensionHeaderOption<HopByHopOptionData<'a>>;
457
458pub(super) type HopByHopOptionsImpl = ExtensionHeaderOptionImpl<HopByHopOptionDataImpl>;
461
462const HBH_OPTION_KIND_RTRALRT: u8 = 5;
466
467const HBH_OPTION_RTRALRT_LEN: usize = 2;
471
472#[allow(missing_docs)]
474#[derive(Debug, PartialEq, Eq, Clone)]
475pub enum HopByHopOptionData<'a> {
476 Unrecognized { kind: u8, len: u8, data: &'a [u8] },
477 RouterAlert { data: u16 },
478}
479
480#[derive(Debug)]
482pub(super) struct HopByHopOptionDataImpl;
483
484impl ExtensionHeaderOptionDataImplLayout for HopByHopOptionDataImpl {
485 type Context = ();
486}
487
488impl ExtensionHeaderOptionDataImpl for HopByHopOptionDataImpl {
489 type OptionData<'a> = HopByHopOptionData<'a>;
490
491 fn parse_option<'a>(
492 kind: u8,
493 data: &'a [u8],
494 _context: &mut Self::Context,
495 allow_unrecognized: bool,
496 ) -> ExtensionHeaderOptionDataParseResult<Self::OptionData<'a>> {
497 match kind {
498 HBH_OPTION_KIND_RTRALRT => {
499 if data.len() == HBH_OPTION_RTRALRT_LEN {
500 ExtensionHeaderOptionDataParseResult::Ok(HopByHopOptionData::RouterAlert {
501 data: NetworkEndian::read_u16(data),
502 })
503 } else {
504 ExtensionHeaderOptionDataParseResult::ErrorAt(1)
507 }
508 }
509 _ => {
510 if allow_unrecognized {
511 ExtensionHeaderOptionDataParseResult::Ok(HopByHopOptionData::Unrecognized {
512 kind,
513 len: data.len() as u8,
514 data,
515 })
516 } else {
517 ExtensionHeaderOptionDataParseResult::UnrecognizedKind
518 }
519 }
520 }
521 }
522}
523
524impl OptionLayout for HopByHopOptionsImpl {
525 type KindLenField = u8;
526 const LENGTH_ENCODING: LengthEncoding = LengthEncoding::ValueOnly;
527}
528
529impl OptionParseLayout for HopByHopOptionsImpl {
530 type Error = OptionParseErr;
531 const END_OF_OPTIONS: Option<u8> = Some(0);
532 const NOP: Option<u8> = Some(1);
533}
534
535#[doc(hidden)]
541pub enum HopByHopOptionLayout {}
542
543impl OptionLayout for HopByHopOptionLayout {
544 type KindLenField = u8;
545 const LENGTH_ENCODING: LengthEncoding = LengthEncoding::ValueOnly;
546}
547
548impl<'a> OptionBuilder for HopByHopOption<'a> {
549 type Layout = HopByHopOptionLayout;
550 fn serialized_len(&self) -> usize {
551 match self.data {
552 HopByHopOptionData::RouterAlert { .. } => HBH_OPTION_RTRALRT_LEN,
553 HopByHopOptionData::Unrecognized { len, .. } => len as usize,
554 }
555 }
556
557 fn option_kind(&self) -> u8 {
558 let action: u8 = self.action.into();
559 let mutable = self.mutable as u8;
560 let type_number = match self.data {
561 HopByHopOptionData::Unrecognized { kind, .. } => kind,
562 HopByHopOptionData::RouterAlert { .. } => HBH_OPTION_KIND_RTRALRT,
563 };
564 (action << 6) | (mutable << 5) | type_number
565 }
566
567 fn serialize_into(&self, mut buffer: &mut [u8]) {
568 match self.data {
569 HopByHopOptionData::Unrecognized { data, .. } => buffer.copy_from_slice(data),
570 HopByHopOptionData::RouterAlert { data } => {
571 (&mut buffer).write_obj_front(&U16::new(data)).unwrap()
574 }
575 }
576 }
577}
578
579impl<'a> AlignedOptionBuilder for HopByHopOption<'a> {
580 fn alignment_requirement(&self) -> (usize, usize) {
581 match self.data {
582 HopByHopOptionData::RouterAlert { .. } => (2, 0),
585 _ => (1, 0),
586 }
587 }
588
589 fn serialize_padding(buf: &mut [u8], length: usize) {
590 assert!(length <= buf.len());
591 assert!(length <= (u8::MAX as usize) + 2);
592
593 #[allow(clippy::comparison_chain)]
594 if length == 1 {
595 buf[0] = 0
597 } else if length > 1 {
598 buf[0] = 1;
600 buf[1] = (length - 2) as u8;
601 #[allow(clippy::needless_range_loop)]
602 for i in 2..length {
603 buf[i] = 0
604 }
605 }
606 }
607}
608
609#[derive(Debug)]
629pub struct RoutingData<'a> {
630 bytes: &'a [u8],
631}
632
633#[derive(Debug, PartialEq, Eq)]
635pub enum RoutingType {}
636
637#[derive(Debug, PartialEq, Eq)]
639pub enum RoutingTypeParseError {
640 UnsupportedType(u8),
643}
644
645impl TryFrom<u8> for RoutingType {
646 type Error = RoutingTypeParseError;
647
648 fn try_from(value: u8) -> Result<Self, Self::Error> {
649 Err(RoutingTypeParseError::UnsupportedType(value))
650 }
651}
652
653impl<'a> RoutingData<'a> {
654 pub fn routing_type(&self) -> Result<RoutingType, RoutingTypeParseError> {
656 debug_assert!(self.bytes.len() >= 6);
657 RoutingType::try_from(self.bytes[0])
658 }
659
660 pub fn segments_left(&self) -> u8 {
662 debug_assert!(self.bytes.len() >= 6);
663 self.bytes[1]
664 }
665}
666
667#[derive(Debug, Copy, Clone)]
683pub struct FragmentData {
684 bytes: [u8; 6],
685}
686
687impl FragmentData {
688 pub fn fragment_offset(&self) -> FragmentOffset {
690 FragmentOffset::new_with_msb(U16::from_bytes([self.bytes[0], self.bytes[1]]).get())
691 }
692
693 pub fn m_flag(&self) -> bool {
695 (self.bytes[1] & 0x1) == 0x01
696 }
697
698 pub fn identification(&self) -> u32 {
700 NetworkEndian::read_u32(&self.bytes[2..6])
701 }
702}
703
704#[derive(Debug)]
710pub struct DestinationOptionsData<'a> {
711 options: Records<&'a [u8], DestinationOptionsImpl>,
712}
713
714impl<'a> DestinationOptionsData<'a> {
715 fn new(options: Records<&'a [u8], DestinationOptionsImpl>) -> DestinationOptionsData<'a> {
717 DestinationOptionsData { options }
718 }
719
720 pub fn iter(&'a self) -> impl Iterator<Item = DestinationOption<'a>> {
723 self.options.iter()
724 }
725}
726
727pub type DestinationOption<'a> = ExtensionHeaderOption<DestinationOptionData<'a>>;
729
730pub(super) type DestinationOptionsImpl = ExtensionHeaderOptionImpl<DestinationOptionDataImpl>;
733
734#[allow(missing_docs)]
736#[derive(Debug)]
737pub enum DestinationOptionData<'a> {
738 Unrecognized { kind: u8, len: u8, data: &'a [u8] },
739}
740
741#[derive(Debug)]
743pub(super) struct DestinationOptionDataImpl;
744
745impl ExtensionHeaderOptionDataImplLayout for DestinationOptionDataImpl {
746 type Context = ();
747}
748
749impl ExtensionHeaderOptionDataImpl for DestinationOptionDataImpl {
750 type OptionData<'a> = DestinationOptionData<'a>;
751
752 fn parse_option<'a>(
753 kind: u8,
754 data: &'a [u8],
755 _context: &mut Self::Context,
756 allow_unrecognized: bool,
757 ) -> ExtensionHeaderOptionDataParseResult<Self::OptionData<'a>> {
758 if allow_unrecognized {
759 ExtensionHeaderOptionDataParseResult::Ok(DestinationOptionData::Unrecognized {
760 kind,
761 len: data.len() as u8,
762 data,
763 })
764 } else {
765 ExtensionHeaderOptionDataParseResult::UnrecognizedKind
766 }
767 }
768}
769
770#[derive(Debug, Clone)]
776pub(super) struct ExtensionHeaderOptionContext<C: Sized + Clone> {
777 options_parsed: usize,
779
780 position: usize,
782
783 specific_context: C,
785}
786
787impl<C: Sized + Clone + Default> ExtensionHeaderOptionContext<C> {
788 fn new(offset: usize) -> Self {
789 ExtensionHeaderOptionContext {
790 options_parsed: 0,
791 position: offset,
792 specific_context: C::default(),
793 }
794 }
795}
796
797impl<C: Sized + Clone> RecordsContext for ExtensionHeaderOptionContext<C> {}
798
799pub(super) trait ExtensionHeaderOptionDataImplLayout {
801 type Context: RecordsContext;
804}
805
806#[derive(PartialEq, Eq, Debug)]
808pub enum ExtensionHeaderOptionDataParseResult<D> {
809 Ok(D),
811
812 ErrorAt(u32),
818
819 UnrecognizedKind,
821}
822
823pub(super) trait ExtensionHeaderOptionDataImpl: ExtensionHeaderOptionDataImplLayout {
825 type OptionData<'a>: Sized;
831
832 fn parse_option<'a>(
843 kind: u8,
844 data: &'a [u8],
845 context: &mut Self::Context,
846 allow_unrecognized: bool,
847 ) -> ExtensionHeaderOptionDataParseResult<Self::OptionData<'a>>;
848}
849
850#[derive(Debug)]
857pub(super) struct ExtensionHeaderOptionImpl<O>(PhantomData<O>);
858
859impl<O> ExtensionHeaderOptionImpl<O> {
860 const PAD1: u8 = 0;
861 const PADN: u8 = 1;
862}
863
864impl<O> RecordsImplLayout for ExtensionHeaderOptionImpl<O>
865where
866 O: ExtensionHeaderOptionDataImplLayout,
867{
868 type Error = ExtensionHeaderOptionParsingError;
869 type Context = ExtensionHeaderOptionContext<O::Context>;
870}
871
872impl<O> RecordsImpl for ExtensionHeaderOptionImpl<O>
873where
874 O: ExtensionHeaderOptionDataImpl,
875{
876 type Record<'a> = ExtensionHeaderOption<O::OptionData<'a>>;
877
878 fn parse_with_context<'a, BV: BufferView<&'a [u8]>>(
879 data: &mut BV,
880 context: &mut Self::Context,
881 ) -> RecordParseResult<Self::Record<'a>, Self::Error> {
882 let kind = match data.take_byte_front() {
884 None => return Ok(ParsedRecord::Done),
885 Some(k) => k,
886 };
887
888 let action =
892 ExtensionHeaderOptionAction::try_from((kind >> 6) & 0x3).expect("Unexpected error");
893 let mutable = ((kind >> 5) & 0x1) == 0x1;
894 if kind == Self::PAD1 {
900 context.options_parsed += 1;
902 context.position += 1;
903
904 return Ok(ParsedRecord::Skipped);
905 }
906
907 let len =
908 data.take_byte_front().ok_or(ExtensionHeaderOptionParsingError::BufferExhausted)?;
909
910 let data = data
911 .take_front(len as usize)
912 .ok_or(ExtensionHeaderOptionParsingError::BufferExhausted)?;
913
914 if kind == Self::PADN {
916 context.options_parsed += 1;
918 context.position += 2 + (len as usize);
919
920 return Ok(ParsedRecord::Skipped);
921 }
922
923 match O::parse_option(
925 kind,
926 data,
927 &mut context.specific_context,
928 action == ExtensionHeaderOptionAction::SkipAndContinue,
929 ) {
930 ExtensionHeaderOptionDataParseResult::Ok(o) => {
931 context.options_parsed += 1;
933 context.position += 2 + (len as usize);
934
935 Ok(ParsedRecord::Parsed(ExtensionHeaderOption { action, mutable, data: o }))
936 }
937 ExtensionHeaderOptionDataParseResult::ErrorAt(offset) => {
938 Err(ExtensionHeaderOptionParsingError::ErroneousOptionField {
943 pointer: u32::try_from(context.position + offset as usize).unwrap(),
944 })
945 }
946 ExtensionHeaderOptionDataParseResult::UnrecognizedKind => {
947 match action {
949 ExtensionHeaderOptionAction::SkipAndContinue => unreachable!(
956 "Should never end up here since action was set to skip and continue"
957 ),
958 _ => Err(ExtensionHeaderOptionParsingError::UnrecognizedOption {
969 pointer: u32::try_from(context.position).unwrap(),
970 action,
971 }),
972 }
973 }
974 }
975 }
976}
977
978#[allow(missing_docs)]
980#[derive(Debug, PartialEq, Eq)]
981pub(crate) enum ExtensionHeaderOptionParsingError {
982 ErroneousOptionField { pointer: u32 },
983 UnrecognizedOption { pointer: u32, action: ExtensionHeaderOptionAction },
984 BufferExhausted,
985}
986
987impl From<Never> for ExtensionHeaderOptionParsingError {
988 fn from(err: Never) -> ExtensionHeaderOptionParsingError {
989 match err {}
990 }
991}
992
993#[derive(Debug, PartialEq, Eq, Clone, Copy)]
999pub enum ExtensionHeaderOptionAction {
1000 SkipAndContinue,
1003
1004 DiscardPacket,
1007
1008 DiscardPacketSendIcmp,
1014
1015 DiscardPacketSendIcmpNoMulticast,
1021}
1022
1023impl TryFrom<u8> for ExtensionHeaderOptionAction {
1024 type Error = ();
1025
1026 fn try_from(value: u8) -> Result<Self, ()> {
1027 match value {
1028 0 => Ok(ExtensionHeaderOptionAction::SkipAndContinue),
1029 1 => Ok(ExtensionHeaderOptionAction::DiscardPacket),
1030 2 => Ok(ExtensionHeaderOptionAction::DiscardPacketSendIcmp),
1031 3 => Ok(ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast),
1032 _ => Err(()),
1033 }
1034 }
1035}
1036
1037impl From<ExtensionHeaderOptionAction> for u8 {
1038 fn from(a: ExtensionHeaderOptionAction) -> u8 {
1039 match a {
1040 ExtensionHeaderOptionAction::SkipAndContinue => 0,
1041 ExtensionHeaderOptionAction::DiscardPacket => 1,
1042 ExtensionHeaderOptionAction::DiscardPacketSendIcmp => 2,
1043 ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast => 3,
1044 }
1045 }
1046}
1047
1048#[derive(PartialEq, Eq, Debug, Clone)]
1054pub struct ExtensionHeaderOption<O> {
1055 pub action: ExtensionHeaderOptionAction,
1057
1058 pub mutable: bool,
1064
1065 pub data: O,
1067}
1068
1069pub(super) fn is_valid_next_header_upper_layer(next_header: u8) -> bool {
1078 match Ipv6Proto::from(next_header) {
1079 Ipv6Proto::Proto(IpProto::Tcp)
1080 | Ipv6Proto::Proto(IpProto::Udp)
1081 | Ipv6Proto::Icmpv6
1082 | Ipv6Proto::NoNextHeader => true,
1083 Ipv6Proto::Proto(IpProto::Reserved) | Ipv6Proto::Other(_) => false,
1084 }
1085}
1086
1087fn check_hop_by_hop_position(
1107 next_header_type: Ipv6ExtHdrType,
1108 context: &Ipv6ExtensionHeaderParsingContext,
1109) -> Result<(), Ipv6ExtensionHeaderParsingError> {
1110 if context.headers_parsed > 0 && next_header_type == Ipv6ExtHdrType::HopByHopOptions {
1111 return Err(Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
1112 pointer: context.next_header_offset as u32,
1113 must_send_icmp: false,
1115 });
1116 }
1117 Ok(())
1118}
1119
1120fn ext_hdr_opt_err_to_ext_hdr_err(
1126 err: ExtensionHeaderOptionParsingError,
1127) -> Ipv6ExtensionHeaderParsingError {
1128 match err {
1129 ExtensionHeaderOptionParsingError::ErroneousOptionField { pointer } => {
1130 Ipv6ExtensionHeaderParsingError::ErroneousHeaderField {
1131 pointer: pointer,
1132 must_send_icmp: false,
1137 }
1138 }
1139 ExtensionHeaderOptionParsingError::UnrecognizedOption { pointer, action } => {
1140 Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1141 pointer: pointer,
1142 must_send_icmp: true,
1143 action,
1144 }
1145 }
1146 ExtensionHeaderOptionParsingError::BufferExhausted => {
1147 Ipv6ExtensionHeaderParsingError::BufferExhausted
1148 }
1149 }
1150}
1151
1152#[cfg(test)]
1153mod tests {
1154 use assert_matches::assert_matches;
1155 use net_declare::net_ip_v6;
1156 use packet::records::{AlignedRecordSequenceBuilder, RecordBuilder};
1157 use packet::{InnerPacketBuilder, NestableSerializer, NoOpSerializationContext, Serializer};
1158 use test_case::test_case;
1159
1160 use crate::ip::Ipv4Proto;
1161 use crate::ipv6::{IPV6_FIXED_HDR_LEN, Ipv6PacketBuilder, Ipv6PacketBuilderWithFragmentHeader};
1162
1163 use super::*;
1164
1165 #[test]
1166 fn test_is_valid_next_header_upper_layer() {
1167 assert!(is_valid_next_header_upper_layer(IpProto::Tcp.into()));
1169 assert!(is_valid_next_header_upper_layer(IpProto::Tcp.into()));
1170
1171 assert!(!is_valid_next_header_upper_layer(Ipv4Proto::Icmp.into()));
1173 assert!(!is_valid_next_header_upper_layer(Ipv4Proto::Icmp.into()));
1174 }
1175
1176 #[test]
1177 fn test_hop_by_hop_options() {
1178 let buffer = [0; 10];
1180 let mut context = ExtensionHeaderOptionContext::new(10);
1181 let options =
1182 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1183 .unwrap();
1184 assert_eq!(options.iter().count(), 0);
1185 assert_eq!(context.position, 20);
1186 assert_eq!(context.options_parsed, 10);
1187
1188 #[rustfmt::skip]
1190 let buffer = [
1191 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, ];
1195 let mut context = ExtensionHeaderOptionContext::new(1);
1196 let options =
1197 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1198 .unwrap();
1199 assert_eq!(options.iter().count(), 0);
1200 assert_eq!(context.position, 14);
1201 assert_eq!(context.options_parsed, 3);
1202
1203 #[rustfmt::skip]
1206 let buffer = [
1207 0, 63, 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1211 let mut context = ExtensionHeaderOptionContext::new(1);
1212 let options =
1213 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1214 .unwrap();
1215 let options: Vec<HopByHopOption<'_>> = options.iter().collect();
1216 assert_eq!(options.len(), 1);
1217 assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1218 assert_eq!(context.position, 13);
1219 assert_eq!(context.options_parsed, 3);
1220 }
1221
1222 #[test]
1223 fn test_hop_by_hop_options_err() {
1224 #[rustfmt::skip]
1226 let buffer = [
1227 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, ];
1231 let mut context = ExtensionHeaderOptionContext::new(5);
1232 assert_eq!(
1233 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1234 .expect_err("Parsed successfully when we were short 2 bytes"),
1235 ExtensionHeaderOptionParsingError::BufferExhausted
1236 );
1237 assert_eq!(context.position, 8);
1238 assert_eq!(context.options_parsed, 2);
1239
1240 #[rustfmt::skip]
1242 let buffer = [
1243 1, 1, 0, 127, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1247 let mut context = ExtensionHeaderOptionContext::new(5);
1248 assert_eq!(
1249 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1250 .expect_err("Parsed successfully when we had an unrecognized option type"),
1251 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1252 pointer: 8,
1253 action: ExtensionHeaderOptionAction::DiscardPacket,
1254 }
1255 );
1256 assert_eq!(context.position, 8);
1257 assert_eq!(context.options_parsed, 1);
1258
1259 #[rustfmt::skip]
1262 let buffer = [
1263 1, 1, 0, 191, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1268 let mut context = ExtensionHeaderOptionContext::new(5);
1269 assert_eq!(
1270 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1271 .expect_err("Parsed successfully when we had an unrecognized option type"),
1272 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1273 pointer: 8,
1274 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmp,
1275 }
1276 );
1277 assert_eq!(context.position, 8);
1278 assert_eq!(context.options_parsed, 1);
1279
1280 #[rustfmt::skip]
1283 let buffer = [
1284 1, 1, 0, 255, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1289 let mut context = ExtensionHeaderOptionContext::new(5);
1290 assert_eq!(
1291 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1292 .expect_err("Parsed successfully when we had an unrecognized option type"),
1293 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1294 pointer: 8,
1295 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1296 }
1297 );
1298 assert_eq!(context.position, 8);
1299 assert_eq!(context.options_parsed, 1);
1300
1301 #[rustfmt::skip]
1303 let buffer = [
1304 0xC0,
1307 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
1310 let mut context = ExtensionHeaderOptionContext::new(5);
1311 assert_eq!(
1312 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1313 .expect_err("Parsed successfully when we had Pad1 with upper bits set"),
1314 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1315 pointer: 5,
1316 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1317 }
1318 );
1319 assert_eq!(context.position, 5);
1320 assert_eq!(context.options_parsed, 0);
1321
1322 #[rustfmt::skip]
1324 let buffer = [
1325 0, 0xC1, 0,
1329 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
1331 let mut context = ExtensionHeaderOptionContext::new(5);
1332 assert_eq!(
1333 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1334 .expect_err("Parsed successfully when we had Pad2 with upper bits set"),
1335 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1336 pointer: 6,
1337 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1338 }
1339 );
1340 assert_eq!(context.position, 6);
1341 assert_eq!(context.options_parsed, 1);
1342
1343 #[rustfmt::skip]
1345 let buffer = [
1346 0, 1, 0, 0xC1, 8, 0, 0, 0, 0, 0, 0, 0, 0,
1351 ];
1352 let mut context = ExtensionHeaderOptionContext::new(5);
1353 assert_eq!(
1354 Records::<_, HopByHopOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1355 .expect_err("Parsed successfully when we had PadN with upper bits set"),
1356 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1357 pointer: 8,
1358 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1359 }
1360 );
1361 assert_eq!(context.position, 8);
1362 assert_eq!(context.options_parsed, 2);
1363 }
1364
1365 #[test]
1366 fn test_destination_options() {
1367 let buffer = [0; 10];
1369 let mut context = ExtensionHeaderOptionContext::new(5);
1370 let options =
1371 Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1372 .unwrap();
1373 assert_eq!(options.iter().count(), 0);
1374 assert_eq!(context.position, 15);
1375 assert_eq!(context.options_parsed, 10);
1376
1377 #[rustfmt::skip]
1379 let buffer = [
1380 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, ];
1384 let mut context = ExtensionHeaderOptionContext::new(5);
1385 let options =
1386 Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1387 .unwrap();
1388 assert_eq!(options.iter().count(), 0);
1389 assert_eq!(context.position, 18);
1390 assert_eq!(context.options_parsed, 3);
1391
1392 #[rustfmt::skip]
1395 let buffer = [
1396 0, 63, 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1400 let mut context = ExtensionHeaderOptionContext::new(5);
1401 let options =
1402 Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1403 .unwrap();
1404 let options: Vec<DestinationOption<'_>> = options.iter().collect();
1405 assert_eq!(options.len(), 1);
1406 assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1407 assert_eq!(context.position, 17);
1408 assert_eq!(context.options_parsed, 3);
1409 }
1410
1411 #[test]
1412 fn test_destination_options_err() {
1413 #[rustfmt::skip]
1415 let buffer = [
1416 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, ];
1420 let mut context = ExtensionHeaderOptionContext::new(5);
1421 assert_eq!(
1422 Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1423 .expect_err("Parsed successfully when we were short 2 bytes"),
1424 ExtensionHeaderOptionParsingError::BufferExhausted
1425 );
1426 assert_eq!(context.position, 8);
1427 assert_eq!(context.options_parsed, 2);
1428
1429 #[rustfmt::skip]
1431 let buffer = [
1432 1, 1, 0, 127, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1436 let mut context = ExtensionHeaderOptionContext::new(5);
1437 assert_eq!(
1438 Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1439 .expect_err("Parsed successfully when we had an unrecognized option type"),
1440 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1441 pointer: 8,
1442 action: ExtensionHeaderOptionAction::DiscardPacket,
1443 }
1444 );
1445 assert_eq!(context.position, 8);
1446 assert_eq!(context.options_parsed, 1);
1447
1448 #[rustfmt::skip]
1451 let buffer = [
1452 1, 1, 0, 191, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1457 let mut context = ExtensionHeaderOptionContext::new(5);
1458 assert_eq!(
1459 Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1460 .expect_err("Parsed successfully when we had an unrecognized option type"),
1461 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1462 pointer: 8,
1463 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmp,
1464 }
1465 );
1466 assert_eq!(context.position, 8);
1467 assert_eq!(context.options_parsed, 1);
1468
1469 #[rustfmt::skip]
1472 let buffer = [
1473 1, 1, 0, 255, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
1478 let mut context = ExtensionHeaderOptionContext::new(5);
1479 assert_eq!(
1480 Records::<_, DestinationOptionsImpl>::parse_with_mut_context(&buffer[..], &mut context)
1481 .expect_err("Parsed successfully when we had an unrecognized option type"),
1482 ExtensionHeaderOptionParsingError::UnrecognizedOption {
1483 pointer: 8,
1484 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast,
1485 }
1486 );
1487 assert_eq!(context.position, 8);
1488 assert_eq!(context.options_parsed, 1);
1489 }
1490
1491 #[test]
1492 fn test_hop_by_hop_options_ext_hdr() {
1493 let context =
1496 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1497 #[rustfmt::skip]
1498 let buffer = [
1499 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 63, 6, 0, 0, 0, 0, 0, 0, ];
1504 let ext_hdrs =
1505 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1506 .unwrap();
1507 let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
1508 assert_eq!(ext_hdrs.len(), 1);
1509 if let Ipv6ExtensionHeader::HopByHopOptions { options } = &ext_hdrs[0] {
1510 let options: Vec<HopByHopOption<'_>> = options.iter().collect();
1512 assert_eq!(options.len(), 1);
1513 assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1514 } else {
1515 panic!("Should have matched HopByHopOptions {:?}", ext_hdrs[0]);
1516 }
1517 }
1518
1519 #[test]
1520 fn test_hop_by_hop_options_ext_hdr_err() {
1521 let context =
1525 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1526 #[rustfmt::skip]
1527 let buffer = [
1528 255, 0, 1, 4, 0, 0, 0, 0, ];
1532 let error =
1533 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1534 .expect_err("Parsed successfully when the next header was invalid");
1535 if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
1536 error
1537 {
1538 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32);
1539 assert!(!must_send_icmp);
1540 } else {
1541 panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
1542 }
1543
1544 let context =
1546 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1547 #[rustfmt::skip]
1548 let buffer = [
1549 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 127, 6, 0, 0, 0, 0, 0, 0, ];
1554 let error =
1555 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1556 .expect_err("Parsed successfully with an unrecognized option type");
1557 if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1558 pointer,
1559 must_send_icmp,
1560 action,
1561 } = error
1562 {
1563 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 8);
1564 assert!(must_send_icmp);
1565 assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacket);
1566 } else {
1567 panic!("Should have matched with UnrecognizedOption: {:?}", error);
1568 }
1569
1570 let context =
1572 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1573 #[rustfmt::skip]
1574 let buffer = [
1575 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 191, 6, 0, 0, 0, 0, 0, 0, ];
1580 let error =
1581 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1582 .expect_err("Parsed successfully with an unrecognized option type");
1583 if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1584 pointer,
1585 must_send_icmp,
1586 action,
1587 } = error
1588 {
1589 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 8);
1590 assert!(must_send_icmp);
1591 assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmp);
1592 } else {
1593 panic!("Should have matched with UnrecognizedOption: {:?}", error);
1594 }
1595
1596 let context =
1598 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1599 #[rustfmt::skip]
1600 let buffer = [
1601 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 255, 6, 0, 0, 0, 0, 0, 0, ];
1607 let error =
1608 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1609 .expect_err("Parsed successfully with an unrecognized option type");
1610 if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1611 pointer,
1612 must_send_icmp,
1613 action,
1614 } = error
1615 {
1616 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 8);
1617 assert!(must_send_icmp);
1618 assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast);
1619 } else {
1620 panic!("Should have matched with UnrecognizedOption: {:?}", error);
1621 }
1622
1623 let context =
1625 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1626 #[rustfmt::skip]
1627 let buffer = [
1628 IpProto::Tcp.into(), 0, 5, 3, 0, 0, 0, 0, ];
1633 let error =
1634 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1635 .expect_err(
1636 "Should fail to parse the header because one of the option is malformed",
1637 );
1638 if let Ipv6ExtensionHeaderParsingError::ErroneousHeaderField { pointer, .. } = error {
1639 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 3);
1640 } else {
1641 panic!("Should have matched with UnrecognizedOption: {:?}", error);
1642 }
1643 }
1644
1645 #[test]
1646 fn test_routing_ext_hdr() {
1647 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1649 #[rustfmt::skip]
1650 let buffer = [
1651 IpProto::Tcp.into(), 4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1658 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1659
1660 ];
1661 let ext_hdrs =
1662 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1663 .unwrap();
1664 let results: Vec<_> = ext_hdrs.iter().collect();
1665 assert_eq!(results.len(), 1);
1666 if let Ipv6ExtensionHeader::Routing { routing_data } = &results[0] {
1667 assert_eq!(routing_data.routing_type(), Err(RoutingTypeParseError::UnsupportedType(0)));
1668 assert_eq!(routing_data.segments_left(), 0);
1669 } else {
1670 panic!("Should have matched with RoutingExtensionHeader");
1671 }
1672 }
1673
1674 #[test]
1675 fn test_routing_ext_hdr_err() {
1676 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1680 #[rustfmt::skip]
1681 let buffer = [
1682 IpProto::Tcp.into(), 4, 0, 1, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1689 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1690 ];
1691 let error =
1692 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1693 .expect_err("Parsed successfully when the routing type was set to 0");
1694 if let Ipv6ExtensionHeaderParsingError::ErroneousHeaderField { pointer, must_send_icmp } =
1695 error
1696 {
1697 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 2);
1698 assert!(must_send_icmp);
1699 } else {
1700 panic!("Should have matched with ErroneousHeaderField: {:?}", error);
1701 }
1702
1703 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1705 #[rustfmt::skip]
1706 let buffer = [
1707 255, 4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1714 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1715
1716 ];
1717 let error =
1718 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1719 .expect_err("Parsed successfully when the next header was invalid");
1720 if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
1721 error
1722 {
1723 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32);
1724 assert!(!must_send_icmp);
1725 } else {
1726 panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
1727 }
1728
1729 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1731 #[rustfmt::skip]
1732 let buffer = [
1733 IpProto::Tcp.into(), 4, 255, 1, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1740 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1741
1742 ];
1743 let error =
1744 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1745 .expect_err("Parsed successfully with an unrecognized routing type");
1746 if let Ipv6ExtensionHeaderParsingError::ErroneousHeaderField { pointer, must_send_icmp } =
1747 error
1748 {
1749 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 2);
1751 assert!(must_send_icmp);
1752 } else {
1753 panic!("Should have matched with ErroneousHeaderField: {:?}", error);
1754 }
1755 }
1756
1757 #[test]
1758 fn test_fragment_ext_hdr() {
1759 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Fragment.into());
1761 let frag_offset_res_m_flag: u16 = (5063 << 3) | 1;
1762 let identification: u32 = 3266246449;
1763 #[rustfmt::skip]
1764 let buffer = [
1765 IpProto::Tcp.into(), 0, (frag_offset_res_m_flag >> 8) as u8, (frag_offset_res_m_flag & 0xFF) as u8, (identification >> 24) as u8,
1771 ((identification >> 16) & 0xFF) as u8,
1772 ((identification >> 8) & 0xFF) as u8,
1773 (identification & 0xFF) as u8,
1774 ];
1775 let ext_hdrs =
1776 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1777 .unwrap();
1778 let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
1779 assert_eq!(ext_hdrs.len(), 1);
1780
1781 if let Ipv6ExtensionHeader::Fragment { fragment_data } = &ext_hdrs[0] {
1782 assert_eq!(fragment_data.fragment_offset().into_raw(), 5063);
1783 assert_eq!(fragment_data.m_flag(), true);
1784 assert_eq!(fragment_data.identification(), 3266246449);
1785 } else {
1786 panic!("Should have matched Fragment: {:?}", &ext_hdrs[0]);
1787 }
1788 }
1789
1790 #[test_case(Ipv6ExtHdrType::DestinationOptions.into(); "valid extension header")]
1796 #[test_case(255; "invalid next header")]
1797 fn test_fragment_ext_hdr_stops_parsing(next_header: u8) {
1798 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Fragment.into());
1799 const FRAGMENT_OFFSET: u16 = 100;
1800 const ID: u32 = 12345;
1801 let frag_offset_res_m_flag: u16 = (FRAGMENT_OFFSET << 3) | 1;
1802 let buffer: Vec<u8> = [
1803 next_header, 0, ]
1806 .into_iter()
1807 .chain(frag_offset_res_m_flag.to_be_bytes())
1808 .chain(ID.to_be_bytes())
1809 .chain([IpProto::Tcp.into(), 0, 0, 0, 0, 0, 0, 0])
1812 .collect();
1813
1814 let ext_hdrs =
1815 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1816 .unwrap();
1817 let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
1818 assert_matches!(&ext_hdrs[..], [Ipv6ExtensionHeader::Fragment { fragment_data }] => {
1819 assert_eq!(fragment_data.fragment_offset().into_raw(), FRAGMENT_OFFSET);
1820 assert_eq!(fragment_data.m_flag(), true);
1821 assert_eq!(fragment_data.identification(), ID);
1822 });
1823 }
1824
1825 #[test]
1826 fn test_fragment_ext_hdr_with_hbh_next_hdr_err() {
1827 const HOP_LIMIT: u8 = 64; const IDENTIFICATION: u32 = 51; let builder = Ipv6PacketBuilder::new(
1839 net_ip_v6!("2001:db8::1"),
1840 net_ip_v6!("2001:db8::2"),
1841 HOP_LIMIT,
1842 Ipv6Proto::Other(Ipv6ExtHdrType::HopByHopOptions.into()),
1843 );
1844 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1845 builder,
1846 FragmentOffset::ZERO,
1847 false,
1848 IDENTIFICATION,
1849 );
1850 let body: [u8; 8] = [IpProto::Tcp.into(), 0, 0, 0, 0, 0, 0, 0];
1852 let buf = (&body[..])
1853 .into_serializer()
1854 .wrap_in(builder)
1855 .serialize_vec_outer(&mut NoOpSerializationContext)
1856 .unwrap();
1857
1858 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Fragment.into());
1859 let error = Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(
1860 &buf.as_ref()[IPV6_FIXED_HDR_LEN..],
1861 context,
1862 )
1863 .expect_err("parsing should fail");
1864 assert_eq!(
1865 error,
1866 Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
1867 pointer: IPV6_FIXED_HDR_LEN as u32,
1868 must_send_icmp: false,
1869 }
1870 );
1871 }
1872
1873 #[test]
1874 fn test_no_next_header_ext_hdr() {
1875 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6Proto::NoNextHeader.into());
1877 #[rustfmt::skip]
1878 let buffer = [0, 0, 0, 0,];
1879 let ext_hdrs =
1880 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1881 .unwrap();
1882 assert_eq!(ext_hdrs.iter().count(), 0);
1883 }
1884
1885 #[test]
1886 fn test_destination_options_ext_hdr() {
1887 let context =
1890 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1891 #[rustfmt::skip]
1892 let buffer = [
1893 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 63, 6, 0, 0, 0, 0, 0, 0, ];
1898 let ext_hdrs =
1899 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1900 .unwrap();
1901 let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
1902 assert_eq!(ext_hdrs.len(), 1);
1903 if let Ipv6ExtensionHeader::DestinationOptions { options } = &ext_hdrs[0] {
1904 let options: Vec<DestinationOption<'_>> = options.iter().collect();
1906 assert_eq!(options.len(), 1);
1907 assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
1908 } else {
1909 panic!("Should have matched DestinationOptions: {:?}", &ext_hdrs[0]);
1910 }
1911 }
1912
1913 #[test]
1914 fn test_destination_options_ext_hdr_err() {
1915 let context =
1917 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1918
1919 #[rustfmt::skip]
1921 let buffer = [
1922 255, 0, 1, 4, 0, 0, 0, 0, ];
1926 let error =
1927 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1928 .expect_err("Parsed successfully when the next header was invalid");
1929 if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
1930 error
1931 {
1932 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32);
1933 assert!(!must_send_icmp);
1934 } else {
1935 panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
1936 }
1937
1938 let context =
1940 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1941 #[rustfmt::skip]
1942 let buffer = [
1943 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 127, 6, 0, 0, 0, 0, 0, 0, ];
1948 let error =
1949 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1950 .expect_err("Parsed successfully with an unrecognized option type");
1951 if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1952 pointer,
1953 must_send_icmp,
1954 action,
1955 } = error
1956 {
1957 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 8);
1958 assert!(must_send_icmp);
1959 assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacket);
1960 } else {
1961 panic!("Should have matched with UnrecognizedOption: {:?}", error);
1962 }
1963
1964 let context =
1966 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1967 #[rustfmt::skip]
1968 let buffer = [
1969 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 191, 6, 0, 0, 0, 0, 0, 0, ];
1974 let error =
1975 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
1976 .expect_err("Parsed successfully with an unrecognized option type");
1977 if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
1978 pointer,
1979 must_send_icmp,
1980 action,
1981 } = error
1982 {
1983 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 8);
1984 assert!(must_send_icmp);
1985 assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmp);
1986 } else {
1987 panic!("Should have matched with UnrecognizedOption: {:?}", error);
1988 }
1989
1990 let context =
1992 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1993 #[rustfmt::skip]
1994 let buffer = [
1995 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 255, 6, 0, 0, 0, 0, 0, 0, ];
2001 let error =
2002 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2003 .expect_err("Parsed successfully with an unrecognized option type");
2004 if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
2005 pointer,
2006 must_send_icmp,
2007 action,
2008 } = error
2009 {
2010 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 8);
2011 assert!(must_send_icmp);
2012 assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast);
2013 } else {
2014 panic!("Should have matched with UnrecognizedOption: {:?}", error);
2015 }
2016 }
2017
2018 #[test]
2019 fn test_multiple_ext_hdrs() {
2020 let context =
2022 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2023 #[rustfmt::skip]
2024 let buffer = [
2025 Ipv6ExtHdrType::Routing.into(), 0, 0, 1, 0, 1, 1, 0, Ipv6ExtHdrType::DestinationOptions.into(), 4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2040 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
2041
2042 IpProto::Tcp.into(), 1, 0, 1, 0, 1, 1, 0, 63, 6, 0, 0, 0, 0, 0, 0, ];
2050 let ext_hdrs =
2051 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2052 .unwrap();
2053
2054 let ext_hdrs: Vec<Ipv6ExtensionHeader<'_>> = ext_hdrs.iter().collect();
2055 assert_eq!(ext_hdrs.len(), 3);
2056
2057 if let Ipv6ExtensionHeader::HopByHopOptions { options } = &ext_hdrs[0] {
2059 assert_eq!(options.iter().count(), 0);
2061 } else {
2062 panic!("Should have matched HopByHopOptions: {:?}", &ext_hdrs[0]);
2063 }
2064
2065 if let Ipv6ExtensionHeader::Routing { routing_data } = &ext_hdrs[1] {
2067 assert_eq!(routing_data.routing_type(), Err(RoutingTypeParseError::UnsupportedType(0)));
2068 assert_eq!(routing_data.segments_left(), 0);
2069 } else {
2070 panic!("Should have matched RoutingExtensionHeader: {:?}", &ext_hdrs[1]);
2071 }
2072
2073 if let Ipv6ExtensionHeader::DestinationOptions { options } = &ext_hdrs[2] {
2075 let options: Vec<DestinationOption<'_>> = options.iter().collect();
2077 assert_eq!(options.len(), 1);
2078 assert_eq!(options[0].action, ExtensionHeaderOptionAction::SkipAndContinue);
2079 } else {
2080 panic!("Should have matched DestinationOptions: {:?}", ext_hdrs[2]);
2081 }
2082 }
2083
2084 #[test]
2085 fn test_multiple_ext_hdrs_errs() {
2086 let context =
2090 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2091 #[rustfmt::skip]
2092 let buffer = [
2093 Ipv6ExtHdrType::Routing.into(), 0, 0, 1, 0, 1, 1, 0, 255, 4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2108 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
2109
2110 IpProto::Tcp.into(), 1, 0, 1, 0, 1, 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
2118 let error =
2119 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2120 .expect_err("Parsed successfully when the next header was invalid");
2121 if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
2122 error
2123 {
2124 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 8);
2125 assert!(!must_send_icmp);
2126 } else {
2127 panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
2128 }
2129
2130 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
2132 #[rustfmt::skip]
2133 let buffer = [
2134 Ipv6ExtHdrType::HopByHopOptions.into(), 4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2142 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
2143
2144 Ipv6ExtHdrType::DestinationOptions.into(), 0, 0, 1, 0, 1, 1, 0, IpProto::Tcp.into(), 1, 0, 1, 0, 1, 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
2159 let error =
2160 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2161 .expect_err("Parsed successfully when a hop by hop extension header was not the fist extension header");
2162 if let Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader { pointer, must_send_icmp } =
2163 error
2164 {
2165 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32);
2166 assert!(!must_send_icmp);
2167 } else {
2168 panic!("Should have matched with UnrecognizedNextHeader: {:?}", error);
2169 }
2170
2171 let context =
2174 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2175 #[rustfmt::skip]
2176 let buffer = [
2177 Ipv6ExtHdrType::DestinationOptions.into(), 0, 0, 1, 0, 1, 1, 0, IpProto::Tcp.into(), 1, 0, 1, 0, 1, 1, 0, 191, 6, 0, 0, 0, 0, 0, 0, ];
2192 let error =
2193 Records::<&[u8], Ipv6ExtensionHeaderImpl>::parse_with_context(&buffer[..], context)
2194 .expect_err("Parsed successfully with an unrecognized destination option type");
2195 if let Ipv6ExtensionHeaderParsingError::UnrecognizedOption {
2196 pointer,
2197 must_send_icmp,
2198 action,
2199 } = error
2200 {
2201 assert_eq!(pointer, IPV6_FIXED_HDR_LEN as u32 + 16);
2202 assert!(must_send_icmp);
2203 assert_eq!(action, ExtensionHeaderOptionAction::DiscardPacketSendIcmp);
2204 } else {
2205 panic!("Should have matched with UnrecognizedOption: {:?}", error);
2206 }
2207 }
2208
2209 #[test]
2210 fn test_serialize_hbh_router_alert() {
2211 let mut buffer = [0u8; 4];
2212 let option = HopByHopOption {
2213 action: ExtensionHeaderOptionAction::SkipAndContinue,
2214 mutable: false,
2215 data: HopByHopOptionData::RouterAlert { data: 0 },
2216 };
2217 <HopByHopOption<'_> as RecordBuilder>::serialize_into(&option, &mut buffer);
2218 assert_eq!(&buffer[..], &[5, 2, 0, 0]);
2219 }
2220
2221 #[test]
2222 fn test_parse_hbh_router_alert() {
2223 let context = ExtensionHeaderOptionContext::new(0);
2225 let buffer = [5, 2, 0, 0];
2226
2227 let options =
2228 Records::<_, HopByHopOptionsImpl>::parse_with_context(&buffer[..], context).unwrap();
2229 let rtralrt = options.iter().next().unwrap();
2230 assert!(!rtralrt.mutable);
2231 assert_eq!(rtralrt.action, ExtensionHeaderOptionAction::SkipAndContinue);
2232 assert_eq!(rtralrt.data, HopByHopOptionData::RouterAlert { data: 0 });
2233
2234 let context = ExtensionHeaderOptionContext::new(5);
2237 let buffer = [0xC5, 2, 0, 0];
2240
2241 let error = Records::<_, HopByHopOptionsImpl>::parse_with_context(&buffer[..], context)
2242 .expect_err("UnrecognizedOption should have been returned");
2243 assert_eq!(
2244 error,
2245 ExtensionHeaderOptionParsingError::UnrecognizedOption {
2246 pointer: 5,
2247 action: ExtensionHeaderOptionAction::DiscardPacketSendIcmpNoMulticast
2248 }
2249 );
2250
2251 let result = <HopByHopOptionDataImpl as ExtensionHeaderOptionDataImpl>::parse_option(
2253 5,
2254 &buffer[1..],
2255 &mut (),
2256 false,
2257 );
2258 assert_eq!(result, ExtensionHeaderOptionDataParseResult::ErrorAt(1));
2259
2260 let context = ExtensionHeaderOptionContext::new(5);
2261 let buffer = [5, 3, 0, 0, 0];
2262
2263 let error = Records::<_, HopByHopOptionsImpl>::parse_with_context(&buffer[..], context)
2264 .expect_err(
2265 "Parsing a malformed option with recognized kind but with wrong data should fail",
2266 );
2267 assert_eq!(error, ExtensionHeaderOptionParsingError::ErroneousOptionField { pointer: 6 });
2268 }
2269
2270 fn trivial_hbh_options(lengths: &[Option<usize>]) -> Vec<HopByHopOption<'static>> {
2277 static ZEROES: [u8; 16] = [0u8; 16];
2278 lengths
2279 .iter()
2280 .map(|l| HopByHopOption {
2281 mutable: false,
2282 action: ExtensionHeaderOptionAction::SkipAndContinue,
2283 data: match l {
2284 Some(l) => HopByHopOptionData::Unrecognized {
2285 kind: 1,
2286 len: (*l - 2) as u8,
2287 data: &ZEROES[0..*l - 2],
2288 },
2289 None => HopByHopOptionData::RouterAlert { data: 0 },
2290 },
2291 })
2292 .collect()
2293 }
2294
2295 #[test]
2296 fn test_aligned_records_serializer() {
2297 for i in 2..12 {
2299 let options = trivial_hbh_options(&[Some(i), None]);
2300 let ser = AlignedRecordSequenceBuilder::<
2301 ExtensionHeaderOption<HopByHopOptionData<'_>>,
2302 _,
2303 >::new(2, options.iter());
2304 let mut buf = [0u8; 16];
2305 ser.serialize_into(&mut buf[0..16]);
2306 let base = (i + 1) & !1;
2307 assert_eq!(&buf[base..base + 4], &[5, 2, 0, 0]);
2309 }
2310 }
2311}