• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use 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 usecases 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)]
18 pub struct LaxIpv4Slice<'a> {
19     pub(crate) header: Ipv4HeaderSlice<'a>,
20     pub(crate) exts: Ipv4ExtensionsSlice<'a>,
21     pub(crate) payload: LaxIpPayloadSlice<'a>,
22 }
23 
24 impl<'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 usecases 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 exteions header.
50     /// * `LaxIpv4Slice::from_slice` ignores inconsistent `total_len` values. When the `total_len`
51     ///   value in the IPv4 header are inconsistant 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 seperated).
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 substitude 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`].
from_slice( slice: &[u8], ) -> Result<(LaxIpv4Slice, Option<err::ip_auth::HeaderSliceError>), err::ipv4::HeaderSliceError>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]
header(&self) -> Ipv4HeaderSlice200     pub fn header(&self) -> Ipv4HeaderSlice {
201         self.header
202     }
203 
204     /// Returns a slice containing the IPv4 extension headers.
205     #[inline]
extensions(&self) -> Ipv4ExtensionsSlice206     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]
payload(&self) -> &LaxIpPayloadSlice<'a>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]
payload_ip_number(&self) -> IpNumber222     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]
is_payload_fragmented(&self) -> bool228     pub fn is_payload_fragmented(&self) -> bool {
229         self.header.is_fragmenting_payload()
230     }
231 }
232 
233 #[cfg(test)]
234 mod 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 
combine_v4( v4: &Ipv4Header, ext: &crate::Ipv4Extensions, payload: &[u8], ) -> crate::IpHeaders276     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             }
342 
343             // error len smaller then min header len
344             for len in 1..Ipv4Header::MIN_LEN {
345                 assert_eq!(
346                     LaxIpv4Slice::from_slice(&buffer[..len]),
347                     Err(E::Len(err::LenError {
348                         required_len: Ipv4Header::MIN_LEN,
349                         len,
350                         len_source: LenSource::Slice,
351                         layer: err::Layer::Ipv4Header,
352                         layer_start_offset: 0,
353                     }))
354                 );
355             }
356 
357             // ihl value error
358             {
359                 let mut bad_ihl_buffer = buffer.clone();
360                 for bad_ihl in 0..5 {
361                     bad_ihl_buffer[0] = (bad_ihl_buffer[0] & 0xf0) | bad_ihl;
362                     assert_eq!(
363                         LaxIpv4Slice::from_slice(&bad_ihl_buffer),
364                         Err(E::Content(HeaderLengthSmallerThanHeader { ihl: bad_ihl }))
365                     );
366                 }
367             }
368 
369             // ihl len error
370             for short_ihl in 5..usize::from(v4.ihl()) {
371                 assert_eq!(
372                     LaxIpv4Slice::from_slice(&buffer[..4*short_ihl]),
373                     Err(E::Len(err::LenError {
374                         required_len: usize::from(v4.ihl())*4,
375                         len: 4*short_ihl,
376                         len_source: LenSource::Slice,
377                         layer: err::Layer::Ipv4Header,
378                         layer_start_offset: 0,
379                     }))
380                 );
381             }
382 
383             // total_len bigger then slice len (fallback to slice len)
384             for payload_len in 0..payload.len(){
385                 let (actual, stop_err) = LaxIpv4Slice::from_slice(&buffer[..v4.header_len() + v4_exts.header_len() + payload_len]).unwrap();
386                 assert_eq!(stop_err, None);
387                 assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
388                 assert_eq!(
389                     actual.payload(),
390                     &LaxIpPayloadSlice{
391                         incomplete: true,
392                         ip_number: header.next_header().unwrap(),
393                         fragmented: header.is_fragmenting_payload(),
394                         len_source: LenSource::Slice,
395                         payload: &payload[..payload_len]
396                     }
397                 );
398             }
399 
400             // len error ipv4 extensions
401             if v4_exts.header_len() > 0 {
402                 let (actual, stop_err) = LaxIpv4Slice::from_slice(&buffer[..v4.header_len() + 1]).unwrap();
403                 assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
404                 assert_eq!(
405                     actual.payload(),
406                     &LaxIpPayloadSlice{
407                         incomplete: true,
408                         ip_number: AUTH,
409                         fragmented: header.is_fragmenting_payload(),
410                         len_source: LenSource::Slice,
411                         payload: &buffer[v4.header_len()..v4.header_len() + 1]
412                     }
413                 );
414                 assert_eq!(stop_err, Some(A::Len(LenError{
415                     required_len: IpAuthHeader::MIN_LEN,
416                     len: 1,
417                     len_source: LenSource::Slice,
418                     layer: err::Layer::IpAuthHeader,
419                     layer_start_offset: header.ipv4().unwrap().0.header_len()
420                 })));
421             }
422 
423             // content error ipv4 extensions
424             if v4_exts.auth.is_some() {
425                 use err::ip_auth::HeaderError::ZeroPayloadLen;
426 
427 
428                 // introduce a auth header zero payload error
429                 let mut errored_buffer = buffer.clone();
430                 // inject length zero into auth header (not valid, will
431                 // trigger a content error)
432                 errored_buffer[v4.header_len() + 1] = 0;
433 
434                 let (actual, stop_err) = LaxIpv4Slice::from_slice(&errored_buffer).unwrap();
435                 assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
436                 assert!(actual.extensions().is_empty());
437                 let auth_offset = header.ipv4().unwrap().0.header_len();
438                 let payload_end = auth_offset + v4_exts.auth.map(|v| v.header_len()).unwrap() + payload.len();
439                 assert_eq!(
440                     actual.payload(),
441                     &LaxIpPayloadSlice{
442                         incomplete: false,
443                         ip_number: AUTH,
444                         fragmented: header.is_fragmenting_payload(),
445                         len_source: LenSource::Ipv4HeaderTotalLen,
446                         payload: &errored_buffer[auth_offset..payload_end]
447                     }
448                 );
449                 assert_eq!(stop_err, Some(A::Content(ZeroPayloadLen)));
450             }
451 
452             // total length smaller the header (fallback to slice len)
453             {
454                 let bad_total_len = (v4.header_len() - 1) as u16;
455 
456                 let mut buffer = buffer.clone();
457                 // inject bad total_len
458                 let bad_total_len_be = bad_total_len.to_be_bytes();
459                 buffer[2] = bad_total_len_be[0];
460                 buffer[3] = bad_total_len_be[1];
461 
462                 let (actual, actual_stop_error) = LaxIpv4Slice::from_slice(&buffer[..]).unwrap();
463                 assert_eq!(actual_stop_error, None);
464 
465                 let (v4_header, v4_exts) = header.ipv4().unwrap();
466                 let expected_headers = IpHeaders::Ipv4(
467                     {
468                         let mut expected_v4 = v4_header.clone();
469                         expected_v4.total_len = bad_total_len;
470                         expected_v4
471                     },
472                     v4_exts.clone()
473                 );
474                 assert_eq!(expected_headers.ipv4().unwrap().0, &actual.header().to_header());
475                 assert_eq!(
476                     actual.payload(),
477                     &LaxIpPayloadSlice{
478                         incomplete: false,
479                         ip_number: header.next_header().unwrap(),
480                         fragmented: header.is_fragmenting_payload(),
481                         len_source: LenSource::Slice,
482                         payload: &buffer[v4_header.header_len() + v4_exts.header_len()..],
483                     }
484                 );
485             }
486         }
487     }
488 
489     #[test]
is_payload_fragmented()490     fn is_payload_fragmented() {
491         use crate::ip_number::UDP;
492         // non-fragmented
493         {
494             let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
495             let ipv4 =
496                 Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
497             let data = {
498                 let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
499                 data.extend_from_slice(&ipv4.to_bytes());
500                 data.extend_from_slice(&payload);
501                 data
502             };
503 
504             let (slice, stop_err) = LaxIpv4Slice::from_slice(&data).unwrap();
505             assert_eq!(None, stop_err);
506             assert!(false == slice.is_payload_fragmented());
507         }
508         // fragmented
509         {
510             let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
511             let mut ipv4 =
512                 Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
513             ipv4.fragment_offset = 123.try_into().unwrap();
514             let data = {
515                 let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
516                 data.extend_from_slice(&ipv4.to_bytes());
517                 data.extend_from_slice(&payload);
518                 data
519             };
520 
521             let (slice, stop_err) = LaxIpv4Slice::from_slice(&data).unwrap();
522             assert_eq!(None, stop_err);
523             assert!(slice.is_payload_fragmented());
524         }
525     }
526 }
527