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}