1 use crate::*;
2 use core::slice::from_raw_parts;
3 
4 /// Slice containing the IPv6 extension headers present after the ip header.
5 ///
6 /// Currently supported:
7 /// * Authentication Header
8 /// * Hop by Hop Options Header
9 /// * Destination Options Header (before and after routing headers)
10 /// * Routing Header
11 /// * Fragment
12 /// * Authentication Header
13 ///
14 /// Currently not supported:
15 /// * Encapsulating Security Payload Header (ESP)
16 /// * Host Identity Protocol (HIP)
17 /// * IP Mobility
18 /// * Site Multihoming by IPv6 Intermediation (SHIM6)
19 #[derive(Clone, Debug, Eq, PartialEq, Default)]
20 pub struct Ipv6ExtensionsSlice<'a> {
21     /// IP protocol number of the first header present in the slice.
22     first_header: Option<IpNumber>,
23     /// True if a fragment header is present in the ipv6 header extensions that causes the payload to be fragmented.
24     fragmented: bool,
25     /// Slice containing ipv6 extension headers.
26     slice: &'a [u8],
27 }
28 
29 impl<'a> Ipv6ExtensionsSlice<'a> {
30     /// Collects all ipv6 extension headers in a slice & checks if
31     /// a fragmentation header that fragments the packet is present.
from_slice( start_ip_number: IpNumber, start_slice: &'a [u8], ) -> Result<(Ipv6ExtensionsSlice<'a>, IpNumber, &'a [u8]), err::ipv6_exts::HeaderSliceError>32     pub fn from_slice(
33         start_ip_number: IpNumber,
34         start_slice: &'a [u8],
35     ) -> Result<(Ipv6ExtensionsSlice<'a>, IpNumber, &'a [u8]), err::ipv6_exts::HeaderSliceError>
36     {
37         let mut rest = start_slice;
38         let mut next_header = start_ip_number;
39         let mut fragmented = false;
40 
41         use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
42         use ip_number::*;
43 
44         // the hop by hop header is required to occur directly after the ipv6 header
45         if IPV6_HOP_BY_HOP == next_header {
46             let slice = Ipv6RawExtHeaderSlice::from_slice(rest).map_err(Len)?;
47             rest = &rest[slice.slice().len()..];
48             next_header = slice.next_header();
49         }
50 
51         loop {
52             match next_header {
53                 IPV6_HOP_BY_HOP => {
54                     return Err(Content(HopByHopNotAtStart));
55                 }
56                 IPV6_DEST_OPTIONS | IPV6_ROUTE => {
57                     let slice = Ipv6RawExtHeaderSlice::from_slice(rest)
58                         .map_err(|err| Len(err.add_offset(start_slice.len() - rest.len())))?;
59                     // SAFETY:
60                     // Ipv6RawExtHeaderSlice::from_slice always generates
61                     // a subslice from the given slice rest. Therefor it is guaranteed
62                     // that len is always greater or equal the len of rest.
63                     rest = unsafe {
64                         let len = slice.slice().len();
65                         from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
66                     };
67                     next_header = slice.next_header();
68                 }
69                 IPV6_FRAG => {
70                     let slice = Ipv6FragmentHeaderSlice::from_slice(rest)
71                         .map_err(|err| Len(err.add_offset(start_slice.len() - rest.len())))?;
72                     // SAFETY:
73                     // Ipv6FragmentHeaderSlice::from_slice always generates
74                     // a subslice from the given slice rest. Therefor it is guaranteed
75                     // that len is always greater or equal the len of rest.
76                     rest = unsafe {
77                         let len = slice.slice().len();
78                         from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
79                     };
80                     next_header = slice.next_header();
81 
82                     // check if the fragment header actually causes fragmentation
83                     fragmented = fragmented || slice.is_fragmenting_payload();
84                 }
85                 AUTH => {
86                     let slice = IpAuthHeaderSlice::from_slice(rest).map_err(|err| {
87                         use err::ip_auth::HeaderSliceError as I;
88                         match err {
89                             I::Len(err) => Len(err.add_offset(start_slice.len() - rest.len())),
90                             I::Content(err) => Content(IpAuth(err)),
91                         }
92                     })?;
93                     // SAFETY:
94                     // IpAuthHeaderSlice::from_slice always generates
95                     // a subslice from the given slice rest. Therefor it is guaranteed
96                     // that len is always greater or equal the len of rest.
97                     rest = unsafe {
98                         let len = slice.slice().len();
99                         from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
100                     };
101                     next_header = slice.next_header();
102                 }
103                 // done parsing, the next header is not a known/supported header extension
104                 _ => break,
105             }
106         }
107 
108         Ok((
109             Ipv6ExtensionsSlice {
110                 first_header: if rest.len() != start_slice.len() {
111                     Some(start_ip_number)
112                 } else {
113                     None
114                 },
115                 fragmented,
116                 slice: &start_slice[..start_slice.len() - rest.len()],
117             },
118             next_header,
119             rest,
120         ))
121     }
122 
123     /// Collects all ipv6 extension headers in a slice until an error
124     /// is encountered or a "non IP extension header" is found and
125     /// returns the successfully parsed parts (+ the unparsed slice
126     /// it's `IpNumber` and the error if one occurred).
127     ///
128     /// The returned values are
129     ///
130     /// * [`Ipv6ExtensionsSlice`] containing the successfully parsed IPv6 extension headers
131     /// * [`IpNumber`] of unparsed data
132     /// * Slice with unparsed data
133     /// * Optional with error if there was an error wich stoped the parsing.
from_slice_lax( start_ip_number: IpNumber, start_slice: &'a [u8], ) -> ( Ipv6ExtensionsSlice<'a>, IpNumber, &'a [u8], Option<(err::ipv6_exts::HeaderSliceError, err::Layer)>, )134     pub fn from_slice_lax(
135         start_ip_number: IpNumber,
136         start_slice: &'a [u8],
137     ) -> (
138         Ipv6ExtensionsSlice<'a>,
139         IpNumber,
140         &'a [u8],
141         Option<(err::ipv6_exts::HeaderSliceError, err::Layer)>,
142     ) {
143         let mut rest = start_slice;
144         let mut next_header = start_ip_number;
145         let mut error = None;
146         let mut fragmented = false;
147 
148         use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
149         use ip_number::*;
150 
151         // the hop by hop header is required to occur directly after the ipv6 header
152         if IPV6_HOP_BY_HOP == next_header {
153             match Ipv6RawExtHeaderSlice::from_slice(rest) {
154                 Ok(slice) => {
155                     rest = &rest[slice.slice().len()..];
156                     next_header = slice.next_header();
157                 }
158                 Err(err) => {
159                     error = Some((Len(err), err::Layer::Ipv6HopByHopHeader));
160                 }
161             }
162         }
163 
164         while error.is_none() {
165             match next_header {
166                 IPV6_HOP_BY_HOP => {
167                     error = Some((Content(HopByHopNotAtStart), err::Layer::Ipv6HopByHopHeader));
168                     break;
169                 }
170                 IPV6_DEST_OPTIONS | IPV6_ROUTE => {
171                     let slice = match Ipv6RawExtHeaderSlice::from_slice(rest) {
172                         Ok(s) => s,
173                         Err(err) => {
174                             error = Some((
175                                 Len(err.add_offset(start_slice.len() - rest.len())),
176                                 if next_header == IPV6_DEST_OPTIONS {
177                                     err::Layer::Ipv6DestOptionsHeader
178                                 } else {
179                                     err::Layer::Ipv6RouteHeader
180                                 },
181                             ));
182                             break;
183                         }
184                     };
185                     // SAFETY:
186                     // Ipv6RawExtHeaderSlice::from_slice always generates
187                     // a subslice from the given slice rest. Therefor it is guranteed
188                     // that len is always greater or equal the len of rest.
189                     rest = unsafe {
190                         let len = slice.slice().len();
191                         from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
192                     };
193                     next_header = slice.next_header();
194                 }
195                 IPV6_FRAG => {
196                     let slice = match Ipv6FragmentHeaderSlice::from_slice(rest) {
197                         Ok(s) => s,
198                         Err(err) => {
199                             error = Some((
200                                 Len(err.add_offset(start_slice.len() - rest.len())),
201                                 err::Layer::Ipv6FragHeader,
202                             ));
203                             break;
204                         }
205                     };
206 
207                     // SAFETY:
208                     // Ipv6FragmentHeaderSlice::from_slice always generates
209                     // a subslice from the given slice rest. Therefor it is guranteed
210                     // that len is always greater or equal the len of rest.
211                     rest = unsafe {
212                         let len = slice.slice().len();
213                         from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
214                     };
215                     next_header = slice.next_header();
216 
217                     // check if the fragment header actually causes fragmentation
218                     fragmented = fragmented || slice.is_fragmenting_payload();
219                 }
220                 AUTH => {
221                     use err::ip_auth::HeaderSliceError as I;
222                     let slice = match IpAuthHeaderSlice::from_slice(rest) {
223                         Ok(s) => s,
224                         Err(err) => {
225                             error = Some((
226                                 match err {
227                                     I::Len(err) => {
228                                         Len(err.add_offset(start_slice.len() - rest.len()))
229                                     }
230                                     I::Content(err) => Content(IpAuth(err)),
231                                 },
232                                 err::Layer::IpAuthHeader,
233                             ));
234                             break;
235                         }
236                     };
237                     // SAFETY:
238                     // IpAuthHeaderSlice::from_slice always generates
239                     // a subslice from the given slice rest. Therefor it is guranteed
240                     // that len is always greater or equal the len of rest.
241                     rest = unsafe {
242                         let len = slice.slice().len();
243                         from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
244                     };
245                     next_header = slice.next_header();
246                 }
247                 // done parsing, the next header is not a known/supported header extension
248                 _ => break,
249             }
250         }
251 
252         (
253             Ipv6ExtensionsSlice {
254                 first_header: if rest.len() != start_slice.len() {
255                     Some(start_ip_number)
256                 } else {
257                     None
258                 },
259                 fragmented,
260                 slice: &start_slice[..start_slice.len() - rest.len()],
261             },
262             next_header,
263             rest,
264             error,
265         )
266     }
267 
268     /// Returns true if a fragmentation header is present in
269     /// the extensions that fragments the payload.
270     ///
271     /// Note: A fragmentation header can still be present
272     /// even if the return value is false in case the fragmentation
273     /// headers don't fragment the payload. This is the case if
274     /// the offset of all fragmentation header is 0 and the
275     /// more fragment bit is not set.
276     #[inline]
is_fragmenting_payload(&self) -> bool277     pub fn is_fragmenting_payload(&self) -> bool {
278         self.fragmented
279     }
280 
281     /// Returns the ip protocol number of the first header in the slice
282     /// if the slice contains an ipv6 extension header. If no ipv6 header
283     /// is present None is returned.
284     ///
285     /// None is only returned if the slice length of this struct is 0.
286     #[inline]
first_header(&self) -> Option<IpNumber>287     pub fn first_header(&self) -> Option<IpNumber> {
288         self.first_header
289     }
290 
291     /// Slice containing the ipv6 extension headers.
292     #[inline]
slice(&self) -> &'a [u8]293     pub fn slice(&self) -> &'a [u8] {
294         self.slice
295     }
296 
297     /// Returns true if no IPv6 extension header is present (slice is empty).
298     #[inline]
is_empty(&self) -> bool299     pub fn is_empty(&self) -> bool {
300         self.slice.is_empty()
301     }
302 }
303 
304 impl<'a> IntoIterator for Ipv6ExtensionsSlice<'a> {
305     type Item = Ipv6ExtensionSlice<'a>;
306     type IntoIter = Ipv6ExtensionSliceIter<'a>;
307 
into_iter(self) -> Self::IntoIter308     fn into_iter(self) -> Self::IntoIter {
309         Ipv6ExtensionSliceIter {
310             // map the next header None value to some non ipv6 ext header
311             // value.
312             next_header: self.first_header.unwrap_or(ip_number::UDP),
313             rest: self.slice,
314         }
315     }
316 }
317 
318 #[cfg(test)]
319 mod test {
320     use super::ipv6_exts_test_helpers::*;
321     use super::*;
322     use crate::ip_number::*;
323     use crate::test_gens::*;
324     use alloc::{borrow::ToOwned, vec::Vec};
325     use proptest::prelude::*;
326 
327     proptest! {
328         #[test]
329         fn from_slice(
330             header_size in any::<u8>(),
331             post_header in ip_number_any()
332                 .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
333                     |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
334                 )
335         ) {
336             use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
337 
338             // no extension headers filled
339             {
340                 let some_data = [1,2,3,4];
341                 let actual = Ipv6ExtensionsSlice::from_slice(UDP, &some_data).unwrap();
342                 assert_eq!(actual.0.is_fragmenting_payload(), false);
343                 assert_eq!(actual.0.first_header(), None);
344                 assert_eq!(actual.0.slice().len(), 0);
345                 assert_eq!(actual.1, UDP);
346                 assert_eq!(actual.2, &some_data);
347             }
348 
349             /// Run a test with the given ip numbers
350             fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
351                 // setup test payload
352                 let e = ExtensionTestPayload::new(
353                     ip_numbers,
354                     header_sizes
355                 );
356 
357                 if e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
358                     // a hop by hop header that is not at the start triggers an error
359                     assert_eq!(
360                         Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap_err(),
361                         Content(HopByHopNotAtStart)
362                     );
363                 } else {
364                     // normal read
365                     let (header, next, rest) = Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap();
366                     assert_eq!(header.first_header(), Some(ip_numbers[0]));
367                     assert_eq!(header.slice(), e.slice());
368                     assert_eq!(next, *ip_numbers.last().unwrap());
369                     assert_eq!(rest, &e.slice()[e.slice().len()..]);
370 
371                     // unexpected end of slice
372                     {
373                         let offset: usize = e.lengths[..e.lengths.len() - 1].into_iter().sum();
374 
375                         assert_eq!(
376                             Ipv6ExtensionsSlice::from_slice(ip_numbers[0], &e.slice()[..e.slice().len() - 1]).unwrap_err(),
377                             Len(err::LenError {
378                                 required_len: e.slice().len() - offset,
379                                 len: e.slice().len() - offset - 1,
380                                 len_source: LenSource::Slice,
381                                 layer: match ip_numbers[ip_numbers.len() - 2] {
382                                     AUTH => err::Layer::IpAuthHeader,
383                                     IPV6_FRAG => err::Layer::Ipv6FragHeader,
384                                     _ => err::Layer::Ipv6ExtHeader
385                                 },
386                                 layer_start_offset: offset,
387                             })
388                         );
389                     }
390                 }
391             }
392 
393             // test the parsing of different extension header combinations
394             for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
395 
396                 // single header parsing
397                 run_test(
398                     &[*first_header, post_header],
399                     &[header_size],
400                 );
401 
402                 for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
403 
404                     // double header parsing
405                     run_test(
406                         &[*first_header, *second_header, post_header],
407                         &[header_size],
408                     );
409 
410                     for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
411                         // tripple header parsing
412                         run_test(
413                             &[*first_header, *second_header, *third_header, post_header],
414                             &[header_size],
415                         );
416                     }
417                 }
418             }
419         }
420     }
421 
422     proptest! {
423         #[test]
424         fn from_slice_lax(
425             header_size in any::<u8>(),
426             post_header in ip_number_any()
427                 .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
428                     |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
429                 )
430         ) {
431             use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
432 
433             // no extension headers filled
434             {
435                 let some_data = [1,2,3,4];
436                 let actual = Ipv6ExtensionsSlice::from_slice_lax(UDP, &some_data);
437                 assert_eq!(actual.0.is_fragmenting_payload(), false);
438                 assert_eq!(actual.0.first_header(), None);
439                 assert_eq!(actual.0.slice().len(), 0);
440                 assert_eq!(actual.1, UDP);
441                 assert_eq!(actual.2, &some_data);
442             }
443 
444             /// Run a test with the given ip numbers
445             fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
446                 // setup test payload
447                 let e = ExtensionTestPayload::new(
448                     ip_numbers,
449                     header_sizes
450                 );
451 
452                 if e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
453                     // a hop by hop header that is not at the start triggers an error
454                     assert_eq!(
455                         Ipv6ExtensionsSlice::from_slice_lax(ip_numbers[0], e.slice()).3.unwrap(),
456                         (Content(HopByHopNotAtStart), err::Layer::Ipv6HopByHopHeader)
457                     );
458                 } else {
459                     // normal read
460                     let actual_normal = Ipv6ExtensionsSlice::from_slice_lax(ip_numbers[0], e.slice());
461                     assert_eq!(actual_normal.0.first_header(), Some(ip_numbers[0]));
462                     assert_eq!(actual_normal.0.slice(), e.slice());
463                     assert_eq!(actual_normal.1, *ip_numbers.last().unwrap());
464                     assert_eq!(actual_normal.2, &[]);
465 
466                     // unexpected end of slice
467                     {
468                         let offset: usize = e.lengths[..e.lengths.len() - 1].into_iter().sum();
469 
470                         let actual = Ipv6ExtensionsSlice::from_slice_lax(
471                             ip_numbers[0],
472                             &e.slice()[..e.slice().len() - 1]
473                         );
474                         assert_eq!(&e.slice()[offset..e.slice().len() - 1], actual.2);
475                         assert_eq!(
476                             actual.3.unwrap().0,
477                             Len(err::LenError {
478                                 required_len: e.slice().len() - offset,
479                                 len: e.slice().len() - offset - 1,
480                                 len_source: LenSource::Slice,
481                                 layer: match ip_numbers[ip_numbers.len() - 2] {
482                                     AUTH => err::Layer::IpAuthHeader,
483                                     IPV6_FRAG => err::Layer::Ipv6FragHeader,
484                                     _ => err::Layer::Ipv6ExtHeader
485                                 },
486                                 layer_start_offset: offset,
487                             })
488                         );
489                     }
490                 }
491             }
492 
493             // test the parsing of different extension header combinations
494             for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
495 
496                 // single header parsing
497                 run_test(
498                     &[*first_header, post_header],
499                     &[header_size],
500                 );
501 
502                 for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
503 
504                     // double header parsing
505                     run_test(
506                         &[*first_header, *second_header, post_header],
507                         &[header_size],
508                     );
509 
510                     for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
511                         // tripple header parsing
512                         run_test(
513                             &[*first_header, *second_header, *third_header, post_header],
514                             &[header_size],
515                         );
516                     }
517                 }
518             }
519 
520             // test that the auth content error gets forwarded
521             {
522                 let auth = IpAuthHeader::new(post_header, 0, 0, &[]).unwrap();
523                 let mut bytes = auth.to_bytes();
524                 // inject an invalid len value
525                 bytes[1] = 0;
526                 let actual = Ipv6ExtensionsSlice::from_slice_lax(AUTH, &bytes);
527 
528                 use err::ipv6_exts::HeaderError::IpAuth;
529                 use err::ip_auth::HeaderError::ZeroPayloadLen;
530                 assert_eq!(actual.0.slice(), &[]);
531                 assert_eq!(actual.1, AUTH);
532                 assert_eq!(actual.2, &bytes[..]);
533                 assert_eq!(actual.3.unwrap().0.content().unwrap(), &IpAuth(ZeroPayloadLen));
534             }
535         }
536     }
537 
538     proptest! {
539         #[test]
540         fn is_fragmenting_payload(
541             hop_by_hop_options in ipv6_raw_ext_any(),
542             destination_options in ipv6_raw_ext_any(),
543             routing in ipv6_raw_ext_any(),
544             auth in ip_auth_any(),
545             final_destination_options in ipv6_raw_ext_any()
546         ) {
547             // no fragment header
548             {
549                 let mut exts = Ipv6Extensions{
550                     hop_by_hop_options: Some(hop_by_hop_options),
551                     destination_options: Some(destination_options),
552                     routing: Some(
553                         Ipv6RoutingExtensions {
554                             routing,
555                             final_destination_options: Some(final_destination_options),
556                         }
557                     ),
558                     fragment: None,
559                     auth: Some(auth),
560                 };
561                 let first_ip_number = exts.set_next_headers(UDP);
562 
563                 let mut bytes = Vec::with_capacity(exts.header_len());
564                 exts.write(&mut bytes, first_ip_number).unwrap();
565 
566                 let (header, _, _) = Ipv6ExtensionsSlice::from_slice(first_ip_number, &bytes).unwrap();
567                 assert_eq!(false, header.is_fragmenting_payload());
568             }
569 
570             // different variants of the fragment header with
571             // variants that fragment and variants that don't fragment
572             let frag_variants : [(bool, Ipv6FragmentHeader);4] = [
573                 (false, Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), false, 123)),
574                 (true, Ipv6FragmentHeader::new(UDP, 2.try_into().unwrap(), false, 123)),
575                 (true, Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), true, 123)),
576                 (true, Ipv6FragmentHeader::new(UDP, 3.try_into().unwrap(), true, 123)),
577             ];
578 
579             for (first_expected, first_header) in frag_variants.iter() {
580                 // single fragment header
581                 {
582                     let bytes = first_header.to_bytes();
583                     let (header, _, _) = Ipv6ExtensionsSlice::from_slice(IPV6_FRAG, &bytes).unwrap();
584                     assert_eq!(*first_expected, header.is_fragmenting_payload());
585                 }
586                 // two fragment headers
587                 for (second_expected, second_header) in frag_variants.iter() {
588                     let mut first_mod = first_header.clone();
589                     first_mod.next_header = IPV6_FRAG;
590                     let mut bytes = Vec::with_capacity(first_mod.header_len() + second_header.header_len());
591                     bytes.extend_from_slice(&first_mod.to_bytes());
592                     bytes.extend_from_slice(&second_header.to_bytes());
593 
594                     let (header, _, _) = Ipv6ExtensionsSlice::from_slice(IPV6_FRAG, &bytes).unwrap();
595                     assert_eq!(
596                         *first_expected || *second_expected,
597                         header.is_fragmenting_payload()
598                     );
599                 }
600             }
601         }
602     }
603 
604     #[test]
is_empty()605     fn is_empty() {
606         // empty
607         {
608             let slice = Ipv6ExtensionsSlice::from_slice(ip_number::UDP, &[])
609                 .unwrap()
610                 .0;
611             assert!(slice.is_empty());
612         }
613 
614         // fragment
615         {
616             let bytes =
617                 Ipv6FragmentHeader::new(ip_number::UDP, IpFragOffset::ZERO, true, 0).to_bytes();
618             let slice = Ipv6ExtensionsSlice::from_slice(ip_number::IPV6_FRAG, &bytes)
619                 .unwrap()
620                 .0;
621             assert_eq!(false, slice.is_empty());
622         }
623     }
624 
625     #[test]
debug()626     fn debug() {
627         use alloc::format;
628 
629         let a: Ipv6ExtensionsSlice = Default::default();
630         assert_eq!(
631             "Ipv6ExtensionsSlice { first_header: None, fragmented: false, slice: [] }",
632             &format!("{:?}", a)
633         );
634     }
635 
636     #[test]
clone_eq()637     fn clone_eq() {
638         let a: Ipv6ExtensionsSlice = Default::default();
639         assert_eq!(a, a.clone());
640     }
641 
642     #[test]
default()643     fn default() {
644         let a: Ipv6ExtensionsSlice = Default::default();
645         assert_eq!(a.is_fragmenting_payload(), false);
646         assert_eq!(a.first_header(), None);
647         assert_eq!(a.slice().len(), 0);
648     }
649 }
650