1 use crate::{icmpv4::*, *}; 2 3 /// A slice containing an ICMPv4 network package. 4 /// 5 /// Struct allows the selective read of fields in the ICMPv4 6 /// packet. 7 #[derive(Clone, Debug, Eq, PartialEq)] 8 pub struct Icmpv4Slice<'a> { 9 pub(crate) slice: &'a [u8], 10 } 11 12 impl<'a> Icmpv4Slice<'a> { 13 /// Creates a slice containing an ICMPv4 packet. 14 /// 15 /// # Errors 16 /// 17 /// The function will return an `Err` `err::LenError` 18 /// if the given slice is too small or does not match the expected 19 /// length in case of a timestamp message. 20 #[inline] from_slice(slice: &'a [u8]) -> Result<Icmpv4Slice<'a>, err::LenError>21 pub fn from_slice(slice: &'a [u8]) -> Result<Icmpv4Slice<'a>, err::LenError> { 22 // check length 23 if slice.len() < Icmpv4Header::MIN_LEN { 24 return Err(err::LenError { 25 required_len: Icmpv4Header::MIN_LEN, 26 len: slice.len(), 27 len_source: LenSource::Slice, 28 layer: err::Layer::Icmpv4, 29 layer_start_offset: 0, 30 }); 31 } 32 33 // SAFETY: 34 // Safe as it is previously checked that the slice has 35 // at least the length of Icmpv4Header::MIN_LEN (8). 36 let icmp_type: u8 = unsafe { *slice.get_unchecked(0) }; 37 let icmp_code: u8 = unsafe { *slice.get_unchecked(1) }; 38 39 // check type specific length 40 match icmp_type { 41 TYPE_TIMESTAMP => { 42 if 0 == icmp_code && TimestampMessage::LEN != slice.len() { 43 return Err(err::LenError { 44 required_len: TimestampMessage::LEN, 45 len: slice.len(), 46 len_source: LenSource::Slice, 47 layer: err::Layer::Icmpv4Timestamp, 48 layer_start_offset: 0, 49 }); 50 } 51 } 52 TYPE_TIMESTAMP_REPLY => { 53 if 0 == icmp_code && TimestampMessage::LEN != slice.len() { 54 return Err(err::LenError { 55 required_len: TimestampMessage::LEN, 56 len: slice.len(), 57 len_source: LenSource::Slice, 58 layer: err::Layer::Icmpv4TimestampReply, 59 layer_start_offset: 0, 60 }); 61 } 62 } 63 _ => {} 64 } 65 66 //done 67 Ok(Icmpv4Slice { slice }) 68 } 69 70 /// Decode the header values into an [`Icmpv4Header`] struct. 71 #[inline] header(&self) -> Icmpv4Header72 pub fn header(&self) -> Icmpv4Header { 73 let icmp_type = self.icmp_type(); 74 Icmpv4Header { 75 icmp_type, 76 checksum: self.checksum(), 77 } 78 } 79 80 /// Number of bytes/octets that will be converted into a 81 /// [`Icmpv4Header`] when [`Icmpv4Slice::header`] gets called. 82 #[inline] header_len(&self) -> usize83 pub fn header_len(&self) -> usize { 84 match self.type_u8() { 85 TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => { 86 if 0 == self.code_u8() { 87 TimestampMessage::LEN 88 } else { 89 8 90 } 91 } 92 _ => 8, 93 } 94 } 95 96 /// Decode the header values (excluding the checksum) into an [`Icmpv4Type`] enum. icmp_type(&self) -> Icmpv4Type97 pub fn icmp_type(&self) -> Icmpv4Type { 98 use Icmpv4Type::*; 99 100 unsafe fn timestamp_message(ptr: *const u8) -> TimestampMessage { 101 TimestampMessage { 102 id: get_unchecked_be_u16(ptr.add(4)), 103 seq: get_unchecked_be_u16(ptr.add(6)), 104 originate_timestamp: get_unchecked_be_u32(ptr.add(8)), 105 receive_timestamp: get_unchecked_be_u32(ptr.add(12)), 106 transmit_timestamp: get_unchecked_be_u32(ptr.add(16)), 107 } 108 } 109 110 match self.type_u8() { 111 TYPE_ECHO_REPLY => { 112 if 0 == self.code_u8() { 113 return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8())); 114 } 115 } 116 TYPE_DEST_UNREACH => { 117 use DestUnreachableHeader::*; 118 match self.code_u8() { 119 CODE_DST_UNREACH_NET => return DestinationUnreachable(Network), 120 CODE_DST_UNREACH_HOST => return DestinationUnreachable(Host), 121 CODE_DST_UNREACH_PROTOCOL => return DestinationUnreachable(Protocol), 122 CODE_DST_UNREACH_PORT => return DestinationUnreachable(Port), 123 CODE_DST_UNREACH_NEED_FRAG => { 124 return DestinationUnreachable(FragmentationNeeded { 125 // SAFETY: 126 // Safe as the contructor checks that the slice has 127 // at least the length of Icmpv4Header::MIN_LEN (8). 128 next_hop_mtu: unsafe { 129 get_unchecked_be_u16(self.slice.as_ptr().add(6)) 130 }, 131 }); 132 } 133 CODE_DST_UNREACH_SOURCE_ROUTE_FAILED => { 134 return DestinationUnreachable(SourceRouteFailed) 135 } 136 CODE_DST_UNREACH_NET_UNKNOWN => return DestinationUnreachable(NetworkUnknown), 137 CODE_DST_UNREACH_HOST_UNKNOWN => return DestinationUnreachable(HostUnknown), 138 CODE_DST_UNREACH_ISOLATED => return DestinationUnreachable(Isolated), 139 CODE_DST_UNREACH_NET_PROHIB => { 140 return DestinationUnreachable(NetworkProhibited) 141 } 142 CODE_DST_UNREACH_HOST_PROHIB => return DestinationUnreachable(HostProhibited), 143 CODE_DST_UNREACH_TOS_NET => return DestinationUnreachable(TosNetwork), 144 CODE_DST_UNREACH_TOS_HOST => return DestinationUnreachable(TosHost), 145 CODE_DST_UNREACH_FILTER_PROHIB => { 146 return DestinationUnreachable(FilterProhibited) 147 } 148 CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION => { 149 return DestinationUnreachable(HostPrecedenceViolation) 150 } 151 CODE_DST_UNREACH_PRECEDENCE_CUTOFF => { 152 return DestinationUnreachable(PrecedenceCutoff) 153 } 154 _ => {} 155 } 156 } 157 TYPE_REDIRECT => { 158 use RedirectCode::*; 159 let code = match self.code_u8() { 160 CODE_REDIRECT_FOR_NETWORK => Some(RedirectForNetwork), 161 CODE_REDIRECT_FOR_HOST => Some(RedirectForHost), 162 CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK => { 163 Some(RedirectForTypeOfServiceAndNetwork) 164 } 165 CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST => Some(RedirectForTypeOfServiceAndHost), 166 _ => None, 167 }; 168 if let Some(code) = code { 169 return Redirect(RedirectHeader { 170 code, 171 gateway_internet_address: self.bytes5to8(), 172 }); 173 } 174 } 175 TYPE_ECHO_REQUEST => { 176 if 0 == self.code_u8() { 177 return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8())); 178 } 179 } 180 TYPE_TIME_EXCEEDED => { 181 use TimeExceededCode::*; 182 match self.code_u8() { 183 CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT => { 184 return TimeExceeded(TtlExceededInTransit); 185 } 186 CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED => { 187 return TimeExceeded(FragmentReassemblyTimeExceeded); 188 } 189 _ => {} 190 } 191 } 192 TYPE_PARAMETER_PROBLEM => { 193 use ParameterProblemHeader::*; 194 match self.code_u8() { 195 CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR => { 196 return ParameterProblem(PointerIndicatesError( 197 // SAFETY: 198 // Safe as the contructor checks that the slice has 199 // at least the length of Icmpv4Header::MIN_LEN (8). 200 unsafe { *self.slice.get_unchecked(4) }, 201 )); 202 } 203 CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION => { 204 return ParameterProblem(MissingRequiredOption); 205 } 206 CODE_PARAMETER_PROBLEM_BAD_LENGTH => { 207 return ParameterProblem(BadLength); 208 } 209 _ => {} 210 } 211 } 212 TYPE_TIMESTAMP => { 213 if 0 == self.code_u8() { 214 // SAFETY: 215 // Safe as the contructor checks that the slice has 216 // the length of TimestampMessage::SERIALIZED_SIZE (20). 217 unsafe { 218 return TimestampRequest(timestamp_message(self.slice.as_ptr())); 219 } 220 } 221 } 222 TYPE_TIMESTAMP_REPLY => { 223 if 0 == self.code_u8() { 224 // SAFETY: 225 // Safe as the contructor checks that the slice has 226 // the length of TimestampMessage::SERIALIZED_SIZE (20). 227 unsafe { 228 return TimestampReply(timestamp_message(self.slice.as_ptr())); 229 } 230 } 231 } 232 _ => {} 233 } 234 235 Unknown { 236 type_u8: self.type_u8(), 237 code_u8: self.code_u8(), 238 bytes5to8: self.bytes5to8(), 239 } 240 } 241 242 /// Returns "type" value in the ICMPv4 header. 243 #[inline] type_u8(&self) -> u8244 pub fn type_u8(&self) -> u8 { 245 // SAFETY: 246 // Safe as the contructor checks that the slice has 247 // at least the length of Icmpv4Header::MIN_LEN (8). 248 unsafe { *self.slice.get_unchecked(0) } 249 } 250 251 /// Returns "code" value in the ICMPv4 header. 252 #[inline] code_u8(&self) -> u8253 pub fn code_u8(&self) -> u8 { 254 // SAFETY: 255 // Safe as the contructor checks that the slice has 256 // at least the length of Icmpv4Header::MIN_LEN (8). 257 unsafe { *self.slice.get_unchecked(1) } 258 } 259 260 /// Returns "checksum" value in the ICMPv4 header. 261 #[inline] checksum(&self) -> u16262 pub fn checksum(&self) -> u16 { 263 // SAFETY: 264 // Safe as the contructor checks that the slice has 265 // at least the length of Icmpv4Header::MIN_LEN (8). 266 unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } 267 } 268 269 /// Returns the bytes from position 4 till and including the 8th position 270 /// in the ICMPv4 header. 271 /// 272 /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP 273 /// packet can depending on the ICMPv4 type and code contain additional data. 274 #[inline] bytes5to8(&self) -> [u8; 4]275 pub fn bytes5to8(&self) -> [u8; 4] { 276 // SAFETY: 277 // Safe as the contructor checks that the slice has 278 // at least the length of Icmpv4Header::MIN_LEN (8). 279 unsafe { 280 [ 281 *self.slice.get_unchecked(4), 282 *self.slice.get_unchecked(5), 283 *self.slice.get_unchecked(6), 284 *self.slice.get_unchecked(7), 285 ] 286 } 287 } 288 289 /// Returns a slice to the bytes not covered by `.header()`. 290 /// 291 /// The contents of the slice returned by `payload()` depends on the type 292 /// and code of the ICMP packet: 293 /// 294 /// | `.header().icmp_type` or `.icmp_type()` | Payload Content | 295 /// |--------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------| 296 /// | [`Icmpv4Type::EchoReply`]<br>[`Icmpv4Type::EchoRequest`]<br> | Data part of the echo message | 297 /// | [`Icmpv4Type::DestinationUnreachable`]<br>[`Icmpv4Type::Redirect`]<br>[`Icmpv4Type::TimeExceeded`]<br>[`Icmpv4Type::ParameterProblem`]<br> | Internet Header + 64 bits of Original Data Datagram causing the ICMP message | 298 /// | [`Icmpv4Type::TimestampRequest`]<br>[`Icmpv4Type::TimestampReply`]<br> | Nothing | 299 /// | [`Icmpv4Type::Unknown`] | Everything after the 8th byte/octet of the ICMP packet. | 300 #[inline] payload(&self) -> &'a [u8]301 pub fn payload(&self) -> &'a [u8] { 302 // explicitly inlined the code to determine the 303 // length of the payload to make the cecking of the 304 // usafe code easier. 305 let header_len = match self.type_u8() { 306 // SAFETY: 307 // Length safe as the contructor checks that the slice has 308 // the length of TimestampMessage::SERIALIZED_SIZE (20) 309 // for the messages types TYPE_TIMESTAMP and TYPE_TIMESTAMP_REPLY. 310 TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => { 311 if 0 == self.code_u8() { 312 TimestampMessage::LEN 313 } else { 314 8 315 } 316 } 317 // SAFETY: 318 // Length safe as the contructor checks that the slice has 319 // at least the length of Icmpv4Header::MIN_LEN(8) for 320 // all message types. 321 _ => 8, 322 }; 323 // SAFETY: 324 // Lengths have been depending on type in the constructor of the 325 // ICMPv4Slice. 326 unsafe { 327 core::slice::from_raw_parts( 328 self.slice.as_ptr().add(header_len), 329 self.slice.len() - header_len, 330 ) 331 } 332 } 333 334 /// Returns the slice containing the ICMPv4 packet. 335 #[inline] slice(&self) -> &'a [u8]336 pub fn slice(&self) -> &'a [u8] { 337 self.slice 338 } 339 } 340 341 #[cfg(test)] 342 mod test { 343 use super::*; 344 use alloc::{format, vec::Vec}; 345 use proptest::prelude::*; 346 347 #[test] from_slice()348 fn from_slice() { 349 // normal case 350 { 351 let bytes = [0u8; 8]; 352 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 353 assert_eq!(slice.slice(), &bytes); 354 } 355 356 // smaller then min size error 357 for bad_len in 0..8 { 358 let bytes = [0u8; 8]; 359 assert_eq!( 360 Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(), 361 err::LenError { 362 required_len: Icmpv4Header::MIN_LEN, 363 len: bad_len, 364 len_source: LenSource::Slice, 365 layer: err::Layer::Icmpv4, 366 layer_start_offset: 0, 367 } 368 ); 369 } 370 371 // timestamp tests 372 for ts_type_u8 in [TYPE_TIMESTAMP, TYPE_TIMESTAMP_REPLY] { 373 let bytes = { 374 let mut bytes = [0u8; 26]; 375 bytes[0] = ts_type_u8; 376 bytes 377 }; 378 379 // valid timestamps 380 { 381 let slice = Icmpv4Slice::from_slice(&bytes[..20]).unwrap(); 382 assert_eq!(slice.slice(), &bytes[..20]); 383 } 384 385 // too short timestamps 386 for bad_len in 8..20 { 387 assert_eq!( 388 Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(), 389 err::LenError { 390 required_len: TimestampMessage::LEN, 391 len: bad_len, 392 len_source: LenSource::Slice, 393 layer: if ts_type_u8 == TYPE_TIMESTAMP { 394 err::Layer::Icmpv4Timestamp 395 } else { 396 err::Layer::Icmpv4TimestampReply 397 }, 398 layer_start_offset: 0, 399 } 400 ); 401 } 402 403 // too large timestamps 404 for bad_len in 21..26 { 405 assert_eq!( 406 Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(), 407 err::LenError { 408 required_len: TimestampMessage::LEN, 409 len: bad_len, 410 len_source: LenSource::Slice, 411 layer: if ts_type_u8 == TYPE_TIMESTAMP { 412 err::Layer::Icmpv4Timestamp 413 } else { 414 err::Layer::Icmpv4TimestampReply 415 }, 416 layer_start_offset: 0, 417 } 418 ); 419 } 420 421 // timestamp with a non zero code 422 for code_u8 in 1..=u8::MAX { 423 let mut bytes = [0u8; 20]; 424 bytes[0] = ts_type_u8; 425 bytes[1] = code_u8; 426 let slice = Icmpv4Slice::from_slice(&bytes[..8]).unwrap(); 427 assert_eq!(slice.slice(), &bytes[..8]); 428 } 429 } 430 } 431 432 proptest! { 433 #[test] 434 fn header(bytes in any::<[u8;20]>()) { 435 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 436 assert_eq!( 437 Icmpv4Header { 438 icmp_type: slice.icmp_type(), 439 checksum: slice.checksum(), 440 }, 441 slice.header() 442 ); 443 } 444 } 445 446 #[test] header_len()447 fn header_len() { 448 use Icmpv4Type::*; 449 let dummy_ts = TimestampMessage { 450 id: 0, 451 seq: 0, 452 originate_timestamp: 0, 453 receive_timestamp: 0, 454 transmit_timestamp: 0, 455 }; 456 let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 }; 457 let dummy_redirect = RedirectHeader { 458 code: RedirectCode::RedirectForNetwork, 459 gateway_internet_address: [0; 4], 460 }; 461 let tests = [ 462 (Unknown { 463 type_u8: u8::MAX, 464 code_u8: 0, 465 bytes5to8: [0; 4], 466 }), 467 (EchoReply(dummy_echo)), 468 (DestinationUnreachable(DestUnreachableHeader::Network)), 469 (Redirect(dummy_redirect)), 470 (EchoRequest(dummy_echo)), 471 (TimeExceeded(TimeExceededCode::TtlExceededInTransit)), 472 (ParameterProblem(ParameterProblemHeader::BadLength)), 473 (TimestampRequest(dummy_ts.clone())), 474 // check that a non zero code value return 8 475 (Unknown { 476 type_u8: TYPE_TIMESTAMP, 477 code_u8: 1, 478 bytes5to8: [0; 4], 479 }), 480 (TimestampReply(dummy_ts)), 481 // check that a non zero code value return 8 482 (Unknown { 483 type_u8: TYPE_TIMESTAMP_REPLY, 484 code_u8: 1, 485 bytes5to8: [0; 4], 486 }), 487 ]; 488 for t in tests { 489 assert_eq!( 490 t.header_len(), 491 Icmpv4Slice::from_slice(&Icmpv4Header::new(t).to_bytes()) 492 .unwrap() 493 .header_len() 494 ); 495 } 496 } 497 498 proptest! { 499 #[test] 500 fn icmp_type(base_bytes in any::<[u8;20]>()) { 501 502 use Icmpv4Type::*; 503 504 let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;20] { 505 let mut bytes = base_bytes; 506 bytes[0] = type_u8; 507 bytes[1] = code_u8; 508 bytes 509 }; 510 511 let assert_unknown = |type_u8: u8, code_u8: u8| { 512 let bytes = gen_bytes(type_u8, code_u8); 513 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 514 assert_eq!( 515 slice.icmp_type(), 516 Unknown{ 517 type_u8, 518 code_u8, 519 bytes5to8: slice.bytes5to8(), 520 } 521 ); 522 }; 523 524 // unknown types 525 for type_u8 in 0..=u8::MAX{ 526 match type_u8 { 527 TYPE_ECHO_REPLY | TYPE_DEST_UNREACH | TYPE_REDIRECT | 528 TYPE_ECHO_REQUEST | TYPE_TIME_EXCEEDED | TYPE_PARAMETER_PROBLEM | 529 TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {}, 530 type_u8 => { 531 assert_unknown(type_u8, base_bytes[1]); 532 } 533 } 534 } 535 536 // echo reply 537 { 538 // matching code 539 { 540 let bytes = gen_bytes(TYPE_ECHO_REPLY, 0); 541 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 542 assert_eq!( 543 slice.icmp_type(), 544 EchoReply(IcmpEchoHeader::from_bytes(slice.bytes5to8())) 545 ); 546 } 547 548 // unknown code 549 for unknow_code in 1..=u8::MAX { 550 assert_unknown(TYPE_ECHO_REPLY, unknow_code); 551 } 552 } 553 554 // destination unreachable 555 { 556 use DestUnreachableHeader::*; 557 // trivial code values 558 { 559 let trivial_tests = [ 560 (CODE_DST_UNREACH_NET, Network), 561 (CODE_DST_UNREACH_HOST, Host), 562 (CODE_DST_UNREACH_PROTOCOL, Protocol), 563 (CODE_DST_UNREACH_PORT, Port), 564 // need frag skipped as contains an additional value 565 (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, SourceRouteFailed), 566 (CODE_DST_UNREACH_NET_UNKNOWN, NetworkUnknown), 567 (CODE_DST_UNREACH_HOST_UNKNOWN, HostUnknown), 568 (CODE_DST_UNREACH_ISOLATED, Isolated), 569 (CODE_DST_UNREACH_NET_PROHIB, NetworkProhibited), 570 (CODE_DST_UNREACH_HOST_PROHIB, HostProhibited), 571 (CODE_DST_UNREACH_TOS_NET, TosNetwork), 572 (CODE_DST_UNREACH_TOS_HOST, TosHost), 573 (CODE_DST_UNREACH_FILTER_PROHIB, FilterProhibited), 574 (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, HostPrecedenceViolation), 575 (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, PrecedenceCutoff), 576 ]; 577 578 for (code_u8, expected) in trivial_tests { 579 let bytes = gen_bytes(TYPE_DEST_UNREACH, code_u8); 580 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 581 assert_eq!( 582 slice.icmp_type(), 583 DestinationUnreachable(expected) 584 ); 585 } 586 } 587 588 // need frag 589 { 590 let bytes = gen_bytes(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG); 591 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 592 assert_eq!( 593 slice.icmp_type(), 594 DestinationUnreachable(FragmentationNeeded { 595 next_hop_mtu: u16::from_be_bytes([bytes[6], bytes[7]]) 596 }) 597 ); 598 } 599 600 // unknown codes 601 for unknow_code in 16..=u8::MAX { 602 assert_unknown(TYPE_ECHO_REPLY, unknow_code); 603 } 604 } 605 606 // redirect 607 { 608 use RedirectCode::*; 609 // known codes 610 { 611 let trivial_tests = [ 612 (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork), 613 (CODE_REDIRECT_FOR_HOST, RedirectForHost), 614 (CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK, RedirectForTypeOfServiceAndNetwork), 615 (CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST, RedirectForTypeOfServiceAndHost), 616 ]; 617 618 for (code_u8, expected) in trivial_tests { 619 let bytes = gen_bytes(TYPE_REDIRECT, code_u8); 620 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 621 assert_eq!( 622 slice.icmp_type(), 623 Redirect(RedirectHeader{ 624 code: expected, 625 gateway_internet_address: slice.bytes5to8(), 626 }) 627 ); 628 } 629 } 630 631 // unknown codes 632 for unknow_code in 4..=u8::MAX { 633 assert_unknown(TYPE_REDIRECT, unknow_code); 634 } 635 } 636 637 // echo request 638 { 639 // matching code 640 { 641 let bytes = gen_bytes(TYPE_ECHO_REQUEST, 0); 642 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 643 assert_eq!( 644 slice.icmp_type(), 645 EchoRequest(IcmpEchoHeader::from_bytes(slice.bytes5to8())) 646 ); 647 } 648 649 // unknown code 650 for unknow_code in 1..=u8::MAX { 651 assert_unknown(TYPE_ECHO_REQUEST, unknow_code); 652 } 653 } 654 655 // time exceeded 656 { 657 use TimeExceededCode::*; 658 // known codes 659 { 660 let trivial_tests = [ 661 (CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT, TtlExceededInTransit), 662 (CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED, FragmentReassemblyTimeExceeded), 663 ]; 664 665 for (code_u8, expected) in trivial_tests { 666 let bytes = gen_bytes(TYPE_TIME_EXCEEDED, code_u8); 667 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 668 assert_eq!( 669 slice.icmp_type(), 670 TimeExceeded(expected) 671 ); 672 } 673 } 674 675 // unknown code 676 for unknow_code in 2..=u8::MAX { 677 assert_unknown(TYPE_TIME_EXCEEDED, unknow_code); 678 } 679 } 680 681 // parameter porblem 682 { 683 use ParameterProblemHeader::*; 684 // trivial code values 685 { 686 let trivial_tests = [ 687 (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, MissingRequiredOption), 688 (CODE_PARAMETER_PROBLEM_BAD_LENGTH, BadLength), 689 ]; 690 691 for (code_u8, expected) in trivial_tests { 692 let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8); 693 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 694 assert_eq!( 695 slice.icmp_type(), 696 ParameterProblem(expected) 697 ); 698 } 699 } 700 701 // with pointer 702 { 703 let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR); 704 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 705 assert_eq!( 706 slice.icmp_type(), 707 ParameterProblem(PointerIndicatesError(bytes[4])) 708 ); 709 } 710 711 // unknown codes 712 for unknow_code in 3..=u8::MAX { 713 assert_unknown(TYPE_PARAMETER_PROBLEM, unknow_code); 714 } 715 } 716 717 // timestamp 718 { 719 // matching code 720 { 721 let bytes = gen_bytes(TYPE_TIMESTAMP, 0); 722 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 723 assert_eq!( 724 slice.icmp_type(), 725 TimestampRequest(TimestampMessage::from_bytes([ 726 bytes[4], bytes[5], bytes[6], bytes[7], 727 bytes[8], bytes[9], bytes[10], bytes[11], 728 bytes[12], bytes[13], bytes[14], bytes[15], 729 bytes[16], bytes[17], bytes[18], bytes[19], 730 ])) 731 ); 732 } 733 734 // unknown code 735 for unknow_code in 1..=u8::MAX { 736 assert_unknown(TYPE_TIMESTAMP, unknow_code); 737 } 738 } 739 740 // timestamp reply 741 { 742 // matching code 743 { 744 let bytes = gen_bytes(TYPE_TIMESTAMP_REPLY, 0); 745 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 746 assert_eq!( 747 slice.icmp_type(), 748 TimestampReply(TimestampMessage::from_bytes([ 749 bytes[4], bytes[5], bytes[6], bytes[7], 750 bytes[8], bytes[9], bytes[10], bytes[11], 751 bytes[12], bytes[13], bytes[14], bytes[15], 752 bytes[16], bytes[17], bytes[18], bytes[19], 753 ])) 754 ); 755 } 756 757 // unknown code 758 for unknow_code in 1..=u8::MAX { 759 assert_unknown(TYPE_TIMESTAMP_REPLY, unknow_code); 760 } 761 } 762 } 763 } 764 765 proptest! { 766 #[test] 767 fn type_u8(bytes in any::<[u8;20]>()) { 768 assert_eq!( 769 bytes[0], 770 Icmpv4Slice::from_slice(&bytes).unwrap().type_u8(), 771 ); 772 } 773 } 774 775 proptest! { 776 #[test] 777 fn code_u8(bytes in any::<[u8;20]>()) { 778 assert_eq!( 779 bytes[1], 780 Icmpv4Slice::from_slice(&bytes).unwrap().code_u8(), 781 ); 782 } 783 } 784 785 proptest! { 786 #[test] 787 fn checksum(bytes in any::<[u8;20]>()) { 788 assert_eq!( 789 u16::from_be_bytes([bytes[2], bytes[3]]), 790 Icmpv4Slice::from_slice(&bytes).unwrap().checksum(), 791 ); 792 } 793 } 794 795 proptest! { 796 #[test] 797 fn bytes5to8(bytes in any::<[u8;20]>()) { 798 assert_eq!( 799 [bytes[4], bytes[5], bytes[6], bytes[7]], 800 Icmpv4Slice::from_slice(&bytes).unwrap().bytes5to8(), 801 ); 802 } 803 } 804 805 proptest! { 806 #[test] 807 fn payload( 808 payload in proptest::collection::vec(any::<u8>(), 8..26) 809 ) { 810 use Icmpv4Type::*; 811 let dummy_ts = TimestampMessage{ 812 id: 0, 813 seq: 0, 814 originate_timestamp: 0, 815 receive_timestamp: 0, 816 transmit_timestamp: 0, 817 }; 818 let dummy_echo = IcmpEchoHeader{ 819 id: 0, 820 seq: 0, 821 }; 822 let dummy_redirect = RedirectHeader{ 823 code: RedirectCode::RedirectForNetwork, 824 gateway_internet_address: [0;4], 825 }; 826 // tests with variable payloads 827 { 828 let var_tests = [ 829 Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]}, 830 EchoReply(dummy_echo), 831 DestinationUnreachable(DestUnreachableHeader::Network), 832 Redirect(dummy_redirect), 833 EchoRequest(dummy_echo), 834 TimeExceeded(TimeExceededCode::TtlExceededInTransit), 835 ParameterProblem(ParameterProblemHeader::BadLength), 836 // timestamps with non-zero code values 837 Unknown{type_u8: TYPE_TIMESTAMP, code_u8: 1, bytes5to8: [0;4]}, 838 Unknown{type_u8: TYPE_TIMESTAMP_REPLY, code_u8: 1, bytes5to8: [0;4]}, 839 ]; 840 for t in var_tests { 841 842 let mut bytes = Vec::with_capacity(t.header_len() + payload.len()); 843 Icmpv4Header::new(t.clone()).write(&mut bytes).unwrap(); 844 bytes.extend_from_slice(&payload); 845 846 assert_eq!( 847 &payload[..], 848 Icmpv4Slice::from_slice(&bytes).unwrap().payload() 849 ); 850 } 851 } 852 // tests with fixed payload sizes 853 { 854 let fixed_tests = [ 855 (0, TimestampRequest(dummy_ts.clone())), 856 (0, TimestampReply(dummy_ts)), 857 ]; 858 for t in fixed_tests { 859 let mut bytes = Vec::with_capacity(t.1.header_len() + t.0); 860 Icmpv4Header::new(t.1.clone()).write(&mut bytes).unwrap(); 861 bytes.extend_from_slice(&payload[..t.0]); 862 863 assert_eq!( 864 &payload[..t.0], 865 Icmpv4Slice::from_slice(&bytes).unwrap().payload() 866 ); 867 } 868 } 869 } 870 } 871 872 proptest! { 873 #[test] 874 fn slice(bytes in proptest::collection::vec(any::<u8>(), 20..1024)) { 875 let slice = if bytes[0] == TYPE_TIMESTAMP || bytes[0] == TYPE_TIMESTAMP_REPLY { 876 &bytes[..20] 877 } else { 878 &bytes[..] 879 }; 880 assert_eq!( 881 slice, 882 Icmpv4Slice::from_slice(slice).unwrap().slice(), 883 ); 884 } 885 } 886 887 proptest! { 888 #[test] 889 fn clone_eq(bytes in any::<[u8;20]>()) { 890 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 891 assert_eq!(slice, slice.clone()); 892 } 893 } 894 895 proptest! { 896 #[test] 897 fn debug(bytes in any::<[u8;20]>()) { 898 let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); 899 assert_eq!( 900 format!("{:?}", slice), 901 format!("Icmpv4Slice {{ slice: {:?} }}", &bytes[..]) 902 ); 903 } 904 } 905 } 906