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
28pub(crate) const IPV6_FRAGMENT_EXT_HDR_LEN: usize = 8;
30
31#[derive(Debug)]
33pub struct Ipv6ExtensionHeader<'a> {
34 pub(super) next_header: u8,
37 data: Ipv6ExtensionHeaderData<'a>,
38}
39
40impl<'a> Ipv6ExtensionHeader<'a> {
41 pub fn data(&self) -> &Ipv6ExtensionHeaderData<'a> {
43 &self.data
44 }
45
46 pub fn into_data(self) -> Ipv6ExtensionHeaderData<'a> {
48 self.data
49 }
50}
51
52#[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#[allow(missing_docs)]
68#[derive(Debug, PartialEq, Eq)]
69pub(super) enum Ipv6ExtensionHeaderParsingError {
70 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#[derive(Debug, Clone)]
90pub(super) struct Ipv6ExtensionHeaderParsingContext {
91 pub(super) next_header: u8,
95
96 iter: bool,
98
99 headers_parsed: usize,
101
102 pub(super) bytes_parsed: usize,
104}
105
106impl Ipv6ExtensionHeaderParsingContext {
107 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#[derive(Debug)]
135pub(super) struct Ipv6ExtensionHeaderImpl;
136
137impl Ipv6ExtensionHeaderImpl {
138 fn valid_next_header(next_header: u8) -> bool {
140 is_valid_next_header(next_header, false)
144 }
145
146 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 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 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 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 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 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 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 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 if segments_left == 0 {
258 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 Err(Ipv6ExtensionHeaderParsingError::ErroneousHeaderField {
272 pointer: (context.bytes_parsed as u32) + 2,
273 must_send_icmp: true,
274 })
275 }
276 }
277
278 fn parse_fragment<'a, BV: BufferView<&'a [u8]>>(
280 data: &mut BV,
281 context: &mut Ipv6ExtensionHeaderParsingContext,
282 ) -> Result<ParsedRecord<Ipv6ExtensionHeader<'a>>, Ipv6ExtensionHeaderParsingError> {
283 if data.len() < 8 {
289 return Err(Ipv6ExtensionHeaderParsingError::BufferExhausted);
290 }
291
292 let (next_header, _) = Self::get_next_hdr_and_len(data, context)?;
296
297 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 fragment_data: FragmentData {
311 bytes: data.take_front(6).unwrap().try_into().unwrap(),
312 },
313 },
314 }))
315 }
316
317 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 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 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 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 Err(Ipv6ExtensionHeaderParsingError::UnrecognizedNextHeader {
393 pointer: u32::MAX,
396 must_send_icmp: false,
399 })
400 }
401 Ipv6ExtHdrType::Other(_) => {
402 if is_valid_next_header_upper_layer(expected_hdr) {
403 Ok(ParsedRecord::Done)
406 } else {
407 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 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 (
448 data.take_byte_front()
449 .ok_or(Ipv6ExtensionHeaderParsingError::BufferExhausted)?,
450 7,
451 )
452 }
453 Ipv6ExtHdrType::EncapsulatingSecurityPayload => {
454 return debug_err!(
458 Err(Ipv6ExtensionHeaderParsingError::MalformedData),
459 "ESP extension header not supported"
460 );
461 }
462 Ipv6ExtHdrType::Authentication => {
463 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#[derive(Debug)]
485pub struct HopByHopOptionsData<'a> {
486 options: Records<&'a [u8], HopByHopOptionsImpl>,
487}
488
489impl<'a> HopByHopOptionsData<'a> {
490 fn new(options: Records<&'a [u8], HopByHopOptionsImpl>) -> HopByHopOptionsData<'a> {
492 HopByHopOptionsData { options }
493 }
494
495 pub fn iter(&'a self) -> impl Iterator<Item = HopByHopOption<'a>> {
498 self.options.iter()
499 }
500}
501
502pub type HopByHopOption<'a> = ExtensionHeaderOption<HopByHopOptionData<'a>>;
504
505pub(super) type HopByHopOptionsImpl = ExtensionHeaderOptionImpl<HopByHopOptionDataImpl>;
508
509const HBH_OPTION_KIND_RTRALRT: u8 = 5;
513
514const HBH_OPTION_RTRALRT_LEN: usize = 2;
518
519#[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#[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 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#[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 (&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 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 buf[0] = 0
644 } else if length > 1 {
645 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#[derive(Debug)]
676pub struct RoutingData<'a> {
677 bytes: &'a [u8],
678}
679
680#[derive(Debug, PartialEq, Eq)]
682pub enum RoutingType {}
683
684#[derive(Debug, PartialEq, Eq)]
686pub enum RoutingTypeParseError {
687 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 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 pub fn segments_left(&self) -> u8 {
709 debug_assert!(self.bytes.len() >= 6);
710 self.bytes[1]
711 }
712}
713
714#[derive(Debug, Copy, Clone)]
730pub struct FragmentData {
731 bytes: [u8; 6],
732}
733
734impl FragmentData {
735 pub fn fragment_offset(&self) -> FragmentOffset {
737 FragmentOffset::new_with_msb(U16::from_bytes([self.bytes[0], self.bytes[1]]).get())
738 }
739
740 pub fn m_flag(&self) -> bool {
742 (self.bytes[1] & 0x1) == 0x01
743 }
744
745 pub fn identification(&self) -> u32 {
747 NetworkEndian::read_u32(&self.bytes[2..6])
748 }
749}
750
751#[derive(Debug)]
757pub struct DestinationOptionsData<'a> {
758 options: Records<&'a [u8], DestinationOptionsImpl>,
759}
760
761impl<'a> DestinationOptionsData<'a> {
762 fn new(options: Records<&'a [u8], DestinationOptionsImpl>) -> DestinationOptionsData<'a> {
764 DestinationOptionsData { options }
765 }
766
767 pub fn iter(&'a self) -> impl Iterator<Item = DestinationOption<'a>> {
770 self.options.iter()
771 }
772}
773
774pub type DestinationOption<'a> = ExtensionHeaderOption<DestinationOptionData<'a>>;
776
777pub(super) type DestinationOptionsImpl = ExtensionHeaderOptionImpl<DestinationOptionDataImpl>;
780
781#[allow(missing_docs)]
783#[derive(Debug)]
784pub enum DestinationOptionData<'a> {
785 Unrecognized { kind: u8, len: u8, data: &'a [u8] },
786}
787
788#[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#[derive(Debug, Clone)]
823pub(super) struct ExtensionHeaderOptionContext<C: Sized + Clone> {
824 options_parsed: usize,
826
827 bytes_parsed: usize,
829
830 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
852pub(super) trait ExtensionHeaderOptionDataImplLayout {
854 type Context: RecordsContext;
857}
858
859#[derive(PartialEq, Eq, Debug)]
861pub enum ExtensionHeaderOptionDataParseResult<D> {
862 Ok(D),
864
865 ErrorAt(u32),
871
872 UnrecognizedKind,
874}
875
876pub(super) trait ExtensionHeaderOptionDataImpl: ExtensionHeaderOptionDataImplLayout {
878 type OptionData<'a>: Sized;
884
885 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#[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 let kind = match data.take_byte_front() {
937 None => return Ok(ParsedRecord::Done),
938 Some(k) => k,
939 };
940
941 let action =
945 ExtensionHeaderOptionAction::try_from((kind >> 6) & 0x3).expect("Unexpected error");
946 let mutable = ((kind >> 5) & 0x1) == 0x1;
947 if kind == Self::PAD1 {
953 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 kind == Self::PADN {
969 context.options_parsed += 1;
971 context.bytes_parsed += 2 + (len as usize);
972
973 return Ok(ParsedRecord::Skipped);
974 }
975
976 match O::parse_option(
978 kind,
979 data,
980 &mut context.specific_context,
981 action == ExtensionHeaderOptionAction::SkipAndContinue,
982 ) {
983 ExtensionHeaderOptionDataParseResult::Ok(o) => {
984 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 Err(ExtensionHeaderOptionParsingError::ErroneousOptionField {
996 pointer: u32::try_from(context.bytes_parsed + offset as usize).unwrap(),
997 })
998 }
999 ExtensionHeaderOptionDataParseResult::UnrecognizedKind => {
1000 match action {
1002 ExtensionHeaderOptionAction::SkipAndContinue => unreachable!(
1009 "Should never end up here since action was set to skip and continue"
1010 ),
1011 _ => Err(ExtensionHeaderOptionParsingError::UnrecognizedOption {
1022 pointer: u32::try_from(context.bytes_parsed).unwrap(),
1023 action,
1024 }),
1025 }
1026 }
1027 }
1028 }
1029}
1030
1031#[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#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1052pub enum ExtensionHeaderOptionAction {
1053 SkipAndContinue,
1056
1057 DiscardPacket,
1060
1061 DiscardPacketSendIcmp,
1067
1068 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#[derive(PartialEq, Eq, Debug, Clone)]
1107pub struct ExtensionHeaderOption<O> {
1108 pub action: ExtensionHeaderOptionAction,
1110
1111 pub mutable: bool,
1117
1118 pub data: O,
1120}
1121
1122pub(super) fn is_valid_next_header(next_header: u8, for_fixed_header: bool) -> bool {
1136 match Ipv6ExtHdrType::from(next_header) {
1140 Ipv6ExtHdrType::HopByHopOptions => for_fixed_header,
1143
1144 Ipv6ExtHdrType::Other(next_header) => is_valid_next_header_upper_layer(next_header),
1147
1148 _ => true,
1150 }
1151}
1152
1153pub(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
1167fn 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 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 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 assert!(is_valid_next_header_upper_layer(IpProto::Tcp.into()));
1218 assert!(is_valid_next_header_upper_layer(IpProto::Tcp.into()));
1219
1220 assert!(!is_valid_next_header_upper_layer(Ipv4Proto::Icmp.into()));
1222 assert!(!is_valid_next_header_upper_layer(Ipv4Proto::Icmp.into()));
1223
1224 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 assert!(is_valid_next_header(Ipv6ExtHdrType::HopByHopOptions.into(), true));
1235 assert!(!is_valid_next_header(Ipv6ExtHdrType::HopByHopOptions.into(), false));
1236
1237 assert!(is_valid_next_header(Ipv6ExtHdrType::Routing.into(), true));
1240 assert!(is_valid_next_header(Ipv6ExtHdrType::Routing.into(), false));
1241
1242 assert!(is_valid_next_header(IpProto::Tcp.into(), true));
1244 assert!(is_valid_next_header(IpProto::Tcp.into(), false));
1245
1246 assert!(!is_valid_next_header(Ipv4Proto::Icmp.into(), true));
1248 assert!(!is_valid_next_header(Ipv4Proto::Icmp.into(), false));
1249
1250 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 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 #[rustfmt::skip]
1270 let buffer = [
1271 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1286 let buffer = [
1287 0, 63, 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1306 let buffer = [
1307 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1322 let buffer = [
1323 1, 1, 0, 127, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1342 let buffer = [
1343 1, 1, 0, 191, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1363 let buffer = [
1364 1, 1, 0, 255, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1383 let buffer = [
1384 0xC0,
1387 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1404 let buffer = [
1405 0, 0xC1, 0,
1409 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1425 let buffer = [
1426 0, 1, 0, 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 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 #[rustfmt::skip]
1459 let buffer = [
1460 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1475 let buffer = [
1476 0, 63, 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1495 let buffer = [
1496 0, 1, 0, 1, 8, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1511 let buffer = [
1512 1, 1, 0, 127, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1531 let buffer = [
1532 1, 1, 0, 191, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 #[rustfmt::skip]
1552 let buffer = [
1553 1, 1, 0, 255, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
1576 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1577 #[rustfmt::skip]
1578 let buffer = [
1579 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 63, 6, 0, 0, 0, 0, 0, 0, ];
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 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 let context =
1606 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1607 #[rustfmt::skip]
1608 let buffer = [
1609 255, 0, 1, 4, 0, 0, 0, 0, ];
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 let context =
1627 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1628 #[rustfmt::skip]
1629 let buffer = [
1630 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 127, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
1653 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1654 #[rustfmt::skip]
1655 let buffer = [
1656 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 191, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
1679 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1680 #[rustfmt::skip]
1681 let buffer = [
1682 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 255, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
1706 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
1707 #[rustfmt::skip]
1708 let buffer = [
1709 IpProto::Tcp.into(), 0, 5, 3, 0, 0, 0, 0, ];
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 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1730 #[rustfmt::skip]
1731 let buffer = [
1732 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,
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 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1762 #[rustfmt::skip]
1763 let buffer = [
1764 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,
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 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1787 #[rustfmt::skip]
1788 let buffer = [
1789 255, 4, 0, 1, 0, 0, 0, 0, 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 let context = Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::Routing.into());
1813 #[rustfmt::skip]
1814 let buffer = [
1815 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,
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 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 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(), 0, (frag_offset_res_m_flag >> 8) as u8, (frag_offset_res_m_flag & 0xFF) as u8, (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 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, 0, (frag_offset_res_m_flag >> 8) as u8, (frag_offset_res_m_flag & 0xFF) as u8, (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 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 let context =
1923 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1924 #[rustfmt::skip]
1925 let buffer = [
1926 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 63, 6, 0, 0, 0, 0, 0, 0, ];
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 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 let context =
1951 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1952
1953 #[rustfmt::skip]
1955 let buffer = [
1956 255, 0, 1, 4, 0, 0, 0, 0, ];
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 let context =
1974 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
1975 #[rustfmt::skip]
1976 let buffer = [
1977 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 127, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
2000 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
2001 #[rustfmt::skip]
2002 let buffer = [
2003 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 191, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
2026 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::DestinationOptions.into());
2027 #[rustfmt::skip]
2028 let buffer = [
2029 IpProto::Tcp.into(), 1, 1, 4, 0, 0, 0, 0, 255, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
2056 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2057 #[rustfmt::skip]
2058 let buffer = [
2059 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,
2074 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
2075
2076 IpProto::Tcp.into(), 1, 0, 1, 0, 1, 1, 0, 63, 6, 0, 0, 0, 0, 0, 0, ];
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 assert_eq!(ext_hdrs[0].next_header, Ipv6ExtHdrType::Routing.into());
2093 if let Ipv6ExtensionHeaderData::HopByHopOptions { options } = ext_hdrs[0].data() {
2094 assert_eq!(options.iter().count(), 0);
2096 } else {
2097 panic!("Should have matched HopByHopOptions: {:?}", ext_hdrs[0].data());
2098 }
2099
2100 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 assert_eq!(ext_hdrs[2].next_header, IpProto::Tcp.into());
2111 if let Ipv6ExtensionHeaderData::DestinationOptions { options } = ext_hdrs[2].data() {
2112 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 let context =
2127 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2128 #[rustfmt::skip]
2129 let buffer = [
2130 Ipv6ExtHdrType::Routing.into(), 0, 0, 1, 0, 1, 1, 0, 255, 4, 0, 1, 0, 0, 0, 0, 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 IpProto::Tcp.into(), 1, 0, 1, 0, 1, 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, ];
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 let context =
2169 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2170 #[rustfmt::skip]
2171 let buffer = [
2172 Ipv6ExtHdrType::HopByHopOptions.into(), 4, 0, 1, 0, 0, 0, 0, 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 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, ];
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 let context =
2212 Ipv6ExtensionHeaderParsingContext::new(Ipv6ExtHdrType::HopByHopOptions.into());
2213 #[rustfmt::skip]
2214 let buffer = [
2215 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, ];
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 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 let context = ExtensionHeaderOptionContext::new();
2275 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 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 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 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 assert_eq!(&buf[base..base + 4], &[5, 2, 0, 0]);
2347 }
2348 }
2349}