1 use crate::*; 2 3 /// A slice containing an ICMPv6 network package. 4 /// 5 /// Struct allows the selective read of fields in the ICMPv6 6 /// packet. 7 #[derive(Clone, Debug, Eq, PartialEq)] 8 pub struct Icmpv6Slice<'a> { 9 pub(crate) slice: &'a [u8], 10 } 11 12 impl<'a> Icmpv6Slice<'a> { 13 /// Creates a slice containing an ICMPv6 packet. 14 /// 15 /// # Errors 16 /// 17 /// The function will return an `Err` [`err::LenError`] 18 /// if the given slice is too small (smaller then [`Icmpv6Header::MIN_LEN`]) or 19 /// too large (bigger then [`icmpv6::MAX_ICMPV6_BYTE_LEN`]). 20 #[inline] from_slice(slice: &'a [u8]) -> Result<Icmpv6Slice<'a>, err::LenError>21 pub fn from_slice(slice: &'a [u8]) -> Result<Icmpv6Slice<'a>, err::LenError> { 22 //check length 23 if slice.len() < Icmpv6Header::MIN_LEN { 24 return Err(err::LenError { 25 required_len: Icmpv6Header::MIN_LEN, 26 len: slice.len(), 27 len_source: LenSource::Slice, 28 layer: err::Layer::Icmpv6, 29 layer_start_offset: 0, 30 }); 31 } 32 if slice.len() > icmpv6::MAX_ICMPV6_BYTE_LEN { 33 return Err(err::LenError { 34 required_len: icmpv6::MAX_ICMPV6_BYTE_LEN, 35 len: slice.len(), 36 len_source: LenSource::Slice, 37 layer: err::Layer::Icmpv6, 38 layer_start_offset: 0, 39 }); 40 } 41 42 //done 43 Ok(Icmpv6Slice { slice }) 44 } 45 46 /// Decode the header fields and copy the results to a [`Icmpv6Header`] struct. 47 #[inline] header(&self) -> Icmpv6Header48 pub fn header(&self) -> Icmpv6Header { 49 Icmpv6Header { 50 icmp_type: self.icmp_type(), 51 checksum: self.checksum(), 52 } 53 } 54 55 /// Number of bytes/octets that will be converted into a 56 /// [`Icmpv6Header`] when [`Icmpv6Slice::header`] gets called. 57 #[inline] header_len(&self) -> usize58 pub fn header_len(&self) -> usize { 59 8 60 } 61 62 /// Decode the header values (excluding the checksum) into an [`Icmpv6Type`] enum. icmp_type(&self) -> Icmpv6Type63 pub fn icmp_type(&self) -> Icmpv6Type { 64 use crate::{icmpv6::*, Icmpv6Type::*}; 65 66 match self.type_u8() { 67 TYPE_DST_UNREACH => { 68 if let Some(code) = DestUnreachableCode::from_u8(self.code_u8()) { 69 return DestinationUnreachable(code); 70 } 71 } 72 TYPE_PACKET_TOO_BIG => { 73 if 0 == self.code_u8() { 74 return PacketTooBig { 75 mtu: u32::from_be_bytes(self.bytes5to8()), 76 }; 77 } 78 } 79 TYPE_TIME_EXCEEDED => { 80 if let Some(code) = TimeExceededCode::from_u8(self.code_u8()) { 81 return TimeExceeded(code); 82 } 83 } 84 TYPE_PARAMETER_PROBLEM => { 85 if let Some(code) = ParameterProblemCode::from_u8(self.code_u8()) { 86 return ParameterProblem(ParameterProblemHeader { 87 code, 88 pointer: u32::from_be_bytes(self.bytes5to8()), 89 }); 90 } 91 } 92 TYPE_ECHO_REQUEST => { 93 if 0 == self.code_u8() { 94 return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8())); 95 } 96 } 97 TYPE_ECHO_REPLY => { 98 if 0 == self.code_u8() { 99 return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8())); 100 } 101 } 102 _ => {} 103 } 104 Unknown { 105 type_u8: self.type_u8(), 106 code_u8: self.code_u8(), 107 bytes5to8: self.bytes5to8(), 108 } 109 } 110 111 /// Returns "type" value in the ICMPv6 header. 112 #[inline] type_u8(&self) -> u8113 pub fn type_u8(&self) -> u8 { 114 // SAFETY: 115 // Safe as the contructor checks that the slice has 116 // at least the length of Icmpv6Header::MIN_LEN (8). 117 unsafe { *self.slice.get_unchecked(0) } 118 } 119 120 /// Returns "code" value in the ICMPv6 header. 121 #[inline] code_u8(&self) -> u8122 pub fn code_u8(&self) -> u8 { 123 // SAFETY: 124 // Safe as the contructor checks that the slice has 125 // at least the length of Icmpv6Header::MIN_LEN (8). 126 unsafe { *self.slice.get_unchecked(1) } 127 } 128 129 /// Returns "checksum" value in the ICMPv6 header. 130 #[inline] checksum(&self) -> u16131 pub fn checksum(&self) -> u16 { 132 // SAFETY: 133 // Safe as the contructor checks that the slice has 134 // at least the length of Icmpv6Header::MIN_LEN (8). 135 unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } 136 } 137 138 /// Returns if the checksum in the slice is correct. is_checksum_valid(&self, source_ip: [u8; 16], destination_ip: [u8; 16]) -> bool139 pub fn is_checksum_valid(&self, source_ip: [u8; 16], destination_ip: [u8; 16]) -> bool { 140 // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader, 141 // unlike Icmp4 142 checksum::Sum16BitWords::new() 143 .add_16bytes(source_ip) 144 .add_16bytes(destination_ip) 145 .add_4bytes((self.slice().len() as u32).to_be_bytes()) 146 .add_2bytes([0, ip_number::IPV6_ICMP.0]) 147 // NOTE: From RFC 1071 148 // To check a checksum, the 1's complement sum is computed over the 149 // same set of octets, including the checksum field. If the result 150 // is all 1 bits (-0 in 1's complement arithmetic), the check 151 // succeeds. 152 .add_slice(self.slice) 153 .ones_complement() 154 == 0 155 } 156 157 /// Returns the bytes from position 4 till and including the 8th position 158 /// in the ICMPv6 header. 159 /// 160 /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP 161 /// packet can depending on the ICMPv6 type and code contain additional data. 162 #[inline] bytes5to8(&self) -> [u8; 4]163 pub fn bytes5to8(&self) -> [u8; 4] { 164 // SAFETY: 165 // Safe as the contructor checks that the slice has 166 // at least the length of Icmpv6Header::MIN_LEN (8). 167 unsafe { 168 [ 169 *self.slice.get_unchecked(4), 170 *self.slice.get_unchecked(5), 171 *self.slice.get_unchecked(6), 172 *self.slice.get_unchecked(7), 173 ] 174 } 175 } 176 177 /// Returns the slice containing the ICMPv6 packet. 178 #[inline] slice(&self) -> &'a [u8]179 pub fn slice(&self) -> &'a [u8] { 180 self.slice 181 } 182 183 /// Returns a slice to the bytes not covered by `.header()`. 184 #[inline] payload(&self) -> &'a [u8]185 pub fn payload(&self) -> &'a [u8] { 186 // SAFETY: 187 // Safe as the contructor checks that the slice has 188 // at least the length of Icmpv6Header::MIN_LEN(8). 189 unsafe { core::slice::from_raw_parts(self.slice.as_ptr().add(8), self.slice.len() - 8) } 190 } 191 } 192 193 #[cfg(test)] 194 mod test { 195 use crate::{icmpv6::*, test_gens::*, Icmpv6Type::*, *}; 196 use alloc::{format, vec::Vec}; 197 use proptest::prelude::*; 198 199 proptest! { 200 #[test] 201 fn from_slice(slice in proptest::collection::vec(any::<u8>(), 8..1024)) { 202 // ok case 203 assert_eq!(Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(), &slice[..]); 204 205 // too small size error case 206 for len in 0..8 { 207 assert_eq!( 208 Icmpv6Slice::from_slice(&slice[..len]).unwrap_err(), 209 err::LenError{ 210 required_len: Icmpv6Header::MIN_LEN, 211 len: len, 212 len_source: LenSource::Slice, 213 layer: err::Layer::Icmpv6, 214 layer_start_offset: 0, 215 } 216 ); 217 } 218 } 219 } 220 221 proptest! { 222 /// This error can only occur on systems with a pointer size 223 /// bigger then 64 bits. 224 #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))] 225 #[test] 226 fn from_slice_too_big_error( 227 bad_len in ((core::u32::MAX as usize) + 1)..=(core::isize::MAX as usize), 228 ) { 229 // too large packet error case 230 { 231 // SAFETY: In case the error is not triggered 232 // a segmentation fault will be triggered. 233 let too_big_slice = unsafe { 234 //NOTE: The pointer must be initialized with a non null value 235 // otherwise a key constraint of slices is not fulfilled 236 // which can lead to crashes in release mode. 237 use core::ptr::NonNull; 238 core::slice::from_raw_parts( 239 NonNull::<u8>::dangling().as_ptr(), 240 bad_len 241 ) 242 }; 243 assert_eq!( 244 Icmpv6Slice::from_slice(too_big_slice).unwrap_err(), 245 err::LenError{ 246 required_len: u32::MAX as usize, 247 len: bad_len, 248 len_source: LenSource::Slice, 249 layer: err::Layer::Icmpv6, 250 layer_start_offset: 0 251 } 252 ); 253 } 254 } 255 } 256 257 proptest! { 258 #[test] 259 fn header( 260 icmp_type in icmpv6_type_any(), 261 checksum in any::<u16>() 262 ) { 263 let expected = Icmpv6Header { 264 icmp_type, 265 checksum 266 }; 267 assert_eq!( 268 Icmpv6Slice::from_slice(&expected.to_bytes()).unwrap().header(), 269 expected 270 ); 271 } 272 } 273 274 proptest! { 275 #[test] 276 fn icmp_type( 277 checksum in any::<[u8;2]>(), 278 bytes5to8 in any::<[u8;4]>() 279 ) { 280 use Icmpv6Type::*; 281 282 let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;8] { 283 [ 284 type_u8, code_u8, checksum[0], checksum[1], 285 bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3] 286 ] 287 }; 288 289 let assert_unknown = |type_u8: u8, code_u8: u8| { 290 assert_eq!( 291 Icmpv6Slice::from_slice(&gen_bytes(type_u8, code_u8)).unwrap().icmp_type(), 292 Unknown{ 293 type_u8, 294 code_u8, 295 bytes5to8, 296 } 297 ); 298 }; 299 300 // destination unreachable 301 { 302 // known codes 303 for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES { 304 assert_eq!( 305 Icmpv6Slice::from_slice(&gen_bytes(TYPE_DST_UNREACH, code_u8)).unwrap().icmp_type(), 306 DestinationUnreachable(code) 307 ); 308 } 309 310 // unknown codes 311 for code_u8 in 7..=u8::MAX { 312 assert_unknown(TYPE_DST_UNREACH, code_u8); 313 } 314 } 315 316 // packet too big 317 { 318 // known code 319 assert_eq!( 320 Icmpv6Slice::from_slice(&gen_bytes(TYPE_PACKET_TOO_BIG, 0)).unwrap().icmp_type(), 321 PacketTooBig { 322 mtu: u32::from_be_bytes(bytes5to8) 323 } 324 ); 325 326 // unknown code 327 for code_u8 in 1..=u8::MAX { 328 assert_unknown(TYPE_PACKET_TOO_BIG, code_u8); 329 } 330 } 331 332 // time exceeded 333 { 334 // known codes 335 for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES { 336 assert_eq!( 337 Icmpv6Slice::from_slice(&gen_bytes(TYPE_TIME_EXCEEDED, code_u8)).unwrap().icmp_type(), 338 TimeExceeded(code) 339 ); 340 } 341 342 // unknown codes 343 for code_u8 in 2..=u8::MAX { 344 assert_unknown(TYPE_TIME_EXCEEDED, code_u8); 345 } 346 } 347 348 // parameter problem 349 { 350 // known codes 351 for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES { 352 assert_eq!( 353 Icmpv6Slice::from_slice(&gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8)).unwrap().icmp_type(), 354 ParameterProblem(ParameterProblemHeader{ 355 code, 356 pointer: u32::from_be_bytes(bytes5to8), 357 }) 358 ); 359 } 360 361 // unknown codes 362 for code_u8 in 11..=u8::MAX { 363 assert_unknown(TYPE_PARAMETER_PROBLEM, code_u8); 364 } 365 } 366 367 // echo request 368 { 369 // known code 370 assert_eq!( 371 Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REQUEST, 0)).unwrap().icmp_type(), 372 EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)) 373 ); 374 375 // unknown codes 376 for code_u8 in 1..=u8::MAX { 377 assert_unknown(TYPE_ECHO_REPLY, code_u8); 378 } 379 } 380 381 // echo reply 382 { 383 // known code 384 assert_eq!( 385 Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REPLY, 0)).unwrap().icmp_type(), 386 EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)) 387 ); 388 389 // unknown codes 390 for code_u8 in 1..=u8::MAX { 391 assert_unknown(TYPE_ECHO_REPLY, code_u8); 392 } 393 } 394 } 395 } 396 397 proptest! { 398 #[test] 399 fn header_len( 400 code_u8 in any::<u8>(), 401 bytes5to8 in any::<[u8;4]>(), 402 ) { 403 let len_8_types = [ 404 DestinationUnreachable(DestUnreachableCode::Prohibited), 405 PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }, 406 TimeExceeded(TimeExceededCode::HopLimitExceeded), 407 ParameterProblem( 408 ParameterProblemHeader{ 409 code: ParameterProblemCode::OptionTooBig, 410 pointer: u32::from_be_bytes(bytes5to8), 411 } 412 ), 413 EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), 414 EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), 415 ]; 416 417 for t in len_8_types { 418 assert_eq!( 419 t.header_len(), 420 Icmpv6Slice::from_slice( 421 &Icmpv6Header::new(t).to_bytes() 422 ).unwrap().header_len() 423 ); 424 } 425 426 for t in 0..=u8::MAX { 427 let header = Icmpv6Header::new( 428 Unknown{ 429 type_u8: t, 430 code_u8, 431 bytes5to8, 432 } 433 ); 434 assert_eq!( 435 8, 436 Icmpv6Slice::from_slice( 437 &header.to_bytes() 438 ).unwrap().header_len() 439 ); 440 } 441 } 442 } 443 444 proptest! { 445 #[test] 446 fn type_u8(slice in proptest::collection::vec(any::<u8>(), 8..16)) { 447 assert_eq!( 448 Icmpv6Slice::from_slice(&slice[..]).unwrap().type_u8(), 449 slice[0] 450 ); 451 } 452 } 453 454 proptest! { 455 #[test] 456 fn code_u8(slice in proptest::collection::vec(any::<u8>(), 8..16)) { 457 assert_eq!( 458 Icmpv6Slice::from_slice(&slice[..]).unwrap().code_u8(), 459 slice[1] 460 ); 461 } 462 } 463 464 proptest! { 465 #[test] 466 fn checksum(slice in proptest::collection::vec(any::<u8>(), 8..16)) { 467 assert_eq!( 468 Icmpv6Slice::from_slice(&slice[..]).unwrap().checksum(), 469 u16::from_be_bytes([slice[2], slice[3]]) 470 ); 471 } 472 } 473 474 proptest! { 475 #[test] 476 fn is_checksum_valid( 477 ip_header in ipv6_any(), 478 icmp_type in icmpv6_type_any(), 479 payload in proptest::collection::vec(any::<u8>(), 0..1024), 480 flip_byte in 0usize..1032, 481 ) { 482 // generate slice with a correct checksum 483 let header = Icmpv6Header::with_checksum(icmp_type, ip_header.source, ip_header.destination, &payload).unwrap(); 484 let bytes = { 485 let mut bytes = Vec::with_capacity(header.header_len() + payload.len()); 486 header.write(&mut bytes).unwrap(); 487 bytes.extend_from_slice(&payload); 488 bytes 489 }; 490 491 // check that the checksum gets reported as ok 492 assert!( 493 Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination) 494 ); 495 496 // corrupt icmp packet 497 { 498 let mut corrupted_bytes = bytes.clone(); 499 let i = flip_byte % corrupted_bytes.len(); 500 corrupted_bytes[i] = !corrupted_bytes[i]; 501 502 assert_eq!( 503 false, 504 Icmpv6Slice::from_slice(&corrupted_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination) 505 ); 506 } 507 508 // corrupt ip source 509 { 510 let mut corrupted_source = ip_header.source; 511 let i = flip_byte % corrupted_source.len(); 512 corrupted_source[i] = !corrupted_source[i]; 513 514 assert_eq!( 515 false, 516 Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(corrupted_source, ip_header.destination) 517 ); 518 } 519 520 // corrupt ip destination 521 { 522 let mut corrupted_dest = ip_header.destination; 523 let i = flip_byte % corrupted_dest.len(); 524 corrupted_dest[i] = !corrupted_dest[i]; 525 526 assert_eq!( 527 false, 528 Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, corrupted_dest) 529 ); 530 } 531 532 // corrupt length 533 { 534 let mut larger_bytes = bytes.clone(); 535 larger_bytes.push(0); 536 larger_bytes.push(0); 537 538 assert_eq!( 539 false, 540 Icmpv6Slice::from_slice(&larger_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination) 541 ); 542 } 543 } 544 } 545 546 proptest! { 547 #[test] 548 fn bytes5to8(slice in proptest::collection::vec(any::<u8>(), 8..16)) { 549 assert_eq!( 550 Icmpv6Slice::from_slice(&slice[..]).unwrap().bytes5to8(), 551 [slice[4], slice[5], slice[6], slice[7]] 552 ); 553 } 554 } 555 556 proptest! { 557 #[test] 558 fn slice(slice in proptest::collection::vec(any::<u8>(), 8..16)) { 559 assert_eq!( 560 Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(), 561 &slice[..] 562 ); 563 } 564 } 565 566 proptest! { 567 #[test] 568 fn payload( 569 type_u8 in any::<u8>(), 570 code_u8 in any::<u8>(), 571 bytes5to8 in any::<[u8;4]>(), 572 payload in proptest::collection::vec(any::<u8>(), 8..16) 573 ) { 574 let len_8_types = [ 575 Unknown{ 576 type_u8, 577 code_u8, 578 bytes5to8, 579 }, 580 DestinationUnreachable(DestUnreachableCode::Prohibited), 581 PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }, 582 TimeExceeded(TimeExceededCode::HopLimitExceeded), 583 ParameterProblem( 584 ParameterProblemHeader{ 585 code: ParameterProblemCode::ExtensionHeaderChainTooLong, 586 pointer: u32::from_be_bytes(bytes5to8), 587 } 588 ), 589 EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), 590 EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), 591 ]; 592 593 for t in len_8_types { 594 let mut bytes = Vec::with_capacity(t.header_len() + payload.len()); 595 Icmpv6Header::new(t.clone()).write(&mut bytes).unwrap(); 596 bytes.extend_from_slice(&payload); 597 598 assert_eq!( 599 Icmpv6Slice::from_slice(&bytes[..]).unwrap().payload(), 600 &payload[..] 601 ); 602 } 603 } 604 } 605 606 #[test] debug()607 fn debug() { 608 let data = [0u8; 8]; 609 assert_eq!( 610 format!("{:?}", Icmpv6Slice::from_slice(&data).unwrap()), 611 format!("Icmpv6Slice {{ slice: {:?} }}", &data) 612 ); 613 } 614 615 proptest! { 616 #[test] 617 fn clone_eq(slice in proptest::collection::vec(any::<u8>(), 8..16)) { 618 assert_eq!( 619 Icmpv6Slice::from_slice(&slice).unwrap().clone(), 620 Icmpv6Slice::from_slice(&slice).unwrap() 621 ); 622 } 623 } 624 } 625