1 use crate::*; 2 use arrayvec::ArrayVec; 3 4 /// A header of an ICMPv4 packet. 5 /// 6 /// What is part of the header depends on the ICMPv4 type 7 /// and code. But usually the static sized elements are part 8 /// of the header. 9 #[derive(Clone, Debug, PartialEq, Eq)] 10 pub struct Icmpv4Header { 11 /// Type & type specific values & code. 12 pub icmp_type: Icmpv4Type, 13 /// Checksum in the ICMP header. 14 pub checksum: u16, 15 } 16 17 impl Icmpv4Header { 18 /// Minimum number of bytes/octets an Icmpv4Header takes up 19 /// in serialized form. 20 pub const MIN_LEN: usize = 8; 21 22 /// Deprecated, use [`Icmpv4Header::MIN_LEN`] instead. 23 #[deprecated(since = "0.14.0", note = "Please use Icmpv4Header::MIN_LEN instead")] 24 pub const MIN_SERIALIZED_SIZE: usize = 8; 25 26 /// Maximum number of bytes/octets an Icmpv4Header takes up 27 /// in serialized form. 28 /// 29 /// Currently this number is determined by the biggest 30 /// supported ICMPv4 header type, which is currently the 31 /// "Timestamp" and "Timestamp Reply Message". 32 pub const MAX_LEN: usize = 20; 33 34 /// Deprecated, use [`Icmpv4Header::MAX_LEN`] instead. 35 #[deprecated(since = "0.14.0", note = "Please use Icmpv4Header::MAX_LEN instead")] 36 pub const MAX_SERIALIZED_SIZE: usize = 20; 37 38 /// Constructs an [`Icmpv4Header`] using the given type 39 /// and the checksum set to 0. new(icmp_type: Icmpv4Type) -> Icmpv4Header40 pub fn new(icmp_type: Icmpv4Type) -> Icmpv4Header { 41 // Note: will calculate checksum on send 42 Icmpv4Header { 43 icmp_type, 44 checksum: 0, 45 } 46 } 47 48 /// Creates a [`Icmpv4Header`] with a checksum calculated based on the given payload. with_checksum(icmp_type: Icmpv4Type, payload: &[u8]) -> Icmpv4Header49 pub fn with_checksum(icmp_type: Icmpv4Type, payload: &[u8]) -> Icmpv4Header { 50 let checksum = icmp_type.calc_checksum(payload); 51 Icmpv4Header { 52 icmp_type, 53 checksum, 54 } 55 } 56 57 /// Reads an icmp4 header from a slice directly and returns a tuple containing the resulting header & unused part of the slice. 58 #[inline] from_slice(slice: &[u8]) -> Result<(Icmpv4Header, &[u8]), err::LenError>59 pub fn from_slice(slice: &[u8]) -> Result<(Icmpv4Header, &[u8]), err::LenError> { 60 let header = Icmpv4Slice::from_slice(slice)?.header(); 61 let rest = &slice[header.header_len()..]; 62 Ok((header, rest)) 63 } 64 65 /// Reads an ICMPv4 header from the given reader. 66 #[cfg(feature = "std")] 67 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] read<T: std::io::Read + Sized>(reader: &mut T) -> Result<Icmpv4Header, std::io::Error>68 pub fn read<T: std::io::Read + Sized>(reader: &mut T) -> Result<Icmpv4Header, std::io::Error> { 69 let mut bytes = [0u8; Icmpv4Header::MAX_LEN]; 70 71 // try reading the initial 8 bytes 72 reader.read_exact(&mut bytes[..8])?; 73 74 match bytes[0] { 75 icmpv4::TYPE_TIMESTAMP_REPLY | icmpv4::TYPE_TIMESTAMP => { 76 if 0 == bytes[1] { 77 // Timetamp messages need additional data read & it and 78 // then set the slice correspondently 79 reader.read_exact(&mut bytes[8..icmpv4::TimestampMessage::LEN])?; 80 Ok(Icmpv4Slice { 81 slice: &bytes[..icmpv4::TimestampMessage::LEN], 82 } 83 .header()) 84 } else { 85 // fallback to unknown 86 Ok(Icmpv4Slice { slice: &bytes[..8] }.header()) 87 } 88 } 89 _ => Ok(Icmpv4Slice { slice: &bytes[..8] }.header()), 90 } 91 } 92 93 /// Write the ICMPv4 header to the given writer. 94 #[cfg(feature = "std")] 95 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error>96 pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> { 97 writer.write_all(&self.to_bytes()) 98 } 99 100 /// Length in bytes/octets of this header type. 101 #[inline] header_len(&self) -> usize102 pub fn header_len(&self) -> usize { 103 self.icmp_type.header_len() 104 } 105 106 /// If the ICMP type has a fixed size returns the number of 107 /// bytes that should be present after the header of this type. 108 #[inline] fixed_payload_size(&self) -> Option<usize>109 pub fn fixed_payload_size(&self) -> Option<usize> { 110 self.icmp_type.fixed_payload_size() 111 } 112 113 /// Calculates & updates the checksum in the header. 114 /// 115 /// Note this method assumes that all unused bytes/octets 116 /// are filled with zeroes. update_checksum(&mut self, payload: &[u8])117 pub fn update_checksum(&mut self, payload: &[u8]) { 118 self.checksum = self.icmp_type.calc_checksum(payload); 119 } 120 121 /// Converts the header to the on the wire bytes. 122 #[rustfmt::skip] to_bytes(&self) -> ArrayVec<u8,123 pub fn to_bytes(&self) -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { 124 let checksum_be = self.checksum.to_be_bytes(); 125 let re_zero = 126 |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { 127 128 #[rustfmt::skip] 129 let mut re = ArrayVec::from([ 130 type_u8, code_u8, checksum_be[0], checksum_be[1], 131 0, 0, 0, 0, 132 0, 0, 0, 0, 133 0, 0, 0, 0, 134 0, 0, 0, 0, 135 ]); 136 // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. 137 unsafe { 138 re.set_len(8); 139 } 140 re 141 }; 142 143 let re_2u16 = |type_u8: u8, 144 code_u8: u8, 145 a_u16: u16, 146 b_u16: u16| 147 -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { 148 let a = a_u16.to_be_bytes(); 149 let b = b_u16.to_be_bytes(); 150 151 #[rustfmt::skip] 152 let mut re = ArrayVec::from([ 153 type_u8, code_u8, checksum_be[0], checksum_be[1], 154 a[0], a[1], b[0], b[1], 155 0, 0, 0, 0, 156 0, 0, 0, 0, 157 0, 0, 0, 0, 158 ]); 159 // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. 160 unsafe { 161 re.set_len(8); 162 } 163 re 164 }; 165 166 let re_4u8 = |type_u8: u8, 167 code_u8: u8, 168 bytes5to8: [u8; 4]| 169 -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { 170 171 #[rustfmt::skip] 172 let mut re = ArrayVec::from([ 173 type_u8, code_u8, checksum_be[0], checksum_be[1], 174 bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3], 175 0, 0, 0, 0, 176 0, 0, 0, 0, 177 0, 0, 0, 0, 178 ]); 179 // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. 180 unsafe { 181 re.set_len(8); 182 } 183 re 184 }; 185 186 let re_timestamp_msg = |type_u8: u8, 187 msg: &icmpv4::TimestampMessage| 188 -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { 189 let id = msg.id.to_be_bytes(); 190 let seq = msg.seq.to_be_bytes(); 191 let o = msg.originate_timestamp.to_be_bytes(); 192 let r = msg.receive_timestamp.to_be_bytes(); 193 let t = msg.transmit_timestamp.to_be_bytes(); 194 195 ArrayVec::from([ 196 type_u8, 0, checksum_be[0], checksum_be[1], 197 id[0], id[1], seq[0], seq[1], 198 o[0], o[1], o[2], o[3], 199 r[0], r[1], r[2], r[3], 200 t[0], t[1], t[2], t[3], 201 ]) 202 }; 203 204 use Icmpv4Type::*; 205 use icmpv4::*; 206 match self.icmp_type { 207 Unknown { 208 type_u8, 209 code_u8, 210 bytes5to8, 211 } => re_4u8(type_u8, code_u8, bytes5to8), 212 EchoReply(echo) => re_2u16(TYPE_ECHO_REPLY, 0, echo.id, echo.seq), 213 DestinationUnreachable(ref dest) => { 214 use DestUnreachableHeader::*; 215 match dest { 216 Network => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET), 217 Host => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST), 218 Protocol => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PROTOCOL), 219 Port => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PORT), 220 FragmentationNeeded { next_hop_mtu } => { 221 let m_be = next_hop_mtu.to_be_bytes(); 222 re_4u8( 223 TYPE_DEST_UNREACH, 224 CODE_DST_UNREACH_NEED_FRAG, 225 [0, 0, m_be[0], m_be[1]], 226 ) 227 } 228 SourceRouteFailed => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED), 229 NetworkUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_UNKNOWN), 230 HostUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_UNKNOWN), 231 Isolated => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_ISOLATED), 232 NetworkProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_PROHIB), 233 HostProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PROHIB), 234 TosNetwork => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_NET), 235 TosHost => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_HOST), 236 FilterProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_FILTER_PROHIB), 237 HostPrecedenceViolation => re_zero( 238 TYPE_DEST_UNREACH, 239 CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, 240 ), 241 PrecedenceCutoff => { 242 re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PRECEDENCE_CUTOFF) 243 } 244 } 245 } 246 Redirect(ref msg) => { 247 re_4u8(TYPE_REDIRECT, msg.code as u8, msg.gateway_internet_address) 248 } 249 EchoRequest(echo) => re_2u16(TYPE_ECHO_REQUEST, 0, echo.id, echo.seq), 250 TimeExceeded(code) => re_zero(TYPE_TIME_EXCEEDED, code as u8), 251 ParameterProblem(ref header) => { 252 use ParameterProblemHeader::*; 253 match header { 254 PointerIndicatesError(pointer) => re_4u8( 255 TYPE_PARAMETER_PROBLEM, 256 CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, 257 [*pointer, 0, 0, 0], 258 ), 259 MissingRequiredOption => re_zero( 260 TYPE_PARAMETER_PROBLEM, 261 CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, 262 ), 263 BadLength => re_zero(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_BAD_LENGTH), 264 } 265 } 266 TimestampRequest(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP, msg), 267 TimestampReply(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP_REPLY, msg), 268 } 269 } 270 } 271 272 #[cfg(test)] 273 mod test { 274 use crate::{ 275 err::{Layer, LenError}, 276 icmpv4::*, 277 test_gens::*, 278 *, 279 }; 280 use alloc::{format, vec::Vec}; 281 use proptest::prelude::*; 282 283 #[test] 284 #[allow(deprecated)] constants()285 fn constants() { 286 assert_eq!(8, Icmpv4Header::MIN_LEN); 287 assert_eq!(20, Icmpv4Header::MAX_LEN); 288 assert_eq!(8, Icmpv4Header::MIN_SERIALIZED_SIZE); 289 assert_eq!(20, Icmpv4Header::MAX_SERIALIZED_SIZE); 290 } 291 292 proptest! { 293 #[test] 294 fn new(icmpv4_type in icmpv4_type_any()) { 295 assert_eq!( 296 Icmpv4Header { 297 icmp_type: icmpv4_type.clone(), 298 checksum: 0, 299 }, 300 Icmpv4Header::new(icmpv4_type) 301 ); 302 } 303 } 304 305 proptest! { 306 #[test] 307 fn with_checksum( 308 icmpv4_type in icmpv4_type_any(), 309 payload in proptest::collection::vec(any::<u8>(), 0..1024), 310 ) { 311 assert_eq!( 312 Icmpv4Header { 313 icmp_type: icmpv4_type.clone(), 314 checksum: icmpv4_type.calc_checksum(&payload), 315 }, 316 Icmpv4Header::with_checksum(icmpv4_type, &payload) 317 ); 318 } 319 } 320 321 proptest! { 322 #[test] 323 fn from_slice( 324 icmpv4_type in icmpv4_type_any(), 325 checksum in any::<u16>(), 326 payload in proptest::collection::vec(any::<u8>(), 0..1024), 327 ) { 328 use Icmpv4Type::*; 329 330 // ok case 331 let header = Icmpv4Header { 332 icmp_type: icmpv4_type.clone(), 333 checksum: checksum, 334 }; 335 let buffer = { 336 let mut buffer = Vec::with_capacity(header.header_len() + payload.len()); 337 buffer.extend_from_slice(&header.to_bytes()); 338 339 match icmpv4_type { 340 // skip the payoad for the timestamp request (those don't have a payload) 341 TimestampRequest(_) | TimestampReply(_) => {}, 342 _ => { 343 buffer.extend_from_slice(&[0u8;36]); 344 } 345 } 346 buffer 347 }; 348 { 349 let (actual, rest) = Icmpv4Header::from_slice(&buffer).unwrap(); 350 assert_eq!(actual, header); 351 assert_eq!(rest, &buffer[header.header_len()..]); 352 } 353 354 // error case 355 for bad_len in 0..header.header_len() { 356 assert_eq!( 357 Icmpv4Header::from_slice(&buffer[..bad_len]), 358 Err(LenError{ 359 required_len: if bad_len < Icmpv4Header::MIN_LEN { 360 Icmpv4Header::MIN_LEN 361 } else { 362 header.header_len() 363 }, 364 len: bad_len, 365 len_source: LenSource::Slice, 366 layer: if bad_len < Icmpv4Header::MIN_LEN { 367 Layer::Icmpv4 368 } else { 369 use crate::Icmpv4Type::*; 370 match icmpv4_type { 371 TimestampRequest(_) => Layer::Icmpv4Timestamp, 372 TimestampReply(_) => Layer::Icmpv4TimestampReply, 373 _ => Layer::Icmpv4, 374 } 375 }, 376 layer_start_offset: 0, 377 }) 378 ); 379 } 380 } 381 } 382 383 proptest! { 384 #[test] 385 fn read( 386 non_timestamp_type in any::<u8>().prop_filter( 387 "type must be a non timestamp type", 388 |v| (*v != icmpv4::TYPE_TIMESTAMP_REPLY && *v != icmpv4::TYPE_TIMESTAMP) 389 ), 390 non_zero_code in 1u8..=u8::MAX, 391 bytes in any::<[u8;icmpv4::TimestampMessage::LEN]>() 392 ) { 393 for (type_u8, code_u8) in [ 394 // non timestamp 395 (non_timestamp_type, bytes[1]), 396 // timestamp with zero code 397 (TYPE_TIMESTAMP_REPLY, 0u8), 398 (TYPE_TIMESTAMP, 0u8), 399 // timestamp with non-zero code 400 (TYPE_TIMESTAMP_REPLY, non_zero_code), 401 (TYPE_TIMESTAMP, non_zero_code), 402 ] { 403 let b = { 404 let mut b = bytes.clone(); 405 b[0] = type_u8; 406 b[1] = code_u8; 407 b 408 }; 409 let expected = Icmpv4Header::from_slice(&b).unwrap().0; 410 411 // ok case 412 { 413 let mut cursor = std::io::Cursor::new(&b); 414 let actual = Icmpv4Header::read(&mut cursor).unwrap(); 415 assert_eq!(expected, actual); 416 assert_eq!(expected.header_len() as u64, cursor.position()); 417 } 418 419 // size error case 420 for bad_len in 0..expected.header_len() { 421 let mut cursor = std::io::Cursor::new(&(b.as_ref()[..bad_len])); 422 assert!(Icmpv4Header::read(&mut cursor).is_err()); 423 } 424 } 425 } 426 } 427 428 proptest! { 429 #[test] 430 fn write( 431 icmpv4_type in icmpv4_type_any(), 432 checksum in any::<u16>(), 433 ) { 434 let header = Icmpv4Header { 435 icmp_type: icmpv4_type.clone(), 436 checksum, 437 }; 438 439 // normal write 440 { 441 let bytes = header.to_bytes(); 442 let mut buffer = Vec::with_capacity(header.header_len()); 443 header.write(&mut buffer).unwrap(); 444 assert_eq!(&bytes[..], &buffer[..]); 445 } 446 447 // error case 448 { 449 for bad_len in 0..icmpv4_type.header_len() { 450 let mut bytes = [0u8;Icmpv6Header::MAX_LEN]; 451 let mut writer = std::io::Cursor::new(&mut bytes[..bad_len]); 452 header.write(&mut writer).unwrap_err(); 453 } 454 } 455 } 456 } 457 458 proptest! { 459 #[test] 460 fn header_len( 461 checksum in any::<u16>(), 462 icmpv4_type in icmpv4_type_any() 463 ) { 464 let header = Icmpv4Header{ 465 icmp_type: icmpv4_type.clone(), 466 checksum, 467 }; 468 assert_eq!(header.header_len(), icmpv4_type.header_len()); 469 } 470 } 471 472 proptest! { 473 #[test] 474 fn fixed_payload_size( 475 checksum in any::<u16>(), 476 icmpv4_type in icmpv4_type_any() 477 ) { 478 let header = Icmpv4Header{ 479 icmp_type: icmpv4_type.clone(), 480 checksum, 481 }; 482 assert_eq!(header.fixed_payload_size(), icmpv4_type.fixed_payload_size()); 483 } 484 } 485 486 proptest! { 487 #[test] 488 fn update_checksum( 489 icmpv4_type in icmpv4_type_any(), 490 checksum in any::<u16>(), 491 payload in proptest::collection::vec(any::<u8>(), 0..1024), 492 ) { 493 let mut header = Icmpv4Header { 494 icmp_type: icmpv4_type.clone(), 495 checksum, 496 }; 497 header.update_checksum(&payload); 498 assert_eq!(header.checksum, icmpv4_type.calc_checksum(&payload)); 499 } 500 } 501 502 proptest! { 503 #[test] 504 #[rustfmt::skip] 505 fn to_bytes( 506 checksum in any::<u16>(), 507 next_hop_mtu in any::<u16>(), 508 redirect_code_u8 in 0u8..=3, 509 gateway_internet_address in any::<[u8;4]>(), 510 time_exceeded_code_u8 in 0u8..=1, 511 id in any::<u16>(), 512 seq in any::<u16>(), 513 originate_timestamp in any::<u32>(), 514 receive_timestamp in any::<u32>(), 515 transmit_timestamp in any::<u32>(), 516 pointer in any::<u8>(), 517 unknown_type_u8 in any::<u8>(), 518 unknown_code_u8 in any::<u8>(), 519 bytes5to8 in any::<[u8;4]>(), 520 ) { 521 use Icmpv4Type::*; 522 use arrayvec::ArrayVec; 523 524 let ts = TimestampMessage{ 525 id, 526 seq, 527 originate_timestamp, 528 receive_timestamp, 529 transmit_timestamp, 530 }; 531 let ts_bytes = { 532 let id_be = id.to_be_bytes(); 533 let seq_be = seq.to_be_bytes(); 534 let ot = originate_timestamp.to_be_bytes(); 535 let rt = receive_timestamp.to_be_bytes(); 536 let tt = transmit_timestamp.to_be_bytes(); 537 [ 538 0, 0, 0, 0, 539 id_be[0], id_be[1], seq_be[0], seq_be[1], 540 ot[0], ot[1], ot[2], ot[3], 541 rt[0], rt[1], rt[2], rt[3], 542 tt[0], tt[1], tt[2], tt[3], 543 ] 544 }; 545 let echo = IcmpEchoHeader{ 546 id, 547 seq, 548 }; 549 let redirect = RedirectHeader{ 550 code: RedirectCode::from_u8(redirect_code_u8).unwrap(), 551 gateway_internet_address, 552 }; 553 554 // test values with no need for subtests 555 let random_values = [ 556 ( 557 Unknown { 558 type_u8: unknown_type_u8, 559 code_u8: unknown_code_u8, 560 bytes5to8: bytes5to8, 561 }, 562 8, 563 [ 564 unknown_type_u8, unknown_code_u8, 0, 0, 565 bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3], 566 0, 0, 0, 0, 567 0, 0, 0, 0, 568 0, 0, 0, 0, 569 ], 570 ), 571 ( 572 EchoReply(echo.clone()), 573 8, 574 { 575 let id_be = id.to_be_bytes(); 576 let seq_be = seq.to_be_bytes(); 577 [ 578 TYPE_ECHO_REPLY, 0, 0, 0, 579 id_be[0], id_be[1], seq_be[0], seq_be[1], 580 0, 0, 0, 0, 581 0, 0, 0, 0, 582 0, 0, 0, 0, 583 ] 584 } 585 ), 586 587 ( 588 Redirect(redirect), 589 8, 590 { 591 let gip = gateway_internet_address; 592 [ 593 TYPE_REDIRECT, redirect_code_u8, 0, 0, 594 gip[0], gip[1], gip[2], gip[3], 595 0, 0, 0, 0, 596 0, 0, 0, 0, 597 0, 0, 0, 0, 598 ] 599 }, 600 ), 601 ( 602 EchoRequest(echo.clone()), 603 8, 604 { 605 let id_be = id.to_be_bytes(); 606 let seq_be = seq.to_be_bytes(); 607 [ 608 TYPE_ECHO_REQUEST, 0, 0, 0, 609 id_be[0], id_be[1], seq_be[0], seq_be[1], 610 0, 0, 0, 0, 611 0, 0, 0, 0, 612 0, 0, 0, 0, 613 ] 614 } 615 ), 616 ( 617 TimeExceeded(TimeExceededCode::from_u8(time_exceeded_code_u8).unwrap()), 618 8, 619 [ 620 TYPE_TIME_EXCEEDED, time_exceeded_code_u8, 0, 0, 621 0, 0, 0, 0, 622 0, 0, 0, 0, 623 0, 0, 0, 0, 624 0, 0, 0, 0, 625 ], 626 ), 627 ( 628 TimestampRequest(ts.clone()), 629 20, 630 { 631 let mut b = ts_bytes; 632 b[0] = TYPE_TIMESTAMP; 633 b 634 } 635 ), 636 ( 637 TimestampReply(ts), 638 20, 639 { 640 let mut b = ts_bytes; 641 b[0] = TYPE_TIMESTAMP_REPLY; 642 b 643 } 644 ), 645 ]; 646 647 for t in random_values { 648 let actual = Icmpv4Header{ 649 icmp_type: t.0.clone(), 650 checksum, 651 }.to_bytes(); 652 653 let mut expected = ArrayVec::from(t.2); 654 unsafe { 655 expected.set_len(t.1) 656 } 657 let checksum_be = checksum.to_be_bytes(); 658 expected[2] = checksum_be[0]; 659 expected[3] = checksum_be[1]; 660 assert_eq!(expected, actual); 661 } 662 663 // destination unreachable 664 { 665 use DestUnreachableHeader::*; 666 let tests = [ 667 (CODE_DST_UNREACH_NET, [0;2], Network), 668 (CODE_DST_UNREACH_HOST, [0;2], Host), 669 (CODE_DST_UNREACH_PROTOCOL, [0;2], Protocol), 670 (CODE_DST_UNREACH_PORT, [0;2], Port), 671 (CODE_DST_UNREACH_NEED_FRAG, next_hop_mtu.to_be_bytes(), FragmentationNeeded{ next_hop_mtu }), 672 (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, [0;2], SourceRouteFailed), 673 (CODE_DST_UNREACH_NET_UNKNOWN, [0;2], NetworkUnknown), 674 (CODE_DST_UNREACH_HOST_UNKNOWN, [0;2], HostUnknown), 675 (CODE_DST_UNREACH_ISOLATED, [0;2], Isolated), 676 (CODE_DST_UNREACH_NET_PROHIB, [0;2], NetworkProhibited), 677 (CODE_DST_UNREACH_HOST_PROHIB, [0;2], HostProhibited), 678 (CODE_DST_UNREACH_TOS_NET, [0;2], TosNetwork), 679 (CODE_DST_UNREACH_TOS_HOST, [0;2], TosHost), 680 (CODE_DST_UNREACH_FILTER_PROHIB, [0;2], FilterProhibited), 681 (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, [0;2], HostPrecedenceViolation), 682 (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, [0;2], PrecedenceCutoff), 683 ]; 684 for t in tests { 685 let checksum_be = checksum.to_be_bytes(); 686 let mut expected = ArrayVec::from([ 687 TYPE_DEST_UNREACH, t.0, checksum_be[0], checksum_be[1], 688 0, 0, t.1[0], t.1[1], 689 0, 0, 0, 0, 690 0, 0, 0, 0, 691 0, 0, 0, 0, 692 ]); 693 unsafe { 694 expected.set_len(8); 695 } 696 let actual = Icmpv4Header{ 697 icmp_type: DestinationUnreachable(t.2.clone()), 698 checksum, 699 }.to_bytes(); 700 assert_eq!(expected, actual); 701 } 702 } 703 704 // parameter problem 705 { 706 use ParameterProblemHeader::*; 707 let tests = [ 708 (CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, pointer, PointerIndicatesError(pointer)), 709 (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, 0, MissingRequiredOption), 710 (CODE_PARAMETER_PROBLEM_BAD_LENGTH, 0, BadLength), 711 ]; 712 for t in tests { 713 let checksum_be = checksum.to_be_bytes(); 714 let mut expected = ArrayVec::from([ 715 TYPE_PARAMETER_PROBLEM, t.0, checksum_be[0], checksum_be[1], 716 t.1, 0, 0, 0, 717 0, 0, 0, 0, 718 0, 0, 0, 0, 719 0, 0, 0, 0, 720 ]); 721 unsafe { 722 expected.set_len(8); 723 } 724 let actual = Icmpv4Header{ 725 icmp_type: ParameterProblem(t.2.clone()), 726 checksum, 727 }.to_bytes(); 728 assert_eq!(expected, actual); 729 } 730 } 731 } 732 } 733 734 #[test] clone_eq()735 fn clone_eq() { 736 use Icmpv4Type::*; 737 let header = Icmpv4Header { 738 icmp_type: ParameterProblem(ParameterProblemHeader::BadLength), 739 checksum: 0, 740 }; 741 assert_eq!(header.clone(), header); 742 } 743 744 #[test] debug()745 fn debug() { 746 use Icmpv4Type::*; 747 let header = Icmpv4Header { 748 icmp_type: ParameterProblem(ParameterProblemHeader::BadLength), 749 checksum: 0, 750 }; 751 assert_eq!( 752 format!("{:?}", header), 753 format!( 754 "Icmpv4Header {{ icmp_type: {:?}, checksum: {:?} }}", 755 header.icmp_type, header.checksum 756 ) 757 ); 758 } 759 } 760