Thanks to visit codestin.com
Credit goes to docs.rs

etherparse/transport/
icmpv4_header.rs

1use crate::*;
2use 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)]
10pub 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
17impl 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.
40    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.
49    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]
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")))]
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")))]
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]
102    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]
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.
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]
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)]
273mod 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)]
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]
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]
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}