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

etherparse/net/
lax_ipv4_slice.rs

1use crate::*;
2
3/// Slice containing laxly separated IPv4 headers & payload.
4///
5/// Compared to the normal [`Ipv4Slice`] this slice allows the
6/// payload to be incomplete/cut off and errors to be present in
7/// the IpPayload.
8///
9/// The main use cases for "laxly" parsed slices are are:
10///
11/// * Parsing packets that have been cut off. This is, for example, useful to
12///   parse packets returned via ICMP as these usually only contain the start.
13/// * Parsing packets where the `total_len` (for IPv4) have not yet been set.
14///   This can be useful when parsing packets which have been recorded in a
15///   layer before the length field was set (e.g. before the operating
16///   system set the length fields).
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct LaxIpv4Slice<'a> {
19    pub(crate) header: Ipv4HeaderSlice<'a>,
20    pub(crate) exts: Ipv4ExtensionsSlice<'a>,
21    pub(crate) payload: LaxIpPayloadSlice<'a>,
22}
23
24impl<'a> LaxIpv4Slice<'a> {
25    /// Separates and validates IPv4 headers (including extension headers) &
26    /// the payload from the given slice with less strict length checks
27    /// (useful for cut off packet or for packets with unset length fields).
28    ///
29    /// If you want to only receive correct IpPayloads use [`Ipv4Slice::from_slice`]
30    /// instead.
31    ///
32    /// The main use cases for this functions are:
33    ///
34    /// * Parsing packets that have been cut off. This is, for example, useful to
35    ///   parse packets returned via ICMP as these usually only contain the start.
36    /// * Parsing packets where the `total_len` (for IPv4) have not yet been set.
37    ///   This can be useful when parsing packets which have been recorded in a
38    ///   layer before the length field was set (e.g. before the operating
39    ///   system set the length fields).
40    ///
41    /// # Differences to `Ipv4Slice::from_slice`:
42    ///
43    /// There are two main differences:
44    ///
45    /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
46    ///   with the successfully parsed parts and the error as optional. Only if an
47    ///   unrecoverable error is encountered in the IP header itself an `Err` is returned.
48    ///   In the normal `Ipv4Slice::from_slice` function an `Err` is returned if an error is
49    ///   encountered in an extension header.
50    /// * `LaxIpv4Slice::from_slice` ignores inconsistent `total_len` values. When the `total_len`
51    ///   value in the IPv4 header are inconsistent the length of the given slice is
52    ///   used as a substitute.
53    ///
54    /// ## What happens in the `total_len` value is inconsistent?
55    ///
56    /// When the total_length value in the IPv4 header is inconsistent the
57    /// length of the given slice is used as a substitute. This can happen
58    /// if the `total_length` field in the IPv4 header is:
59    ///
60    ///  * Bigger then the given slice (payload cannot fully be separated).
61    ///  * Too small to contain at least the IPv4 header.
62    ///
63    /// Additionally you can check if more data was expected based on the
64    /// `total_len` but the given slice was too small by checking if `incomplete`
65    /// is set to `true` in the returned [`LaxIpPayloadSlice`].
66    ///
67    /// You can check if the slice length was used as a substitute by checking
68    /// if the `len_source` value in the returned [`LaxIpPayloadSlice`] is set to
69    /// [`LenSource::Slice`]. If a substitution was not needed `len_source`
70    /// is set to [`LenSource::Ipv4HeaderTotalLen`].
71    pub fn from_slice(
72        slice: &[u8],
73    ) -> Result<(LaxIpv4Slice, Option<err::ip_auth::HeaderSliceError>), err::ipv4::HeaderSliceError>
74    {
75        use crate::ip_number::AUTH;
76
77        // decode the header
78        let header = Ipv4HeaderSlice::from_slice(slice)?;
79
80        // validate total_len at least contains the header
81        let header_total_len: usize = header.total_len().into();
82        let (header_payload, len_source, incomplete) = if header_total_len < header.slice().len() {
83            // total_length is smaller then the header itself
84            // fall back to the slice for the length
85            (
86                unsafe {
87                    core::slice::from_raw_parts(
88                        slice.as_ptr().add(header.slice().len()),
89                        slice.len() - header.slice().len(),
90                    )
91                },
92                LenSource::Slice,
93                // note that we have no indication that the packet is incomplete
94                false,
95            )
96        } else if header_total_len > slice.len() {
97            // more data was expected, fallback to slice and report payload as "incomplete"
98            (
99                unsafe {
100                    core::slice::from_raw_parts(
101                        slice.as_ptr().add(header.slice().len()),
102                        slice.len() - header.slice().len(),
103                    )
104                },
105                LenSource::Slice,
106                true, // incomplete
107            )
108        } else {
109            // all good the packet seems to be complete
110            (
111                unsafe {
112                    core::slice::from_raw_parts(
113                        slice.as_ptr().add(header.slice().len()),
114                        header_total_len - header.slice().len(),
115                    )
116                },
117                LenSource::Ipv4HeaderTotalLen,
118                false,
119            )
120        };
121
122        // decode the authentication header if needed
123        let fragmented = header.is_fragmenting_payload();
124        match header.protocol() {
125            AUTH => {
126                use crate::err::ip_auth::HeaderSliceError as E;
127
128                // parse extension headers
129                match IpAuthHeaderSlice::from_slice(header_payload) {
130                    Ok(auth) => {
131                        // remove the extension header from the payload
132                        let payload = unsafe {
133                            core::slice::from_raw_parts(
134                                header_payload.as_ptr().add(auth.slice().len()),
135                                header_payload.len() - auth.slice().len(),
136                            )
137                        };
138                        let ip_number = auth.next_header();
139                        Ok((
140                            LaxIpv4Slice {
141                                header,
142                                exts: Ipv4ExtensionsSlice { auth: Some(auth) },
143                                payload: LaxIpPayloadSlice {
144                                    incomplete,
145                                    ip_number,
146                                    fragmented,
147                                    len_source,
148                                    payload,
149                                },
150                            },
151                            None,
152                        ))
153                    }
154                    Err(err) => {
155                        let err = match err {
156                            E::Len(mut l) => {
157                                // change the length source to the ipv4 header
158                                l.len_source = len_source;
159                                l.layer_start_offset += header.slice().len();
160                                E::Len(l)
161                            }
162                            E::Content(err) => E::Content(err),
163                        };
164                        Ok((
165                            LaxIpv4Slice {
166                                header,
167                                exts: Ipv4ExtensionsSlice { auth: None },
168                                payload: LaxIpPayloadSlice {
169                                    incomplete,
170                                    ip_number: AUTH,
171                                    fragmented,
172                                    len_source,
173                                    payload: header_payload,
174                                },
175                            },
176                            Some(err),
177                        ))
178                    }
179                }
180            }
181            ip_number => Ok((
182                LaxIpv4Slice {
183                    header,
184                    exts: Ipv4ExtensionsSlice { auth: None },
185                    payload: LaxIpPayloadSlice {
186                        incomplete,
187                        ip_number,
188                        fragmented,
189                        len_source,
190                        payload: header_payload,
191                    },
192                },
193                None,
194            )),
195        }
196    }
197
198    /// Returns a slice containing the IPv4 header.
199    #[inline]
200    pub fn header(&self) -> Ipv4HeaderSlice {
201        self.header
202    }
203
204    /// Returns a slice containing the IPv4 extension headers.
205    #[inline]
206    pub fn extensions(&self) -> Ipv4ExtensionsSlice {
207        self.exts
208    }
209
210    /// Returns a slice containing the data after the IPv4 header
211    /// and IPv4 extensions headers.
212    #[inline]
213    pub fn payload(&self) -> &LaxIpPayloadSlice<'a> {
214        &self.payload
215    }
216
217    /// Returns the ip number the type of payload of the IPv4 packet.
218    ///
219    /// This function returns the ip number stored in the last
220    /// IPv4 header or extension header.
221    #[inline]
222    pub fn payload_ip_number(&self) -> IpNumber {
223        self.payload.ip_number
224    }
225
226    /// Returns true if the payload is flagged as being fragmented.
227    #[inline]
228    pub fn is_payload_fragmented(&self) -> bool {
229        self.header.is_fragmenting_payload()
230    }
231}
232
233#[cfg(test)]
234mod test {
235    use super::*;
236    use crate::{err::LenError, ip_number::AUTH, test_gens::*};
237    use alloc::{format, vec::Vec};
238    use proptest::prelude::*;
239
240    proptest! {
241        #[test]
242        fn debug_clone_eq(
243            ipv4_base in ipv4_any(),
244            auth in ip_auth_any()
245        ) {
246            let payload: [u8;4] = [1,2,3,4];
247            let mut data = Vec::with_capacity(
248                ipv4_base.header_len() +
249                auth.header_len() +
250                payload.len()
251            );
252            let mut ipv4 = ipv4_base.clone();
253            ipv4.protocol = crate::ip_number::AUTH;
254            ipv4.set_payload_len(auth.header_len() + payload.len()).unwrap();
255            data.extend_from_slice(&ipv4.to_bytes());
256            data.extend_from_slice(&auth.to_bytes());
257            data.extend_from_slice(&payload);
258
259            // decode packet
260            let (slice, _) = LaxIpv4Slice::from_slice(&data).unwrap();
261
262            // check debug output
263            prop_assert_eq!(
264                format!("{:?}", slice),
265                format!(
266                    "LaxIpv4Slice {{ header: {:?}, exts: {:?}, payload: {:?} }}",
267                    slice.header(),
268                    slice.extensions(),
269                    slice.payload()
270                )
271            );
272            prop_assert_eq!(slice.clone(), slice);
273        }
274    }
275
276    fn combine_v4(
277        v4: &Ipv4Header,
278        ext: &crate::Ipv4Extensions,
279        payload: &[u8],
280    ) -> crate::IpHeaders {
281        use crate::ip_number::UDP;
282        crate::IpHeaders::Ipv4(
283            {
284                let mut v4 = v4.clone();
285                v4.protocol = if ext.auth.is_some() { AUTH } else { UDP };
286                v4.total_len = (v4.header_len() + ext.header_len() + payload.len()) as u16;
287                v4.header_checksum = v4.calc_header_checksum();
288                v4
289            },
290            ext.clone(),
291        )
292    }
293
294    proptest! {
295        #[test]
296        fn from_slice(
297            v4 in ipv4_any(),
298            v4_exts in ipv4_extensions_any()
299        ) {
300            use crate::err::{self, ipv4::HeaderError::*};
301            use crate::err::ipv4::HeaderSliceError as E;
302            use err::ip_auth::HeaderSliceError as A;
303
304            let payload = [1,2,3,4];
305
306            // empty error
307            assert_eq!(
308                LaxIpv4Slice::from_slice(&[]),
309                Err(E::Len(err::LenError {
310                    required_len: 20,
311                    len: 0,
312                    len_source: LenSource::Slice,
313                    layer: err::Layer::Ipv4Header,
314                    layer_start_offset: 0,
315                }))
316            );
317
318            // build a buffer with a valid packet
319            let header = combine_v4(&v4, &v4_exts, &payload);
320            let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
321            header.write(&mut buffer).unwrap();
322            buffer.extend_from_slice(&payload);
323            buffer.push(1); // add some value to check the return slice
324
325            // normal read
326            {
327                let (actual, actual_stop_err) = LaxIpv4Slice::from_slice(&buffer).unwrap();
328                assert_eq!(None, actual_stop_err);
329                assert_eq!(&actual.header.to_header(), header.ipv4().unwrap().0);
330                assert_eq!(&actual.extensions().to_header(), header.ipv4().unwrap().1);
331                assert_eq!(
332                    actual.payload,
333                    LaxIpPayloadSlice{
334                        incomplete: false,
335                        ip_number: header.next_header().unwrap(),
336                        fragmented: header.is_fragmenting_payload(),
337                        len_source: LenSource::Ipv4HeaderTotalLen,
338                        payload: &payload
339                    }
340                );
341                assert_eq!(actual.payload_ip_number(), header.next_header().unwrap());
342            }
343
344            // error len smaller then min header len
345            for len in 1..Ipv4Header::MIN_LEN {
346                assert_eq!(
347                    LaxIpv4Slice::from_slice(&buffer[..len]),
348                    Err(E::Len(err::LenError {
349                        required_len: Ipv4Header::MIN_LEN,
350                        len,
351                        len_source: LenSource::Slice,
352                        layer: err::Layer::Ipv4Header,
353                        layer_start_offset: 0,
354                    }))
355                );
356            }
357
358            // ihl value error
359            {
360                let mut bad_ihl_buffer = buffer.clone();
361                for bad_ihl in 0..5 {
362                    bad_ihl_buffer[0] = (bad_ihl_buffer[0] & 0xf0) | bad_ihl;
363                    assert_eq!(
364                        LaxIpv4Slice::from_slice(&bad_ihl_buffer),
365                        Err(E::Content(HeaderLengthSmallerThanHeader { ihl: bad_ihl }))
366                    );
367                }
368            }
369
370            // ihl len error
371            for short_ihl in 5..usize::from(v4.ihl()) {
372                assert_eq!(
373                    LaxIpv4Slice::from_slice(&buffer[..4*short_ihl]),
374                    Err(E::Len(err::LenError {
375                        required_len: usize::from(v4.ihl())*4,
376                        len: 4*short_ihl,
377                        len_source: LenSource::Slice,
378                        layer: err::Layer::Ipv4Header,
379                        layer_start_offset: 0,
380                    }))
381                );
382            }
383
384            // total_len bigger then slice len (fallback to slice len)
385            for payload_len in 0..payload.len(){
386                let (actual, stop_err) = LaxIpv4Slice::from_slice(&buffer[..v4.header_len() + v4_exts.header_len() + payload_len]).unwrap();
387                assert_eq!(stop_err, None);
388                assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
389                assert_eq!(
390                    actual.payload(),
391                    &LaxIpPayloadSlice{
392                        incomplete: true,
393                        ip_number: header.next_header().unwrap(),
394                        fragmented: header.is_fragmenting_payload(),
395                        len_source: LenSource::Slice,
396                        payload: &payload[..payload_len]
397                    }
398                );
399            }
400
401            // len error ipv4 extensions
402            if v4_exts.header_len() > 0 {
403                let (actual, stop_err) = LaxIpv4Slice::from_slice(&buffer[..v4.header_len() + 1]).unwrap();
404                assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
405                assert_eq!(
406                    actual.payload(),
407                    &LaxIpPayloadSlice{
408                        incomplete: true,
409                        ip_number: AUTH,
410                        fragmented: header.is_fragmenting_payload(),
411                        len_source: LenSource::Slice,
412                        payload: &buffer[v4.header_len()..v4.header_len() + 1]
413                    }
414                );
415                assert_eq!(stop_err, Some(A::Len(LenError{
416                    required_len: IpAuthHeader::MIN_LEN,
417                    len: 1,
418                    len_source: LenSource::Slice,
419                    layer: err::Layer::IpAuthHeader,
420                    layer_start_offset: header.ipv4().unwrap().0.header_len()
421                })));
422            }
423
424            // content error ipv4 extensions
425            if v4_exts.auth.is_some() {
426                use err::ip_auth::HeaderError::ZeroPayloadLen;
427
428
429                // introduce a auth header zero payload error
430                let mut errored_buffer = buffer.clone();
431                // inject length zero into auth header (not valid, will
432                // trigger a content error)
433                errored_buffer[v4.header_len() + 1] = 0;
434
435                let (actual, stop_err) = LaxIpv4Slice::from_slice(&errored_buffer).unwrap();
436                assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
437                assert!(actual.extensions().is_empty());
438                let auth_offset = header.ipv4().unwrap().0.header_len();
439                let payload_end = auth_offset + v4_exts.auth.map(|v| v.header_len()).unwrap() + payload.len();
440                assert_eq!(
441                    actual.payload(),
442                    &LaxIpPayloadSlice{
443                        incomplete: false,
444                        ip_number: AUTH,
445                        fragmented: header.is_fragmenting_payload(),
446                        len_source: LenSource::Ipv4HeaderTotalLen,
447                        payload: &errored_buffer[auth_offset..payload_end]
448                    }
449                );
450                assert_eq!(stop_err, Some(A::Content(ZeroPayloadLen)));
451            }
452
453            // total length smaller the header (fallback to slice len)
454            {
455                let bad_total_len = (v4.header_len() - 1) as u16;
456
457                let mut buffer = buffer.clone();
458                // inject bad total_len
459                let bad_total_len_be = bad_total_len.to_be_bytes();
460                buffer[2] = bad_total_len_be[0];
461                buffer[3] = bad_total_len_be[1];
462
463                let (actual, actual_stop_error) = LaxIpv4Slice::from_slice(&buffer[..]).unwrap();
464                assert_eq!(actual_stop_error, None);
465
466                let (v4_header, v4_exts) = header.ipv4().unwrap();
467                let expected_headers = IpHeaders::Ipv4(
468                    {
469                        let mut expected_v4 = v4_header.clone();
470                        expected_v4.total_len = bad_total_len;
471                        expected_v4
472                    },
473                    v4_exts.clone()
474                );
475                assert_eq!(expected_headers.ipv4().unwrap().0, &actual.header().to_header());
476                assert_eq!(
477                    actual.payload(),
478                    &LaxIpPayloadSlice{
479                        incomplete: false,
480                        ip_number: header.next_header().unwrap(),
481                        fragmented: header.is_fragmenting_payload(),
482                        len_source: LenSource::Slice,
483                        payload: &buffer[v4_header.header_len() + v4_exts.header_len()..],
484                    }
485                );
486            }
487        }
488    }
489
490    #[test]
491    fn is_payload_fragmented() {
492        use crate::ip_number::UDP;
493        // non-fragmented
494        {
495            let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
496            let ipv4 =
497                Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
498            let data = {
499                let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
500                data.extend_from_slice(&ipv4.to_bytes());
501                data.extend_from_slice(&payload);
502                data
503            };
504
505            let (slice, stop_err) = LaxIpv4Slice::from_slice(&data).unwrap();
506            assert_eq!(None, stop_err);
507            assert!(false == slice.is_payload_fragmented());
508        }
509        // fragmented
510        {
511            let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
512            let mut ipv4 =
513                Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
514            ipv4.fragment_offset = 123.try_into().unwrap();
515            let data = {
516                let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
517                data.extend_from_slice(&ipv4.to_bytes());
518                data.extend_from_slice(&payload);
519                data
520            };
521
522            let (slice, stop_err) = LaxIpv4Slice::from_slice(&data).unwrap();
523            assert_eq!(None, stop_err);
524            assert!(slice.is_payload_fragmented());
525        }
526    }
527}