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}