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