Thanks to visit codestin.com
Credit goes to docs.rs

etherparse/net/
lax_ipv6_slice.rs

1use 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 use cases 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)]
21pub struct LaxIpv6Slice<'a> {
22    pub(crate) header: Ipv6HeaderSlice<'a>,
23    pub(crate) exts: Ipv6ExtensionsSlice<'a>,
24    pub(crate) payload: LaxIpPayloadSlice<'a>,
25}
26
27impl<'a> LaxIpv6Slice<'a> {
28    /// separate 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 use cases 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 extension header.
53    /// * `LaxIpv4Slice::from_slice` ignores inconsistent `payload_length` values. When the
54    ///   `payload_length` value in the IPv6 header is inconsistent 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 substitute 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/substitute if the `payload_length`
65    /// field in the IPv6 header is
66    ///
67    /// * Bigger then the given slice (payload cannot fully be separated).
68    /// * The value `0`.
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]
156    pub fn header(&self) -> Ipv6HeaderSlice<'a> {
157        self.header
158    }
159
160    /// Returns a slice containing the IPv6 extension headers.
161    #[inline]
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]
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]
175    pub fn is_payload_fragmented(&self) -> bool {
176        self.payload.fragmented
177    }
178}
179
180#[cfg(test)]
181mod 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]
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}