etherparse/transport/
icmpv4_type.rs

1use crate::*;
2
3/// Starting contents of an ICMPv4 packet without the checksum.
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub 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
199impl Icmpv4Type {
200    /// Returns the length in bytes/octets of the header of
201    /// this ICMPv4 message type.
202    #[inline]
203    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]
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.
243    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)]
346mod test {
347    use crate::{icmpv4::*, Icmpv4Type::*, *};
348    use alloc::format;
349    use proptest::prelude::*;
350
351    #[test]
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]
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]
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]
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}