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