• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{
2     err::{ipv6, ipv6_exts},
3     *,
4 };
5 
6 /// Slice containing laxly separated IPv6 headers & payload.
7 ///
8 /// Compared to the normal [`Ipv6Slice`] this slice allows the
9 /// payload to incomplete/cut off and errors to be present in
10 /// the IpPayload.
11 ///
12 /// The main usecases for "laxly" parsed slices are are:
13 ///
14 /// * Parsing packets that have been cut off. This is, for example, useful to
15 ///   parse packets returned via ICMP as these usually only contain the start.
16 /// * Parsing packets where the `total_len` (for IPv4) have not yet been set.
17 ///   This can be useful when parsing packets which have been recorded in a
18 ///   layer before the length field was set (e.g. before the operating
19 ///   system set the length fields).
20 #[derive(Clone, Debug, Eq, PartialEq)]
21 pub struct LaxIpv6Slice<'a> {
22     pub(crate) header: Ipv6HeaderSlice<'a>,
23     pub(crate) exts: Ipv6ExtensionsSlice<'a>,
24     pub(crate) payload: LaxIpPayloadSlice<'a>,
25 }
26 
27 impl<'a> LaxIpv6Slice<'a> {
28     /// Seperate an IPv6 header (+ extensions) & the payload from the given slice with
29     /// less strict length checks (useful for cut off packet or for packets with
30     /// unset length fields).
31     ///
32     /// If you want to only receive correct IpPayloads use [`crate::Ipv4Slice::from_slice`]
33     /// instead.
34     ///
35     /// The main usecases for this functions are:
36     ///
37     /// * Parsing packets that have been cut off. This is, for example, useful to
38     ///   parse packets returned via ICMP as these usually only contain the start.
39     /// * Parsing packets where the `payload_length` (in the IPv6 header) has not
40     ///   yet been set. This can be useful when parsing packets which have been
41     ///   recorded in a layer before the length field was set (e.g. before the operating
42     ///   system set the length fields).
43     ///
44     /// # Differences to `from_slice`:
45     ///
46     /// There are two main differences:
47     ///
48     /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
49     ///   with the successfully parsed parts and the error as optional. Only if an
50     ///   unrecoverable error is encountered in the IP header itself an `Err` is returned.
51     ///   In the normal `Ipv4Slice::from_slice` function an `Err` is returned if an error is
52     ///   encountered in an exteions header.
53     /// * `LaxIpv4Slice::from_slice` ignores inconsistent `payload_length` values. When the
54     ///   `payload_length` value in the IPv6 header is inconsistant the length of
55     ///   the given slice is used as a substitute.
56     ///
57     /// You can check if the slice length was used as a substitude by checking
58     /// if the `len_source` value in the returned [`IpPayloadSlice`] is set to
59     /// [`LenSource::Slice`]. If a substitution was not needed `len_source`
60     /// is set to [`LenSource::Ipv6HeaderPayloadLen`].
61     ///
62     /// # When is the slice length used as a fallback?
63     ///
64     /// The slice length is used as a fallback/substitude if the `payload_length`
65     /// field in the IPv6 header is
66     ///
67     /// * Bigger then the given slice (payload cannot fully be seperated).
68     /// * The value `0`.
from_slice( slice: &'a [u8], ) -> Result< ( LaxIpv6Slice<'a>, Option<(ipv6_exts::HeaderSliceError, err::Layer)>, ), ipv6::HeaderSliceError, >69     pub fn from_slice(
70         slice: &'a [u8],
71     ) -> Result<
72         (
73             LaxIpv6Slice<'a>,
74             Option<(ipv6_exts::HeaderSliceError, err::Layer)>,
75         ),
76         ipv6::HeaderSliceError,
77     > {
78         // try reading the header
79         let header = Ipv6HeaderSlice::from_slice(slice)?;
80 
81         // restrict slice by the length specified in the header
82         let (header_payload, len_source, incomplete) =
83             if 0 == header.payload_length() && slice.len() > Ipv6Header::LEN {
84                 // In case the payload_length is 0 assume that the entire
85                 // rest of the slice is part of the packet until the jumbogram
86                 // parameters can be parsed.
87 
88                 // TODO: Add payload length parsing from the jumbogram
89                 (
90                     unsafe {
91                         core::slice::from_raw_parts(
92                             slice.as_ptr().add(Ipv6Header::LEN),
93                             slice.len() - Ipv6Header::LEN,
94                         )
95                     },
96                     LenSource::Slice,
97                     false,
98                 )
99             } else {
100                 let payload_len = usize::from(header.payload_length());
101                 let expected_len = Ipv6Header::LEN + payload_len;
102                 if slice.len() < expected_len {
103                     (
104                         unsafe {
105                             core::slice::from_raw_parts(
106                                 slice.as_ptr().add(Ipv6Header::LEN),
107                                 slice.len() - Ipv6Header::LEN,
108                             )
109                         },
110                         LenSource::Slice,
111                         true,
112                     )
113                 } else {
114                     (
115                         unsafe {
116                             core::slice::from_raw_parts(
117                                 slice.as_ptr().add(Ipv6Header::LEN),
118                                 payload_len,
119                             )
120                         },
121                         LenSource::Ipv6HeaderPayloadLen,
122                         false,
123                     )
124                 }
125             };
126 
127         // parse extension headers
128         let (exts, payload_ip_number, payload, mut ext_stop_err) =
129             Ipv6ExtensionsSlice::from_slice_lax(header.next_header(), header_payload);
130 
131         // modify length errors
132         if let Some((ipv6_exts::HeaderSliceError::Len(err), _)) = &mut ext_stop_err {
133             err.len_source = len_source;
134             err.layer_start_offset += Ipv6Header::LEN;
135         };
136 
137         let fragmented = exts.is_fragmenting_payload();
138         Ok((
139             LaxIpv6Slice {
140                 header,
141                 exts,
142                 payload: LaxIpPayloadSlice {
143                     incomplete,
144                     ip_number: payload_ip_number,
145                     fragmented,
146                     len_source,
147                     payload,
148                 },
149             },
150             ext_stop_err,
151         ))
152     }
153 
154     /// Returns a slice containing the IPv6 header.
155     #[inline]
header(&self) -> Ipv6HeaderSlice<'a>156     pub fn header(&self) -> Ipv6HeaderSlice<'a> {
157         self.header
158     }
159 
160     /// Returns a slice containing the IPv6 extension headers.
161     #[inline]
extensions(&self) -> &Ipv6ExtensionsSlice<'a>162     pub fn extensions(&self) -> &Ipv6ExtensionsSlice<'a> {
163         &self.exts
164     }
165 
166     /// Returns a slice containing the data after the IPv6 header
167     /// and IPv6 extensions headers.
168     #[inline]
payload(&self) -> &LaxIpPayloadSlice<'a>169     pub fn payload(&self) -> &LaxIpPayloadSlice<'a> {
170         &self.payload
171     }
172 
173     /// Returns true if the payload is flagged as being fragmented.
174     #[inline]
is_payload_fragmented(&self) -> bool175     pub fn is_payload_fragmented(&self) -> bool {
176         self.payload.fragmented
177     }
178 }
179 
180 #[cfg(test)]
181 mod test {
182     use super::*;
183     use crate::{
184         err::{Layer, LenError},
185         ip_number::{AUTH, IGMP, UDP},
186         test_gens::*,
187     };
188     use alloc::{format, vec::Vec};
189     use proptest::prelude::*;
190 
191     proptest! {
192         #[test]
193         fn debug_clone_eq(
194             ipv6_base in ipv6_any(),
195             auth_base in ip_auth_any()
196         ) {
197             let mut auth = auth_base.clone();
198             auth.next_header = IGMP;
199             let payload: [u8;4] = [1,2,3,4];
200             let mut data = Vec::with_capacity(
201                 ipv6_base.header_len() +
202                 auth.header_len() +
203                 payload.len()
204             );
205             let mut ipv6 = ipv6_base.clone();
206             ipv6.next_header = AUTH;
207             ipv6.payload_length = (auth.header_len() + payload.len()) as u16;
208             data.extend_from_slice(&ipv6.to_bytes());
209             data.extend_from_slice(&auth.to_bytes());
210             data.extend_from_slice(&payload);
211 
212             // decode packet
213             let (slice, _) = LaxIpv6Slice::from_slice(&data).unwrap();
214 
215             // check debug output
216             prop_assert_eq!(
217                 format!("{:?}", slice),
218                 format!(
219                     "LaxIpv6Slice {{ header: {:?}, exts: {:?}, payload: {:?} }}",
220                     slice.header(),
221                     slice.extensions(),
222                     slice.payload()
223                 )
224             );
225             prop_assert_eq!(slice.clone(), slice);
226         }
227     }
228 
229     proptest! {
230         #[test]
231         fn from_slice(
232             ipv6_base in ipv6_any(),
233             auth_base in ip_auth_any()
234         ) {
235             let payload: [u8;6] = [1,2,3,4,5,6];
236 
237             // build packets
238             let data_without_ext = {
239                 let mut data = Vec::with_capacity(
240                     ipv6_base.header_len() +
241                     payload.len() +
242                     4
243                 );
244                 let mut ipv6 = ipv6_base.clone();
245                 ipv6.payload_length = (payload.len()) as u16;
246                 ipv6.next_header = UDP;
247                 data.extend_from_slice(&ipv6.to_bytes());
248                 data.extend_from_slice(&payload);
249                 data.extend_from_slice(&[0,0,0,0]);
250                 data
251             };
252             let data_with_ext = {
253                 let payload: [u8;6] = [1,2,3,4,5,6];
254                 let mut data = Vec::with_capacity(
255                     ipv6_base.header_len() +
256                     auth_base.header_len() +
257                     payload.len() +
258                     4
259                 );
260                 let mut ipv6 = ipv6_base.clone();
261                 ipv6.payload_length = (auth_base.header_len() + payload.len()) as u16;
262                 ipv6.next_header = AUTH;
263                 let mut auth = auth_base.clone();
264                 auth.next_header = UDP;
265                 data.extend_from_slice(&ipv6.to_bytes());
266                 data.extend_from_slice(&auth.to_bytes());
267                 data.extend_from_slice(&payload);
268                 data.extend_from_slice(&[0,0,0,0]);
269                 data
270             };
271 
272             // parsing without extensions (normal length)
273             {
274                 let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data_without_ext).unwrap();
275                 prop_assert_eq!(None, actual_stop_err);
276                 prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv6_base.header_len()]);
277                 prop_assert!(actual.extensions().first_header().is_none());
278                 prop_assert_eq!(
279                     actual.payload(),
280                     &LaxIpPayloadSlice{
281                         incomplete: false,
282                         ip_number: UDP.into(),
283                         fragmented: false,
284                         len_source: LenSource::Ipv6HeaderPayloadLen,
285                         payload: &payload,
286                     }
287                 );
288             }
289 
290             // parsing with extensions (normal length)
291             {
292                 let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data_with_ext).unwrap();
293                 prop_assert_eq!(None, actual_stop_err);
294                 prop_assert_eq!(actual.header().slice(), &data_with_ext[..ipv6_base.header_len()]);
295                 let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data_with_ext[ipv6_base.header_len()..]).unwrap();
296                 prop_assert_eq!(
297                     actual.extensions(),
298                     &expected
299                 );
300                 prop_assert_eq!(
301                     actual.payload(),
302                     &LaxIpPayloadSlice{
303                         incomplete: false,
304                         ip_number: UDP.into(),
305                         fragmented: false,
306                         len_source: LenSource::Ipv6HeaderPayloadLen,
307                         payload: &payload,
308                     }
309                 );
310             }
311 
312             // parsing without extensions (zero length, fallback to slice length)
313             {
314                 // inject zero as payload length
315                 let mut data = data_without_ext.clone();
316                 data[4] = 0;
317                 data[5] = 0;
318                 let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data).unwrap();
319                 prop_assert_eq!(None, actual_stop_err);
320                 prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
321                 prop_assert!(actual.extensions().first_header().is_none());
322                 prop_assert_eq!(
323                     actual.payload(),
324                     &LaxIpPayloadSlice{
325                         incomplete: false,
326                         ip_number: UDP.into(),
327                         fragmented: false,
328                         len_source: LenSource::Slice,
329                         payload: &data[ipv6_base.header_len()..],
330                     }
331                 );
332             }
333 
334             // parsing with extensions (zero length, fallback to slice length)
335             {
336                 // inject zero as payload length
337                 let mut data = data_with_ext.clone();
338                 data[4] = 0;
339                 data[5] = 0;
340                 let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data).unwrap();
341                 prop_assert_eq!(None, actual_stop_err);
342                 prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
343                 let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data[ipv6_base.header_len()..]).unwrap();
344                 prop_assert_eq!(
345                     actual.extensions(),
346                     &expected
347                 );
348                 prop_assert_eq!(
349                     actual.payload(),
350                     &LaxIpPayloadSlice{
351                         incomplete: false,
352                         ip_number: UDP.into(),
353                         fragmented: false,
354                         len_source: LenSource::Slice,
355                         payload: &data[ipv6_base.header_len() + auth_base.header_len()..],
356                     }
357                 );
358             }
359 
360             // header content error
361             {
362                 use crate::err::ipv6::HeaderError;
363                 // inject invalid ip version
364                 let mut data = data_without_ext.clone();
365                 data[0] = data[0] & 0x0f; // version 0
366                 prop_assert_eq!(
367                     LaxIpv6Slice::from_slice(&data).unwrap_err(),
368                     ipv6::HeaderSliceError::Content(
369                         HeaderError::UnexpectedVersion{ version_number: 0 }
370                     )
371                 );
372             }
373 
374             // header length error
375             for len in 0..Ipv6Header::LEN {
376                 prop_assert_eq!(
377                     LaxIpv6Slice::from_slice(&data_without_ext[..len]).unwrap_err(),
378                     ipv6::HeaderSliceError::Len(
379                         LenError{
380                             required_len: Ipv6Header::LEN,
381                             len,
382                             len_source: LenSource::Slice,
383                             layer: Layer::Ipv6Header,
384                             layer_start_offset: 0
385                         }
386                     )
387                 );
388             }
389 
390             // payload length larger then slice (fallback to slice length)
391             {
392                 let len = ipv6_base.header_len() + payload.len() - 1;
393                 let (actual , actual_stop_err) = LaxIpv6Slice::from_slice(&data_without_ext[..len]).unwrap();
394                 prop_assert_eq!(actual_stop_err, None);
395                 prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv6_base.header_len()]);
396                 prop_assert_eq!(
397                     0,
398                     actual.extensions().slice().len()
399                 );
400                 prop_assert_eq!(
401                     actual.payload(),
402                     &LaxIpPayloadSlice{
403                         incomplete: true,
404                         ip_number: UDP.into(),
405                         fragmented: false,
406                         len_source: LenSource::Slice,
407                         payload: &data_without_ext[ipv6_base.header_len()..len],
408                     }
409                 );
410             }
411 
412             // payload length error auth header
413             {
414                 use crate::err::{LenError, Layer};
415 
416                 let required_len = ipv6_base.header_len() + auth_base.header_len();
417                 let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data_with_ext[..required_len - 1]).unwrap();
418                 prop_assert_eq!(
419                     actual_stop_err.unwrap(),
420                     (
421                         ipv6_exts::HeaderSliceError::Len(LenError{
422                             required_len: required_len - Ipv6Header::LEN,
423                             len: required_len - Ipv6Header::LEN - 1,
424                             len_source: LenSource::Slice,
425                             layer: Layer::IpAuthHeader,
426                             layer_start_offset: Ipv6Header::LEN,
427                         }),
428                         err::Layer::IpAuthHeader
429                     )
430                 );
431                 prop_assert_eq!(actual.header().slice(), &data_with_ext[..ipv6_base.header_len()]);
432                 prop_assert_eq!(
433                     actual.payload(),
434                     &LaxIpPayloadSlice{
435                         incomplete: true,
436                         ip_number: AUTH,
437                         fragmented: false,
438                         len_source: LenSource::Slice,
439                         payload: &data_with_ext[ipv6_base.header_len()..required_len - 1],
440                     }
441                 );
442             }
443 
444             // auth length error
445             {
446                 use crate::err::{LenError, Layer};
447 
448                 // inject payload length that is smaller then the auth header
449                 let mut data = data_with_ext.clone();
450                 let payload_len_too_small = auth_base.header_len() - 1;
451                 {
452                     let plts = (payload_len_too_small as u16).to_be_bytes();
453                     data[4] = plts[0];
454                     data[5] = plts[1];
455                 }
456 
457                 let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data).unwrap();
458                 prop_assert_eq!(
459                     actual_stop_err.unwrap(),
460                     (
461                         ipv6_exts::HeaderSliceError::Len(
462                             LenError{
463                                 required_len: auth_base.header_len(),
464                                 len: auth_base.header_len() - 1,
465                                 len_source: LenSource::Ipv6HeaderPayloadLen,
466                                 layer: Layer::IpAuthHeader,
467                                 layer_start_offset: ipv6_base.header_len(),
468                             }
469                         ),
470                         err::Layer::IpAuthHeader
471                     )
472                 );
473                 prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
474                 prop_assert_eq!(
475                     actual.payload(),
476                     &LaxIpPayloadSlice{
477                         incomplete: false,
478                         ip_number: AUTH,
479                         fragmented: false,
480                         len_source: LenSource::Ipv6HeaderPayloadLen,
481                         payload: &data[ipv6_base.header_len()..ipv6_base.header_len() + payload_len_too_small],
482                     }
483                 );
484             }
485 
486             // auth content error
487             {
488                 use crate::err::{ip_auth, ipv6_exts};
489 
490                 // inject zero as auth header length
491                 let mut data = data_with_ext.clone();
492                 data[ipv6_base.header_len() + 1] = 0;
493 
494                 let (actual, actual_stop_error) = LaxIpv6Slice::from_slice(&data).unwrap();
495 
496                 prop_assert_eq!(
497                     actual_stop_error.unwrap(),
498                     (
499                         ipv6_exts::HeaderSliceError::Content(ipv6_exts::HeaderError::IpAuth(
500                             ip_auth::HeaderError::ZeroPayloadLen
501                         )),
502                         err::Layer::IpAuthHeader
503                     )
504                 );
505                 prop_assert_eq!(
506                     actual.header().slice(),
507                     &data[..ipv6_base.header_len()]
508                 );
509                 prop_assert_eq!(
510                     actual.payload(),
511                     &LaxIpPayloadSlice{
512                         incomplete: false,
513                         ip_number: AUTH,
514                         fragmented: false,
515                         len_source: LenSource::Ipv6HeaderPayloadLen,
516                         payload: &data[ipv6_base.header_len()..ipv6_base.header_len() + auth_base.header_len() + payload.len()],
517                     }
518                 );
519             }
520         }
521     }
522 
523     #[test]
is_payload_fragmented()524     fn is_payload_fragmented() {
525         use crate::ip_number::{IPV6_FRAG, UDP};
526 
527         // not fragmented
528         {
529             let data = Ipv6Header {
530                 traffic_class: 0,
531                 flow_label: 1.try_into().unwrap(),
532                 payload_length: 0,
533                 next_header: UDP,
534                 hop_limit: 4,
535                 source: [0; 16],
536                 destination: [0; 16],
537             }
538             .to_bytes();
539             assert_eq!(
540                 false,
541                 LaxIpv6Slice::from_slice(&data)
542                     .unwrap()
543                     .0
544                     .is_payload_fragmented()
545             );
546         }
547 
548         // fragmented
549         {
550             let ipv6_frag = Ipv6FragmentHeader {
551                 next_header: UDP,
552                 fragment_offset: 0.try_into().unwrap(),
553                 more_fragments: true,
554                 identification: 0,
555             };
556             let ipv6 = Ipv6Header {
557                 traffic_class: 0,
558                 flow_label: 1.try_into().unwrap(),
559                 payload_length: ipv6_frag.header_len() as u16,
560                 next_header: IPV6_FRAG,
561                 hop_limit: 4,
562                 source: [0; 16],
563                 destination: [0; 16],
564             };
565 
566             let mut data = Vec::with_capacity(ipv6.header_len() + ipv6_frag.header_len());
567             data.extend_from_slice(&ipv6.to_bytes());
568             data.extend_from_slice(&ipv6_frag.to_bytes());
569             assert!(Ipv6Slice::from_slice(&data)
570                 .unwrap()
571                 .is_payload_fragmented());
572         }
573     }
574 }
575