Skip to main content

network_types/
icmp.rs

1use core::mem;
2use core::net;
3
4use num_traits::FromPrimitive as _;
5
6use crate::getter_be;
7use crate::setter_be;
8
9/// An enum representing either an ICMPv4 or ICMPv6 header.
10///
11/// - `V4` contains an IPv4 ICMP header as defined in RFC 792 (see `Icmpv4Hdr`)
12/// - `V6` contains an IPv6 ICMP header as defined in RFC 4443 (see `Icmpv6Hdr`)
13///
14/// This enum allows working with both ICMP protocol versions through a unified interface.
15#[derive(Debug, Copy, Clone)]
16pub enum Icmp {
17    V4(Icmpv4Hdr),
18    V6(Icmpv6Hdr),
19}
20
21/// An enum representing errors that can occur while processing ICMP headers.
22///
23/// # Variants
24/// - `InvalidIcmpType`: Indicates an attempt to access a field with an incompatible ICMP message type.
25///   For example, trying to access echo fields on a redirect message.
26#[derive(Debug)]
27pub enum IcmpError {
28    InvalidIcmpType(u8),
29}
30
31/// Represents an ICMP header as defined in RFC 792.
32/// The header consists of a type and code field identifying the message type,
33/// a checksum for error detection, and a data field whose format depends on the message type.
34///
35/// The `type_` field identifies the general category of message, such as:
36/// - 0: Echo Reply
37/// - 3: Destination Unreachable
38/// - 5: Redirect
39/// - 8: Echo Request
40/// - 30: Traceroute
41/// - 40: PHOTURIS
42///
43/// The `code` field provides additional context for the message type.
44///
45/// The `check` field contains a checksum calculated over the ICMP header and its payload.
46///
47/// The `data` field contains type-specific data such as echo identifiers/sequence numbers,
48/// redirect gateway addresses, or pointers to errors in received packets.
49#[repr(C)]
50#[derive(Debug, Copy, Clone)]
51#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
52#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
53pub struct Icmpv4Hdr {
54    pub type_: u8,
55    pub code: u8,
56    pub check: [u8; 2],
57    pub data: [u8; 4],
58}
59
60#[derive(Clone, Copy, num_derive::FromPrimitive, num_derive::ToPrimitive)]
61#[repr(u8)]
62pub enum Icmpv4Type {
63    EchoReply = 0,
64    DestinationUnreachable = 3,
65    Redirect = 5,
66    Echo = 8,
67    ParameterProblem = 12,
68    Timestamp = 13,
69    TimestampReply = 14,
70    InformationRequest = 15,
71    InformationReply = 16,
72    AddressMaskRequest = 17,
73    AddressMaskReply = 18,
74    Traceroute = 30,
75    DomainNameRequest = 37,
76    DomainNameReply = 38,
77    Photuris = 40,
78}
79
80/// Strongly typed view of [`Icmpv4Hdr::data`].
81#[derive(Debug, Copy, Clone)]
82pub enum Icmpv4HdrData<'a> {
83    EchoReply(&'a IcmpIdSequence),
84    DestinationUnreachable(&'a IcmpDstUnreachable),
85    Redirect(&'a Icmpv4Redirect),
86    Echo(&'a IcmpIdSequence),
87    ParameterProblem(&'a Icmpv4ParamProblem),
88    Timestamp(&'a IcmpIdSequence),
89    TimestampReply(&'a IcmpIdSequence),
90    InformationRequest(&'a IcmpIdSequence),
91    InformationReply(&'a IcmpIdSequence),
92    AddressMaskRequest(&'a IcmpIdSequence),
93    AddressMaskReply(&'a IcmpIdSequence),
94    Traceroute(&'a IcmpTraceroute),
95    DomainNameRequest(&'a IcmpIdSequence),
96    DomainNameReply(&'a IcmpIdSequence),
97    Photuris(&'a IcmpHdrPhoturis),
98}
99
100/// Mutable strongly typed view of [`Icmpv4Hdr::data`].
101#[derive(Debug)]
102pub enum Icmpv4HdrDataMut<'a> {
103    EchoReply(&'a mut IcmpIdSequence),
104    DestinationUnreachable(&'a mut IcmpDstUnreachable),
105    Redirect(&'a mut Icmpv4Redirect),
106    Echo(&'a mut IcmpIdSequence),
107    ParameterProblem(&'a mut Icmpv4ParamProblem),
108    Timestamp(&'a mut IcmpIdSequence),
109    TimestampReply(&'a mut IcmpIdSequence),
110    InformationRequest(&'a mut IcmpIdSequence),
111    InformationReply(&'a mut IcmpIdSequence),
112    AddressMaskRequest(&'a mut IcmpIdSequence),
113    AddressMaskReply(&'a mut IcmpIdSequence),
114    Traceroute(&'a mut IcmpTraceroute),
115    DomainNameRequest(&'a mut IcmpIdSequence),
116    DomainNameReply(&'a mut IcmpIdSequence),
117    Photuris(&'a mut IcmpHdrPhoturis),
118}
119
120impl Icmpv4Hdr {
121    pub const LEN: usize = mem::size_of::<Icmpv4Hdr>();
122
123    #[inline]
124    pub fn icmp_type(&self) -> Result<Icmpv4Type, IcmpError> {
125        Icmpv4Type::from_u8(self.type_).ok_or(IcmpError::InvalidIcmpType(self.type_))
126    }
127
128    /// Returns the ICMP header checksum value in host byte order.
129    /// This field is used to detect data corruption in the ICMP header and payload.
130    pub fn checksum(&self) -> u16 {
131        // SAFETY: Pointer arithmetic in bounds of the struct.
132        unsafe { getter_be!(self, check, u16) }
133    }
134
135    /// Sets the ICMP header checksum field to the given value.
136    /// The checksum value should be calculated over the entire ICMP message (header and payload)
137    /// according to RFC 792. The value will be stored in network byte order.
138    pub fn set_checksum(&mut self, checksum: u16) {
139        // SAFETY: Pointer arithmetic in bounds of the struct.
140        unsafe { setter_be!(self, check, checksum) }
141    }
142
143    /// Returns a type-safe view over the `data` bytes based on the ICMPv4 type.
144    #[inline]
145    pub fn data(&self) -> Result<Icmpv4HdrData<'_>, IcmpError> {
146        match self.icmp_type()? {
147            Icmpv4Type::EchoReply => Ok(Icmpv4HdrData::EchoReply(unsafe {
148                self.id_sequence_unchecked()
149            })),
150            Icmpv4Type::DestinationUnreachable => {
151                Ok(Icmpv4HdrData::DestinationUnreachable(unsafe {
152                    self.destination_unreachable_unchecked()
153                }))
154            }
155            Icmpv4Type::Redirect => Ok(Icmpv4HdrData::Redirect(unsafe {
156                self.redirect_unchecked()
157            })),
158            Icmpv4Type::Echo => Ok(Icmpv4HdrData::Echo(unsafe { self.id_sequence_unchecked() })),
159            Icmpv4Type::ParameterProblem => Ok(Icmpv4HdrData::ParameterProblem(unsafe {
160                self.parameter_problem_unchecked()
161            })),
162            Icmpv4Type::Timestamp => Ok(Icmpv4HdrData::Timestamp(unsafe {
163                self.id_sequence_unchecked()
164            })),
165            Icmpv4Type::TimestampReply => Ok(Icmpv4HdrData::TimestampReply(unsafe {
166                self.id_sequence_unchecked()
167            })),
168            Icmpv4Type::InformationRequest => Ok(Icmpv4HdrData::InformationRequest(unsafe {
169                self.id_sequence_unchecked()
170            })),
171            Icmpv4Type::InformationReply => Ok(Icmpv4HdrData::InformationReply(unsafe {
172                self.id_sequence_unchecked()
173            })),
174            Icmpv4Type::AddressMaskRequest => Ok(Icmpv4HdrData::AddressMaskRequest(unsafe {
175                self.id_sequence_unchecked()
176            })),
177            Icmpv4Type::AddressMaskReply => Ok(Icmpv4HdrData::AddressMaskReply(unsafe {
178                self.id_sequence_unchecked()
179            })),
180            Icmpv4Type::Traceroute => Ok(Icmpv4HdrData::Traceroute(unsafe {
181                self.traceroute_unchecked()
182            })),
183            Icmpv4Type::DomainNameRequest => Ok(Icmpv4HdrData::DomainNameRequest(unsafe {
184                self.id_sequence_unchecked()
185            })),
186            Icmpv4Type::DomainNameReply => Ok(Icmpv4HdrData::DomainNameReply(unsafe {
187                self.id_sequence_unchecked()
188            })),
189            Icmpv4Type::Photuris => Ok(Icmpv4HdrData::Photuris(unsafe {
190                self.photuris_unchecked()
191            })),
192        }
193    }
194
195    /// Returns a mutable type-safe view over the `data` bytes based on the ICMPv4 type.
196    #[inline]
197    pub fn data_mut(&mut self) -> Result<Icmpv4HdrDataMut<'_>, IcmpError> {
198        match self.icmp_type()? {
199            Icmpv4Type::EchoReply => Ok(Icmpv4HdrDataMut::EchoReply(unsafe {
200                self.id_sequence_mut_unchecked()
201            })),
202            Icmpv4Type::DestinationUnreachable => {
203                Ok(Icmpv4HdrDataMut::DestinationUnreachable(unsafe {
204                    self.destination_unreachable_mut_unchecked()
205                }))
206            }
207            Icmpv4Type::Redirect => Ok(Icmpv4HdrDataMut::Redirect(unsafe {
208                self.redirect_mut_unchecked()
209            })),
210            Icmpv4Type::Echo => Ok(Icmpv4HdrDataMut::Echo(unsafe {
211                self.id_sequence_mut_unchecked()
212            })),
213            Icmpv4Type::ParameterProblem => Ok(Icmpv4HdrDataMut::ParameterProblem(unsafe {
214                self.parameter_problem_mut_unchecked()
215            })),
216            Icmpv4Type::Timestamp => Ok(Icmpv4HdrDataMut::Timestamp(unsafe {
217                self.id_sequence_mut_unchecked()
218            })),
219            Icmpv4Type::TimestampReply => Ok(Icmpv4HdrDataMut::TimestampReply(unsafe {
220                self.id_sequence_mut_unchecked()
221            })),
222            Icmpv4Type::InformationRequest => Ok(Icmpv4HdrDataMut::InformationRequest(unsafe {
223                self.id_sequence_mut_unchecked()
224            })),
225            Icmpv4Type::InformationReply => Ok(Icmpv4HdrDataMut::InformationReply(unsafe {
226                self.id_sequence_mut_unchecked()
227            })),
228            Icmpv4Type::AddressMaskRequest => Ok(Icmpv4HdrDataMut::AddressMaskRequest(unsafe {
229                self.id_sequence_mut_unchecked()
230            })),
231            Icmpv4Type::AddressMaskReply => Ok(Icmpv4HdrDataMut::AddressMaskReply(unsafe {
232                self.id_sequence_mut_unchecked()
233            })),
234            Icmpv4Type::Traceroute => Ok(Icmpv4HdrDataMut::Traceroute(unsafe {
235                self.traceroute_mut_unchecked()
236            })),
237            Icmpv4Type::DomainNameRequest => Ok(Icmpv4HdrDataMut::DomainNameRequest(unsafe {
238                self.id_sequence_mut_unchecked()
239            })),
240            Icmpv4Type::DomainNameReply => Ok(Icmpv4HdrDataMut::DomainNameReply(unsafe {
241                self.id_sequence_mut_unchecked()
242            })),
243            Icmpv4Type::Photuris => Ok(Icmpv4HdrDataMut::Photuris(unsafe {
244                self.photuris_mut_unchecked()
245            })),
246        }
247    }
248}
249
250/// These are the unsafe alternatives to the safe functions on `IcmpHdr` that do prevent undefined behavior.
251impl Icmpv4Hdr {
252    /// Returns a reference to the ID and sequence fields.
253    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
254    ///
255    /// # Safety
256    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
257    /// before calling this function. Calling this method with other ICMP types may result
258    /// in undefined behavior.
259    #[inline]
260    pub unsafe fn id_sequence_unchecked(&self) -> &IcmpIdSequence {
261        mem::transmute(&self.data)
262    }
263
264    /// Returns a mutable reference to the ID and sequence fields.
265    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
266    ///
267    /// # Safety
268    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
269    /// before calling this function. Calling this method with other ICMP types may result
270    /// in undefined behavior.
271    #[inline]
272    pub unsafe fn id_sequence_mut_unchecked(&mut self) -> &mut IcmpIdSequence {
273        mem::transmute(&mut self.data)
274    }
275
276    /// Returns a reference to the Redirect message payload (Type 5).
277    ///
278    /// # Safety
279    /// Caller must ensure ICMP type is 5 (Redirect) before calling this function.
280    #[inline]
281    pub unsafe fn redirect_unchecked(&self) -> &Icmpv4Redirect {
282        mem::transmute(&self.data)
283    }
284
285    /// Returns a mutable reference to the Redirect message payload (Type 5).
286    ///
287    /// # Safety
288    /// Caller must ensure ICMP type is 5 (Redirect) before calling this function.
289    #[inline]
290    pub unsafe fn redirect_mut_unchecked(&mut self) -> &mut Icmpv4Redirect {
291        mem::transmute(&mut self.data)
292    }
293
294    /// Returns a reference to the Destination Unreachable message.
295    /// Used for Path MTU Discovery (RFC 1191).
296    ///
297    /// # Safety
298    /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function.
299    /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior.
300    #[inline]
301    pub unsafe fn destination_unreachable_unchecked(&self) -> &IcmpDstUnreachable {
302        mem::transmute(&self.data)
303    }
304
305    /// Returns a mutable reference to the Destination Unreachable message.
306    /// Used for Path MTU Discovery (RFC 1191).
307    ///
308    /// # Safety
309    /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function.
310    /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior.
311    #[inline]
312    pub unsafe fn destination_unreachable_mut_unchecked(&mut self) -> &mut IcmpDstUnreachable {
313        mem::transmute(&mut self.data)
314    }
315
316    /// Returns a reference to the Parameter Problem message (Type 12)
317    ///
318    /// # Safety
319    /// Caller must ensure ICMP type is 12 (Parameter Problem) before calling this function.
320    /// Accessing the param_problem field with other ICMP types may result in undefined behavior.
321    #[inline]
322    pub unsafe fn parameter_problem_unchecked(&self) -> &Icmpv4ParamProblem {
323        mem::transmute(&self.data)
324    }
325
326    /// Returns a reference to the Parameter Problem message (Type 12)
327    ///
328    /// # Safety
329    /// Caller must ensure ICMP type is 12 (Parameter Problem) before calling this function.
330    /// Accessing the param_problem field with other ICMP types may result in undefined behavior.
331    #[inline]
332    pub unsafe fn parameter_problem_mut_unchecked(&mut self) -> &mut Icmpv4ParamProblem {
333        mem::transmute(&mut self.data)
334    }
335
336    /// Returns a reference the Traceroute message (Type 30).
337    /// This is only valid for ICMP Type 30 (Traceroute Request).
338    ///
339    /// # Safety
340    /// Caller must ensure ICMP type is 30 (Traceroute Request) before calling
341    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
342    #[inline]
343    pub unsafe fn traceroute_unchecked(&self) -> &IcmpTraceroute {
344        mem::transmute(&self.data)
345    }
346
347    /// Returns a mutable reference the Traceroute message (Type 30).
348    /// This is only valid for ICMP Type 30 (Traceroute Request).
349    ///
350    /// # Safety
351    /// Caller must ensure ICMP type is 30 (Traceroute Request) before calling
352    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
353    #[inline]
354    pub unsafe fn traceroute_mut_unchecked(&mut self) -> &mut IcmpTraceroute {
355        mem::transmute(&mut self.data)
356    }
357
358    /// Returns a reference to the PHOTURIS message (Type 40).
359    ///
360    /// # Safety
361    /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function.
362    /// Accessing the photuris field with other ICMP types may result in undefined behavior.
363    #[inline]
364    pub unsafe fn photuris_unchecked(&self) -> &IcmpHdrPhoturis {
365        mem::transmute(&self.data)
366    }
367
368    /// Returns a mutable reference to the PHOTURIS message (Type 40).
369    ///
370    /// # Safety
371    /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function.
372    /// Accessing the photuris field with other ICMP types may result in undefined behavior.
373    #[inline]
374    pub unsafe fn photuris_mut_unchecked(&mut self) -> &mut IcmpHdrPhoturis {
375        mem::transmute(&mut self.data)
376    }
377}
378
379/// Represents the ID and sequence fields, used by the following ICMP Types:
380///
381/// - 0: Echo Reply
382/// - 8: Echo Request
383/// - 13: Timestamp Request
384/// - 14: Timestamp Reply
385/// - 15: Information Request (deprecated)
386/// - 16: Information Reply (deprecated)
387/// - 17: Address Mask Request (deprecated)
388/// - 18: Address Mask Reply (deprecated)
389/// - 37: Domain Name Request (deprecated)
390/// - 38: Domain Name Reply (deprecated)
391#[repr(C)]
392#[derive(Debug, Copy, Clone)]
393#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
394#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
395pub struct IcmpIdSequence {
396    pub id: [u8; 2],
397    pub sequence: [u8; 2],
398}
399
400impl IcmpIdSequence {
401    /// Returns the identification field from ICMP Echo/Timestamp/Info/Mask messages.
402    #[inline]
403    pub fn id(&self) -> u16 {
404        unsafe { getter_be!(self, id, u16) }
405    }
406
407    /// Sets the identification field for ICMP Echo/Timestamp/Info/Mask messages.
408    #[inline]
409    pub fn set_id(&mut self, id: u16) {
410        unsafe { setter_be!(self, id, id) }
411    }
412
413    /// Returns the sequence number from ICMP Echo/Timestamp/Info/Mask messages.
414    #[inline]
415    pub fn sequence(&self) -> u16 {
416        unsafe { getter_be!(self, sequence, u16) }
417    }
418
419    /// Sets the sequence number for ICMP Echo/Timestamp/Info/Mask messages.
420    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
421    ///
422    /// # Safety
423    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
424    /// before calling this function. Accessing the echo fields with other ICMP types may result
425    /// in undefined behavior.
426    #[inline]
427    pub fn set_sequence(&mut self, sequence: u16) {
428        unsafe { setter_be!(self, sequence, sequence) }
429    }
430}
431
432/// Represents the payload of an ICMP Redirect message (Type 5).
433/// The four bytes encode the gateway internet address in network byte order.
434#[repr(C)]
435#[derive(Debug, Copy, Clone)]
436#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
437#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
438pub struct Icmpv4Redirect {
439    gateway: [u8; 4],
440}
441
442impl Icmpv4Redirect {
443    #[inline]
444    pub fn gateway_address(&self) -> net::Ipv4Addr {
445        net::Ipv4Addr::from(self.gateway)
446    }
447
448    #[inline]
449    pub fn set_gateway_address(&mut self, addr: net::Ipv4Addr) {
450        self.gateway = addr.octets();
451    }
452}
453
454/// For ICMP Type 3 "Destination Unreachable" Message (RFC 792) with support for PMTUD (RFC 1191)
455/// Contains 2 unused bytes followed by a Next-Hop MTU field indicating the maximum transmission unit
456/// of the next-hop network on which fragmentation is required.
457#[repr(C)]
458#[derive(Debug, Copy, Clone)]
459#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
460#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
461pub struct IcmpDstUnreachable {
462    pub _unused: [u8; 2],
463    pub mtu: [u8; 2],
464}
465
466impl IcmpDstUnreachable {
467    /// Returns the Next-Hop MTU field from a Destination Unreachable message
468    /// in host byte order. Used for Path MTU Discovery (RFC 1191).
469    ///
470    /// # Safety
471    /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function.
472    /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior.
473    #[inline]
474    pub fn mtu(&self) -> u16 {
475        unsafe { getter_be!(self, mtu, u16) }
476    }
477
478    #[inline]
479    pub fn set_mtu(&mut self, mtu: u16) {
480        unsafe { setter_be!(self, mtu, mtu) }
481    }
482}
483
484/// For ICMP Type 12 "Parameter Problem" Message (RFC 792)
485/// Contains a pointer to the byte in the original datagram that caused the error
486/// and 3 bytes of unused padding to make the field a total of 4 bytes.
487#[repr(C)]
488#[derive(Debug, Copy, Clone)]
489#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
490#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
491pub struct Icmpv4ParamProblem {
492    pub pointer: u8,
493    pub _unused: [u8; 3], // To make up 4 bytes
494}
495
496impl Icmpv4ParamProblem {
497    #[inline]
498    pub fn pointer(&self) -> u8 {
499        self.pointer
500    }
501
502    #[inline]
503    pub fn set_pointer(&mut self, pointer: u8) {
504        self.pointer = pointer;
505    }
506}
507
508/// For ICMP Type 40 (PHOTURIS) Message (RFC 2521)
509/// Contains 2 "Reserved" bytes followed by the Security Parameters Index used
510/// for a security association between two peers. Also includes a 2-byte pointer
511/// field indicating where in the message the error was detected.
512#[repr(C)]
513#[derive(Debug, Copy, Clone)]
514#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
515#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
516pub struct IcmpHdrPhoturis {
517    pub reserved_spi: [u8; 2],
518    pub pointer: [u8; 2],
519}
520
521impl IcmpHdrPhoturis {
522    #[inline]
523    pub fn reserved_spi(&self) -> u16 {
524        unsafe { getter_be!(self, reserved_spi, u16) }
525    }
526
527    #[inline]
528    pub fn set_reserved_spi(&mut self, spi: u16) {
529        unsafe { setter_be!(self, reserved_spi, spi) }
530    }
531
532    #[inline]
533    pub fn pointer(&self) -> u16 {
534        unsafe { getter_be!(self, pointer, u16) }
535    }
536
537    #[inline]
538    pub fn set_pointer(&mut self, pointer: u16) {
539        unsafe { setter_be!(self, pointer, pointer) }
540    }
541}
542
543/// For ICMP Type 30 "Traceroute" Message (RFC 1393)
544/// Contains a 16-bit ID Number field used by the source to match responses to outgoing requests
545/// followed by 2 unused bytes to make a total of 4 bytes.
546#[repr(C)]
547#[derive(Debug, Copy, Clone)]
548#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
549#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
550pub struct IcmpTraceroute {
551    pub id: [u8; 2],
552    pub _unused: [u8; 2],
553}
554
555impl IcmpTraceroute {
556    /// Returns the ID Number field from a Traceroute message (Type 30).
557    /// This is only valid for ICMP Type 30 (Traceroute Request).
558    ///
559    /// # Safety
560    /// Caller must ensure ICMP type is 30 (Traceroute Request) before calling
561    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
562    #[inline]
563    pub fn id(&self) -> u16 {
564        unsafe { getter_be!(self, id, u16) }
565    }
566
567    /// Sets the ID Number field for a Traceroute message (Type 30).
568    ///
569    /// # Safety
570    /// Caller must ensure ICMP type is 30 (Traceroute Request) before calling
571    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
572    #[inline]
573    pub fn set_id(&mut self, id: u16) {
574        unsafe { setter_be!(self, id, id) }
575    }
576}
577
578/// Represents the variable length portion of a Timestamp Request/Reply message (RFC 792)
579/// that follows the ICMP header.
580///
581/// Timestamps are milliseconds since midnight UT and are stored in network byte order.
582///
583/// # Example
584/// ```no_run,rust,standalone_crate
585/// use aya_ebpf::programs::TcContext;
586/// use network_types::eth::EthHdr;
587/// use network_types::icmp::{Icmpv4Hdr, IcmpError, Icmpv4HdrData, IcmpTimestampMsgPart};
588/// use network_types::ip::Ipv4Hdr;
589///
590/// fn handle_icmp_timestamp(ctx: &TcContext) -> Result<u32, IcmpError> {
591///     // Parse the ICMP header from start of payload
592///     let icmp_start = ctx.data() + EthHdr::LEN + Ipv4Hdr::LEN;
593///     let icmp_hdr: *mut Icmpv4Hdr = icmp_start as *mut Icmpv4Hdr;
594///
595///     // Timestamp request/reply share the same echo layout; ensure the type matches
596///     match unsafe { (*icmp_hdr).data()? } {
597///         Icmpv4HdrData::Timestamp(id_seq)
598///             | Icmpv4HdrData::TimestampReply(id_seq) => {
599///             let timestamps_ptr_location = icmp_start + Icmpv4Hdr::LEN;
600///             let timestamps: *const IcmpTimestampMsgPart =
601///                 timestamps_ptr_location as *const IcmpTimestampMsgPart;
602///
603///             let _id = id_seq.id();
604///             let _sequence = id_seq.sequence();
605///             unsafe {
606///                 let _orig = (*timestamps).originate_timestamp();
607///                 let _recv = (*timestamps).receive_timestamp();
608///                 let _xmit = (*timestamps).transmit_timestamp();
609///                 // Use the timestamp fields as needed.
610///             }
611///         },
612///         _ => {},
613///     }
614///     Ok(0)
615/// }
616/// ```
617#[repr(C)]
618#[derive(Debug, Copy, Clone)]
619#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
620#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
621pub struct IcmpTimestampMsgPart {
622    pub originate_timestamp: [u8; 4],
623    pub receive_timestamp: [u8; 4],
624    pub transmit_timestamp: [u8; 4],
625}
626
627impl IcmpTimestampMsgPart {
628    pub const LEN: usize = mem::size_of::<IcmpTimestampMsgPart>();
629
630    /// Returns the originate timestamp in host byte order (milliseconds since midnight UT)
631    pub fn originate_timestamp(&self) -> u32 {
632        // SAFETY: Pointer arithmetic in bounds of the struct.
633        unsafe { getter_be!(self, originate_timestamp, u32) }
634    }
635
636    /// Sets the originate timestamp field (milliseconds since midnight UT).
637    /// The value will be stored in network byte order.
638    pub fn set_originate_timestamp(&mut self, timestamp: u32) {
639        // SAFETY: Pointer arithmetic in bounds of the struct.
640        unsafe { setter_be!(self, originate_timestamp, timestamp) }
641    }
642
643    /// Returns the receive timestamp in host byte order (milliseconds since midnight UT)
644    pub fn receive_timestamp(&self) -> u32 {
645        // SAFETY: Pointer arithmetic in bounds of the struct.
646        unsafe { getter_be!(self, receive_timestamp, u32) }
647    }
648
649    /// Sets the receive timestamp field (milliseconds since midnight UT).
650    /// The value will be stored in network byte order.
651    pub fn set_receive_timestamp(&mut self, timestamp: u32) {
652        // SAFETY: Pointer arithmetic in bounds of the struct.
653        unsafe { setter_be!(self, receive_timestamp, timestamp) }
654    }
655
656    /// Returns the transmit timestamp in host byte order (milliseconds since midnight UT)
657    pub fn transmit_timestamp(&self) -> u32 {
658        // SAFETY: Pointer arithmetic in bounds of the struct.
659        unsafe { getter_be!(self, transmit_timestamp, u32) }
660    }
661
662    /// Sets the transmit timestamp field (milliseconds since midnight UT).
663    /// The value will be stored in network byte order.
664    pub fn set_transmit_timestamp(&mut self, timestamp: u32) {
665        // SAFETY: Pointer arithmetic in bounds of the struct.
666        unsafe { setter_be!(self, transmit_timestamp, timestamp) }
667    }
668}
669
670/// Represents the variable length portion of a Traceroute message (RFC 1393)
671/// that follows the ICMP header.
672///
673/// Contains hop counts, bandwidth, and MTU information about the traced route.
674/// All fields are stored in network byte order.
675///
676/// # Example
677/// ```no_run,rust,standalone_crate
678/// use core::mem;
679/// use aya_ebpf::programs::TcContext;
680/// use network_types::eth::EthHdr;
681/// use network_types::icmp::{Icmpv4Hdr, Icmpv4HdrData, IcmpTracerouteMsgPart};
682/// use network_types::ip::Ipv4Hdr;
683///
684/// fn handle_icmp_traceroute(ctx: &TcContext) -> Result<u32, ()> {
685///     // Parse the ICMP header from start of payload
686///     let icmp_start = ctx.data() + EthHdr::LEN + Ipv4Hdr::LEN;
687///     let icmp_hdr: *mut Icmpv4Hdr = icmp_start as *mut Icmpv4Hdr;
688///
689///     // Check if it's a Traceroute Request/Reply message by matching on the safe enum.
690///     if let Ok(Icmpv4HdrData::Traceroute(traceroute_hdr)) = unsafe { (*icmp_hdr).data() } {
691///         // Access the traceroute-specific fields without repeating the type checks.
692///         let traceroute_msg: *const IcmpTracerouteMsgPart = unsafe {
693///             (icmp_start as *const u8)
694///                 .add(Icmpv4Hdr::LEN) as *const IcmpTracerouteMsgPart
695///         };
696///
697///         // Consume the traceroute fields in network byte order
698///         let _id = traceroute_hdr.id();
699///         unsafe {
700///             let _hops_out = (*traceroute_msg).hops_out();
701///             let _bandwidth = (*traceroute_msg).bandwidth_out();
702///             let _mtu = (*traceroute_msg).mtu_out();
703///             // Do something meaningful with these values here.
704///         }
705///     }
706///     Ok(0)
707/// }
708/// ```
709#[repr(C)]
710#[derive(Debug, Copy, Clone)]
711#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
712#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
713pub struct IcmpTracerouteMsgPart {
714    pub hops_out: [u8; 2],
715    pub hops_in: [u8; 2],
716    pub bandwidth_out: [u8; 4],
717    pub mtu_out: [u8; 4],
718}
719
720impl IcmpTracerouteMsgPart {
721    pub const LEN: usize = mem::size_of::<IcmpTracerouteMsgPart>();
722
723    /// Returns the outbound hop count in host byte order.
724    /// This indicates the maximum number of hops that can be traversed to the target.
725    pub fn hops_out(&self) -> u16 {
726        // SAFETY: Pointer arithmetic in bounds of the struct.
727        unsafe { getter_be!(self, hops_out, u16) }
728    }
729
730    /// Sets the outbound hop count field. The value will be stored in network byte order.
731    /// This should be set to the maximum number of hops that can be traversed to the target.
732    pub fn set_hops_out(&mut self, hops: u16) {
733        // SAFETY: Pointer arithmetic in bounds of the struct.
734        unsafe { setter_be!(self, hops_out, hops) }
735    }
736
737    /// Returns the inbound hop count in host byte order.
738    /// This indicates the maximum number of hops that can be traversed in the return path.
739    pub fn hops_in(&self) -> u16 {
740        // SAFETY: Pointer arithmetic in bounds of the struct.
741        unsafe { getter_be!(self, hops_in, u16) }
742    }
743
744    /// Sets the inbound hop count field. The value will be stored in network byte order.
745    /// This should be set to the maximum number of hops that can be traversed in the return path.
746    pub fn set_hops_in(&mut self, hops: u16) {
747        // SAFETY: Pointer arithmetic in bounds of the struct.
748        unsafe { setter_be!(self, hops_in, hops) }
749    }
750
751    /// Returns the outbound bandwidth estimate in host byte order.
752    /// This represents the minimum bandwidth along the forward path in bytes per second.
753    pub fn bandwidth_out(&self) -> u32 {
754        // SAFETY: Pointer arithmetic in bounds of the struct.
755        unsafe { getter_be!(self, bandwidth_out, u32) }
756    }
757
758    /// Sets the outbound bandwidth field. The value will be stored in network byte order.
759    /// This should be set to the minimum bandwidth along the forward path in bytes per second.
760    pub fn set_bandwidth_out(&mut self, bandwidth: u32) {
761        // SAFETY: Pointer arithmetic in bounds of the struct.
762        unsafe { setter_be!(self, bandwidth_out, bandwidth) }
763    }
764
765    /// Returns the outbound MTU in host byte order.
766    /// This represents the minimum MTU along the forward path in bytes.
767    pub fn mtu_out(&self) -> u32 {
768        // SAFETY: Pointer arithmetic in bounds of the struct.
769        unsafe { getter_be!(self, mtu_out, u32) }
770    }
771
772    /// Sets the outbound MTU field. The value will be stored in network byte order.
773    /// This should be set to the minimum MTU along the forward path in bytes.
774    pub fn set_mtu_out(&mut self, mtu: u32) {
775        // SAFETY: Pointer arithmetic in bounds of the struct.
776        unsafe { setter_be!(self, mtu_out, mtu) }
777    }
778}
779
780/// Represents an ICMPv6 header as defined in RFC 4443.
781/// The header consists of a type and code field identifying the message type,
782/// a checksum for error detection, and a data field whose format depends on the message type.
783///
784/// The `type_` field identifies the general category of message, such as:
785/// - 1: Destination Unreachable
786/// - 2: Packet Too Big
787/// - 3: Time Exceeded
788/// - 4: Parameter Problem
789/// - 128: Echo Request
790/// - 129: Echo Reply
791///
792/// The `code` field provides additional context for the message type.
793///
794/// The `check` field contains a checksum calculated over an IPv6 pseudo-header,
795/// the ICMPv6 header, and its payload.
796///
797/// The `data` field contains type-specific data such as echo identifiers/sequence numbers,
798/// MTU values, or pointers to errors in received packets.
799#[repr(C)]
800#[derive(Debug, Copy, Clone)]
801#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
802#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
803pub struct Icmpv6Hdr {
804    pub type_: u8,
805    pub code: u8,
806    pub check: [u8; 2],
807    pub data: [u8; 4],
808}
809
810#[derive(Clone, Copy, num_derive::FromPrimitive, num_derive::ToPrimitive)]
811#[repr(u8)]
812pub enum Icmpv6Type {
813    PacketTooBig = 2,
814    ParameterProblem = 4,
815    EchoRequest = 128,
816    EchoReply = 129,
817    RedirectMessage = 137,
818}
819
820/// Strongly typed view of [`Icmpv6Hdr::data`].
821#[derive(Debug)]
822pub enum Icmpv6HdrData<'a> {
823    PacketTooBig(&'a IcmpPacketTooBig),
824    ParameterProblem(&'a Icmpv6ParamProblem),
825    EchoRequest(&'a IcmpIdSequence),
826    EchoReply(&'a IcmpIdSequence),
827    RedirectMessage(&'a Icmpv6Redirect),
828}
829
830/// Mutable strongly typed view of [`Icmpv6Hdr::data`].
831#[derive(Debug)]
832pub enum Icmpv6HdrDataMut<'a> {
833    PacketTooBig(&'a mut IcmpPacketTooBig),
834    ParameterProblem(&'a mut Icmpv6ParamProblem),
835    EchoRequest(&'a mut IcmpIdSequence),
836    EchoReply(&'a mut IcmpIdSequence),
837    RedirectMessage(&'a mut Icmpv6Redirect),
838}
839
840impl Icmpv6Hdr {
841    pub const LEN: usize = mem::size_of::<Icmpv6Hdr>();
842
843    #[inline]
844    pub fn icmp_type(&self) -> Result<Icmpv6Type, IcmpError> {
845        Icmpv6Type::from_u8(self.type_).ok_or(IcmpError::InvalidIcmpType(self.type_))
846    }
847
848    /// Returns the ICMPv6 header checksum value in host byte order.
849    /// This field is used to detect corruption in the ICMPv6 header and payload.
850    pub fn checksum(&self) -> u16 {
851        // SAFETY: Pointer arithmetic in bounds of the struct.
852        unsafe { getter_be!(self, check, u16) }
853    }
854
855    /// Sets the ICMPv6 header checksum field to the given value.
856    /// The checksum value should be calculated over the pseudo-header, ICMPv6 header, and payload
857    /// according to RFC 4443. The value will be stored in network byte order.
858    pub fn set_checksum(&mut self, checksum: u16) {
859        // SAFETY: Pointer arithmetic in bounds of the struct.
860        unsafe { setter_be!(self, check, checksum) }
861    }
862
863    /// Returns a type-safe view over the `data` bytes based on the ICMPv6 type.
864    #[inline]
865    pub fn data(&self) -> Result<Icmpv6HdrData<'_>, IcmpError> {
866        match self.icmp_type()? {
867            Icmpv6Type::PacketTooBig => Ok(Icmpv6HdrData::PacketTooBig(unsafe {
868                self.packet_too_big_unchecked()
869            })),
870            Icmpv6Type::ParameterProblem => Ok(Icmpv6HdrData::ParameterProblem(unsafe {
871                self.parameter_problem_unchecked()
872            })),
873            Icmpv6Type::EchoRequest => Ok(Icmpv6HdrData::EchoRequest(unsafe {
874                self.id_sequence_unchecked()
875            })),
876            Icmpv6Type::EchoReply => Ok(Icmpv6HdrData::EchoReply(unsafe {
877                self.id_sequence_unchecked()
878            })),
879            Icmpv6Type::RedirectMessage => Ok(Icmpv6HdrData::RedirectMessage(unsafe {
880                self.redirect_unchecked()
881            })),
882        }
883    }
884
885    /// Returns a mutable type-safe view over the `data` bytes based on the ICMPv6 type.
886    #[inline]
887    pub fn data_mut(&mut self) -> Result<Icmpv6HdrDataMut<'_>, IcmpError> {
888        match self.icmp_type()? {
889            Icmpv6Type::PacketTooBig => Ok(Icmpv6HdrDataMut::PacketTooBig(unsafe {
890                self.packet_too_big_mut_unchecked()
891            })),
892            Icmpv6Type::ParameterProblem => Ok(Icmpv6HdrDataMut::ParameterProblem(unsafe {
893                self.parameter_problem_mut_unchecked()
894            })),
895            Icmpv6Type::EchoRequest => Ok(Icmpv6HdrDataMut::EchoRequest(unsafe {
896                self.id_sequence_mut_unchecked()
897            })),
898            Icmpv6Type::EchoReply => Ok(Icmpv6HdrDataMut::EchoReply(unsafe {
899                self.id_sequence_mut_unchecked()
900            })),
901            Icmpv6Type::RedirectMessage => Ok(Icmpv6HdrDataMut::RedirectMessage(unsafe {
902                self.redirect_mut_unchecked()
903            })),
904        }
905    }
906}
907
908/// These are the unsafe alternatives to the safe functions on `Icmpv6Hdr` that do prevent undefined behavior.
909impl Icmpv6Hdr {
910    /// Returns a reference to the ID and sequence fields.
911    ///
912    /// # Safety
913    /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling
914    /// this function. Accessing the fields with other ICMPv6 types may result in undefined
915    /// behavior.
916    #[inline]
917    pub unsafe fn id_sequence_unchecked(&self) -> &IcmpIdSequence {
918        mem::transmute(&self.data)
919    }
920
921    /// Returns a mutable reference to the ID and sequence fields.
922    ///
923    /// # Safety
924    /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling
925    /// this function. Accessing the fields with other ICMPv6 types may result in undefined
926    /// behavior.
927    #[inline]
928    pub unsafe fn id_sequence_mut_unchecked(&mut self) -> &mut IcmpIdSequence {
929        mem::transmute(&mut self.data)
930    }
931
932    /// Returns a reference to the Packet Too Big message (Type 2).
933    ///
934    /// # Safety
935    /// Caller must ensure ICMPv6 type is 2 (Packet Too Big) before calling.
936    /// Accessing the fields with other types may result in undefined behavior.
937    #[inline]
938    pub unsafe fn packet_too_big_unchecked(&self) -> &IcmpPacketTooBig {
939        mem::transmute(&self.data)
940    }
941
942    /// Returns a mutable reference to the Packet Too Big message (Type 2).
943    ///
944    /// # Safety
945    /// Caller must ensure ICMPv6 type is 2 (Packet Too Big) before calling.
946    /// Accessing the fields with other types may result in undefined behavior.
947    #[inline]
948    pub unsafe fn packet_too_big_mut_unchecked(&mut self) -> &mut IcmpPacketTooBig {
949        mem::transmute(&mut self.data)
950    }
951
952    /// Returns a reference to the Parameter Problem message (Type 4).
953    ///
954    /// # Safety
955    /// Caller must ensure ICMPv6 type is 4 (Parameter Problem) before calling.
956    /// Accessing the fields with other types may result in undefined behavior.
957    #[inline]
958    pub unsafe fn parameter_problem_unchecked(&self) -> &Icmpv6ParamProblem {
959        mem::transmute(&self.data)
960    }
961
962    /// Returns a mutable reference to the Parameter Problem message (Type 4).
963    ///
964    /// # Safety
965    /// Caller must ensure ICMPv6 type is 4 (Parameter Problem) before calling.
966    /// Accessing the fields with other types may result in undefined behavior.
967    #[inline]
968    pub unsafe fn parameter_problem_mut_unchecked(&mut self) -> &mut Icmpv6ParamProblem {
969        mem::transmute(&mut self.data)
970    }
971
972    /// Returns a reference to the ICMPv6 Redirect message (Type 137).
973    /// This field is currently unused and MUST be initialized to zeros by the sender.
974    ///
975    /// # Safety
976    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
977    /// Accessing redirect fields with other types may result in undefined behavior.
978    #[inline]
979    pub unsafe fn redirect_unchecked(&self) -> &Icmpv6Redirect {
980        mem::transmute(&self.data)
981    }
982
983    /// Returns a mutable reference to the ICMPv6 Redirect message (Type 137).
984    /// This field is currently unused and MUST be initialized to zeros by the sender.
985    ///
986    /// # Safety
987    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
988    /// Accessing redirect fields with other types may result in undefined behavior.
989    #[inline]
990    pub unsafe fn redirect_mut_unchecked(&mut self) -> &mut Icmpv6Redirect {
991        mem::transmute(&mut self.data)
992    }
993}
994
995/// Represents the Packet Too Big message.
996#[repr(C)]
997#[derive(Debug, Copy, Clone)]
998#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
999#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
1000pub struct IcmpPacketTooBig {
1001    mtu: [u8; 4],
1002}
1003
1004impl IcmpPacketTooBig {
1005    #[inline]
1006    pub fn mtu(&self) -> u32 {
1007        // SAFETY: Pointer arithmetic in bounds of the struct.
1008        unsafe { getter_be!(self, mtu, u32) }
1009    }
1010
1011    #[inline]
1012    pub fn set_mtu(&mut self, mtu: u32) {
1013        // SAFETY: Pointer arithmetic in bounds of the struct.
1014        unsafe { setter_be!(self, mtu, mtu) }
1015    }
1016}
1017
1018/// Represents the [Parameter Problem message for ICMPv6][parameter-problem-v6].
1019///
1020/// [parameter-problem-v6]: https://datatracker.ietf.org/doc/html/rfc4443#section-3.4
1021#[repr(C)]
1022#[derive(Debug, Copy, Clone)]
1023#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
1024#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
1025pub struct Icmpv6ParamProblem {
1026    pub pointer: [u8; 4],
1027}
1028
1029impl Icmpv6ParamProblem {
1030    #[inline]
1031    pub fn pointer(&self) -> u32 {
1032        // SAFETY: Pointer arithmetic in bounds of the struct.
1033        unsafe { getter_be!(self, pointer, u32) }
1034    }
1035
1036    #[inline]
1037    pub fn set_pointer(&mut self, pointer: u32) {
1038        // SAFETY: Pointer arithmetic in bounds of the struct.
1039        unsafe { setter_be!(self, pointer, pointer) }
1040    }
1041}
1042
1043/// Represents the [Redirect message][redirect-v6].
1044///
1045/// [redirect-v6]: https://datatracker.ietf.org/doc/html/rfc4861#section-4.5
1046#[repr(C)]
1047#[derive(Debug, Copy, Clone)]
1048#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
1049#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
1050pub struct Icmpv6Redirect {
1051    pub reserved: [u8; 4],
1052}
1053
1054#[cfg(test)]
1055mod tests {
1056    use super::*;
1057    use core::net::Ipv4Addr;
1058
1059    macro_rules! expect_data {
1060        ($hdr:expr, $enum:ident, $variant:ident) => {{
1061            match $hdr.data().expect("invalid ICMP message") {
1062                $enum::$variant(value) => value,
1063                _ => panic!("expected {} message", stringify!($variant)),
1064            }
1065        }};
1066    }
1067
1068    macro_rules! expect_data_mut {
1069        ($hdr:expr, $enum:ident, $variant:ident) => {{
1070            match $hdr.data_mut().expect("invalid ICMP message") {
1071                $enum::$variant(value) => value,
1072                _ => panic!("expected {} message", stringify!($variant)),
1073            }
1074        }};
1075    }
1076
1077    #[test]
1078    fn test_icmp_hdr_size() {
1079        // Icmpv4Hdr should be exactly 8 bytes: type(1) + code(1) + check(2) + data(4)
1080        assert_eq!(Icmpv4Hdr::LEN, 8);
1081        assert_eq!(Icmpv4Hdr::LEN, mem::size_of::<Icmpv4Hdr>());
1082    }
1083
1084    // Helper function to create a default Icmpv4Hdr for testing
1085    fn create_test_icmp_hdr() -> Icmpv4Hdr {
1086        Icmpv4Hdr {
1087            type_: 0,
1088            code: 0,
1089            check: [0, 0],
1090            data: [0, 0, 0, 0],
1091        }
1092    }
1093
1094    #[test]
1095    fn test_checksum() {
1096        let mut hdr = create_test_icmp_hdr();
1097        let test_checksum: u16 = 0x1234;
1098
1099        // Convert test value to network byte order
1100        let bytes = test_checksum.to_be_bytes();
1101        hdr.check = bytes;
1102
1103        // Check that getter properly converts from network to host byte order
1104        assert_eq!(hdr.checksum(), test_checksum);
1105
1106        // Test setter
1107        hdr.set_checksum(0xABCD);
1108        assert_eq!(hdr.check, [0xAB, 0xCD]);
1109        assert_eq!(hdr.checksum(), 0xABCD);
1110    }
1111
1112    #[test]
1113    fn test_echo_fields() {
1114        let mut hdr = create_test_icmp_hdr();
1115        // Set type to Echo Reply (0) which is valid for echo fields
1116        hdr.type_ = 0;
1117
1118        // Test echo ID
1119        let test_id: u16 = 0x4321;
1120        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, EchoReply).set_id(test_id);
1121        assert_eq!(expect_data!(&hdr, Icmpv4HdrData, EchoReply).id(), test_id);
1122
1123        // Verify byte order in raw storage
1124        assert_eq!(hdr.data[..2], test_id.to_be_bytes());
1125
1126        // Test echo sequence
1127        let test_seq: u16 = 0x8765;
1128        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, EchoReply).set_sequence(test_seq);
1129        assert_eq!(
1130            expect_data!(&hdr, Icmpv4HdrData, EchoReply).sequence(),
1131            test_seq
1132        );
1133
1134        // Verify byte order in raw storage
1135        assert_eq!(hdr.data[2..], test_seq.to_be_bytes());
1136    }
1137
1138    #[test]
1139    fn test_gateway_address() {
1140        let mut hdr = create_test_icmp_hdr();
1141        // Set type to Redirect (5) which is valid for gateway address
1142        hdr.type_ = 5;
1143        let test_addr = Ipv4Addr::new(192, 168, 1, 1);
1144
1145        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Redirect).set_gateway_address(test_addr);
1146        assert_eq!(
1147            expect_data!(&hdr, Icmpv4HdrData, Redirect).gateway_address(),
1148            test_addr
1149        );
1150
1151        // Verify the raw bytes
1152        assert_eq!(hdr.data, [192, 168, 1, 1]);
1153    }
1154
1155    #[test]
1156    fn test_message_enum_echo() {
1157        let mut hdr = create_test_icmp_hdr();
1158        hdr.type_ = 8;
1159
1160        match hdr.data_mut().expect("echo view") {
1161            Icmpv4HdrDataMut::Echo(echo) => {
1162                echo.set_id(0xABCD);
1163                echo.set_sequence(0x1234);
1164            }
1165            _ => panic!("unexpected variant"),
1166        }
1167
1168        match hdr.data().expect("echo view") {
1169            Icmpv4HdrData::Echo(echo) => {
1170                assert_eq!(echo.id(), 0xABCD);
1171                assert_eq!(echo.sequence(), 0x1234);
1172            }
1173            _ => panic!("unexpected variant"),
1174        }
1175    }
1176
1177    #[test]
1178    fn test_message_enum_invalid_type() {
1179        let hdr = Icmpv4Hdr {
1180            type_: 9,
1181            code: 0,
1182            check: [0, 0],
1183            data: [0, 0, 0, 0],
1184        };
1185
1186        assert!(hdr.data().is_err());
1187    }
1188
1189    #[test]
1190    fn test_next_hop_mtu() {
1191        let mut hdr = create_test_icmp_hdr();
1192        // Set type to Destination Unreachable (3) which is valid for next_hop_mtu
1193        hdr.type_ = 3;
1194        let test_mtu: u16 = 1500;
1195
1196        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, DestinationUnreachable).set_mtu(test_mtu);
1197        assert_eq!(
1198            expect_data!(&hdr, Icmpv4HdrData, DestinationUnreachable).mtu(),
1199            test_mtu
1200        );
1201
1202        // Verify byte order in raw storage
1203        assert_eq!(hdr.data[2..], test_mtu.to_be_bytes());
1204    }
1205
1206    #[test]
1207    fn test_parameter_pointer() {
1208        let mut hdr = create_test_icmp_hdr();
1209        // Set type to Parameter Problem (12) which is valid for parameter_pointer
1210        hdr.type_ = 12;
1211        let test_pointer: u8 = 42;
1212
1213        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, ParameterProblem).set_pointer(test_pointer);
1214        assert_eq!(
1215            expect_data!(&hdr, Icmpv4HdrData, ParameterProblem).pointer(),
1216            test_pointer
1217        );
1218
1219        // Verify the raw byte
1220        assert_eq!(hdr.data[0], test_pointer);
1221    }
1222
1223    #[test]
1224    fn test_traceroute_id() {
1225        let mut hdr = create_test_icmp_hdr();
1226        // Set type to Traceroute (30) which is valid for traceroute_id
1227        hdr.type_ = 30;
1228        let test_id: u16 = 0x9876;
1229
1230        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Traceroute).set_id(test_id);
1231        assert_eq!(expect_data!(&hdr, Icmpv4HdrData, Traceroute).id(), test_id);
1232
1233        // Verify byte order in raw storage
1234        assert_eq!(hdr.data[..2], test_id.to_be_bytes());
1235    }
1236
1237    #[test]
1238    fn test_photuris_spi() {
1239        let mut hdr = create_test_icmp_hdr();
1240        // Set type to PHOTURIS (40) which is valid for photuris_spi
1241        hdr.type_ = 40;
1242        let test_spi: u16 = 0xFEDC;
1243
1244        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Photuris).set_reserved_spi(test_spi);
1245        assert_eq!(
1246            expect_data!(&hdr, Icmpv4HdrData, Photuris).reserved_spi(),
1247            test_spi
1248        );
1249
1250        // Verify byte order in raw storage
1251        assert_eq!(hdr.data[..2], test_spi.to_be_bytes());
1252    }
1253
1254    #[test]
1255    fn test_photuris_pointer() {
1256        let mut hdr = create_test_icmp_hdr();
1257        // Set type to PHOTURIS (40) which is valid for photuris_pointer
1258        hdr.type_ = 40;
1259        let test_pointer: u16 = 0x1A2B;
1260
1261        expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Photuris).set_pointer(test_pointer);
1262        assert_eq!(
1263            expect_data!(&hdr, Icmpv4HdrData, Photuris).pointer(),
1264            test_pointer
1265        );
1266
1267        // Verify byte order in raw storage
1268        assert_eq!(hdr.data[2..], test_pointer.to_be_bytes());
1269    }
1270
1271    #[test]
1272    fn test_type_and_code_fields() {
1273        let mut hdr = create_test_icmp_hdr();
1274
1275        // Test common ICMP types and codes
1276        // Echo Request
1277        hdr.type_ = 8;
1278        hdr.code = 0;
1279        assert_eq!(hdr.type_, 8);
1280        assert_eq!(hdr.code, 0);
1281
1282        // Destination Unreachable - Host Unreachable
1283        hdr.type_ = 3;
1284        hdr.code = 1;
1285        assert_eq!(hdr.type_, 3);
1286        assert_eq!(hdr.code, 1);
1287    }
1288
1289    #[test]
1290    fn test_icmp_hdr_bitwise_operations() {
1291        // This test covers operations that might be common in packet processing
1292        let mut hdr = create_test_icmp_hdr();
1293
1294        // Set a sample checksum
1295        hdr.set_checksum(0x1234);
1296
1297        // Modify the checksum using bit operations
1298        let modified_checksum = hdr.checksum() ^ 0xFFFF; // Bitwise NOT
1299        hdr.set_checksum(modified_checksum);
1300
1301        assert_eq!(hdr.checksum(), 0xEDCB); // 0x1234 XOR 0xFFFF = 0xEDCB
1302    }
1303
1304    #[test]
1305    fn test_icmp_common_type_constants() {
1306        // This test verifies common ICMP type handling
1307        let mut hdr = create_test_icmp_hdr();
1308
1309        // Echo Request
1310        hdr.type_ = 8;
1311        assert_eq!(hdr.type_, 8);
1312
1313        // Echo Reply
1314        hdr.type_ = 0;
1315        assert_eq!(hdr.type_, 0);
1316
1317        // Destination Unreachable
1318        hdr.type_ = 3;
1319        assert_eq!(hdr.type_, 3);
1320
1321        // Redirect
1322        hdr.type_ = 5;
1323        assert_eq!(hdr.type_, 5);
1324    }
1325
1326    #[test]
1327    fn test_icmp_timestamp_msg_part_size() {
1328        // IcmpTimestampMsgPart should be exactly 12 bytes: 3 timestamps of 4 bytes each
1329        assert_eq!(IcmpTimestampMsgPart::LEN, 12);
1330        assert_eq!(
1331            IcmpTimestampMsgPart::LEN,
1332            mem::size_of::<IcmpTimestampMsgPart>()
1333        );
1334    }
1335
1336    #[test]
1337    fn test_timestamp_originate() {
1338        let mut timestamp_part = IcmpTimestampMsgPart {
1339            originate_timestamp: [0, 0, 0, 0],
1340            receive_timestamp: [0, 0, 0, 0],
1341            transmit_timestamp: [0, 0, 0, 0],
1342        };
1343
1344        // Test with a standard value
1345        let test_timestamp: u32 = 0x12345678;
1346        timestamp_part.set_originate_timestamp(test_timestamp);
1347        assert_eq!(timestamp_part.originate_timestamp(), test_timestamp);
1348
1349        // Verify byte order in raw storage (big-endian/network byte order)
1350        assert_eq!(timestamp_part.originate_timestamp, [0x12, 0x34, 0x56, 0x78]);
1351
1352        // Test with zero
1353        timestamp_part.set_originate_timestamp(0);
1354        assert_eq!(timestamp_part.originate_timestamp(), 0);
1355        assert_eq!(timestamp_part.originate_timestamp, [0, 0, 0, 0]);
1356
1357        // Test with max value
1358        timestamp_part.set_originate_timestamp(u32::MAX);
1359        assert_eq!(timestamp_part.originate_timestamp(), u32::MAX);
1360        assert_eq!(timestamp_part.originate_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
1361    }
1362
1363    #[test]
1364    fn test_timestamp_receive() {
1365        let mut timestamp_part = IcmpTimestampMsgPart {
1366            originate_timestamp: [0, 0, 0, 0],
1367            receive_timestamp: [0, 0, 0, 0],
1368            transmit_timestamp: [0, 0, 0, 0],
1369        };
1370
1371        // Test with a standard value
1372        let test_timestamp: u32 = 0x87654321;
1373        timestamp_part.set_receive_timestamp(test_timestamp);
1374        assert_eq!(timestamp_part.receive_timestamp(), test_timestamp);
1375
1376        // Verify byte order in raw storage (big-endian/network byte order)
1377        assert_eq!(timestamp_part.receive_timestamp, [0x87, 0x65, 0x43, 0x21]);
1378
1379        // Test with zero
1380        timestamp_part.set_receive_timestamp(0);
1381        assert_eq!(timestamp_part.receive_timestamp(), 0);
1382        assert_eq!(timestamp_part.receive_timestamp, [0, 0, 0, 0]);
1383
1384        // Test with max value
1385        timestamp_part.set_receive_timestamp(u32::MAX);
1386        assert_eq!(timestamp_part.receive_timestamp(), u32::MAX);
1387        assert_eq!(timestamp_part.receive_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
1388    }
1389
1390    #[test]
1391    fn test_timestamp_transmit() {
1392        let mut timestamp_part = IcmpTimestampMsgPart {
1393            originate_timestamp: [0, 0, 0, 0],
1394            receive_timestamp: [0, 0, 0, 0],
1395            transmit_timestamp: [0, 0, 0, 0],
1396        };
1397
1398        // Test with a standard value
1399        let test_timestamp: u32 = 0xABCDEF01;
1400        timestamp_part.set_transmit_timestamp(test_timestamp);
1401        assert_eq!(timestamp_part.transmit_timestamp(), test_timestamp);
1402
1403        // Verify byte order in raw storage (big-endian/network byte order)
1404        assert_eq!(timestamp_part.transmit_timestamp, [0xAB, 0xCD, 0xEF, 0x01]);
1405
1406        // Test with zero
1407        timestamp_part.set_transmit_timestamp(0);
1408        assert_eq!(timestamp_part.transmit_timestamp(), 0);
1409        assert_eq!(timestamp_part.transmit_timestamp, [0, 0, 0, 0]);
1410
1411        // Test with max value
1412        timestamp_part.set_transmit_timestamp(u32::MAX);
1413        assert_eq!(timestamp_part.transmit_timestamp(), u32::MAX);
1414        assert_eq!(timestamp_part.transmit_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
1415    }
1416
1417    #[test]
1418    fn test_timestamp_msg_part_construction() {
1419        // Test creating a complete timestamp message part
1420        let mut timestamp_part = IcmpTimestampMsgPart {
1421            originate_timestamp: [0, 0, 0, 0],
1422            receive_timestamp: [0, 0, 0, 0],
1423            transmit_timestamp: [0, 0, 0, 0],
1424        };
1425
1426        // Set all three timestamps
1427        timestamp_part.set_originate_timestamp(0x11223344);
1428        timestamp_part.set_receive_timestamp(0x55667788);
1429        timestamp_part.set_transmit_timestamp(0x99AABBCC);
1430
1431        // Verify all values are correctly set and retrieved
1432        assert_eq!(timestamp_part.originate_timestamp(), 0x11223344);
1433        assert_eq!(timestamp_part.receive_timestamp(), 0x55667788);
1434        assert_eq!(timestamp_part.transmit_timestamp(), 0x99AABBCC);
1435
1436        // Verify raw byte storage
1437        assert_eq!(timestamp_part.originate_timestamp, [0x11, 0x22, 0x33, 0x44]);
1438        assert_eq!(timestamp_part.receive_timestamp, [0x55, 0x66, 0x77, 0x88]);
1439        assert_eq!(timestamp_part.transmit_timestamp, [0x99, 0xAA, 0xBB, 0xCC]);
1440    }
1441
1442    #[test]
1443    fn test_icmp_traceroute_msg_part_size() {
1444        // IcmpTracerouteMsgPart should be exactly 12 bytes: hops_out(2) + hops_in(2) + bandwidth_out(4) + mtu_out(4)
1445        assert_eq!(IcmpTracerouteMsgPart::LEN, 12);
1446        assert_eq!(
1447            IcmpTracerouteMsgPart::LEN,
1448            mem::size_of::<IcmpTracerouteMsgPart>()
1449        );
1450    }
1451
1452    #[test]
1453    fn test_traceroute_hops_out() {
1454        let mut traceroute_part = IcmpTracerouteMsgPart {
1455            hops_out: [0, 0],
1456            hops_in: [0, 0],
1457            bandwidth_out: [0, 0, 0, 0],
1458            mtu_out: [0, 0, 0, 0],
1459        };
1460
1461        // Test with a standard value
1462        let test_hops: u16 = 0x1234;
1463        traceroute_part.set_hops_out(test_hops);
1464        assert_eq!(traceroute_part.hops_out(), test_hops);
1465
1466        // Verify byte order in raw storage (big-endian/network byte order)
1467        assert_eq!(traceroute_part.hops_out, [0x12, 0x34]);
1468
1469        // Test with zero
1470        traceroute_part.set_hops_out(0);
1471        assert_eq!(traceroute_part.hops_out(), 0);
1472        assert_eq!(traceroute_part.hops_out, [0, 0]);
1473
1474        // Test with max value
1475        traceroute_part.set_hops_out(u16::MAX);
1476        assert_eq!(traceroute_part.hops_out(), u16::MAX);
1477        assert_eq!(traceroute_part.hops_out, [0xFF, 0xFF]);
1478    }
1479
1480    #[test]
1481    fn test_traceroute_hops_in() {
1482        let mut traceroute_part = IcmpTracerouteMsgPart {
1483            hops_out: [0, 0],
1484            hops_in: [0, 0],
1485            bandwidth_out: [0, 0, 0, 0],
1486            mtu_out: [0, 0, 0, 0],
1487        };
1488
1489        // Test with a standard value
1490        let test_hops: u16 = 0x5678;
1491        traceroute_part.set_hops_in(test_hops);
1492        assert_eq!(traceroute_part.hops_in(), test_hops);
1493
1494        // Verify byte order in raw storage (big-endian/network byte order)
1495        assert_eq!(traceroute_part.hops_in, [0x56, 0x78]);
1496
1497        // Test with zero
1498        traceroute_part.set_hops_in(0);
1499        assert_eq!(traceroute_part.hops_in(), 0);
1500        assert_eq!(traceroute_part.hops_in, [0, 0]);
1501
1502        // Test with max value
1503        traceroute_part.set_hops_in(u16::MAX);
1504        assert_eq!(traceroute_part.hops_in(), u16::MAX);
1505        assert_eq!(traceroute_part.hops_in, [0xFF, 0xFF]);
1506    }
1507
1508    #[test]
1509    fn test_traceroute_bandwidth_out() {
1510        let mut traceroute_part = IcmpTracerouteMsgPart {
1511            hops_out: [0, 0],
1512            hops_in: [0, 0],
1513            bandwidth_out: [0, 0, 0, 0],
1514            mtu_out: [0, 0, 0, 0],
1515        };
1516
1517        // Test with a standard value
1518        let test_bandwidth: u32 = 0x12345678;
1519        traceroute_part.set_bandwidth_out(test_bandwidth);
1520        assert_eq!(traceroute_part.bandwidth_out(), test_bandwidth);
1521
1522        // Verify byte order in raw storage (big-endian/network byte order)
1523        assert_eq!(traceroute_part.bandwidth_out, [0x12, 0x34, 0x56, 0x78]);
1524
1525        // Test with zero
1526        traceroute_part.set_bandwidth_out(0);
1527        assert_eq!(traceroute_part.bandwidth_out(), 0);
1528        assert_eq!(traceroute_part.bandwidth_out, [0, 0, 0, 0]);
1529
1530        // Test with max value
1531        traceroute_part.set_bandwidth_out(u32::MAX);
1532        assert_eq!(traceroute_part.bandwidth_out(), u32::MAX);
1533        assert_eq!(traceroute_part.bandwidth_out, [0xFF, 0xFF, 0xFF, 0xFF]);
1534    }
1535
1536    #[test]
1537    fn test_traceroute_mtu_out() {
1538        let mut traceroute_part = IcmpTracerouteMsgPart {
1539            hops_out: [0, 0],
1540            hops_in: [0, 0],
1541            bandwidth_out: [0, 0, 0, 0],
1542            mtu_out: [0, 0, 0, 0],
1543        };
1544
1545        // Test with a standard value
1546        let test_mtu: u32 = 0x87654321;
1547        traceroute_part.set_mtu_out(test_mtu);
1548        assert_eq!(traceroute_part.mtu_out(), test_mtu);
1549
1550        // Verify byte order in raw storage (big-endian/network byte order)
1551        assert_eq!(traceroute_part.mtu_out, [0x87, 0x65, 0x43, 0x21]);
1552
1553        // Test with zero
1554        traceroute_part.set_mtu_out(0);
1555        assert_eq!(traceroute_part.mtu_out(), 0);
1556        assert_eq!(traceroute_part.mtu_out, [0, 0, 0, 0]);
1557
1558        // Test with max value
1559        traceroute_part.set_mtu_out(u32::MAX);
1560        assert_eq!(traceroute_part.mtu_out(), u32::MAX);
1561        assert_eq!(traceroute_part.mtu_out, [0xFF, 0xFF, 0xFF, 0xFF]);
1562    }
1563
1564    #[test]
1565    fn test_traceroute_msg_part_construction() {
1566        // Test creating a complete traceroute message part
1567        let mut traceroute_part = IcmpTracerouteMsgPart {
1568            hops_out: [0, 0],
1569            hops_in: [0, 0],
1570            bandwidth_out: [0, 0, 0, 0],
1571            mtu_out: [0, 0, 0, 0],
1572        };
1573
1574        // Set all fields
1575        traceroute_part.set_hops_out(30);
1576        traceroute_part.set_hops_in(25);
1577        traceroute_part.set_bandwidth_out(100000000); // 100 Mbps
1578        traceroute_part.set_mtu_out(1500);
1579
1580        // Verify all values are correctly set and retrieved
1581        assert_eq!(traceroute_part.hops_out(), 30);
1582        assert_eq!(traceroute_part.hops_in(), 25);
1583        assert_eq!(traceroute_part.bandwidth_out(), 100000000);
1584        assert_eq!(traceroute_part.mtu_out(), 1500);
1585
1586        // Verify raw byte storage
1587        assert_eq!(traceroute_part.hops_out, [0, 30]);
1588        assert_eq!(traceroute_part.hops_in, [0, 25]);
1589        assert_eq!(traceroute_part.bandwidth_out, [0x05, 0xF5, 0xE1, 0x00]); // 100000000 in hex
1590        assert_eq!(traceroute_part.mtu_out, [0, 0, 0x05, 0xDC]); // 1500 in hex
1591    }
1592
1593    #[test]
1594    fn test_icmpv6_hdr_size() {
1595        // IcmpV6Hdr is the base header: type(1) + code(1) + check(2) + data(4)
1596        assert_eq!(Icmpv6Hdr::LEN, 8);
1597        assert_eq!(Icmpv6Hdr::LEN, mem::size_of::<Icmpv6Hdr>());
1598    }
1599
1600    // Helper function to create a default IcmpV6Hdr for testing
1601    fn create_test_icmpv6_hdr() -> Icmpv6Hdr {
1602        Icmpv6Hdr {
1603            type_: 0,
1604            code: 0,
1605            check: [0, 0],
1606            data: [0, 0, 0, 0],
1607        }
1608    }
1609
1610    #[test]
1611    fn test_icmpv6_checksum() {
1612        let mut hdr = create_test_icmpv6_hdr();
1613        let test_checksum: u16 = 0x1234;
1614
1615        // Convert test value to network byte order
1616        let bytes = test_checksum.to_be_bytes();
1617        hdr.check = bytes;
1618
1619        // Check that getter properly converts from network to host byte order
1620        assert_eq!(hdr.checksum(), test_checksum);
1621
1622        // Test setter
1623        hdr.set_checksum(0xABCD);
1624        assert_eq!(hdr.check, [0xAB, 0xCD]);
1625        assert_eq!(hdr.checksum(), 0xABCD);
1626    }
1627
1628    #[test]
1629    fn test_icmpv6_echo_fields() {
1630        let mut hdr = create_test_icmpv6_hdr();
1631        // Set type to Echo Request (128) which is valid for echo fields
1632        hdr.type_ = 128;
1633
1634        // Test echo ID
1635        let test_id: u16 = 0x4321;
1636        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, EchoRequest).set_id(test_id);
1637        assert_eq!(expect_data!(&hdr, Icmpv6HdrData, EchoRequest).id(), test_id);
1638
1639        // Verify byte order in raw storage
1640        let test_id_bytes = test_id.to_be_bytes();
1641        assert_eq!(&hdr.data[..2], &test_id_bytes);
1642
1643        // Test echo sequence
1644        let test_seq: u16 = 0x8765;
1645        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, EchoRequest).set_sequence(test_seq);
1646        assert_eq!(
1647            expect_data!(&hdr, Icmpv6HdrData, EchoRequest).sequence(),
1648            test_seq
1649        );
1650
1651        // Verify byte order in raw storage
1652        let test_seq_bytes = test_seq.to_be_bytes();
1653        assert_eq!(&hdr.data[2..], &test_seq_bytes);
1654    }
1655
1656    #[test]
1657    fn test_icmpv6_mtu() {
1658        let mut hdr = create_test_icmpv6_hdr();
1659        // Set type to Packet Too Big (2) which is valid for mtu
1660        hdr.type_ = 2;
1661        let test_mtu: u32 = 0x12345678;
1662
1663        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, PacketTooBig).set_mtu(test_mtu);
1664        assert_eq!(
1665            expect_data!(&hdr, Icmpv6HdrData, PacketTooBig).mtu(),
1666            test_mtu
1667        );
1668
1669        // Verify byte order in raw storage
1670        assert_eq!(hdr.data, test_mtu.to_be_bytes());
1671
1672        // Test with zero
1673        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, PacketTooBig).set_mtu(0);
1674        assert_eq!(expect_data!(&hdr, Icmpv6HdrData, PacketTooBig).mtu(), 0);
1675        assert_eq!(hdr.data, [0, 0, 0, 0]);
1676
1677        // Test with max value
1678        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, PacketTooBig).set_mtu(u32::MAX);
1679        assert_eq!(
1680            expect_data!(&hdr, Icmpv6HdrData, PacketTooBig).mtu(),
1681            u32::MAX
1682        );
1683        assert_eq!(hdr.data, [0xFF, 0xFF, 0xFF, 0xFF]);
1684    }
1685
1686    #[test]
1687    fn test_icmpv6_pointer() {
1688        let mut hdr = create_test_icmpv6_hdr();
1689        // Set type to Parameter Problem (4) which is valid for pointer
1690        hdr.type_ = 4;
1691        let test_pointer: u32 = 0x87654321;
1692
1693        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, ParameterProblem).set_pointer(test_pointer);
1694        assert_eq!(
1695            expect_data!(&hdr, Icmpv6HdrData, ParameterProblem).pointer(),
1696            test_pointer
1697        );
1698
1699        // Verify byte order in raw storage
1700        assert_eq!(hdr.data, test_pointer.to_be_bytes());
1701
1702        // Test with zero
1703        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, ParameterProblem).set_pointer(0);
1704        assert_eq!(
1705            expect_data!(&hdr, Icmpv6HdrData, ParameterProblem).pointer(),
1706            0
1707        );
1708        assert_eq!(hdr.data, [0, 0, 0, 0]);
1709
1710        // Test with max value
1711        expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, ParameterProblem).set_pointer(u32::MAX);
1712        assert_eq!(
1713            expect_data!(&hdr, Icmpv6HdrData, ParameterProblem).pointer(),
1714            u32::MAX
1715        );
1716        assert_eq!(hdr.data, [0xFF, 0xFF, 0xFF, 0xFF]);
1717    }
1718
1719    #[test]
1720    fn test_icmpv6_type_and_code_fields() {
1721        let mut hdr = create_test_icmpv6_hdr();
1722
1723        // Test common ICMPv6 types and codes
1724        // Echo Request
1725        hdr.type_ = 128;
1726        hdr.code = 0;
1727        assert_eq!(hdr.type_, 128);
1728        assert_eq!(hdr.code, 0);
1729
1730        // Echo Reply
1731        hdr.type_ = 129;
1732        hdr.code = 0;
1733        assert_eq!(hdr.type_, 129);
1734        assert_eq!(hdr.code, 0);
1735
1736        // Destination Unreachable - No route to destination
1737        hdr.type_ = 1;
1738        hdr.code = 0;
1739        assert_eq!(hdr.type_, 1);
1740        assert_eq!(hdr.code, 0);
1741
1742        // Packet Too Big
1743        hdr.type_ = 2;
1744        hdr.code = 0;
1745        assert_eq!(hdr.type_, 2);
1746        assert_eq!(hdr.code, 0);
1747
1748        // Time Exceeded - Hop limit exceeded in transit
1749        hdr.type_ = 3;
1750        hdr.code = 0;
1751        assert_eq!(hdr.type_, 3);
1752        assert_eq!(hdr.code, 0);
1753
1754        // Parameter Problem - Erroneous header field encountered
1755        hdr.type_ = 4;
1756        hdr.code = 0;
1757        assert_eq!(hdr.type_, 4);
1758        assert_eq!(hdr.code, 0);
1759    }
1760}
1761
1762#[cfg(all(test, feature = "wincode"))]
1763mod wincode_prop_tests {
1764    use super::*;
1765    use proptest::array::{uniform2, uniform4};
1766    use proptest::prelude::*;
1767    use proptest::test_runner::Config as ProptestConfig;
1768    use wincode::{SchemaRead, SchemaWrite, config::DefaultConfig};
1769
1770    const MAX_PACKET_SIZE: usize = Icmpv6Hdr::LEN;
1771
1772    trait FixedPacket {
1773        const SERIALIZED_LEN: usize;
1774    }
1775
1776    impl FixedPacket for Icmpv4Hdr {
1777        const SERIALIZED_LEN: usize = Icmpv4Hdr::LEN;
1778    }
1779
1780    impl FixedPacket for Icmpv6Hdr {
1781        const SERIALIZED_LEN: usize = Icmpv6Hdr::LEN;
1782    }
1783
1784    fn round_trip<T>(value: &T) -> T
1785    where
1786        T: SchemaWrite<DefaultConfig, Src = T>,
1787        for<'de> T: SchemaRead<'de, DefaultConfig, Dst = T>,
1788        T: FixedPacket,
1789    {
1790        let mut bytes = [0u8; MAX_PACKET_SIZE];
1791        let len = T::SERIALIZED_LEN;
1792        assert!(len <= bytes.len());
1793        wincode::serialize_into(bytes.as_mut_slice(), value).unwrap();
1794        wincode::deserialize(&bytes).unwrap()
1795    }
1796
1797    fn icmp_hdr_strategy() -> impl Strategy<Value = Icmpv4Hdr> {
1798        (
1799            any::<u8>(),
1800            any::<u8>(),
1801            uniform2(any::<u8>()),
1802            uniform4(any::<u8>()),
1803        )
1804            .prop_map(|(type_, code, check, data)| Icmpv4Hdr {
1805                type_,
1806                code,
1807                check,
1808                data,
1809            })
1810    }
1811
1812    fn echo_bytes(id: [u8; 2], seq: [u8; 2]) -> [u8; 4] {
1813        let mut bytes = [0u8; 4];
1814        bytes[..2].copy_from_slice(&id);
1815        bytes[2..].copy_from_slice(&seq);
1816        bytes
1817    }
1818
1819    fn icmpv6_hdr_strategy() -> impl Strategy<Value = Icmpv6Hdr> {
1820        let echo = (
1821            Just(128u8),
1822            any::<u8>(),
1823            uniform2(any::<u8>()),
1824            uniform2(any::<u8>()),
1825            uniform2(any::<u8>()),
1826        )
1827            .prop_map(|(type_, code, check, id, seq)| Icmpv6Hdr {
1828                type_,
1829                code,
1830                check,
1831                data: echo_bytes(id, seq),
1832            });
1833
1834        let echo_reply = (
1835            Just(129u8),
1836            any::<u8>(),
1837            uniform2(any::<u8>()),
1838            uniform2(any::<u8>()),
1839            uniform2(any::<u8>()),
1840        )
1841            .prop_map(|(type_, code, check, id, seq)| Icmpv6Hdr {
1842                type_,
1843                code,
1844                check,
1845                data: echo_bytes(id, seq),
1846            });
1847
1848        let packet_too_big = (
1849            Just(2u8),
1850            any::<u8>(),
1851            uniform2(any::<u8>()),
1852            uniform4(any::<u8>()),
1853        )
1854            .prop_map(|(type_, code, check, bytes)| Icmpv6Hdr {
1855                type_,
1856                code,
1857                check,
1858                data: bytes,
1859            });
1860
1861        let param_problem = (
1862            Just(4u8),
1863            any::<u8>(),
1864            uniform2(any::<u8>()),
1865            uniform4(any::<u8>()),
1866        )
1867            .prop_map(|(type_, code, check, bytes)| Icmpv6Hdr {
1868                type_,
1869                code,
1870                check,
1871                data: bytes,
1872            });
1873
1874        let redirect = (
1875            Just(137u8),
1876            any::<u8>(),
1877            uniform2(any::<u8>()),
1878            uniform4(any::<u8>()),
1879        )
1880            .prop_map(|(type_, code, check, reserved)| Icmpv6Hdr {
1881                type_,
1882                code,
1883                check,
1884                data: reserved,
1885            });
1886
1887        let fallback = (
1888            any::<u8>().prop_filter("use reserved field", |ty| {
1889                !matches!(ty, 128 | 129 | 2 | 4 | 137)
1890            }),
1891            any::<u8>(),
1892            uniform2(any::<u8>()),
1893            uniform4(any::<u8>()),
1894        )
1895            .prop_map(|(type_, code, check, bytes)| Icmpv6Hdr {
1896                type_,
1897                code,
1898                check,
1899                data: bytes,
1900            });
1901
1902        prop_oneof![
1903            echo,
1904            echo_reply,
1905            packet_too_big,
1906            param_problem,
1907            redirect,
1908            fallback
1909        ]
1910    }
1911
1912    proptest! {
1913        #![proptest_config(ProptestConfig {
1914            failure_persistence: None,
1915            ..ProptestConfig::default()
1916        })]
1917
1918        #[test]
1919        fn icmp_hdr_round_trips(hdr in icmp_hdr_strategy()) {
1920            let decoded = round_trip(&hdr);
1921            prop_assert_eq!(decoded.type_, hdr.type_);
1922            prop_assert_eq!(decoded.code, hdr.code);
1923            prop_assert_eq!(decoded.check, hdr.check);
1924            prop_assert_eq!(decoded.data, hdr.data);
1925        }
1926
1927        #[test]
1928        fn icmpv6_hdr_round_trips(hdr in icmpv6_hdr_strategy()) {
1929            let decoded = round_trip(&hdr);
1930            prop_assert_eq!(decoded.type_, hdr.type_);
1931            prop_assert_eq!(decoded.code, hdr.code);
1932            prop_assert_eq!(decoded.check, hdr.check);
1933            prop_assert_eq!(decoded.data, hdr.data);
1934        }
1935    }
1936}