1 use crate::*; 2 3 /// Starting contents of an ICMPv4 packet without the checksum. 4 #[derive(Clone, Debug, PartialEq, Eq)] 5 pub enum Icmpv4Type { 6 /// In case of an unknown ICMP type and code combination is received the 7 /// header elements are stored raw in this enum value. The `Unknown` value can 8 /// also be passed to the `Icmpv4Header::write` function to write arbitrary ICMP 9 /// packets. 10 /// 11 /// # What is part of the header for `Icmpv4Type::Unknown`? 12 /// 13 /// For unknown ICMP type & code combination the first 8 bytes are stored 14 /// in the [`Icmpv4Header`] and the rest is stored in the payload 15 /// ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]). 16 /// 17 /// 18 /// ```text 19 /// 0 1 2 3 4 20 /// +---------------------------------------------------------------+ - 21 /// | type_u8 | code_u8 | checksum (in Icmpv4Header) | | 22 /// +---------------------------------------------------------------+ | part of header & type 23 /// | bytes5to8 | ↓ 24 /// +---------------------------------------------------------------+ - 25 /// | | | 26 /// ... ... ... | part of payload 27 /// | | ↓ 28 /// +---------------------------------------------------------------+ - 29 /// ``` 30 Unknown { 31 /// ICMP type (present in the first byte of the ICMP packet). 32 type_u8: u8, 33 /// ICMP code (present in the 2nd byte of the ICMP packet). 34 code_u8: u8, 35 /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet. 36 bytes5to8: [u8; 4], 37 }, 38 39 /// Response to an `EchoRequest` message (defined in RFC792). 40 /// 41 /// # What is part of the header for `Icmpv4Type::EchoReply`? 42 /// 43 /// For the [`Icmpv4Type::EchoReply`] type the first 8 bytes/octets of the 44 /// ICMP packet are part of the header. This includes the `id` and `seq` 45 /// fields. The data part of the ICMP Echo Reply packet is part of the 46 /// payload ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]) 47 /// and not part of the [`Icmpv4Header`]. 48 /// 49 /// ```text 50 /// 0 1 2 3 4 51 /// +---------------------------------------------------------------+ - 52 /// | 0 | 0 | checksum (in Icmpv4Header) | | 53 /// +---------------------------------------------------------------+ | part of header & type 54 /// | [value].id | [value].seq | ↓ 55 /// +---------------------------------------------------------------+ - 56 /// | | | 57 /// ... <data> ... | part of payload 58 /// | | ↓ 59 /// +---------------------------------------------------------------+ - 60 /// ``` 61 EchoReply(IcmpEchoHeader), 62 63 /// Message sent to inform the client that the destination is unreachable for 64 /// some reason (defined in RFC792). 65 /// 66 /// # What is part of the header for `Icmpv4Type::DestinationUnreachable`? 67 /// 68 /// For the [`Icmpv4Type::DestinationUnreachable`] type the first 8 bytes/octets 69 /// of the ICMP packet are part of the header. This includes the `next_hop_mtu` 70 /// field. The `unused` part is not stored and droped. The offending packet 71 /// is stored in the payload part of the packet ([`Icmpv4Slice::payload`] or 72 /// [`PacketHeaders::payload`]) and is not part of the [`Icmpv4Header`]. 73 /// 74 /// ```text 75 /// 0 1 2 3 4 76 /// +---------------------------------------------------------------+ - 77 /// | 3 | 0 | checksum (in Icmpv4Header) | | 78 /// +---------------------------------------------------------------+ | part of header & type 79 /// |[v].next_hop...| <unused> | ↓ 80 /// +---------------------------------------------------------------+ - 81 /// | | | 82 /// ... Internet Header + 64 bits of Original Data Datagram ... | part of payload 83 /// | | ↓ 84 /// +---------------------------------------------------------------+ - 85 /// ``` 86 DestinationUnreachable(icmpv4::DestUnreachableHeader), 87 88 /// Requests data packets be sent on an alternative route (defined in RFC792). 89 /// 90 /// # What is part of the header for `Icmpv4Type::Redirect`? 91 /// 92 /// For the [`Icmpv4Type::Redirect`] type the first 8 bytes/octets of the ICMP 93 /// packet are part of the header. This includes the `gateway_internet_address` 94 /// field. The offending packet is stored in the payload part of the packet 95 /// ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]) and is not part of 96 /// the [`Icmpv4Header`]. 97 /// 98 /// ```text 99 /// 0 1 2 3 4 100 /// +---------------------------------------------------------------+ - 101 /// | 5 | [value].code | checksum (in Icmpv4Header) | | 102 /// +---------------------------------------------------------------+ | part of header & type 103 /// | [value].gateway_internet_address | ↓ 104 /// +---------------------------------------------------------------+ - 105 /// | | | 106 /// .. Internet Header + 64 bits of Original Data Datagram ... | part of payload 107 /// | | ↓ 108 /// +---------------------------------------------------------------+ - 109 Redirect(icmpv4::RedirectHeader), 110 111 /// Requesting an `EchoReply` from the receiver (defined in RFC792) 112 /// 113 /// # What is part of the header for `Icmpv4Type::EchoRequest`? 114 /// 115 /// For the [`Icmpv4Type::EchoRequest`] type the first 8 bytes/octets of the 116 /// ICMP packet are part of the header. This includes the `id` and `seq` 117 /// fields. The data part of the ICMP echo request packet is part of the payload 118 /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and not part of the 119 /// [`Icmpv4Header`]. 120 /// 121 /// ```text 122 /// 0 1 2 3 4 123 /// +---------------------------------------------------------------+ - 124 /// | 8 | 0 | checksum (in Icmpv4Header) | | 125 /// +---------------------------------------------------------------+ | part of header & type 126 /// | [value].id | [value].seq | ↓ 127 /// +---------------------------------------------------------------+ - 128 /// | | | 129 /// ... <data> ... | part of payload 130 /// | | ↓ 131 /// +---------------------------------------------------------------+ - 132 /// ``` 133 EchoRequest(IcmpEchoHeader), 134 135 /// Generated when a datagram had to be discarded due to the time to live field 136 /// reaching zero (defined in RFC792). 137 /// 138 /// # What is part of the header for `Icmpv4Type::TimeExceeded`? 139 /// 140 /// For the `Icmpv4Type::TimeExceeded` type the first 8 bytes/octets of the ICMP 141 /// packet are part of the header. The `unused` part is not stored and droped. 142 /// The offending packet is stored in the payload part of the packet 143 /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and is not part of 144 /// the [`Icmpv4Header`]. 145 /// 146 /// ```text 147 /// 0 1 2 3 4 148 /// +---------------------------------------------------------------+ - 149 /// | 11 | [value as u8] | checksum (in Icmpv4Header) | | 150 /// +---------------------------------------------------------------+ | part of header & type 151 /// | <unused> | ↓ 152 /// +---------------------------------------------------------------+ - 153 /// | | | 154 /// ... Internet Header + 64 bits of Original Data Datagram ... | part of payload 155 /// | | ↓ 156 /// +---------------------------------------------------------------+ - 157 TimeExceeded(icmpv4::TimeExceededCode), 158 159 /// Sent if there is a problem with a parameter in a received packet. 160 /// 161 /// # What is part of the header for `Icmpv4Type::ParameterProblem`? 162 /// 163 /// For the `Icmpv4Type::ParameterProblem` type the first 8 bytes/octets of the ICMP 164 /// packet are part of the header. The `unused` part is not stored and droped. 165 /// The offending packet is stored in the payload part of the packet 166 /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and is not part of 167 /// the [`Icmpv4Header`]. 168 /// 169 /// ```text 170 /// 0 1 2 3 4 171 /// +---------------------------------------------------------------+ - 172 /// | 12 | [v].code_u8() | checksum (in Icmpv4Header) | | 173 /// +---------------------------------------------------------------+ | part of header & type 174 /// |[value].pointer| <unused> | ↓ 175 /// +---------------------------------------------------------------+ - 176 /// | | | 177 /// ... Internet Header + 64 bits of Original Data Datagram ... | part of payload 178 /// | | ↓ 179 /// +---------------------------------------------------------------+ - 180 ParameterProblem(icmpv4::ParameterProblemHeader), 181 182 /// Timestamp is used for time synchronization. 183 /// 184 /// # What is part of the header for `Icmpv4Type::TimestampRequest`? 185 /// 186 /// For the `Icmpv4Type::TimestampRequest` type the entire ICMP packet is 187 /// contained within the header. The payload data is empty. 188 TimestampRequest(icmpv4::TimestampMessage), 189 190 /// Anwser to a `TimestampRequest` message. 191 /// 192 /// # What is part of the header for `Icmpv4Type::TimestampReply`? 193 /// 194 /// For the `Icmpv4Type::TimestampReply` type the entire ICMP packet is 195 /// contained within the header. The payload data is empty. 196 TimestampReply(icmpv4::TimestampMessage), 197 } 198 199 impl Icmpv4Type { 200 /// Returns the length in bytes/octets of the header of 201 /// this ICMPv4 message type. 202 #[inline] header_len(&self) -> usize203 pub fn header_len(&self) -> usize { 204 use Icmpv4Type::*; 205 match self { 206 Unknown { 207 type_u8: _, 208 code_u8: _, 209 bytes5to8: _, 210 } 211 | EchoReply(_) 212 | DestinationUnreachable(_) 213 | Redirect(_) 214 | EchoRequest(_) 215 | TimeExceeded(_) 216 | ParameterProblem(_) => 8, 217 TimestampRequest(_) | TimestampReply(_) => icmpv4::TimestampMessage::LEN, 218 } 219 } 220 221 /// If the ICMP type has a fixed size returns the number of 222 /// bytes that should be present after the header of this type. 223 #[inline] fixed_payload_size(&self) -> Option<usize>224 pub fn fixed_payload_size(&self) -> Option<usize> { 225 use Icmpv4Type::*; 226 match self { 227 Unknown { 228 type_u8: _, 229 code_u8: _, 230 bytes5to8: _, 231 } 232 | EchoReply(_) 233 | DestinationUnreachable(_) 234 | Redirect(_) 235 | EchoRequest(_) 236 | TimeExceeded(_) 237 | ParameterProblem(_) => None, 238 TimestampRequest(_) | TimestampReply(_) => Some(0), 239 } 240 } 241 242 /// Calculate the ICMP checksum value. calc_checksum(&self, payload: &[u8]) -> u16243 pub fn calc_checksum(&self, payload: &[u8]) -> u16 { 244 use crate::{icmpv4::*, Icmpv4Type::*}; 245 match self { 246 Unknown { 247 type_u8, 248 code_u8, 249 bytes5to8, 250 } => checksum::Sum16BitWords::new() 251 .add_2bytes([*type_u8, *code_u8]) 252 .add_4bytes(*bytes5to8), 253 EchoReply(header) => checksum::Sum16BitWords::new() 254 .add_2bytes([TYPE_ECHO_REPLY, 0]) 255 .add_2bytes(header.id.to_be_bytes()) 256 .add_2bytes(header.seq.to_be_bytes()), 257 DestinationUnreachable(ref header) => { 258 use DestUnreachableHeader::*; 259 match header { 260 Network => checksum::Sum16BitWords::new() 261 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET]), 262 Host => checksum::Sum16BitWords::new() 263 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST]), 264 Protocol => checksum::Sum16BitWords::new() 265 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PROTOCOL]), 266 Port => checksum::Sum16BitWords::new() 267 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PORT]), 268 FragmentationNeeded { next_hop_mtu } => checksum::Sum16BitWords::new() 269 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG]) 270 .add_2bytes(next_hop_mtu.to_be_bytes()), 271 SourceRouteFailed => checksum::Sum16BitWords::new() 272 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED]), 273 NetworkUnknown => checksum::Sum16BitWords::new() 274 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_UNKNOWN]), 275 HostUnknown => checksum::Sum16BitWords::new() 276 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_UNKNOWN]), 277 Isolated => checksum::Sum16BitWords::new() 278 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_ISOLATED]), 279 NetworkProhibited => checksum::Sum16BitWords::new() 280 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_PROHIB]), 281 HostProhibited => checksum::Sum16BitWords::new() 282 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PROHIB]), 283 TosNetwork => checksum::Sum16BitWords::new() 284 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_NET]), 285 TosHost => checksum::Sum16BitWords::new() 286 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_HOST]), 287 FilterProhibited => checksum::Sum16BitWords::new() 288 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_FILTER_PROHIB]), 289 HostPrecedenceViolation => checksum::Sum16BitWords::new().add_2bytes([ 290 TYPE_DEST_UNREACH, 291 CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, 292 ]), 293 PrecedenceCutoff => checksum::Sum16BitWords::new() 294 .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PRECEDENCE_CUTOFF]), 295 } 296 } 297 Redirect(header) => checksum::Sum16BitWords::new() 298 .add_2bytes([TYPE_REDIRECT, header.code as u8]) 299 .add_4bytes(header.gateway_internet_address), 300 EchoRequest(header) => checksum::Sum16BitWords::new() 301 .add_2bytes([TYPE_ECHO_REQUEST, 0]) 302 .add_2bytes(header.id.to_be_bytes()) 303 .add_2bytes(header.seq.to_be_bytes()), 304 TimeExceeded(code) => { 305 checksum::Sum16BitWords::new().add_2bytes([TYPE_TIME_EXCEEDED, *code as u8]) 306 } 307 ParameterProblem(header) => { 308 use ParameterProblemHeader::*; 309 match header { 310 PointerIndicatesError(pointer) => checksum::Sum16BitWords::new() 311 .add_2bytes([ 312 TYPE_PARAMETER_PROBLEM, 313 CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, 314 ]) 315 .add_2bytes([*pointer, 0]), 316 MissingRequiredOption => checksum::Sum16BitWords::new().add_2bytes([ 317 TYPE_PARAMETER_PROBLEM, 318 CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, 319 ]), 320 BadLength => checksum::Sum16BitWords::new() 321 .add_2bytes([TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_BAD_LENGTH]), 322 } 323 } 324 TimestampRequest(msg) => checksum::Sum16BitWords::new() 325 .add_2bytes([TYPE_TIMESTAMP, 0]) 326 .add_2bytes(msg.id.to_be_bytes()) 327 .add_2bytes(msg.seq.to_be_bytes()) 328 .add_4bytes(msg.originate_timestamp.to_be_bytes()) 329 .add_4bytes(msg.receive_timestamp.to_be_bytes()) 330 .add_4bytes(msg.transmit_timestamp.to_be_bytes()), 331 TimestampReply(msg) => checksum::Sum16BitWords::new() 332 .add_2bytes([TYPE_TIMESTAMP_REPLY, 0]) 333 .add_2bytes(msg.id.to_be_bytes()) 334 .add_2bytes(msg.seq.to_be_bytes()) 335 .add_4bytes(msg.originate_timestamp.to_be_bytes()) 336 .add_4bytes(msg.receive_timestamp.to_be_bytes()) 337 .add_4bytes(msg.transmit_timestamp.to_be_bytes()), 338 } 339 .add_slice(payload) 340 .ones_complement() 341 .to_be() 342 } 343 } 344 345 #[cfg(test)] 346 mod test { 347 use crate::{icmpv4::*, Icmpv4Type::*, *}; 348 use alloc::format; 349 use proptest::prelude::*; 350 351 #[test] header_len()352 fn header_len() { 353 let dummy_ts = TimestampMessage { 354 id: 0, 355 seq: 0, 356 originate_timestamp: 0, 357 receive_timestamp: 0, 358 transmit_timestamp: 0, 359 }; 360 let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 }; 361 let dummy_redirect = RedirectHeader { 362 code: RedirectCode::RedirectForNetwork, 363 gateway_internet_address: [0; 4], 364 }; 365 let tests = [ 366 ( 367 8, 368 Unknown { 369 type_u8: 0, 370 code_u8: 0, 371 bytes5to8: [0; 4], 372 }, 373 ), 374 (8, EchoReply(dummy_echo)), 375 (8, DestinationUnreachable(DestUnreachableHeader::Network)), 376 (8, Redirect(dummy_redirect)), 377 (8, EchoRequest(dummy_echo)), 378 (8, TimeExceeded(TimeExceededCode::TtlExceededInTransit)), 379 (8, ParameterProblem(ParameterProblemHeader::BadLength)), 380 (20, TimestampRequest(dummy_ts.clone())), 381 (20, TimestampReply(dummy_ts)), 382 ]; 383 for t in tests { 384 assert_eq!(t.0, t.1.header_len()); 385 } 386 } 387 388 #[test] fixed_payload_size()389 fn fixed_payload_size() { 390 use Icmpv4Type::*; 391 392 let dummy_ts = TimestampMessage { 393 id: 0, 394 seq: 0, 395 originate_timestamp: 0, 396 receive_timestamp: 0, 397 transmit_timestamp: 0, 398 }; 399 let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 }; 400 let dummy_redirect = RedirectHeader { 401 code: RedirectCode::RedirectForNetwork, 402 gateway_internet_address: [0; 4], 403 }; 404 let tests = [ 405 ( 406 None, 407 Unknown { 408 type_u8: 0, 409 code_u8: 0, 410 bytes5to8: [0; 4], 411 }, 412 ), 413 (None, EchoReply(dummy_echo)), 414 (None, DestinationUnreachable(DestUnreachableHeader::Network)), 415 (None, Redirect(dummy_redirect)), 416 (None, EchoRequest(dummy_echo)), 417 (None, TimeExceeded(TimeExceededCode::TtlExceededInTransit)), 418 (None, ParameterProblem(ParameterProblemHeader::BadLength)), 419 (Some(0), TimestampRequest(dummy_ts.clone())), 420 (Some(0), TimestampReply(dummy_ts)), 421 ]; 422 for t in tests { 423 assert_eq!(t.0, t.1.fixed_payload_size()); 424 } 425 } 426 427 proptest! { 428 #[test] 429 fn calc_checksum( 430 dest_unreach_code_u8 in 0u8..=15, 431 next_hop_mtu in any::<u16>(), 432 redirect_code_u8 in 0u8..=3, 433 gateway_internet_address in any::<[u8;4]>(), 434 time_exceeded_code_u8 in 0u8..=1, 435 id in any::<u16>(), 436 seq in any::<u16>(), 437 originate_timestamp in any::<u32>(), 438 receive_timestamp in any::<u32>(), 439 transmit_timestamp in any::<u32>(), 440 param_problem_code_u8 in 0u8..=2, 441 pointer in any::<u8>(), 442 unknown_type_u8 in any::<u8>(), 443 unknown_code_u8 in any::<u8>(), 444 bytes5to8 in any::<[u8;4]>(), 445 payload in proptest::collection::vec(any::<u8>(), 0..1024) 446 ) { 447 let ts = TimestampMessage{ 448 id, 449 seq, 450 originate_timestamp, 451 receive_timestamp, 452 transmit_timestamp, 453 }; 454 let echo = IcmpEchoHeader{ 455 id, 456 seq, 457 }; 458 let redirect = RedirectHeader{ 459 code: RedirectCode::from_u8(redirect_code_u8).unwrap(), 460 gateway_internet_address, 461 }; 462 let dest_unreach = DestUnreachableHeader::from_values(dest_unreach_code_u8, next_hop_mtu).unwrap(); 463 let param_prob = ParameterProblemHeader::from_values(param_problem_code_u8, pointer).unwrap(); 464 let values = [ 465 Unknown { 466 type_u8: unknown_type_u8, 467 code_u8: unknown_code_u8, 468 bytes5to8: bytes5to8, 469 }, 470 EchoReply(echo.clone()), 471 DestinationUnreachable(dest_unreach), 472 Redirect(redirect), 473 EchoRequest(echo), 474 TimeExceeded(TimeExceededCode::from_u8(time_exceeded_code_u8).unwrap()), 475 ParameterProblem(param_prob), 476 TimestampRequest(ts.clone()), 477 TimestampReply(ts), 478 ]; 479 480 for t in values { 481 let bytes = Icmpv4Header{ 482 icmp_type: t.clone(), 483 checksum: 0, // use zero so the checksum calculation from the bytes works 484 }.to_bytes(); 485 let expected = crate::checksum::Sum16BitWords::new() 486 .add_slice(bytes.as_ref()) 487 .add_slice(&payload) 488 .ones_complement() 489 .to_be(); 490 assert_eq!(expected, t.calc_checksum(&payload)); 491 } 492 } 493 } 494 495 #[test] clone_eq()496 fn clone_eq() { 497 let dummy_ts = TimestampMessage { 498 id: 0, 499 seq: 0, 500 originate_timestamp: 0, 501 receive_timestamp: 0, 502 transmit_timestamp: 0, 503 }; 504 let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 }; 505 let dummy_redirect = RedirectHeader { 506 code: RedirectCode::RedirectForNetwork, 507 gateway_internet_address: [0; 4], 508 }; 509 let tests = [ 510 Unknown { 511 type_u8: 0, 512 code_u8: 0, 513 bytes5to8: [0; 4], 514 }, 515 EchoReply(dummy_echo), 516 DestinationUnreachable(DestUnreachableHeader::Network), 517 Redirect(dummy_redirect), 518 EchoRequest(dummy_echo), 519 TimeExceeded(TimeExceededCode::TtlExceededInTransit), 520 ParameterProblem(ParameterProblemHeader::BadLength), 521 TimestampRequest(dummy_ts.clone()), 522 TimestampReply(dummy_ts), 523 ]; 524 for t in tests { 525 assert_eq!(t.clone(), t); 526 } 527 } 528 529 #[test] debug()530 fn debug() { 531 let dummy_ts = TimestampMessage { 532 id: 0, 533 seq: 0, 534 originate_timestamp: 0, 535 receive_timestamp: 0, 536 transmit_timestamp: 0, 537 }; 538 let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 }; 539 540 assert_eq!( 541 format!( 542 "{:?}", 543 Unknown { 544 type_u8: 0, 545 code_u8: 0, 546 bytes5to8: [0; 4] 547 } 548 ), 549 format!( 550 "Unknown {{ type_u8: {:?}, code_u8: {:?}, bytes5to8: {:?} }}", 551 0u8, 0u8, [0u8; 4] 552 ) 553 ); 554 assert_eq!( 555 format!("{:?}", EchoReply(dummy_echo)), 556 format!("EchoReply({:?})", dummy_echo) 557 ); 558 assert_eq!( 559 format!( 560 "{:?}", 561 DestinationUnreachable(DestUnreachableHeader::Network) 562 ), 563 format!( 564 "DestinationUnreachable({:?})", 565 DestUnreachableHeader::Network 566 ) 567 ); 568 { 569 let dummy_redirect = RedirectHeader { 570 code: RedirectCode::RedirectForNetwork, 571 gateway_internet_address: [0; 4], 572 }; 573 assert_eq!( 574 format!("{:?}", Redirect(dummy_redirect.clone())), 575 format!("Redirect({:?})", dummy_redirect) 576 ); 577 } 578 assert_eq!( 579 format!("{:?}", EchoRequest(dummy_echo)), 580 format!("EchoRequest({:?})", dummy_echo) 581 ); 582 assert_eq!( 583 format!("{:?}", TimeExceeded(TimeExceededCode::TtlExceededInTransit)), 584 format!("TimeExceeded({:?})", TimeExceededCode::TtlExceededInTransit) 585 ); 586 assert_eq!( 587 format!("{:?}", ParameterProblem(ParameterProblemHeader::BadLength)), 588 format!("ParameterProblem({:?})", ParameterProblemHeader::BadLength) 589 ); 590 assert_eq!( 591 format!("{:?}", TimestampRequest(dummy_ts.clone())), 592 format!("TimestampRequest({:?})", dummy_ts) 593 ); 594 assert_eq!( 595 format!("{:?}", TimestampReply(dummy_ts.clone())), 596 format!("TimestampReply({:?})", dummy_ts) 597 ); 598 } 599 } 600