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