• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{icmpv4::*, *};
2 
3 /// A slice containing an ICMPv4 network package.
4 ///
5 /// Struct allows the selective read of fields in the ICMPv4
6 /// packet.
7 #[derive(Clone, Debug, Eq, PartialEq)]
8 pub struct Icmpv4Slice<'a> {
9     pub(crate) slice: &'a [u8],
10 }
11 
12 impl<'a> Icmpv4Slice<'a> {
13     /// Creates a slice containing an ICMPv4 packet.
14     ///
15     /// # Errors
16     ///
17     /// The function will return an `Err` `err::LenError`
18     /// if the given slice is too small or does not match the expected
19     /// length in case of a timestamp message.
20     #[inline]
from_slice(slice: &'a [u8]) -> Result<Icmpv4Slice<'a>, err::LenError>21     pub fn from_slice(slice: &'a [u8]) -> Result<Icmpv4Slice<'a>, err::LenError> {
22         // check length
23         if slice.len() < Icmpv4Header::MIN_LEN {
24             return Err(err::LenError {
25                 required_len: Icmpv4Header::MIN_LEN,
26                 len: slice.len(),
27                 len_source: LenSource::Slice,
28                 layer: err::Layer::Icmpv4,
29                 layer_start_offset: 0,
30             });
31         }
32 
33         // SAFETY:
34         // Safe as it is previously checked that the slice has
35         // at least the length of Icmpv4Header::MIN_LEN (8).
36         let icmp_type: u8 = unsafe { *slice.get_unchecked(0) };
37         let icmp_code: u8 = unsafe { *slice.get_unchecked(1) };
38 
39         // check type specific length
40         match icmp_type {
41             TYPE_TIMESTAMP => {
42                 if 0 == icmp_code && TimestampMessage::LEN != slice.len() {
43                     return Err(err::LenError {
44                         required_len: TimestampMessage::LEN,
45                         len: slice.len(),
46                         len_source: LenSource::Slice,
47                         layer: err::Layer::Icmpv4Timestamp,
48                         layer_start_offset: 0,
49                     });
50                 }
51             }
52             TYPE_TIMESTAMP_REPLY => {
53                 if 0 == icmp_code && TimestampMessage::LEN != slice.len() {
54                     return Err(err::LenError {
55                         required_len: TimestampMessage::LEN,
56                         len: slice.len(),
57                         len_source: LenSource::Slice,
58                         layer: err::Layer::Icmpv4TimestampReply,
59                         layer_start_offset: 0,
60                     });
61                 }
62             }
63             _ => {}
64         }
65 
66         //done
67         Ok(Icmpv4Slice { slice })
68     }
69 
70     /// Decode the header values into an [`Icmpv4Header`] struct.
71     #[inline]
header(&self) -> Icmpv4Header72     pub fn header(&self) -> Icmpv4Header {
73         let icmp_type = self.icmp_type();
74         Icmpv4Header {
75             icmp_type,
76             checksum: self.checksum(),
77         }
78     }
79 
80     /// Number of bytes/octets that will be converted into a
81     /// [`Icmpv4Header`] when [`Icmpv4Slice::header`] gets called.
82     #[inline]
header_len(&self) -> usize83     pub fn header_len(&self) -> usize {
84         match self.type_u8() {
85             TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {
86                 if 0 == self.code_u8() {
87                     TimestampMessage::LEN
88                 } else {
89                     8
90                 }
91             }
92             _ => 8,
93         }
94     }
95 
96     /// Decode the header values (excluding the checksum) into an [`Icmpv4Type`] enum.
icmp_type(&self) -> Icmpv4Type97     pub fn icmp_type(&self) -> Icmpv4Type {
98         use Icmpv4Type::*;
99 
100         unsafe fn timestamp_message(ptr: *const u8) -> TimestampMessage {
101             TimestampMessage {
102                 id: get_unchecked_be_u16(ptr.add(4)),
103                 seq: get_unchecked_be_u16(ptr.add(6)),
104                 originate_timestamp: get_unchecked_be_u32(ptr.add(8)),
105                 receive_timestamp: get_unchecked_be_u32(ptr.add(12)),
106                 transmit_timestamp: get_unchecked_be_u32(ptr.add(16)),
107             }
108         }
109 
110         match self.type_u8() {
111             TYPE_ECHO_REPLY => {
112                 if 0 == self.code_u8() {
113                     return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8()));
114                 }
115             }
116             TYPE_DEST_UNREACH => {
117                 use DestUnreachableHeader::*;
118                 match self.code_u8() {
119                     CODE_DST_UNREACH_NET => return DestinationUnreachable(Network),
120                     CODE_DST_UNREACH_HOST => return DestinationUnreachable(Host),
121                     CODE_DST_UNREACH_PROTOCOL => return DestinationUnreachable(Protocol),
122                     CODE_DST_UNREACH_PORT => return DestinationUnreachable(Port),
123                     CODE_DST_UNREACH_NEED_FRAG => {
124                         return DestinationUnreachable(FragmentationNeeded {
125                             // SAFETY:
126                             // Safe as the contructor checks that the slice has
127                             // at least the length of Icmpv4Header::MIN_LEN (8).
128                             next_hop_mtu: unsafe {
129                                 get_unchecked_be_u16(self.slice.as_ptr().add(6))
130                             },
131                         });
132                     }
133                     CODE_DST_UNREACH_SOURCE_ROUTE_FAILED => {
134                         return DestinationUnreachable(SourceRouteFailed)
135                     }
136                     CODE_DST_UNREACH_NET_UNKNOWN => return DestinationUnreachable(NetworkUnknown),
137                     CODE_DST_UNREACH_HOST_UNKNOWN => return DestinationUnreachable(HostUnknown),
138                     CODE_DST_UNREACH_ISOLATED => return DestinationUnreachable(Isolated),
139                     CODE_DST_UNREACH_NET_PROHIB => {
140                         return DestinationUnreachable(NetworkProhibited)
141                     }
142                     CODE_DST_UNREACH_HOST_PROHIB => return DestinationUnreachable(HostProhibited),
143                     CODE_DST_UNREACH_TOS_NET => return DestinationUnreachable(TosNetwork),
144                     CODE_DST_UNREACH_TOS_HOST => return DestinationUnreachable(TosHost),
145                     CODE_DST_UNREACH_FILTER_PROHIB => {
146                         return DestinationUnreachable(FilterProhibited)
147                     }
148                     CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION => {
149                         return DestinationUnreachable(HostPrecedenceViolation)
150                     }
151                     CODE_DST_UNREACH_PRECEDENCE_CUTOFF => {
152                         return DestinationUnreachable(PrecedenceCutoff)
153                     }
154                     _ => {}
155                 }
156             }
157             TYPE_REDIRECT => {
158                 use RedirectCode::*;
159                 let code = match self.code_u8() {
160                     CODE_REDIRECT_FOR_NETWORK => Some(RedirectForNetwork),
161                     CODE_REDIRECT_FOR_HOST => Some(RedirectForHost),
162                     CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK => {
163                         Some(RedirectForTypeOfServiceAndNetwork)
164                     }
165                     CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST => Some(RedirectForTypeOfServiceAndHost),
166                     _ => None,
167                 };
168                 if let Some(code) = code {
169                     return Redirect(RedirectHeader {
170                         code,
171                         gateway_internet_address: self.bytes5to8(),
172                     });
173                 }
174             }
175             TYPE_ECHO_REQUEST => {
176                 if 0 == self.code_u8() {
177                     return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8()));
178                 }
179             }
180             TYPE_TIME_EXCEEDED => {
181                 use TimeExceededCode::*;
182                 match self.code_u8() {
183                     CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT => {
184                         return TimeExceeded(TtlExceededInTransit);
185                     }
186                     CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED => {
187                         return TimeExceeded(FragmentReassemblyTimeExceeded);
188                     }
189                     _ => {}
190                 }
191             }
192             TYPE_PARAMETER_PROBLEM => {
193                 use ParameterProblemHeader::*;
194                 match self.code_u8() {
195                     CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR => {
196                         return ParameterProblem(PointerIndicatesError(
197                             // SAFETY:
198                             // Safe as the contructor checks that the slice has
199                             // at least the length of Icmpv4Header::MIN_LEN (8).
200                             unsafe { *self.slice.get_unchecked(4) },
201                         ));
202                     }
203                     CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION => {
204                         return ParameterProblem(MissingRequiredOption);
205                     }
206                     CODE_PARAMETER_PROBLEM_BAD_LENGTH => {
207                         return ParameterProblem(BadLength);
208                     }
209                     _ => {}
210                 }
211             }
212             TYPE_TIMESTAMP => {
213                 if 0 == self.code_u8() {
214                     // SAFETY:
215                     // Safe as the contructor checks that the slice has
216                     // the length of TimestampMessage::SERIALIZED_SIZE (20).
217                     unsafe {
218                         return TimestampRequest(timestamp_message(self.slice.as_ptr()));
219                     }
220                 }
221             }
222             TYPE_TIMESTAMP_REPLY => {
223                 if 0 == self.code_u8() {
224                     // SAFETY:
225                     // Safe as the contructor checks that the slice has
226                     // the length of TimestampMessage::SERIALIZED_SIZE (20).
227                     unsafe {
228                         return TimestampReply(timestamp_message(self.slice.as_ptr()));
229                     }
230                 }
231             }
232             _ => {}
233         }
234 
235         Unknown {
236             type_u8: self.type_u8(),
237             code_u8: self.code_u8(),
238             bytes5to8: self.bytes5to8(),
239         }
240     }
241 
242     /// Returns "type" value in the ICMPv4 header.
243     #[inline]
type_u8(&self) -> u8244     pub fn type_u8(&self) -> u8 {
245         // SAFETY:
246         // Safe as the contructor checks that the slice has
247         // at least the length of Icmpv4Header::MIN_LEN (8).
248         unsafe { *self.slice.get_unchecked(0) }
249     }
250 
251     /// Returns "code" value in the ICMPv4 header.
252     #[inline]
code_u8(&self) -> u8253     pub fn code_u8(&self) -> u8 {
254         // SAFETY:
255         // Safe as the contructor checks that the slice has
256         // at least the length of Icmpv4Header::MIN_LEN (8).
257         unsafe { *self.slice.get_unchecked(1) }
258     }
259 
260     /// Returns "checksum" value in the ICMPv4 header.
261     #[inline]
checksum(&self) -> u16262     pub fn checksum(&self) -> u16 {
263         // SAFETY:
264         // Safe as the contructor checks that the slice has
265         // at least the length of Icmpv4Header::MIN_LEN (8).
266         unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
267     }
268 
269     /// Returns the bytes from position 4 till and including the 8th position
270     /// in the ICMPv4 header.
271     ///
272     /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP
273     /// packet can depending on the ICMPv4 type and code contain additional data.
274     #[inline]
bytes5to8(&self) -> [u8; 4]275     pub fn bytes5to8(&self) -> [u8; 4] {
276         // SAFETY:
277         // Safe as the contructor checks that the slice has
278         // at least the length of Icmpv4Header::MIN_LEN (8).
279         unsafe {
280             [
281                 *self.slice.get_unchecked(4),
282                 *self.slice.get_unchecked(5),
283                 *self.slice.get_unchecked(6),
284                 *self.slice.get_unchecked(7),
285             ]
286         }
287     }
288 
289     /// Returns a slice to the bytes not covered by `.header()`.
290     ///
291     /// The contents of the slice returned by `payload()` depends on the type
292     /// and code of the ICMP packet:
293     ///
294     /// | `.header().icmp_type` or `.icmp_type()`                                                                                                    | Payload Content                                                              |
295     /// |--------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
296     /// | [`Icmpv4Type::EchoReply`]<br>[`Icmpv4Type::EchoRequest`]<br>                                                                               | Data part of the echo message                                                |
297     /// | [`Icmpv4Type::DestinationUnreachable`]<br>[`Icmpv4Type::Redirect`]<br>[`Icmpv4Type::TimeExceeded`]<br>[`Icmpv4Type::ParameterProblem`]<br> | Internet Header + 64 bits of Original Data Datagram causing the ICMP message |
298     /// | [`Icmpv4Type::TimestampRequest`]<br>[`Icmpv4Type::TimestampReply`]<br>                                                                     | Nothing                                                                      |
299     /// | [`Icmpv4Type::Unknown`]                                                                                                                    | Everything after the 8th byte/octet of the ICMP packet.                      |
300     #[inline]
payload(&self) -> &'a [u8]301     pub fn payload(&self) -> &'a [u8] {
302         // explicitly inlined the code to determine the
303         // length of the payload to make the cecking of the
304         // usafe code easier.
305         let header_len = match self.type_u8() {
306             // SAFETY:
307             // Length safe as the contructor checks that the slice has
308             // the length of TimestampMessage::SERIALIZED_SIZE (20)
309             // for the messages types TYPE_TIMESTAMP and TYPE_TIMESTAMP_REPLY.
310             TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {
311                 if 0 == self.code_u8() {
312                     TimestampMessage::LEN
313                 } else {
314                     8
315                 }
316             }
317             // SAFETY:
318             // Length safe as the contructor checks that the slice has
319             // at least the length of Icmpv4Header::MIN_LEN(8) for
320             // all message types.
321             _ => 8,
322         };
323         // SAFETY:
324         // Lengths have been depending on type in the constructor of the
325         // ICMPv4Slice.
326         unsafe {
327             core::slice::from_raw_parts(
328                 self.slice.as_ptr().add(header_len),
329                 self.slice.len() - header_len,
330             )
331         }
332     }
333 
334     /// Returns the slice containing the ICMPv4 packet.
335     #[inline]
slice(&self) -> &'a [u8]336     pub fn slice(&self) -> &'a [u8] {
337         self.slice
338     }
339 }
340 
341 #[cfg(test)]
342 mod test {
343     use super::*;
344     use alloc::{format, vec::Vec};
345     use proptest::prelude::*;
346 
347     #[test]
from_slice()348     fn from_slice() {
349         // normal case
350         {
351             let bytes = [0u8; 8];
352             let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
353             assert_eq!(slice.slice(), &bytes);
354         }
355 
356         // smaller then min size error
357         for bad_len in 0..8 {
358             let bytes = [0u8; 8];
359             assert_eq!(
360                 Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(),
361                 err::LenError {
362                     required_len: Icmpv4Header::MIN_LEN,
363                     len: bad_len,
364                     len_source: LenSource::Slice,
365                     layer: err::Layer::Icmpv4,
366                     layer_start_offset: 0,
367                 }
368             );
369         }
370 
371         // timestamp tests
372         for ts_type_u8 in [TYPE_TIMESTAMP, TYPE_TIMESTAMP_REPLY] {
373             let bytes = {
374                 let mut bytes = [0u8; 26];
375                 bytes[0] = ts_type_u8;
376                 bytes
377             };
378 
379             // valid timestamps
380             {
381                 let slice = Icmpv4Slice::from_slice(&bytes[..20]).unwrap();
382                 assert_eq!(slice.slice(), &bytes[..20]);
383             }
384 
385             // too short timestamps
386             for bad_len in 8..20 {
387                 assert_eq!(
388                     Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(),
389                     err::LenError {
390                         required_len: TimestampMessage::LEN,
391                         len: bad_len,
392                         len_source: LenSource::Slice,
393                         layer: if ts_type_u8 == TYPE_TIMESTAMP {
394                             err::Layer::Icmpv4Timestamp
395                         } else {
396                             err::Layer::Icmpv4TimestampReply
397                         },
398                         layer_start_offset: 0,
399                     }
400                 );
401             }
402 
403             // too large timestamps
404             for bad_len in 21..26 {
405                 assert_eq!(
406                     Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(),
407                     err::LenError {
408                         required_len: TimestampMessage::LEN,
409                         len: bad_len,
410                         len_source: LenSource::Slice,
411                         layer: if ts_type_u8 == TYPE_TIMESTAMP {
412                             err::Layer::Icmpv4Timestamp
413                         } else {
414                             err::Layer::Icmpv4TimestampReply
415                         },
416                         layer_start_offset: 0,
417                     }
418                 );
419             }
420 
421             // timestamp with a non zero code
422             for code_u8 in 1..=u8::MAX {
423                 let mut bytes = [0u8; 20];
424                 bytes[0] = ts_type_u8;
425                 bytes[1] = code_u8;
426                 let slice = Icmpv4Slice::from_slice(&bytes[..8]).unwrap();
427                 assert_eq!(slice.slice(), &bytes[..8]);
428             }
429         }
430     }
431 
432     proptest! {
433         #[test]
434         fn header(bytes in any::<[u8;20]>()) {
435             let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
436             assert_eq!(
437                 Icmpv4Header {
438                     icmp_type: slice.icmp_type(),
439                     checksum: slice.checksum(),
440                 },
441                 slice.header()
442             );
443         }
444     }
445 
446     #[test]
header_len()447     fn header_len() {
448         use Icmpv4Type::*;
449         let dummy_ts = TimestampMessage {
450             id: 0,
451             seq: 0,
452             originate_timestamp: 0,
453             receive_timestamp: 0,
454             transmit_timestamp: 0,
455         };
456         let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 };
457         let dummy_redirect = RedirectHeader {
458             code: RedirectCode::RedirectForNetwork,
459             gateway_internet_address: [0; 4],
460         };
461         let tests = [
462             (Unknown {
463                 type_u8: u8::MAX,
464                 code_u8: 0,
465                 bytes5to8: [0; 4],
466             }),
467             (EchoReply(dummy_echo)),
468             (DestinationUnreachable(DestUnreachableHeader::Network)),
469             (Redirect(dummy_redirect)),
470             (EchoRequest(dummy_echo)),
471             (TimeExceeded(TimeExceededCode::TtlExceededInTransit)),
472             (ParameterProblem(ParameterProblemHeader::BadLength)),
473             (TimestampRequest(dummy_ts.clone())),
474             // check that a non zero code value return 8
475             (Unknown {
476                 type_u8: TYPE_TIMESTAMP,
477                 code_u8: 1,
478                 bytes5to8: [0; 4],
479             }),
480             (TimestampReply(dummy_ts)),
481             // check that a non zero code value return 8
482             (Unknown {
483                 type_u8: TYPE_TIMESTAMP_REPLY,
484                 code_u8: 1,
485                 bytes5to8: [0; 4],
486             }),
487         ];
488         for t in tests {
489             assert_eq!(
490                 t.header_len(),
491                 Icmpv4Slice::from_slice(&Icmpv4Header::new(t).to_bytes())
492                     .unwrap()
493                     .header_len()
494             );
495         }
496     }
497 
498     proptest! {
499         #[test]
500         fn icmp_type(base_bytes in any::<[u8;20]>()) {
501 
502             use Icmpv4Type::*;
503 
504             let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;20] {
505                 let mut bytes = base_bytes;
506                 bytes[0] = type_u8;
507                 bytes[1] = code_u8;
508                 bytes
509             };
510 
511             let assert_unknown = |type_u8: u8, code_u8: u8| {
512                 let bytes = gen_bytes(type_u8, code_u8);
513                 let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
514                 assert_eq!(
515                     slice.icmp_type(),
516                     Unknown{
517                         type_u8,
518                         code_u8,
519                         bytes5to8: slice.bytes5to8(),
520                     }
521                 );
522             };
523 
524             // unknown types
525             for type_u8 in 0..=u8::MAX{
526                 match type_u8 {
527                     TYPE_ECHO_REPLY | TYPE_DEST_UNREACH | TYPE_REDIRECT |
528                     TYPE_ECHO_REQUEST | TYPE_TIME_EXCEEDED | TYPE_PARAMETER_PROBLEM |
529                     TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {},
530                     type_u8 => {
531                         assert_unknown(type_u8, base_bytes[1]);
532                     }
533                 }
534             }
535 
536             // echo reply
537             {
538                 // matching code
539                 {
540                     let bytes = gen_bytes(TYPE_ECHO_REPLY, 0);
541                     let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
542                     assert_eq!(
543                         slice.icmp_type(),
544                         EchoReply(IcmpEchoHeader::from_bytes(slice.bytes5to8()))
545                     );
546                 }
547 
548                 // unknown code
549                 for unknow_code in 1..=u8::MAX {
550                     assert_unknown(TYPE_ECHO_REPLY, unknow_code);
551                 }
552             }
553 
554             // destination unreachable
555             {
556                 use DestUnreachableHeader::*;
557                 // trivial code values
558                 {
559                     let trivial_tests = [
560                         (CODE_DST_UNREACH_NET, Network),
561                         (CODE_DST_UNREACH_HOST, Host),
562                         (CODE_DST_UNREACH_PROTOCOL, Protocol),
563                         (CODE_DST_UNREACH_PORT, Port),
564                         // need frag skipped as contains an additional value
565                         (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, SourceRouteFailed),
566                         (CODE_DST_UNREACH_NET_UNKNOWN, NetworkUnknown),
567                         (CODE_DST_UNREACH_HOST_UNKNOWN, HostUnknown),
568                         (CODE_DST_UNREACH_ISOLATED, Isolated),
569                         (CODE_DST_UNREACH_NET_PROHIB, NetworkProhibited),
570                         (CODE_DST_UNREACH_HOST_PROHIB, HostProhibited),
571                         (CODE_DST_UNREACH_TOS_NET, TosNetwork),
572                         (CODE_DST_UNREACH_TOS_HOST, TosHost),
573                         (CODE_DST_UNREACH_FILTER_PROHIB, FilterProhibited),
574                         (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, HostPrecedenceViolation),
575                         (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, PrecedenceCutoff),
576                     ];
577 
578                     for (code_u8, expected) in trivial_tests {
579                         let bytes = gen_bytes(TYPE_DEST_UNREACH, code_u8);
580                         let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
581                         assert_eq!(
582                             slice.icmp_type(),
583                             DestinationUnreachable(expected)
584                         );
585                     }
586                 }
587 
588                 // need frag
589                 {
590                     let bytes = gen_bytes(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG);
591                     let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
592                     assert_eq!(
593                         slice.icmp_type(),
594                         DestinationUnreachable(FragmentationNeeded {
595                             next_hop_mtu: u16::from_be_bytes([bytes[6], bytes[7]])
596                         })
597                     );
598                 }
599 
600                 // unknown codes
601                 for unknow_code in 16..=u8::MAX {
602                     assert_unknown(TYPE_ECHO_REPLY, unknow_code);
603                 }
604             }
605 
606             // redirect
607             {
608                 use RedirectCode::*;
609                 // known codes
610                 {
611                     let trivial_tests = [
612                         (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork),
613                         (CODE_REDIRECT_FOR_HOST, RedirectForHost),
614                         (CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK, RedirectForTypeOfServiceAndNetwork),
615                         (CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST, RedirectForTypeOfServiceAndHost),
616                     ];
617 
618                     for (code_u8, expected) in trivial_tests {
619                         let bytes = gen_bytes(TYPE_REDIRECT, code_u8);
620                         let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
621                         assert_eq!(
622                             slice.icmp_type(),
623                             Redirect(RedirectHeader{
624                                 code: expected,
625                                 gateway_internet_address: slice.bytes5to8(),
626                             })
627                         );
628                     }
629                 }
630 
631                 // unknown codes
632                 for unknow_code in 4..=u8::MAX {
633                     assert_unknown(TYPE_REDIRECT, unknow_code);
634                 }
635             }
636 
637             // echo request
638             {
639                 // matching code
640                 {
641                     let bytes = gen_bytes(TYPE_ECHO_REQUEST, 0);
642                     let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
643                     assert_eq!(
644                         slice.icmp_type(),
645                         EchoRequest(IcmpEchoHeader::from_bytes(slice.bytes5to8()))
646                     );
647                 }
648 
649                 // unknown code
650                 for unknow_code in 1..=u8::MAX {
651                     assert_unknown(TYPE_ECHO_REQUEST, unknow_code);
652                 }
653             }
654 
655             // time exceeded
656             {
657                 use TimeExceededCode::*;
658                 // known codes
659                 {
660                     let trivial_tests = [
661                         (CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT, TtlExceededInTransit),
662                         (CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED, FragmentReassemblyTimeExceeded),
663                     ];
664 
665                     for (code_u8, expected) in trivial_tests {
666                         let bytes = gen_bytes(TYPE_TIME_EXCEEDED, code_u8);
667                         let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
668                         assert_eq!(
669                             slice.icmp_type(),
670                             TimeExceeded(expected)
671                         );
672                     }
673                 }
674 
675                 // unknown code
676                 for unknow_code in 2..=u8::MAX {
677                     assert_unknown(TYPE_TIME_EXCEEDED, unknow_code);
678                 }
679             }
680 
681             // parameter porblem
682             {
683                 use ParameterProblemHeader::*;
684                 // trivial code values
685                 {
686                     let trivial_tests = [
687                         (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, MissingRequiredOption),
688                         (CODE_PARAMETER_PROBLEM_BAD_LENGTH, BadLength),
689                     ];
690 
691                     for (code_u8, expected) in trivial_tests {
692                         let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8);
693                         let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
694                         assert_eq!(
695                             slice.icmp_type(),
696                             ParameterProblem(expected)
697                         );
698                     }
699                 }
700 
701                 // with pointer
702                 {
703                     let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR);
704                     let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
705                     assert_eq!(
706                         slice.icmp_type(),
707                         ParameterProblem(PointerIndicatesError(bytes[4]))
708                     );
709                 }
710 
711                 // unknown codes
712                 for unknow_code in 3..=u8::MAX {
713                     assert_unknown(TYPE_PARAMETER_PROBLEM, unknow_code);
714                 }
715             }
716 
717             // timestamp
718             {
719                 // matching code
720                 {
721                     let bytes = gen_bytes(TYPE_TIMESTAMP, 0);
722                     let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
723                     assert_eq!(
724                         slice.icmp_type(),
725                         TimestampRequest(TimestampMessage::from_bytes([
726                             bytes[4], bytes[5], bytes[6], bytes[7],
727                             bytes[8], bytes[9], bytes[10], bytes[11],
728                             bytes[12], bytes[13], bytes[14], bytes[15],
729                             bytes[16], bytes[17], bytes[18], bytes[19],
730                         ]))
731                     );
732                 }
733 
734                 // unknown code
735                 for unknow_code in 1..=u8::MAX {
736                     assert_unknown(TYPE_TIMESTAMP, unknow_code);
737                 }
738             }
739 
740             // timestamp reply
741             {
742                 // matching code
743                 {
744                     let bytes = gen_bytes(TYPE_TIMESTAMP_REPLY, 0);
745                     let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
746                     assert_eq!(
747                         slice.icmp_type(),
748                         TimestampReply(TimestampMessage::from_bytes([
749                             bytes[4], bytes[5], bytes[6], bytes[7],
750                             bytes[8], bytes[9], bytes[10], bytes[11],
751                             bytes[12], bytes[13], bytes[14], bytes[15],
752                             bytes[16], bytes[17], bytes[18], bytes[19],
753                         ]))
754                     );
755                 }
756 
757                 // unknown code
758                 for unknow_code in 1..=u8::MAX {
759                     assert_unknown(TYPE_TIMESTAMP_REPLY, unknow_code);
760                 }
761             }
762         }
763     }
764 
765     proptest! {
766         #[test]
767         fn type_u8(bytes in any::<[u8;20]>()) {
768             assert_eq!(
769                 bytes[0],
770                 Icmpv4Slice::from_slice(&bytes).unwrap().type_u8(),
771             );
772         }
773     }
774 
775     proptest! {
776         #[test]
777         fn code_u8(bytes in any::<[u8;20]>()) {
778             assert_eq!(
779                 bytes[1],
780                 Icmpv4Slice::from_slice(&bytes).unwrap().code_u8(),
781             );
782         }
783     }
784 
785     proptest! {
786         #[test]
787         fn checksum(bytes in any::<[u8;20]>()) {
788             assert_eq!(
789                 u16::from_be_bytes([bytes[2], bytes[3]]),
790                 Icmpv4Slice::from_slice(&bytes).unwrap().checksum(),
791             );
792         }
793     }
794 
795     proptest! {
796         #[test]
797         fn bytes5to8(bytes in any::<[u8;20]>()) {
798             assert_eq!(
799                 [bytes[4], bytes[5], bytes[6], bytes[7]],
800                 Icmpv4Slice::from_slice(&bytes).unwrap().bytes5to8(),
801             );
802         }
803     }
804 
805     proptest! {
806         #[test]
807         fn payload(
808             payload in proptest::collection::vec(any::<u8>(), 8..26)
809         ) {
810             use Icmpv4Type::*;
811             let dummy_ts = TimestampMessage{
812                 id: 0,
813                 seq: 0,
814                 originate_timestamp: 0,
815                 receive_timestamp: 0,
816                 transmit_timestamp: 0,
817             };
818             let dummy_echo = IcmpEchoHeader{
819                 id: 0,
820                 seq: 0,
821             };
822             let dummy_redirect = RedirectHeader{
823                 code: RedirectCode::RedirectForNetwork,
824                 gateway_internet_address: [0;4],
825             };
826             // tests with variable payloads
827             {
828                 let var_tests = [
829                     Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]},
830                     EchoReply(dummy_echo),
831                     DestinationUnreachable(DestUnreachableHeader::Network),
832                     Redirect(dummy_redirect),
833                     EchoRequest(dummy_echo),
834                     TimeExceeded(TimeExceededCode::TtlExceededInTransit),
835                     ParameterProblem(ParameterProblemHeader::BadLength),
836                     // timestamps with non-zero code values
837                     Unknown{type_u8: TYPE_TIMESTAMP, code_u8: 1, bytes5to8: [0;4]},
838                     Unknown{type_u8: TYPE_TIMESTAMP_REPLY, code_u8: 1, bytes5to8: [0;4]},
839                 ];
840                 for t in var_tests {
841 
842                     let mut bytes = Vec::with_capacity(t.header_len() + payload.len());
843                     Icmpv4Header::new(t.clone()).write(&mut bytes).unwrap();
844                     bytes.extend_from_slice(&payload);
845 
846                     assert_eq!(
847                         &payload[..],
848                         Icmpv4Slice::from_slice(&bytes).unwrap().payload()
849                     );
850                 }
851             }
852             // tests with fixed payload sizes
853             {
854                 let fixed_tests = [
855                     (0, TimestampRequest(dummy_ts.clone())),
856                     (0, TimestampReply(dummy_ts)),
857                 ];
858                 for t in fixed_tests {
859                     let mut bytes = Vec::with_capacity(t.1.header_len() + t.0);
860                     Icmpv4Header::new(t.1.clone()).write(&mut bytes).unwrap();
861                     bytes.extend_from_slice(&payload[..t.0]);
862 
863                     assert_eq!(
864                         &payload[..t.0],
865                         Icmpv4Slice::from_slice(&bytes).unwrap().payload()
866                     );
867                 }
868             }
869         }
870     }
871 
872     proptest! {
873         #[test]
874         fn slice(bytes in proptest::collection::vec(any::<u8>(), 20..1024)) {
875             let slice = if bytes[0] == TYPE_TIMESTAMP || bytes[0] == TYPE_TIMESTAMP_REPLY {
876                 &bytes[..20]
877             } else {
878                 &bytes[..]
879             };
880             assert_eq!(
881                 slice,
882                 Icmpv4Slice::from_slice(slice).unwrap().slice(),
883             );
884         }
885     }
886 
887     proptest! {
888         #[test]
889         fn clone_eq(bytes in any::<[u8;20]>()) {
890             let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
891             assert_eq!(slice, slice.clone());
892         }
893     }
894 
895     proptest! {
896         #[test]
897         fn debug(bytes in any::<[u8;20]>()) {
898             let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
899             assert_eq!(
900                 format!("{:?}", slice),
901                 format!("Icmpv4Slice {{ slice: {:?} }}", &bytes[..])
902             );
903         }
904     }
905 }
906