network_types/
icmp.rs

1use core::mem;
2use core::net;
3
4use crate::getter_be;
5use crate::setter_be;
6
7/// An enum representing either an ICMPv4 or ICMPv6 header.
8///
9/// - `V4` contains an IPv4 ICMP header as defined in RFC 792 (see `IcmpHdr`)
10/// - `V6` contains an IPv6 ICMP header as defined in RFC 4443 (see `IcmpV6Hdr`)
11///
12/// This enum allows working with both ICMP protocol versions through a unified interface.
13#[derive(Debug)]
14pub enum Icmp {
15    V4(IcmpHdr),
16    V6(IcmpV6Hdr),
17}
18
19/// An enum representing errors that can occur while processing ICMP headers.
20///
21/// # Variants
22/// - `InvalidIcmpType`: Indicates an attempt to access a field with an incompatible ICMP message type.
23///   For example, trying to access echo fields on a redirect message.
24#[derive(Debug)]
25pub enum IcmpError {
26    InvalidIcmpType,
27}
28
29/// Represents an ICMP header as defined in RFC 792.
30/// The header consists of a type and code field identifying the message type,
31/// a checksum for error detection, and a data field whose format depends on the message type.
32///
33/// The `type_` field identifies the general category of message, such as:
34/// - 0: Echo Reply
35/// - 3: Destination Unreachable
36/// - 5: Redirect
37/// - 8: Echo Request
38/// - 30: Traceroute
39/// - 40: PHOTURIS
40///
41/// The `code` field provides additional context for the message type.
42///
43/// The `check` field contains a checksum calculated over the ICMP header and its payload.
44///
45/// The `data` field contains type-specific data such as echo identifiers/sequence numbers,
46/// redirect gateway addresses, or pointers to errors in received packets.
47#[repr(C, packed)]
48#[derive(Debug, Copy, Clone)]
49#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
50pub struct IcmpHdr {
51    pub type_: u8,
52    pub code: u8,
53    pub check: [u8; 2],
54    pub data: IcmpHdrUn,
55}
56
57impl IcmpHdr {
58    pub const LEN: usize = mem::size_of::<IcmpHdr>();
59
60    /// Returns the ICMP header checksum value in host byte order.
61    /// This field is used to detect data corruption in the ICMP header and payload.
62    pub fn checksum(&self) -> u16 {
63        // SAFETY: Pointer arithmetic in bounds of the struct.
64        unsafe { getter_be!(self, check, u16) }
65    }
66
67    /// Sets the ICMP header checksum field to the given value.
68    /// The checksum value should be calculated over the entire ICMP message (header and payload)
69    /// according to RFC 792. The value will be stored in network byte order.
70    pub fn set_checksum(&mut self, checksum: u16) {
71        // SAFETY: Pointer arithmetic in bounds of the struct.
72        unsafe { setter_be!(self, check, checksum) }
73    }
74
75    /// Returns the identification field from ICMP Echo/Timestamp/Info/Mask messages.
76    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
77    #[inline]
78    pub fn echo_id(&self) -> Result<u16, IcmpError> {
79        if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) {
80            return Err(IcmpError::InvalidIcmpType);
81        }
82        Ok(unsafe { self.echo_id_unchecked() })
83    }
84
85    /// Sets the identification field for ICMP Echo/Timestamp/Info/Mask messages.
86    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
87    #[inline]
88    pub fn set_echo_id(&mut self, id: u16) -> Result<(), IcmpError> {
89        if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) {
90            return Err(IcmpError::InvalidIcmpType);
91        }
92        unsafe {
93            self.set_echo_id_unchecked(id);
94        }
95        Ok(())
96    }
97
98    /// Returns the sequence number from ICMP Echo/Timestamp/Info/Mask messages.
99    #[inline]
100    pub fn echo_sequence(&self) -> Result<u16, IcmpError> {
101        if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) {
102            return Err(IcmpError::InvalidIcmpType);
103        }
104        Ok(unsafe { self.echo_sequence_unchecked() })
105    }
106
107    /// Sets the sequence number for ICMP Echo/Timestamp/Info/Mask messages.
108    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
109    #[inline]
110    pub fn set_echo_sequence(&mut self, sequence: u16) -> Result<(), IcmpError> {
111        if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) {
112            return Err(IcmpError::InvalidIcmpType);
113        }
114        unsafe {
115            self.set_echo_sequence_unchecked(sequence);
116        }
117        Ok(())
118    }
119
120    /// Returns the gateway internet address from an ICMP Redirect message (Type 5)
121    #[inline]
122    pub fn gateway_address(&self) -> Result<net::Ipv4Addr, IcmpError> {
123        if self.type_ != 5 {
124            return Err(IcmpError::InvalidIcmpType);
125        }
126        Ok(unsafe { self.gateway_address_unchecked() })
127    }
128
129    /// Sets the gateway internet address for an ICMP Redirect message (Type 5)
130    #[inline]
131    pub fn set_gateway_address(&mut self, addr: net::Ipv4Addr) -> Result<(), IcmpError> {
132        if self.type_ != 5 {
133            return Err(IcmpError::InvalidIcmpType);
134        }
135        unsafe {
136            self.set_gateway_address_unchecked(addr);
137        }
138        Ok(())
139    }
140
141    /// Returns the Next-Hop MTU field from a Destination Unreachable message
142    /// in host byte order. Used for Path MTU Discovery (RFC 1191).
143    #[inline]
144    pub fn next_hop_mtu(&self) -> Result<u16, IcmpError> {
145        if self.type_ != 3 {
146            return Err(IcmpError::InvalidIcmpType);
147        }
148        Ok(unsafe { self.next_hop_mtu_unchecked() })
149    }
150
151    /// Sets the Next-Hop MTU field for a Destination Unreachable message.
152    /// Used for Path MTU Discovery (RFC 1191).
153    #[inline]
154    pub fn set_next_hop_mtu(&mut self, mtu: u16) -> Result<(), IcmpError> {
155        if self.type_ != 3 {
156            return Err(IcmpError::InvalidIcmpType);
157        }
158        unsafe {
159            self.set_next_hop_mtu_unchecked(mtu);
160        }
161        Ok(())
162    }
163
164    /// Returns the pointer to the errored byte from a Parameter Problem message (Type 12)
165    #[inline]
166    pub fn parameter_pointer(&self) -> Result<u8, IcmpError> {
167        if self.type_ != 12 {
168            return Err(IcmpError::InvalidIcmpType);
169        }
170        Ok(unsafe { self.parameter_pointer_unchecked() })
171    }
172
173    /// Sets the pointer to the errored byte for a Parameter Problem message (Type 12)
174    #[inline]
175    pub fn set_parameter_pointer(&mut self, pointer: u8) -> Result<(), IcmpError> {
176        if self.type_ != 12 {
177            return Err(IcmpError::InvalidIcmpType);
178        }
179        unsafe {
180            self.set_parameter_pointer_unchecked(pointer);
181        }
182        Ok(())
183    }
184
185    /// Returns the ID Number field from a Traceroute message (Type 30).
186    /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages.
187    /// This is only valid for ICMP Type 30 (Traceroute Request) and Type 31 (Traceroute Reply).
188    #[inline]
189    pub fn traceroute_id(&self) -> Result<u16, IcmpError> {
190        if !matches!(self.type_, 30 | 31) {
191            return Err(IcmpError::InvalidIcmpType);
192        }
193        Ok(unsafe { self.traceroute_id_unchecked() })
194    }
195
196    /// Sets the ID Number field for a Traceroute message (Type 30).
197    /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages.
198    #[inline]
199    pub fn set_traceroute_id(&mut self, id: u16) -> Result<(), IcmpError> {
200        if !matches!(self.type_, 30 | 31) {
201            return Err(IcmpError::InvalidIcmpType);
202        }
203        unsafe {
204            self.set_traceroute_id_unchecked(id);
205        }
206        Ok(())
207    }
208
209    /// Returns the Security Parameters Index (SPI) from a PHOTURIS message (Type 40).
210    /// The SPI identifies a security association between two peers.
211    #[inline]
212    pub fn photuris_spi(&self) -> Result<u16, IcmpError> {
213        if self.type_ != 40 {
214            return Err(IcmpError::InvalidIcmpType);
215        }
216        Ok(unsafe { self.photuris_spi_unchecked() })
217    }
218
219    /// Sets the Security Parameters Index (SPI) for a PHOTURIS message (Type 40).
220    /// The SPI identifies a security association between two peers.
221    #[inline]
222    pub fn set_photuris_spi(&mut self, spi: u16) -> Result<(), IcmpError> {
223        if self.type_ != 40 {
224            return Err(IcmpError::InvalidIcmpType);
225        }
226        unsafe {
227            self.set_photuris_spi_unchecked(spi);
228        }
229        Ok(())
230    }
231
232    /// Returns the pointer to the byte where an error was detected in a PHOTURIS message (Type 40).
233    /// Used to identify the location of errors during PHOTURIS protocol processing.
234    #[inline]
235    pub fn photuris_pointer(&self) -> Result<u16, IcmpError> {
236        if self.type_ != 40 {
237            return Err(IcmpError::InvalidIcmpType);
238        }
239        Ok(unsafe { self.photuris_pointer_unchecked() })
240    }
241
242    /// Sets the pointer to the byte where an error was detected in a PHOTURIS message (Type 40).
243    /// Used to identify the location of errors during PHOTURIS protocol processing.
244    #[inline]
245    pub fn set_photuris_pointer(&mut self, pointer: u16) -> Result<(), IcmpError> {
246        if self.type_ != 40 {
247            return Err(IcmpError::InvalidIcmpType);
248        }
249        unsafe {
250            self.set_photuris_pointer_unchecked(pointer);
251        }
252        Ok(())
253    }
254}
255
256/// These are the unsafe alternatives to the safe functions on `IcmpHdr` that do prevent undefined behavior.
257impl IcmpHdr {
258    /// Returns the identification field from ICMP Echo/Timestamp/Info/Mask messages.
259    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
260    ///
261    /// # Safety
262    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
263    /// before calling this function. Accessing the echo fields with other ICMP types may result
264    /// in undefined behavior.
265    #[inline]
266    pub unsafe fn echo_id_unchecked(&self) -> u16 {
267        self.data.echo.id_unchecked()
268    }
269
270    /// Sets the identification field for ICMP Echo/Timestamp/Info/Mask messages.
271    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
272    ///
273    /// # Safety
274    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
275    /// before calling this function. Accessing the echo fields with other ICMP types may result
276    /// in undefined behavior.
277    #[inline]
278    pub unsafe fn set_echo_id_unchecked(&mut self, id: u16) {
279        self.data.echo.set_id_unchecked(id);
280    }
281
282    /// Returns the sequence number from ICMP Echo/Timestamp/Info/Mask messages.
283    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
284    ///
285    /// # Safety
286    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
287    /// before calling this function. Accessing the echo fields with other ICMP types may result
288    /// in undefined behavior.
289    #[inline]
290    pub unsafe fn echo_sequence_unchecked(&self) -> u16 {
291        self.data.echo.sequence_unchecked()
292    }
293
294    /// Sets the sequence number for ICMP Echo/Timestamp/Info/Mask messages.
295    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
296    ///
297    /// # Safety
298    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
299    /// before calling this function. Accessing the echo fields with other ICMP types may result
300    /// in undefined behavior.
301    #[inline]
302    pub unsafe fn set_echo_sequence_unchecked(&mut self, sequence: u16) {
303        self.data.echo.set_sequence_unchecked(sequence);
304    }
305
306    /// Returns the gateway internet address from an ICMP Redirect message (Type 5)
307    ///
308    /// # Safety
309    /// Caller must ensure ICMP type is 5 (Redirect) before calling this function.
310    /// Accessing the redirect field with other ICMP types may result in undefined behavior.
311    #[inline]
312    pub unsafe fn gateway_address_unchecked(&self) -> net::Ipv4Addr {
313        net::Ipv4Addr::from(self.data.redirect)
314    }
315
316    /// Sets the gateway internet address for an ICMP Redirect message (Type 5)
317    ///
318    /// # Safety
319    /// Caller must ensure ICMP type is 5 (Redirect) before calling this function.
320    /// Accessing the redirect field with other ICMP types may result in undefined behavior.
321    #[inline]
322    pub unsafe fn set_gateway_address_unchecked(&mut self, addr: net::Ipv4Addr) {
323        self.data.redirect = addr.octets();
324    }
325
326    /// Returns the Next-Hop MTU field from a Destination Unreachable message
327    /// in host byte order. Used for Path MTU Discovery (RFC 1191).
328    ///
329    /// # Safety
330    /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function.
331    /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior.
332    #[inline]
333    pub unsafe fn next_hop_mtu_unchecked(&self) -> u16 {
334        self.data.dst_unreachable.mtu_unchecked()
335    }
336
337    /// Sets the Next-Hop MTU field for a Destination Unreachable message.
338    /// Used for Path MTU Discovery (RFC 1191).
339    ///
340    /// # Safety
341    /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function.
342    /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior.
343    #[inline]
344    pub unsafe fn set_next_hop_mtu_unchecked(&mut self, mtu: u16) {
345        self.data.dst_unreachable.set_mtu_unchecked(mtu)
346    }
347
348    /// Returns the pointer to the errored byte from a Parameter Problem message (Type 12)
349    ///
350    /// # Safety
351    /// Caller must ensure ICMP type is 12 (Parameter Problem) before calling this function.
352    /// Accessing the param_problem field with other ICMP types may result in undefined behavior.
353    #[inline]
354    pub unsafe fn parameter_pointer_unchecked(&self) -> u8 {
355        self.data.param_problem.pointer
356    }
357
358    /// Sets the pointer to the errored byte for a Parameter Problem message (Type 12)
359    ///
360    /// # Safety
361    /// Caller must ensure ICMP type is 12 (Parameter Problem) before calling this function.
362    /// Accessing the param_problem field with other ICMP types may result in undefined behavior.
363    #[inline]
364    pub unsafe fn set_parameter_pointer_unchecked(&mut self, pointer: u8) {
365        self.data.param_problem.pointer = pointer;
366    }
367
368    /// Returns the ID Number field from a Traceroute message (Type 30).
369    /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages.
370    /// This is only valid for ICMP Type 30 (Traceroute Request) and Type 31 (Traceroute Reply).
371    ///
372    /// # Safety
373    /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling
374    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
375    #[inline]
376    pub unsafe fn traceroute_id_unchecked(&self) -> u16 {
377        self.data.traceroute.id_unchecked()
378    }
379
380    /// Sets the ID Number field for a Traceroute message (Type 30).
381    /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages.
382    ///
383    /// # Safety
384    /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling
385    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
386    #[inline]
387    pub unsafe fn set_traceroute_id_unchecked(&mut self, id: u16) {
388        self.data.traceroute.set_id_unchecked(id);
389    }
390
391    /// Returns the Security Parameters Index (SPI) from a PHOTURIS message (Type 40).
392    /// The SPI identifies a security association between two peers.
393    ///
394    /// # Safety
395    /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function.
396    /// Accessing the photuris field with other ICMP types may result in undefined behavior.
397    #[inline]
398    pub unsafe fn photuris_spi_unchecked(&self) -> u16 {
399        self.data.photuris.reserved_spi_unchecked()
400    }
401
402    /// Sets the Security Parameters Index (SPI) for a PHOTURIS message (Type 40).
403    /// The SPI identifies a security association between two peers.
404    ///
405    /// # Safety
406    /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function.
407    /// Accessing the photuris field with other ICMP types may result in undefined behavior.
408    #[inline]
409    pub unsafe fn set_photuris_spi_unchecked(&mut self, spi: u16) {
410        self.data.photuris.set_reserved_spi_unckecked(spi);
411    }
412
413    /// Returns the pointer to the byte where an error was detected in a PHOTURIS message (Type 40).
414    /// Used to identify the location of errors during PHOTURIS protocol processing.
415    ///
416    /// # Safety
417    /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function.
418    /// Accessing the photuris field with other ICMP types may result in undefined behavior.
419    #[inline]
420    pub unsafe fn photuris_pointer_unchecked(&self) -> u16 {
421        self.data.photuris.pointer_unchecked()
422    }
423
424    /// Sets the pointer to the byte where an error was detected in a PHOTURIS message (Type 40).
425    /// Used to identify the location of errors during PHOTURIS protocol processing.
426    ///
427    /// # Safety
428    /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function.
429    /// Accessing the photuris field with other ICMP types may result in undefined behavior.
430    #[inline]
431    pub unsafe fn set_photuris_pointer_unchecked(&mut self, pointer: u16) {
432        self.data.photuris.set_pointer_unchecked(pointer);
433    }
434}
435
436/// Union holding the variable 4-byte field after the first 4 bytes of an ICMP header.
437/// The meaning of this field depends on the ICMP type:
438/// - `echo`: Used for Echo Request/Reply and other messages with ID/sequence numbers (Types: 0,8,13,14,15,16,17,18,37,38)
439/// - `redirect`: Used for Redirect messages (Type 5) to hold gateway IPv4 address
440/// - `dst_unreachable`: Used for Destination Unreachable messages (Type 3) to hold Next-Hop MTU
441/// - `param_problem`: Used for Parameter Problem messages (Type 12) to point to error location
442/// - `traceroute`: Used for Traceroute messages (Type 30) to hold ID number
443/// - `photuris`: Used for PHOTURIS security messages (Type 40) to hold SPI and error pointer
444/// - `reserved`: Generic 4-byte field for types not covered by other variants
445#[repr(C, packed)]
446#[derive(Copy, Clone)]
447#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
448pub union IcmpHdrUn {
449    pub echo: IcmpEcho,
450    pub redirect: [u8; 4],
451    pub dst_unreachable: IcmpDstUnreachable,
452    pub param_problem: IcmpParamProblem,
453    pub traceroute: IcmpTraceroute,
454    pub photuris: IcmpHdrPhoturis,
455    pub reserved: [u8; 4], // Generic 4-byte data, also for "Unused" fields
456}
457
458impl core::fmt::Debug for IcmpHdrUn {
459    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
460        // Safe approach: just show the raw 4 bytes
461        let bytes = unsafe { self.reserved };
462        write!(
463            f,
464            "IcmpHdrUn([{:#04x}, {:#04x}, {:#04x}, {:#04x}])",
465            bytes[0], bytes[1], bytes[2], bytes[3]
466        )
467    }
468}
469
470/// Represents Echo Request/Reply messages and other message types that share the same header format.
471/// Used for ICMP Types:
472/// - 0: Echo Reply
473/// - 8: Echo Request
474/// - 13: Timestamp Request
475/// - 14: Timestamp Reply
476/// - 15: Information Request (deprecated)
477/// - 16: Information Reply (deprecated)
478/// - 17: Address Mask Request (deprecated)
479/// - 18: Address Mask Reply (deprecated)
480/// - 37: Domain Name Request (deprecated)
481/// - 38: Domain Name Reply (deprecated)
482#[repr(C, packed)]
483#[derive(Debug, Copy, Clone)]
484#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
485pub struct IcmpEcho {
486    pub id: [u8; 2],
487    pub sequence: [u8; 2],
488}
489
490impl IcmpEcho {
491    /// Returns the identification field from ICMP Echo/Timestamp/Info/Mask messages.
492    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
493    ///
494    /// # Safety
495    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
496    /// before calling this function. Accessing the echo fields with other ICMP types may result
497    /// in undefined behavior.
498    #[inline]
499    unsafe fn id_unchecked(&self) -> u16 {
500        getter_be!(self, id, u16)
501    }
502
503    /// Sets the identification field for ICMP Echo/Timestamp/Info/Mask messages.
504    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
505    ///
506    /// # Safety
507    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
508    /// before calling this function. Accessing the echo fields with other ICMP types may result
509    /// in undefined behavior.
510    #[inline]
511    unsafe fn set_id_unchecked(&mut self, id: u16) {
512        setter_be!(self, id, id)
513    }
514
515    /// Returns the sequence number from ICMP Echo/Timestamp/Info/Mask messages.
516    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
517    ///
518    /// # Safety
519    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
520    /// before calling this function. Accessing the echo fields with other ICMP types may result
521    /// in undefined behavior.
522    #[inline]
523    unsafe fn sequence_unchecked(&self) -> u16 {
524        getter_be!(self, sequence, u16)
525    }
526
527    /// Sets the sequence number for ICMP Echo/Timestamp/Info/Mask messages.
528    /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38.
529    ///
530    /// # Safety
531    /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38
532    /// before calling this function. Accessing the echo fields with other ICMP types may result
533    /// in undefined behavior.
534    #[inline]
535    unsafe fn set_sequence_unchecked(&mut self, sequence: u16) {
536        setter_be!(self, sequence, sequence)
537    }
538}
539
540/// For ICMP Type 3 "Destination Unreachable" Message (RFC 792) with support for PMTUD (RFC 1191)
541/// Contains 2 unused bytes followed by a Next-Hop MTU field indicating the maximum transmission unit
542/// of the next-hop network on which fragmentation is required.
543#[repr(C, packed)]
544#[derive(Debug, Copy, Clone)]
545#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
546pub struct IcmpDstUnreachable {
547    pub _unused: [u8; 2],
548    pub mtu: [u8; 2],
549}
550
551impl IcmpDstUnreachable {
552    /// Returns the Next-Hop MTU field from a Destination Unreachable message
553    /// in host byte order. Used for Path MTU Discovery (RFC 1191).
554    ///
555    /// # Safety
556    /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function.
557    /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior.
558    #[inline]
559    unsafe fn mtu_unchecked(&self) -> u16 {
560        getter_be!(self, mtu, u16)
561    }
562
563    #[inline]
564    unsafe fn set_mtu_unchecked(&mut self, mtu: u16) {
565        setter_be!(self, mtu, mtu)
566    }
567}
568
569/// For ICMP Type 12 "Parameter Problem" Message (RFC 792)
570/// Contains a pointer to the byte in the original datagram that caused the error
571/// and 3 bytes of unused padding to make the field a total of 4 bytes.
572#[repr(C, packed)]
573#[derive(Debug, Copy, Clone)]
574#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
575pub struct IcmpParamProblem {
576    pub pointer: u8,
577    pub _unused: [u8; 3], // To make up 4 bytes
578}
579
580/// For ICMP Type 40 (PHOTURIS) Message (RFC 2521)
581/// Contains 2 "Reserved" bytes followed by the Security Parameters Index used
582/// for a security association between two peers. Also includes a 2-byte pointer
583/// field indicating where in the message the error was detected.
584#[repr(C, packed)]
585#[derive(Debug, Copy, Clone)]
586#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
587pub struct IcmpHdrPhoturis {
588    pub reserved_spi: [u8; 2],
589    pub pointer: [u8; 2],
590}
591
592impl IcmpHdrPhoturis {
593    #[inline]
594    unsafe fn reserved_spi_unchecked(&self) -> u16 {
595        getter_be!(self, reserved_spi, u16)
596    }
597
598    #[inline]
599    unsafe fn set_reserved_spi_unckecked(&mut self, spi: u16) {
600        setter_be!(self, reserved_spi, spi)
601    }
602
603    #[inline]
604    unsafe fn pointer_unchecked(&self) -> u16 {
605        getter_be!(self, pointer, u16)
606    }
607
608    #[inline]
609    unsafe fn set_pointer_unchecked(&mut self, pointer: u16) {
610        setter_be!(self, pointer, pointer)
611    }
612}
613
614/// For ICMP Type 30 "Traceroute" Message (RFC 1393)
615/// Contains a 16-bit ID Number field used by the source to match responses to outgoing requests
616/// followed by 2 unused bytes to make a total of 4 bytes. The ID Number helps match Reply messages
617/// (type 31) to their corresponding Requests.
618#[repr(C, packed)]
619#[derive(Debug, Copy, Clone)]
620#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
621pub struct IcmpTraceroute {
622    pub id: [u8; 2],
623    pub _unused: [u8; 2],
624}
625
626impl IcmpTraceroute {
627    /// Returns the ID Number field from a Traceroute message (Type 30).
628    /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages.
629    /// This is only valid for ICMP Type 30 (Traceroute Request) and Type 31 (Traceroute Reply).
630    ///
631    /// # Safety
632    /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling
633    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
634    #[inline]
635    unsafe fn id_unchecked(&self) -> u16 {
636        getter_be!(self, id, u16)
637    }
638
639    /// Sets the ID Number field for a Traceroute message (Type 30).
640    /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages.
641    ///
642    /// # Safety
643    /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling
644    /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior.
645    #[inline]
646    unsafe fn set_id_unchecked(&mut self, id: u16) {
647        setter_be!(self, id, id)
648    }
649}
650
651/// Represents the variable length portion of a Timestamp Request/Reply message (RFC 792)
652/// that follows the ICMP header.
653///
654/// Timestamps are milliseconds since midnight UT and are stored in network byte order.
655///
656/// # Example
657/// ```
658/// use core::mem;
659/// use aya_ebpf::programs::TcContext;
660/// use network_types::eth::EthHdr;
661/// use network_types::icmp::{IcmpHdr, IcmpTimestampMsgPart};
662/// use network_types::ip::Ipv4Hdr;
663/// // Assuming aya_log_ebpf is available for logging, as per project dependencies.
664/// // If not, remove or adapt the log lines.
665/// // use aya_log_ebpf::{info, warn};
666///
667///
668/// // This is an adaptation of the example code provided in the doc comment
669/// // for IcmpTimestampMsgPart, corrected to resolve the E0599 error.
670/// // The actual code at src/icmp.rs:355 likely follows this pattern.
671/// fn handle_icmp_timestamp(ctx: &TcContext) -> Result<u32, ()> {
672///     // Parse the ICMP header from start of payload
673///     let icmp_start = ctx.data() + EthHdr::LEN + Ipv4Hdr::LEN;
674///
675///     // Boundary check: Ensure icmp_start is within packet bounds
676///     // This check is simplified; a real check would involve ctx.data_end().
677///     if icmp_start + IcmpHdr::LEN > ctx.data_end() {
678///         // warn!(ctx, "ICMP header out of bounds");
679///         return Err(());
680///     }
681///     let icmp: *const IcmpHdr = icmp_start as *const IcmpHdr;
682///
683///     // Check if it's a Timestamp message (type 13 or 14)
684///     // Reading from a raw pointer is unsafe.
685///     if unsafe { (*icmp).type_ } == 13 || unsafe { (*icmp).type_ } == 14 {
686///         // Calculate pointer to the timestamp part
687///         let timestamps_ptr_location = icmp_start + IcmpHdr::LEN;
688///
689///         // Boundary check: Ensure IcmpTimestampMsgPart is within packet bounds
690///         if timestamps_ptr_location + IcmpTimestampMsgPart::LEN > ctx.data_end() {
691///             // warn!(ctx, "ICMP timestamp message part out of bounds");
692///             return Err(());
693///         }
694///
695///         let timestamps_ptr: *const IcmpTimestampMsgPart = timestamps_ptr_location as *const IcmpTimestampMsgPart;
696///
697///         // Safely dereference the pointer to get a reference
698///         match unsafe { timestamps_ptr.as_ref() } {
699///             Some(timestamps_ref) => {
700///                 // Now you can read the timestamps in network byte order
701///                 let orig = timestamps_ref.originate_timestamp();
702///                 let recv = timestamps_ref.receive_timestamp();
703///                 let xmit = timestamps_ref.transmit_timestamp();
704///
705///                 // You can now use orig, recv, and xmit. For example, log them:
706///                 // info!(ctx, "ICMP Timestamps: O={}, R={}, T={}", orig, recv, xmit);
707///
708///                 // Placeholder for further processing:
709///                 // For example, return one of the timestamps or 0 for success.
710///             }
711///             None => {
712///                 // This case implies timestamps_ptr was null.
713///                 // While less common if pointer arithmetic is correct and data_end checks pass,
714///                 // it's good practice to handle it.
715///                 // warn!(ctx, "Failed to get reference to ICMP timestamps: pointer was null");
716///                 return Err(());
717///             }
718///         }
719///     }
720///     Ok(0)
721/// }
722/// ```
723#[repr(C, packed)]
724#[derive(Debug, Copy, Clone)]
725#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
726pub struct IcmpTimestampMsgPart {
727    pub originate_timestamp: [u8; 4],
728    pub receive_timestamp: [u8; 4],
729    pub transmit_timestamp: [u8; 4],
730}
731
732impl IcmpTimestampMsgPart {
733    pub const LEN: usize = mem::size_of::<IcmpTimestampMsgPart>();
734
735    /// Returns the originate timestamp in host byte order (milliseconds since midnight UT)
736    pub fn originate_timestamp(&self) -> u32 {
737        // SAFETY: Pointer arithmetic in bounds of the struct.
738        unsafe { getter_be!(self, originate_timestamp, u32) }
739    }
740
741    /// Sets the originate timestamp field (milliseconds since midnight UT).
742    /// The value will be stored in network byte order.
743    pub fn set_originate_timestamp(&mut self, timestamp: u32) {
744        // SAFETY: Pointer arithmetic in bounds of the struct.
745        unsafe { setter_be!(self, originate_timestamp, timestamp) }
746    }
747
748    /// Returns the receive timestamp in host byte order (milliseconds since midnight UT)
749    pub fn receive_timestamp(&self) -> u32 {
750        // SAFETY: Pointer arithmetic in bounds of the struct.
751        unsafe { getter_be!(self, receive_timestamp, u32) }
752    }
753
754    /// Sets the receive timestamp field (milliseconds since midnight UT).
755    /// The value will be stored in network byte order.
756    pub fn set_receive_timestamp(&mut self, timestamp: u32) {
757        // SAFETY: Pointer arithmetic in bounds of the struct.
758        unsafe { setter_be!(self, receive_timestamp, timestamp) }
759    }
760
761    /// Returns the transmit timestamp in host byte order (milliseconds since midnight UT)
762    pub fn transmit_timestamp(&self) -> u32 {
763        // SAFETY: Pointer arithmetic in bounds of the struct.
764        unsafe { getter_be!(self, transmit_timestamp, u32) }
765    }
766
767    /// Sets the transmit timestamp field (milliseconds since midnight UT).
768    /// The value will be stored in network byte order.
769    pub fn set_transmit_timestamp(&mut self, timestamp: u32) {
770        // SAFETY: Pointer arithmetic in bounds of the struct.
771        unsafe { setter_be!(self, transmit_timestamp, timestamp) }
772    }
773}
774
775/// Represents the variable length portion of a Traceroute message (RFC 1393)
776/// that follows the ICMP header.
777///
778/// Contains hop counts, bandwidth, and MTU information about the traced route.
779/// All fields are stored in network byte order.
780///
781/// # Example
782/// ```
783/// use core::mem;
784/// use aya_ebpf::programs::TcContext;
785/// use network_types::eth::EthHdr;
786/// use network_types::icmp::{IcmpHdr, IcmpTracerouteMsgPart};
787/// use network_types::ip::Ipv4Hdr;
788///
789/// fn handle_icmp_traceroute(ctx: &TcContext) -> Result<u32, ()> {
790///     // Parse the ICMP header from start of payload
791///     let icmp_start = ctx.data() + EthHdr::LEN + Ipv4Hdr::LEN;
792///     let icmp: *const IcmpHdr = icmp_start as *const IcmpHdr;
793///     
794///     // Check if it's a Traceroute message (type 30)
795///     // Ensure 'icmp' is within bounds before dereferencing.
796///     // if (icmp as *const u8).add(IcmpHdr::LEN) > ctx.data_end() { return Err(()); }
797///     if unsafe { (*icmp).type_ } == 30 {
798///         // Access the traceroute part that follows the header
799///         let traceroute_ptr: *const IcmpTracerouteMsgPart = unsafe {
800///             (icmp_start as *const u8)
801///                 .add(IcmpHdr::LEN) as *const IcmpTracerouteMsgPart
802///         };
803///
804///         // Before dereferencing traceroute_ptr, ensure it's within packet bounds.
805///         // For example:
806///         // if (traceroute_ptr as *const u8).add(IcmpTracerouteMsgPart::LEN) > ctx.data_end() {
807///         //     aya_log_ebpf::error!(ctx, "Traceroute part out of bounds");
808///         //     return Err(());
809///         // }
810///
811///         // Safely get a reference to IcmpTracerouteMsgPart
812///         if let Some(traceroute_ref) = unsafe { traceroute_ptr.as_ref() } {
813///             // Now you can read the traceroute fields in network byte order
814///             let hops_out = traceroute_ref.hops_out();
815///             let bandwidth = traceroute_ref.bandwidth_out();
816///             let mtu = traceroute_ref.mtu_out();
817///
818///             // You can now use hops_out, bandwidth, mtu
819///             // For example, in a real eBPF program, you might log them or store them in a map.
820///             // aya_log_ebpf::info!(ctx, "Hops: {}, BW: {}, MTU: {}", hops_out, bandwidth, mtu);
821///         } else {
822///             // Handle the case where traceroute_ptr is null or misaligned.
823///             // This indicates an issue, possibly a malformed packet.
824///             // aya_log_ebpf::error!(ctx, "Failed to get reference to IcmpTracerouteMsgPart: pointer invalid");
825///             return Err(()); // Or other appropriate error handling
826///         }
827///     }
828///     Ok(0)
829/// }
830/// ```
831#[repr(C, packed)]
832#[derive(Debug, Copy, Clone)]
833#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
834pub struct IcmpTracerouteMsgPart {
835    pub hops_out: [u8; 2],
836    pub hops_in: [u8; 2],
837    pub bandwidth_out: [u8; 4],
838    pub mtu_out: [u8; 4],
839}
840
841impl IcmpTracerouteMsgPart {
842    pub const LEN: usize = mem::size_of::<IcmpTracerouteMsgPart>();
843
844    /// Returns the outbound hop count in host byte order.
845    /// This indicates the maximum number of hops that can be traversed to the target.
846    pub fn hops_out(&self) -> u16 {
847        // SAFETY: Pointer arithmetic in bounds of the struct.
848        unsafe { getter_be!(self, hops_out, u16) }
849    }
850
851    /// Sets the outbound hop count field. The value will be stored in network byte order.
852    /// This should be set to the maximum number of hops that can be traversed to the target.
853    pub fn set_hops_out(&mut self, hops: u16) {
854        // SAFETY: Pointer arithmetic in bounds of the struct.
855        unsafe { setter_be!(self, hops_out, hops) }
856    }
857
858    /// Returns the inbound hop count in host byte order.
859    /// This indicates the maximum number of hops that can be traversed in the return path.
860    pub fn hops_in(&self) -> u16 {
861        // SAFETY: Pointer arithmetic in bounds of the struct.
862        unsafe { getter_be!(self, hops_in, u16) }
863    }
864
865    /// Sets the inbound hop count field. The value will be stored in network byte order.
866    /// This should be set to the maximum number of hops that can be traversed in the return path.
867    pub fn set_hops_in(&mut self, hops: u16) {
868        // SAFETY: Pointer arithmetic in bounds of the struct.
869        unsafe { setter_be!(self, hops_in, hops) }
870    }
871
872    /// Returns the outbound bandwidth estimate in host byte order.
873    /// This represents the minimum bandwidth along the forward path in bytes per second.
874    pub fn bandwidth_out(&self) -> u32 {
875        // SAFETY: Pointer arithmetic in bounds of the struct.
876        unsafe { getter_be!(self, bandwidth_out, u32) }
877    }
878
879    /// Sets the outbound bandwidth field. The value will be stored in network byte order.
880    /// This should be set to the minimum bandwidth along the forward path in bytes per second.
881    pub fn set_bandwidth_out(&mut self, bandwidth: u32) {
882        // SAFETY: Pointer arithmetic in bounds of the struct.
883        unsafe { setter_be!(self, bandwidth_out, bandwidth) }
884    }
885
886    /// Returns the outbound MTU in host byte order.
887    /// This represents the minimum MTU along the forward path in bytes.
888    pub fn mtu_out(&self) -> u32 {
889        // SAFETY: Pointer arithmetic in bounds of the struct.
890        unsafe { getter_be!(self, mtu_out, u32) }
891    }
892
893    /// Sets the outbound MTU field. The value will be stored in network byte order.
894    /// This should be set to the minimum MTU along the forward path in bytes.
895    pub fn set_mtu_out(&mut self, mtu: u32) {
896        // SAFETY: Pointer arithmetic in bounds of the struct.
897        unsafe { setter_be!(self, mtu_out, mtu) }
898    }
899}
900
901/// Represents an ICMPv6 header as defined in RFC 4443.
902/// The header consists of a type and code field identifying the message type,
903/// a checksum for error detection, and a data field whose format depends on the message type.
904///
905/// The `type_` field identifies the general category of message, such as:
906/// - 1: Destination Unreachable
907/// - 2: Packet Too Big
908/// - 3: Time Exceeded
909/// - 4: Parameter Problem
910/// - 128: Echo Request
911/// - 129: Echo Reply
912///
913/// The `code` field provides additional context for the message type.
914///
915/// The `check` field contains a checksum calculated over an IPv6 pseudo-header,
916/// the ICMPv6 header, and its payload.
917///
918/// The `data` field contains type-specific data such as echo identifiers/sequence numbers,
919/// MTU values, or pointers to errors in received packets.
920#[repr(C, packed)]
921#[derive(Debug, Copy, Clone)]
922#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
923pub struct IcmpV6Hdr {
924    pub type_: u8,
925    pub code: u8,
926    pub check: [u8; 2],
927    pub data: IcmpV6HdrUn,
928}
929
930/// Union holding the variable 4-byte field after the first 4 bytes of an ICMPv6 header.
931/// The meaning of this field depends on the ICMPv6 type:
932/// - `echo`: Used for Echo Request/Reply messages (Types: 128, 129)
933/// - `packet_too_big_mtu`: Used in Packet Too Big messages (Type 2) to indicate next-hop MTU
934/// - `param_problem_pointer`: Used in Parameter Problem messages (Type 4) to point to error location
935/// - `reserved`: Generic 4-byte field for unused/reserved data in other message types
936#[repr(C, packed)]
937#[derive(Copy, Clone)]
938#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
939pub union IcmpV6HdrUn {
940    pub echo: IcmpEcho,
941    pub packet_too_big_mtu: [u8; 4],
942    pub param_problem_pointer: [u8; 4],
943    pub redirect: IcmpV6Redirect,
944    pub reserved: [u8; 4],
945}
946
947impl IcmpV6HdrUn {
948    #[inline]
949    unsafe fn mtu_unchecked(&self) -> u32 {
950        getter_be!(self, packet_too_big_mtu, u32)
951    }
952
953    #[inline]
954    unsafe fn set_mtu_unchecked(&mut self, mtu: u32) {
955        setter_be!(self, packet_too_big_mtu, mtu)
956    }
957
958    #[inline]
959    unsafe fn pointer_unchecked(&self) -> u32 {
960        getter_be!(self, param_problem_pointer, u32)
961    }
962
963    #[inline]
964    unsafe fn set_pointer_unchecked(&mut self, pointer: u32) {
965        setter_be!(self, param_problem_pointer, pointer)
966    }
967}
968
969impl core::fmt::Debug for IcmpV6HdrUn {
970    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
971        // Safe approach: just show the raw 4 bytes
972        let bytes = unsafe { self.reserved };
973        write!(
974            f,
975            "IcmpV6HdrUn([{:#04x}, {:#04x}, {:#04x}, {:#04x}])",
976            bytes[0], bytes[1], bytes[2], bytes[3]
977        )
978    }
979}
980
981#[repr(C, packed)]
982#[derive(Debug, Copy, Clone)]
983#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
984pub struct IcmpV6Redirect {
985    reserved: [u8; 4],
986    target_address: [u8; 16],
987    destination_address: [u8; 16],
988}
989
990impl IcmpV6Hdr {
991    pub const LEN: usize = mem::size_of::<IcmpV6Hdr>();
992
993    /// Returns the ICMPv6 header checksum value in host byte order.
994    /// This field is used to detect corruption in the ICMPv6 header and payload.
995    pub fn checksum(&self) -> u16 {
996        // SAFETY: Pointer arithmetic in bounds of the struct.
997        unsafe { getter_be!(self, check, u16) }
998    }
999
1000    /// Sets the ICMPv6 header checksum field to the given value.
1001    /// The checksum value should be calculated over the pseudo-header, ICMPv6 header, and payload
1002    /// according to RFC 4443. The value will be stored in network byte order.
1003    pub fn set_checksum(&mut self, checksum: u16) {
1004        // SAFETY: Pointer arithmetic in bounds of the struct.
1005        unsafe { setter_be!(self, check, checksum) }
1006    }
1007
1008    /// Returns the identification field from ICMPv6 Echo Request/Reply messages.
1009    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1010    #[inline]
1011    pub fn echo_id(&self) -> Result<u16, IcmpError> {
1012        if !matches!(self.type_, 128 | 129) {
1013            return Err(IcmpError::InvalidIcmpType);
1014        }
1015        Ok(unsafe { self.echo_id_unchecked() })
1016    }
1017
1018    /// Sets the identification field for ICMPv6 Echo Request/Reply messages.
1019    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1020    #[inline]
1021    pub fn set_echo_id(&mut self, id: u16) -> Result<(), IcmpError> {
1022        if !matches!(self.type_, 128 | 129) {
1023            return Err(IcmpError::InvalidIcmpType);
1024        }
1025        unsafe {
1026            self.set_echo_id_unchecked(id);
1027        }
1028        Ok(())
1029    }
1030
1031    /// Returns the sequence number from ICMPv6 Echo Request/Reply messages.
1032    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1033    #[inline]
1034    pub fn echo_sequence(&self) -> Result<u16, IcmpError> {
1035        if !matches!(self.type_, 128 | 129) {
1036            return Err(IcmpError::InvalidIcmpType);
1037        }
1038        Ok(unsafe { self.echo_sequence_unchecked() })
1039    }
1040
1041    /// Sets the sequence number for ICMPv6 Echo Request/Reply messages.
1042    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1043    #[inline]
1044    pub fn set_echo_sequence(&mut self, sequence: u16) -> Result<(), IcmpError> {
1045        if !matches!(self.type_, 128 | 129) {
1046            return Err(IcmpError::InvalidIcmpType);
1047        }
1048        unsafe {
1049            self.set_echo_sequence_unchecked(sequence);
1050        }
1051        Ok(())
1052    }
1053
1054    /// Returns the MTU field from an ICMPv6 Packet Too Big message (Type 2).
1055    /// This value indicates the maximum packet size that can be handled by the next hop.
1056    #[inline]
1057    pub fn mtu(&self) -> Result<u32, IcmpError> {
1058        if self.type_ != 2 {
1059            return Err(IcmpError::InvalidIcmpType);
1060        }
1061        Ok(unsafe { self.mtu_unchecked() })
1062    }
1063
1064    /// Sets the MTU field for an ICMPv6 Packet Too Big message (Type 2).
1065    /// This should be set to the maximum packet size that can be handled by the next hop.
1066    #[inline]
1067    pub fn set_mtu(&mut self, mtu: u32) -> Result<(), IcmpError> {
1068        if self.type_ != 2 {
1069            return Err(IcmpError::InvalidIcmpType);
1070        }
1071        unsafe {
1072            self.set_mtu_unchecked(mtu);
1073        }
1074        Ok(())
1075    }
1076
1077    /// Returns the pointer field from an ICMPv6 Parameter Problem message (Type 4).
1078    /// The pointer indicates the offset within the invoking packet where the error was detected.
1079    #[inline]
1080    pub fn pointer(&self) -> Result<u32, IcmpError> {
1081        if self.type_ != 4 {
1082            return Err(IcmpError::InvalidIcmpType);
1083        }
1084        Ok(unsafe { self.pointer_unchecked() })
1085    }
1086
1087    /// Sets the pointer field for an ICMPv6 Parameter Problem message (Type 4).
1088    /// The pointer should indicate the offset within the invoking packet where the error was detected.
1089    #[inline]
1090    pub fn set_pointer(&mut self, pointer: u32) -> Result<(), IcmpError> {
1091        if self.type_ != 4 {
1092            return Err(IcmpError::InvalidIcmpType);
1093        }
1094        unsafe {
1095            self.set_pointer_unchecked(pointer);
1096        }
1097        Ok(())
1098    }
1099
1100    /// Returns the 4-byte reserved field from an ICMPv6 Redirect message (Type 137).
1101    /// This field is currently unused and MUST be initialized to zeros by the sender.
1102    #[inline]
1103    pub fn redirect_reserved(&self) -> Result<[u8; 4], IcmpError> {
1104        if self.type_ != 137 {
1105            return Err(IcmpError::InvalidIcmpType);
1106        }
1107        Ok(unsafe { self.redirect_reserved_unchecked() })
1108    }
1109
1110    /// Sets the 4-byte reserved field for an ICMPv6 Redirect message (Type 137).
1111    /// This field is currently unused and MUST be set to zeros.
1112    #[inline]
1113    pub fn set_redirect_reserved(&mut self, reserved: [u8; 4]) -> Result<(), IcmpError> {
1114        if self.type_ != 137 {
1115            return Err(IcmpError::InvalidIcmpType);
1116        }
1117        unsafe {
1118            self.set_redirect_reserved_unchecked(reserved);
1119        }
1120        Ok(())
1121    }
1122
1123    /// Returns the Target Address from an ICMPv6 Redirect message (Type 137).
1124    /// This field contains the address that is a better first hop to use for the destination.
1125    #[inline]
1126    pub fn redirect_target_address(&self) -> Result<net::Ipv6Addr, IcmpError> {
1127        if self.type_ != 137 {
1128            return Err(IcmpError::InvalidIcmpType);
1129        }
1130        Ok(unsafe { self.redirect_target_address_unchecked() })
1131    }
1132
1133    /// Sets the Target Address for an ICMPv6 Redirect message (Type 137).
1134    /// This should be set to the address that is a better first hop to use for the destination.
1135    #[inline]
1136    pub fn set_redirect_target_address(&mut self, addr: net::Ipv6Addr) -> Result<(), IcmpError> {
1137        if self.type_ != 137 {
1138            return Err(IcmpError::InvalidIcmpType);
1139        }
1140        unsafe {
1141            self.set_redirect_target_address_unchecked(addr);
1142        }
1143        Ok(())
1144    }
1145
1146    /// Returns the Destination Address from an ICMPv6 Redirect message (Type 137).
1147    /// This field contains the IP address of the destination that is redirected to the target.
1148    #[inline]
1149    pub fn redirect_destination_address(&self) -> Result<net::Ipv6Addr, IcmpError> {
1150        if self.type_ != 137 {
1151            return Err(IcmpError::InvalidIcmpType);
1152        }
1153        Ok(unsafe { self.redirect_destination_address_unchecked() })
1154    }
1155
1156    /// Sets the Destination Address for an ICMPv6 Redirect message (Type 137).
1157    /// This should be set to the IP address of the destination that is redirected to the target.
1158    #[inline]
1159    pub fn set_redirect_destination_address(
1160        &mut self,
1161        addr: net::Ipv6Addr,
1162    ) -> Result<(), IcmpError> {
1163        if self.type_ != 137 {
1164            return Err(IcmpError::InvalidIcmpType);
1165        }
1166        unsafe {
1167            self.set_redirect_destination_address_unchecked(addr);
1168        }
1169        Ok(())
1170    }
1171}
1172
1173impl IcmpV6Hdr {
1174    /// Returns the identification field from ICMPv6 Echo Request/Reply messages.
1175    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1176    ///
1177    /// # Safety
1178    /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling.
1179    /// Accessing echo fields with other types may result in undefined behavior.
1180    #[inline]
1181    pub unsafe fn echo_id_unchecked(&self) -> u16 {
1182        self.data.echo.id_unchecked()
1183    }
1184
1185    /// Sets the identification field for ICMPv6 Echo Request/Reply messages.
1186    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1187    ///
1188    /// # Safety
1189    /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling.
1190    /// Accessing echo fields with other types may result in undefined behavior.
1191    #[inline]
1192    pub unsafe fn set_echo_id_unchecked(&mut self, id: u16) {
1193        self.data.echo.set_id_unchecked(id);
1194    }
1195
1196    /// Returns the sequence number from ICMPv6 Echo Request/Reply messages.
1197    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1198    ///
1199    /// # Safety
1200    /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling.
1201    /// Accessing echo fields with other types may result in undefined behavior.
1202    #[inline]
1203    pub unsafe fn echo_sequence_unchecked(&self) -> u16 {
1204        self.data.echo.sequence_unchecked()
1205    }
1206
1207    /// Sets the sequence number for ICMPv6 Echo Request/Reply messages.
1208    /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply).
1209    ///
1210    /// # Safety
1211    /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling.
1212    /// Accessing echo fields with other types may result in undefined behavior.
1213    #[inline]
1214    pub unsafe fn set_echo_sequence_unchecked(&mut self, sequence: u16) {
1215        self.data.echo.set_sequence_unchecked(sequence);
1216    }
1217
1218    /// Returns the MTU field from an ICMPv6 Packet Too Big message (Type 2).
1219    /// This value indicates the maximum packet size that can be handled by the next hop.
1220    ///
1221    /// # Safety
1222    /// Caller must ensure ICMPv6 type is 2 (Packet Too Big) before calling.
1223    /// Accessing MTU field with other types may result in undefined behavior.
1224    #[inline]
1225    pub unsafe fn mtu_unchecked(&self) -> u32 {
1226        self.data.mtu_unchecked()
1227    }
1228
1229    /// Sets the MTU field for an ICMPv6 Packet Too Big message (Type 2).
1230    /// This should be set to the maximum packet size that can be handled by the next hop.
1231    ///
1232    /// # Safety
1233    /// Caller must ensure ICMPv6 type is 2 (Packet Too Big) before calling.
1234    /// Accessing MTU field with other types may result in undefined behavior.
1235    #[inline]
1236    pub unsafe fn set_mtu_unchecked(&mut self, mtu: u32) {
1237        self.data.set_mtu_unchecked(mtu);
1238    }
1239
1240    /// Returns the pointer field from an ICMPv6 Parameter Problem message (Type 4).
1241    /// The pointer indicates the offset within the invoking packet where the error was detected.
1242    ///
1243    /// # Safety
1244    /// Caller must ensure ICMPv6 type is 4 (Parameter Problem) before calling.
1245    /// Accessing pointer field with other types may result in undefined behavior.
1246    #[inline]
1247    pub unsafe fn pointer_unchecked(&self) -> u32 {
1248        self.data.pointer_unchecked()
1249    }
1250
1251    /// Sets the pointer field for an ICMPv6 Parameter Problem message (Type 4).
1252    /// The pointer should indicate the offset within the invoking packet where the error was detected.
1253    ///
1254    /// # Safety
1255    /// Caller must ensure ICMPv6 type is 4 (Parameter Problem) before calling.
1256    /// Accessing pointer field with other types may result in undefined behavior.
1257    #[inline]
1258    pub unsafe fn set_pointer_unchecked(&mut self, pointer: u32) {
1259        self.data.set_pointer_unchecked(pointer);
1260    }
1261
1262    /// Returns the 4-byte reserved field from an ICMPv6 Redirect message (Type 137).
1263    /// This field is currently unused and MUST be initialized to zeros by the sender.
1264    ///
1265    /// # Safety
1266    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
1267    /// Accessing redirect fields with other types may result in undefined behavior.
1268    #[inline]
1269    pub unsafe fn redirect_reserved_unchecked(&self) -> [u8; 4] {
1270        self.data.redirect.reserved
1271    }
1272
1273    /// Sets the 4-byte reserved field for an ICMPv6 Redirect message (Type 137).
1274    /// This field is currently unused and MUST be set to zeros.
1275    ///
1276    /// # Safety
1277    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
1278    /// Accessing redirect fields with other types may result in undefined behavior.
1279    #[inline]
1280    pub unsafe fn set_redirect_reserved_unchecked(&mut self, reserved: [u8; 4]) {
1281        self.data.redirect.reserved = reserved;
1282    }
1283
1284    /// Returns the Target Address from an ICMPv6 Redirect message (Type 137).
1285    /// This field contains the address that is a better first hop to use for the destination.
1286    ///
1287    /// # Safety
1288    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
1289    /// Accessing redirect fields with other types may result in undefined behavior.
1290    #[inline]
1291    pub unsafe fn redirect_target_address_unchecked(&self) -> net::Ipv6Addr {
1292        net::Ipv6Addr::from(unsafe { self.data.redirect.target_address })
1293    }
1294
1295    /// Sets the Target Address for an ICMPv6 Redirect message (Type 137).
1296    /// This should be set to the address that is a better first hop to use for the destination.
1297    ///
1298    /// # Safety
1299    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
1300    /// Accessing redirect fields with other types may result in undefined behavior.
1301    #[inline]
1302    pub unsafe fn set_redirect_target_address_unchecked(&mut self, addr: net::Ipv6Addr) {
1303        self.data.redirect.target_address = addr.octets();
1304    }
1305
1306    /// Returns the Destination Address from an ICMPv6 Redirect message (Type 137).
1307    /// This field contains the IP address of the destination that is redirected to the target.
1308    ///
1309    /// # Safety
1310    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
1311    /// Accessing redirect fields with other types may result in undefined behavior.
1312    #[inline]
1313    pub unsafe fn redirect_destination_address_unchecked(&self) -> net::Ipv6Addr {
1314        net::Ipv6Addr::from(unsafe { self.data.redirect.destination_address })
1315    }
1316
1317    /// Sets the Destination Address for an ICMPv6 Redirect message (Type 137).
1318    /// This should be set to the IP address of the destination that is redirected to the target.
1319    ///
1320    /// # Safety
1321    /// Caller must ensure ICMPv6 type is 137 (Redirect) before calling.
1322    /// Accessing redirect fields with other types may result in undefined behavior.
1323    #[inline]
1324    pub unsafe fn set_redirect_destination_address_unchecked(&mut self, addr: net::Ipv6Addr) {
1325        self.data.redirect.destination_address = addr.octets();
1326    }
1327}
1328
1329#[cfg(test)]
1330mod tests {
1331    use super::*;
1332    use core::mem;
1333    use core::net::Ipv4Addr;
1334
1335    #[test]
1336    fn test_icmp_hdr_size() {
1337        // IcmpHdr should be exactly 8 bytes: type(1) + code(1) + check(2) + data(4)
1338        assert_eq!(IcmpHdr::LEN, 8);
1339        assert_eq!(IcmpHdr::LEN, mem::size_of::<IcmpHdr>());
1340    }
1341
1342    // Helper function to create a default IcmpHdr for testing
1343    fn create_test_icmp_hdr() -> IcmpHdr {
1344        IcmpHdr {
1345            type_: 0,
1346            code: 0,
1347            check: [0, 0],
1348            data: IcmpHdrUn {
1349                reserved: [0, 0, 0, 0],
1350            },
1351        }
1352    }
1353
1354    #[test]
1355    fn test_checksum() {
1356        let mut hdr = create_test_icmp_hdr();
1357        let test_checksum: u16 = 0x1234;
1358
1359        // Convert test value to network byte order
1360        let bytes = test_checksum.to_be_bytes();
1361        hdr.check = bytes;
1362
1363        // Check that getter properly converts from network to host byte order
1364        assert_eq!(hdr.checksum(), test_checksum);
1365
1366        // Test setter
1367        hdr.set_checksum(0xABCD);
1368        assert_eq!(hdr.check, [0xAB, 0xCD]);
1369        assert_eq!(hdr.checksum(), 0xABCD);
1370    }
1371
1372    #[test]
1373    fn test_echo_fields() {
1374        let mut hdr = create_test_icmp_hdr();
1375        // Set type to Echo Reply (0) which is valid for echo fields
1376        hdr.type_ = 0;
1377
1378        // Test echo ID
1379        let test_id: u16 = 0x4321;
1380        hdr.set_echo_id(test_id).unwrap();
1381        assert_eq!(hdr.echo_id().unwrap(), test_id);
1382
1383        // Verify byte order in raw storage
1384        unsafe {
1385            assert_eq!(hdr.data.echo.id, test_id.to_be_bytes());
1386        }
1387
1388        // Test echo sequence
1389        let test_seq: u16 = 0x8765;
1390        hdr.set_echo_sequence(test_seq).unwrap();
1391        assert_eq!(hdr.echo_sequence().unwrap(), test_seq);
1392
1393        // Verify byte order in raw storage
1394        unsafe {
1395            assert_eq!(hdr.data.echo.sequence, test_seq.to_be_bytes());
1396        }
1397    }
1398
1399    #[test]
1400    fn test_gateway_address() {
1401        let mut hdr = create_test_icmp_hdr();
1402        // Set type to Redirect (5) which is valid for gateway address
1403        hdr.type_ = 5;
1404        let test_addr = Ipv4Addr::new(192, 168, 1, 1);
1405
1406        hdr.set_gateway_address(test_addr).unwrap();
1407        assert_eq!(hdr.gateway_address().unwrap(), test_addr);
1408
1409        // Verify the raw bytes
1410        unsafe {
1411            assert_eq!(hdr.data.redirect, [192, 168, 1, 1]);
1412        }
1413    }
1414
1415    #[test]
1416    fn test_next_hop_mtu() {
1417        let mut hdr = create_test_icmp_hdr();
1418        // Set type to Destination Unreachable (3) which is valid for next_hop_mtu
1419        hdr.type_ = 3;
1420        let test_mtu: u16 = 1500;
1421
1422        hdr.set_next_hop_mtu(test_mtu).unwrap();
1423        assert_eq!(hdr.next_hop_mtu().unwrap(), test_mtu);
1424
1425        // Verify byte order in raw storage
1426        unsafe {
1427            assert_eq!(hdr.data.dst_unreachable.mtu, test_mtu.to_be_bytes());
1428        }
1429    }
1430
1431    #[test]
1432    fn test_parameter_pointer() {
1433        let mut hdr = create_test_icmp_hdr();
1434        // Set type to Parameter Problem (12) which is valid for parameter_pointer
1435        hdr.type_ = 12;
1436        let test_pointer: u8 = 42;
1437
1438        hdr.set_parameter_pointer(test_pointer).unwrap();
1439        assert_eq!(hdr.parameter_pointer().unwrap(), test_pointer);
1440
1441        // Verify the raw byte
1442        unsafe {
1443            assert_eq!(hdr.data.param_problem.pointer, test_pointer);
1444        }
1445    }
1446
1447    #[test]
1448    fn test_traceroute_id() {
1449        let mut hdr = create_test_icmp_hdr();
1450        // Set type to Traceroute (30) which is valid for traceroute_id
1451        hdr.type_ = 30;
1452        let test_id: u16 = 0x9876;
1453
1454        hdr.set_traceroute_id(test_id).unwrap();
1455        assert_eq!(hdr.traceroute_id().unwrap(), test_id);
1456
1457        // Verify byte order in raw storage
1458        unsafe {
1459            assert_eq!(hdr.data.traceroute.id, test_id.to_be_bytes());
1460        }
1461    }
1462
1463    #[test]
1464    fn test_photuris_spi() {
1465        let mut hdr = create_test_icmp_hdr();
1466        // Set type to PHOTURIS (40) which is valid for photuris_spi
1467        hdr.type_ = 40;
1468        let test_spi: u16 = 0xFEDC;
1469
1470        hdr.set_photuris_spi(test_spi).unwrap();
1471        assert_eq!(hdr.photuris_spi().unwrap(), test_spi);
1472
1473        // Verify byte order in raw storage
1474        unsafe {
1475            assert_eq!(hdr.data.photuris.reserved_spi, test_spi.to_be_bytes());
1476        }
1477    }
1478
1479    #[test]
1480    fn test_photuris_pointer() {
1481        let mut hdr = create_test_icmp_hdr();
1482        // Set type to PHOTURIS (40) which is valid for photuris_pointer
1483        hdr.type_ = 40;
1484        let test_pointer: u16 = 0x1A2B;
1485
1486        hdr.set_photuris_pointer(test_pointer).unwrap();
1487        assert_eq!(hdr.photuris_pointer().unwrap(), test_pointer);
1488
1489        // Verify byte order in raw storage
1490        unsafe {
1491            assert_eq!(hdr.data.photuris.pointer, test_pointer.to_be_bytes());
1492        }
1493    }
1494
1495    #[test]
1496    fn test_type_and_code_fields() {
1497        let mut hdr = create_test_icmp_hdr();
1498
1499        // Test common ICMP types and codes
1500        // Echo Request
1501        hdr.type_ = 8;
1502        hdr.code = 0;
1503        assert_eq!(hdr.type_, 8);
1504        assert_eq!(hdr.code, 0);
1505
1506        // Destination Unreachable - Host Unreachable
1507        hdr.type_ = 3;
1508        hdr.code = 1;
1509        assert_eq!(hdr.type_, 3);
1510        assert_eq!(hdr.code, 1);
1511    }
1512
1513    #[test]
1514    fn test_union_field_access_safety() {
1515        // This test demonstrates that different union field accesses
1516        // manipulate the same memory
1517        let mut hdr = create_test_icmp_hdr();
1518
1519        // Set type to Echo Reply (0) which is valid for echo_id
1520        hdr.type_ = 0;
1521
1522        // Set echo ID and verify the memory is shared with redirect
1523        hdr.set_echo_id(0xABCD).unwrap();
1524
1525        unsafe {
1526            assert_eq!(hdr.data.redirect[0], 0xAB);
1527            assert_eq!(hdr.data.redirect[1], 0xCD);
1528        }
1529
1530        // Set type to Redirect (5) which is valid for gateway_address
1531        hdr.type_ = 5;
1532
1533        // Set gateway address and verify it affects the echo ID
1534        hdr.set_gateway_address(Ipv4Addr::new(1, 2, 3, 4)).unwrap();
1535
1536        // Set type back to Echo Reply (0) to check echo_id
1537        hdr.type_ = 0;
1538        assert_eq!(hdr.echo_id().unwrap(), 0x0102); // First two bytes of the IP address
1539    }
1540
1541    #[test]
1542    fn test_icmp_hdr_bitwise_operations() {
1543        // This test covers operations that might be common in packet processing
1544        let mut hdr = create_test_icmp_hdr();
1545
1546        // Set a sample checksum
1547        hdr.set_checksum(0x1234);
1548
1549        // Modify the checksum using bit operations
1550        let modified_checksum = hdr.checksum() ^ 0xFFFF; // Bitwise NOT
1551        hdr.set_checksum(modified_checksum);
1552
1553        assert_eq!(hdr.checksum(), 0xEDCB); // 0x1234 XOR 0xFFFF = 0xEDCB
1554    }
1555
1556    #[test]
1557    fn test_icmp_common_type_constants() {
1558        // This test verifies common ICMP type handling
1559        let mut hdr = create_test_icmp_hdr();
1560
1561        // Echo Request
1562        hdr.type_ = 8;
1563        assert_eq!(hdr.type_, 8);
1564
1565        // Echo Reply
1566        hdr.type_ = 0;
1567        assert_eq!(hdr.type_, 0);
1568
1569        // Destination Unreachable
1570        hdr.type_ = 3;
1571        assert_eq!(hdr.type_, 3);
1572
1573        // Redirect
1574        hdr.type_ = 5;
1575        assert_eq!(hdr.type_, 5);
1576    }
1577
1578    #[test]
1579    fn test_icmp_echo_message_construction() {
1580        // Test creating a typical ICMP Echo Request
1581        let mut hdr = create_test_icmp_hdr();
1582
1583        hdr.type_ = 8; // Echo Request
1584        hdr.code = 0;
1585        hdr.set_checksum(0); // Would be calculated later based on the entire message
1586
1587        // Echo Request (type 8) is valid for echo_id and echo_sequence
1588        hdr.set_echo_id(0x1234).unwrap();
1589        hdr.set_echo_sequence(0x5678).unwrap();
1590
1591        assert_eq!(hdr.type_, 8);
1592        assert_eq!(hdr.code, 0);
1593        assert_eq!(hdr.checksum(), 0);
1594        assert_eq!(hdr.echo_id().unwrap(), 0x1234);
1595        assert_eq!(hdr.echo_sequence().unwrap(), 0x5678);
1596    }
1597
1598    #[test]
1599    fn test_icmp_destination_unreachable_construction() {
1600        // Test creating a Destination Unreachable message
1601        let mut hdr = create_test_icmp_hdr();
1602
1603        hdr.type_ = 3; // Destination Unreachable
1604        hdr.code = 4; // Fragmentation needed but DF bit set
1605        hdr.set_checksum(0); // Would be calculated later
1606
1607        // Destination Unreachable (type 3) is valid for next_hop_mtu
1608        hdr.set_next_hop_mtu(1400).unwrap(); // Example MTU value
1609
1610        assert_eq!(hdr.type_, 3);
1611        assert_eq!(hdr.code, 4);
1612        assert_eq!(hdr.checksum(), 0);
1613        assert_eq!(hdr.next_hop_mtu().unwrap(), 1400);
1614    }
1615
1616    #[test]
1617    fn test_icmp_parameter_problem_construction() {
1618        // Test creating a Parameter Problem message
1619        let mut hdr = create_test_icmp_hdr();
1620
1621        hdr.type_ = 12; // Parameter Problem
1622        hdr.code = 0; // Pointer indicates the error
1623        hdr.set_checksum(0); // Would be calculated later
1624
1625        // Parameter Problem (type 12) is valid for parameter_pointer
1626        hdr.set_parameter_pointer(20).unwrap(); // Error at byte offset 20
1627
1628        assert_eq!(hdr.type_, 12);
1629        assert_eq!(hdr.code, 0);
1630        assert_eq!(hdr.checksum(), 0);
1631        assert_eq!(hdr.parameter_pointer().unwrap(), 20);
1632    }
1633
1634    #[test]
1635    fn test_icmp_redirect_construction() {
1636        // Test creating a Redirect message
1637        let mut hdr = create_test_icmp_hdr();
1638
1639        hdr.type_ = 5; // Redirect
1640        hdr.code = 1; // Redirect for host
1641        hdr.set_checksum(0); // Would be calculated later
1642
1643        // Redirect (type 5) is valid for gateway_address
1644        hdr.set_gateway_address(Ipv4Addr::new(10, 0, 0, 1)).unwrap(); // Gateway address
1645
1646        assert_eq!(hdr.type_, 5);
1647        assert_eq!(hdr.code, 1);
1648        assert_eq!(hdr.checksum(), 0);
1649        assert_eq!(hdr.gateway_address().unwrap(), Ipv4Addr::new(10, 0, 0, 1));
1650    }
1651
1652    #[test]
1653    fn test_icmp_timestamp_msg_part_size() {
1654        // IcmpTimestampMsgPart should be exactly 12 bytes: 3 timestamps of 4 bytes each
1655        assert_eq!(IcmpTimestampMsgPart::LEN, 12);
1656        assert_eq!(
1657            IcmpTimestampMsgPart::LEN,
1658            mem::size_of::<IcmpTimestampMsgPart>()
1659        );
1660    }
1661
1662    #[test]
1663    fn test_timestamp_originate() {
1664        let mut timestamp_part = IcmpTimestampMsgPart {
1665            originate_timestamp: [0, 0, 0, 0],
1666            receive_timestamp: [0, 0, 0, 0],
1667            transmit_timestamp: [0, 0, 0, 0],
1668        };
1669
1670        // Test with a standard value
1671        let test_timestamp: u32 = 0x12345678;
1672        timestamp_part.set_originate_timestamp(test_timestamp);
1673        assert_eq!(timestamp_part.originate_timestamp(), test_timestamp);
1674
1675        // Verify byte order in raw storage (big-endian/network byte order)
1676        assert_eq!(timestamp_part.originate_timestamp, [0x12, 0x34, 0x56, 0x78]);
1677
1678        // Test with zero
1679        timestamp_part.set_originate_timestamp(0);
1680        assert_eq!(timestamp_part.originate_timestamp(), 0);
1681        assert_eq!(timestamp_part.originate_timestamp, [0, 0, 0, 0]);
1682
1683        // Test with max value
1684        timestamp_part.set_originate_timestamp(u32::MAX);
1685        assert_eq!(timestamp_part.originate_timestamp(), u32::MAX);
1686        assert_eq!(timestamp_part.originate_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
1687    }
1688
1689    #[test]
1690    fn test_timestamp_receive() {
1691        let mut timestamp_part = IcmpTimestampMsgPart {
1692            originate_timestamp: [0, 0, 0, 0],
1693            receive_timestamp: [0, 0, 0, 0],
1694            transmit_timestamp: [0, 0, 0, 0],
1695        };
1696
1697        // Test with a standard value
1698        let test_timestamp: u32 = 0x87654321;
1699        timestamp_part.set_receive_timestamp(test_timestamp);
1700        assert_eq!(timestamp_part.receive_timestamp(), test_timestamp);
1701
1702        // Verify byte order in raw storage (big-endian/network byte order)
1703        assert_eq!(timestamp_part.receive_timestamp, [0x87, 0x65, 0x43, 0x21]);
1704
1705        // Test with zero
1706        timestamp_part.set_receive_timestamp(0);
1707        assert_eq!(timestamp_part.receive_timestamp(), 0);
1708        assert_eq!(timestamp_part.receive_timestamp, [0, 0, 0, 0]);
1709
1710        // Test with max value
1711        timestamp_part.set_receive_timestamp(u32::MAX);
1712        assert_eq!(timestamp_part.receive_timestamp(), u32::MAX);
1713        assert_eq!(timestamp_part.receive_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
1714    }
1715
1716    #[test]
1717    fn test_timestamp_transmit() {
1718        let mut timestamp_part = IcmpTimestampMsgPart {
1719            originate_timestamp: [0, 0, 0, 0],
1720            receive_timestamp: [0, 0, 0, 0],
1721            transmit_timestamp: [0, 0, 0, 0],
1722        };
1723
1724        // Test with a standard value
1725        let test_timestamp: u32 = 0xABCDEF01;
1726        timestamp_part.set_transmit_timestamp(test_timestamp);
1727        assert_eq!(timestamp_part.transmit_timestamp(), test_timestamp);
1728
1729        // Verify byte order in raw storage (big-endian/network byte order)
1730        assert_eq!(timestamp_part.transmit_timestamp, [0xAB, 0xCD, 0xEF, 0x01]);
1731
1732        // Test with zero
1733        timestamp_part.set_transmit_timestamp(0);
1734        assert_eq!(timestamp_part.transmit_timestamp(), 0);
1735        assert_eq!(timestamp_part.transmit_timestamp, [0, 0, 0, 0]);
1736
1737        // Test with max value
1738        timestamp_part.set_transmit_timestamp(u32::MAX);
1739        assert_eq!(timestamp_part.transmit_timestamp(), u32::MAX);
1740        assert_eq!(timestamp_part.transmit_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
1741    }
1742
1743    #[test]
1744    fn test_timestamp_msg_part_construction() {
1745        // Test creating a complete timestamp message part
1746        let mut timestamp_part = IcmpTimestampMsgPart {
1747            originate_timestamp: [0, 0, 0, 0],
1748            receive_timestamp: [0, 0, 0, 0],
1749            transmit_timestamp: [0, 0, 0, 0],
1750        };
1751
1752        // Set all three timestamps
1753        timestamp_part.set_originate_timestamp(0x11223344);
1754        timestamp_part.set_receive_timestamp(0x55667788);
1755        timestamp_part.set_transmit_timestamp(0x99AABBCC);
1756
1757        // Verify all values are correctly set and retrieved
1758        assert_eq!(timestamp_part.originate_timestamp(), 0x11223344);
1759        assert_eq!(timestamp_part.receive_timestamp(), 0x55667788);
1760        assert_eq!(timestamp_part.transmit_timestamp(), 0x99AABBCC);
1761
1762        // Verify raw byte storage
1763        assert_eq!(timestamp_part.originate_timestamp, [0x11, 0x22, 0x33, 0x44]);
1764        assert_eq!(timestamp_part.receive_timestamp, [0x55, 0x66, 0x77, 0x88]);
1765        assert_eq!(timestamp_part.transmit_timestamp, [0x99, 0xAA, 0xBB, 0xCC]);
1766    }
1767
1768    #[test]
1769    fn test_icmp_traceroute_msg_part_size() {
1770        // IcmpTracerouteMsgPart should be exactly 12 bytes: hops_out(2) + hops_in(2) + bandwidth_out(4) + mtu_out(4)
1771        assert_eq!(IcmpTracerouteMsgPart::LEN, 12);
1772        assert_eq!(
1773            IcmpTracerouteMsgPart::LEN,
1774            mem::size_of::<IcmpTracerouteMsgPart>()
1775        );
1776    }
1777
1778    #[test]
1779    fn test_traceroute_hops_out() {
1780        let mut traceroute_part = IcmpTracerouteMsgPart {
1781            hops_out: [0, 0],
1782            hops_in: [0, 0],
1783            bandwidth_out: [0, 0, 0, 0],
1784            mtu_out: [0, 0, 0, 0],
1785        };
1786
1787        // Test with a standard value
1788        let test_hops: u16 = 0x1234;
1789        traceroute_part.set_hops_out(test_hops);
1790        assert_eq!(traceroute_part.hops_out(), test_hops);
1791
1792        // Verify byte order in raw storage (big-endian/network byte order)
1793        assert_eq!(traceroute_part.hops_out, [0x12, 0x34]);
1794
1795        // Test with zero
1796        traceroute_part.set_hops_out(0);
1797        assert_eq!(traceroute_part.hops_out(), 0);
1798        assert_eq!(traceroute_part.hops_out, [0, 0]);
1799
1800        // Test with max value
1801        traceroute_part.set_hops_out(u16::MAX);
1802        assert_eq!(traceroute_part.hops_out(), u16::MAX);
1803        assert_eq!(traceroute_part.hops_out, [0xFF, 0xFF]);
1804    }
1805
1806    #[test]
1807    fn test_traceroute_hops_in() {
1808        let mut traceroute_part = IcmpTracerouteMsgPart {
1809            hops_out: [0, 0],
1810            hops_in: [0, 0],
1811            bandwidth_out: [0, 0, 0, 0],
1812            mtu_out: [0, 0, 0, 0],
1813        };
1814
1815        // Test with a standard value
1816        let test_hops: u16 = 0x5678;
1817        traceroute_part.set_hops_in(test_hops);
1818        assert_eq!(traceroute_part.hops_in(), test_hops);
1819
1820        // Verify byte order in raw storage (big-endian/network byte order)
1821        assert_eq!(traceroute_part.hops_in, [0x56, 0x78]);
1822
1823        // Test with zero
1824        traceroute_part.set_hops_in(0);
1825        assert_eq!(traceroute_part.hops_in(), 0);
1826        assert_eq!(traceroute_part.hops_in, [0, 0]);
1827
1828        // Test with max value
1829        traceroute_part.set_hops_in(u16::MAX);
1830        assert_eq!(traceroute_part.hops_in(), u16::MAX);
1831        assert_eq!(traceroute_part.hops_in, [0xFF, 0xFF]);
1832    }
1833
1834    #[test]
1835    fn test_traceroute_bandwidth_out() {
1836        let mut traceroute_part = IcmpTracerouteMsgPart {
1837            hops_out: [0, 0],
1838            hops_in: [0, 0],
1839            bandwidth_out: [0, 0, 0, 0],
1840            mtu_out: [0, 0, 0, 0],
1841        };
1842
1843        // Test with a standard value
1844        let test_bandwidth: u32 = 0x12345678;
1845        traceroute_part.set_bandwidth_out(test_bandwidth);
1846        assert_eq!(traceroute_part.bandwidth_out(), test_bandwidth);
1847
1848        // Verify byte order in raw storage (big-endian/network byte order)
1849        assert_eq!(traceroute_part.bandwidth_out, [0x12, 0x34, 0x56, 0x78]);
1850
1851        // Test with zero
1852        traceroute_part.set_bandwidth_out(0);
1853        assert_eq!(traceroute_part.bandwidth_out(), 0);
1854        assert_eq!(traceroute_part.bandwidth_out, [0, 0, 0, 0]);
1855
1856        // Test with max value
1857        traceroute_part.set_bandwidth_out(u32::MAX);
1858        assert_eq!(traceroute_part.bandwidth_out(), u32::MAX);
1859        assert_eq!(traceroute_part.bandwidth_out, [0xFF, 0xFF, 0xFF, 0xFF]);
1860    }
1861
1862    #[test]
1863    fn test_traceroute_mtu_out() {
1864        let mut traceroute_part = IcmpTracerouteMsgPart {
1865            hops_out: [0, 0],
1866            hops_in: [0, 0],
1867            bandwidth_out: [0, 0, 0, 0],
1868            mtu_out: [0, 0, 0, 0],
1869        };
1870
1871        // Test with a standard value
1872        let test_mtu: u32 = 0x87654321;
1873        traceroute_part.set_mtu_out(test_mtu);
1874        assert_eq!(traceroute_part.mtu_out(), test_mtu);
1875
1876        // Verify byte order in raw storage (big-endian/network byte order)
1877        assert_eq!(traceroute_part.mtu_out, [0x87, 0x65, 0x43, 0x21]);
1878
1879        // Test with zero
1880        traceroute_part.set_mtu_out(0);
1881        assert_eq!(traceroute_part.mtu_out(), 0);
1882        assert_eq!(traceroute_part.mtu_out, [0, 0, 0, 0]);
1883
1884        // Test with max value
1885        traceroute_part.set_mtu_out(u32::MAX);
1886        assert_eq!(traceroute_part.mtu_out(), u32::MAX);
1887        assert_eq!(traceroute_part.mtu_out, [0xFF, 0xFF, 0xFF, 0xFF]);
1888    }
1889
1890    #[test]
1891    fn test_traceroute_msg_part_construction() {
1892        // Test creating a complete traceroute message part
1893        let mut traceroute_part = IcmpTracerouteMsgPart {
1894            hops_out: [0, 0],
1895            hops_in: [0, 0],
1896            bandwidth_out: [0, 0, 0, 0],
1897            mtu_out: [0, 0, 0, 0],
1898        };
1899
1900        // Set all fields
1901        traceroute_part.set_hops_out(30);
1902        traceroute_part.set_hops_in(25);
1903        traceroute_part.set_bandwidth_out(100000000); // 100 Mbps
1904        traceroute_part.set_mtu_out(1500);
1905
1906        // Verify all values are correctly set and retrieved
1907        assert_eq!(traceroute_part.hops_out(), 30);
1908        assert_eq!(traceroute_part.hops_in(), 25);
1909        assert_eq!(traceroute_part.bandwidth_out(), 100000000);
1910        assert_eq!(traceroute_part.mtu_out(), 1500);
1911
1912        // Verify raw byte storage
1913        assert_eq!(traceroute_part.hops_out, [0, 30]);
1914        assert_eq!(traceroute_part.hops_in, [0, 25]);
1915        assert_eq!(traceroute_part.bandwidth_out, [0x05, 0xF5, 0xE1, 0x00]); // 100000000 in hex
1916        assert_eq!(traceroute_part.mtu_out, [0, 0, 0x05, 0xDC]); // 1500 in hex
1917    }
1918
1919    #[test]
1920    fn test_icmpv6_hdr_size() {
1921        // IcmpV6Hdr size includes the union which contains IcmpV6Redirect (largest variant)
1922        // type(1) + code(1) + check(2) + data(union with IcmpV6Redirect which is 36 bytes)
1923        assert_eq!(IcmpV6Hdr::LEN, 40);
1924        assert_eq!(IcmpV6Hdr::LEN, mem::size_of::<IcmpV6Hdr>());
1925    }
1926
1927    // Helper function to create a default IcmpV6Hdr for testing
1928    fn create_test_icmpv6_hdr() -> IcmpV6Hdr {
1929        IcmpV6Hdr {
1930            type_: 0,
1931            code: 0,
1932            check: [0, 0],
1933            data: IcmpV6HdrUn {
1934                reserved: [0, 0, 0, 0],
1935            },
1936        }
1937    }
1938
1939    #[test]
1940    fn test_icmpv6_checksum() {
1941        let mut hdr = create_test_icmpv6_hdr();
1942        let test_checksum: u16 = 0x1234;
1943
1944        // Convert test value to network byte order
1945        let bytes = test_checksum.to_be_bytes();
1946        hdr.check = bytes;
1947
1948        // Check that getter properly converts from network to host byte order
1949        assert_eq!(hdr.checksum(), test_checksum);
1950
1951        // Test setter
1952        hdr.set_checksum(0xABCD);
1953        assert_eq!(hdr.check, [0xAB, 0xCD]);
1954        assert_eq!(hdr.checksum(), 0xABCD);
1955    }
1956
1957    #[test]
1958    fn test_icmpv6_echo_fields() {
1959        let mut hdr = create_test_icmpv6_hdr();
1960        // Set type to Echo Request (128) which is valid for echo fields
1961        hdr.type_ = 128;
1962
1963        // Test echo ID
1964        let test_id: u16 = 0x4321;
1965        hdr.set_echo_id(test_id).unwrap();
1966        assert_eq!(hdr.echo_id().unwrap(), test_id);
1967
1968        // Verify byte order in raw storage
1969        unsafe {
1970            assert_eq!(hdr.data.echo.id, test_id.to_be_bytes());
1971        }
1972
1973        // Test echo sequence
1974        let test_seq: u16 = 0x8765;
1975        hdr.set_echo_sequence(test_seq).unwrap();
1976        assert_eq!(hdr.echo_sequence().unwrap(), test_seq);
1977
1978        // Verify byte order in raw storage
1979        unsafe {
1980            assert_eq!(hdr.data.echo.sequence, test_seq.to_be_bytes());
1981        }
1982    }
1983
1984    #[test]
1985    fn test_icmpv6_mtu() {
1986        let mut hdr = create_test_icmpv6_hdr();
1987        // Set type to Packet Too Big (2) which is valid for mtu
1988        hdr.type_ = 2;
1989        let test_mtu: u32 = 0x12345678;
1990
1991        hdr.set_mtu(test_mtu).unwrap();
1992        assert_eq!(hdr.mtu().unwrap(), test_mtu);
1993
1994        // Verify byte order in raw storage
1995        unsafe {
1996            assert_eq!(hdr.data.packet_too_big_mtu, test_mtu.to_be_bytes());
1997        }
1998
1999        // Test with zero
2000        hdr.set_mtu(0).unwrap();
2001        assert_eq!(hdr.mtu().unwrap(), 0);
2002        unsafe {
2003            assert_eq!(hdr.data.packet_too_big_mtu, [0, 0, 0, 0]);
2004        }
2005
2006        // Test with max value
2007        hdr.set_mtu(u32::MAX).unwrap();
2008        assert_eq!(hdr.mtu().unwrap(), u32::MAX);
2009        unsafe {
2010            assert_eq!(hdr.data.packet_too_big_mtu, [0xFF, 0xFF, 0xFF, 0xFF]);
2011        }
2012    }
2013
2014    #[test]
2015    fn test_icmpv6_pointer() {
2016        let mut hdr = create_test_icmpv6_hdr();
2017        // Set type to Parameter Problem (4) which is valid for pointer
2018        hdr.type_ = 4;
2019        let test_pointer: u32 = 0x87654321;
2020
2021        hdr.set_pointer(test_pointer).unwrap();
2022        assert_eq!(hdr.pointer().unwrap(), test_pointer);
2023
2024        // Verify byte order in raw storage
2025        unsafe {
2026            assert_eq!(hdr.data.param_problem_pointer, test_pointer.to_be_bytes());
2027        }
2028
2029        // Test with zero
2030        hdr.set_pointer(0).unwrap();
2031        assert_eq!(hdr.pointer().unwrap(), 0);
2032        unsafe {
2033            assert_eq!(hdr.data.param_problem_pointer, [0, 0, 0, 0]);
2034        }
2035
2036        // Test with max value
2037        hdr.set_pointer(u32::MAX).unwrap();
2038        assert_eq!(hdr.pointer().unwrap(), u32::MAX);
2039        unsafe {
2040            assert_eq!(hdr.data.param_problem_pointer, [0xFF, 0xFF, 0xFF, 0xFF]);
2041        }
2042    }
2043
2044    #[test]
2045    fn test_icmpv6_redirect_fields() {
2046        use core::net::Ipv6Addr;
2047        let mut hdr = create_test_icmpv6_hdr();
2048        // Set type to Redirect (137) which is valid for redirect fields
2049        hdr.type_ = 137;
2050
2051        // Test reserved field
2052        let test_reserved: [u8; 4] = [0, 0, 0, 0]; // Should be zeros per RFC
2053        hdr.set_redirect_reserved(test_reserved).unwrap();
2054        assert_eq!(hdr.redirect_reserved().unwrap(), test_reserved);
2055
2056        // Test target address
2057        let test_target = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
2058        hdr.set_redirect_target_address(test_target).unwrap();
2059        assert_eq!(hdr.redirect_target_address().unwrap(), test_target);
2060
2061        // Test destination address
2062        let test_dest = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
2063        hdr.set_redirect_destination_address(test_dest).unwrap();
2064        assert_eq!(hdr.redirect_destination_address().unwrap(), test_dest);
2065
2066        // Verify raw byte storage for target address
2067        unsafe {
2068            assert_eq!(hdr.data.redirect.target_address, test_target.octets());
2069        }
2070
2071        // Verify raw byte storage for destination address
2072        unsafe {
2073            assert_eq!(hdr.data.redirect.destination_address, test_dest.octets());
2074        }
2075    }
2076
2077    #[test]
2078    fn test_icmpv6_type_and_code_fields() {
2079        let mut hdr = create_test_icmpv6_hdr();
2080
2081        // Test common ICMPv6 types and codes
2082        // Echo Request
2083        hdr.type_ = 128;
2084        hdr.code = 0;
2085        assert_eq!(hdr.type_, 128);
2086        assert_eq!(hdr.code, 0);
2087
2088        // Echo Reply
2089        hdr.type_ = 129;
2090        hdr.code = 0;
2091        assert_eq!(hdr.type_, 129);
2092        assert_eq!(hdr.code, 0);
2093
2094        // Destination Unreachable - No route to destination
2095        hdr.type_ = 1;
2096        hdr.code = 0;
2097        assert_eq!(hdr.type_, 1);
2098        assert_eq!(hdr.code, 0);
2099
2100        // Packet Too Big
2101        hdr.type_ = 2;
2102        hdr.code = 0;
2103        assert_eq!(hdr.type_, 2);
2104        assert_eq!(hdr.code, 0);
2105
2106        // Time Exceeded - Hop limit exceeded in transit
2107        hdr.type_ = 3;
2108        hdr.code = 0;
2109        assert_eq!(hdr.type_, 3);
2110        assert_eq!(hdr.code, 0);
2111
2112        // Parameter Problem - Erroneous header field encountered
2113        hdr.type_ = 4;
2114        hdr.code = 0;
2115        assert_eq!(hdr.type_, 4);
2116        assert_eq!(hdr.code, 0);
2117    }
2118
2119    #[test]
2120    fn test_icmpv6_echo_request_construction() {
2121        // Test creating a typical ICMPv6 Echo Request
2122        let mut hdr = create_test_icmpv6_hdr();
2123
2124        hdr.type_ = 128; // Echo Request
2125        hdr.code = 0;
2126        hdr.set_checksum(0); // Would be calculated later based on the entire message
2127        hdr.set_echo_id(0x1234).unwrap();
2128        hdr.set_echo_sequence(0x5678).unwrap();
2129
2130        assert_eq!(hdr.type_, 128);
2131        assert_eq!(hdr.code, 0);
2132        assert_eq!(hdr.checksum(), 0);
2133        assert_eq!(hdr.echo_id().unwrap(), 0x1234);
2134        assert_eq!(hdr.echo_sequence().unwrap(), 0x5678);
2135    }
2136
2137    #[test]
2138    fn test_icmpv6_packet_too_big_construction() {
2139        // Test creating a Packet Too Big message
2140        let mut hdr = create_test_icmpv6_hdr();
2141
2142        hdr.type_ = 2; // Packet Too Big
2143        hdr.code = 0;
2144        hdr.set_checksum(0); // Would be calculated later
2145        hdr.set_mtu(1500).unwrap(); // Example MTU value
2146
2147        assert_eq!(hdr.type_, 2);
2148        assert_eq!(hdr.code, 0);
2149        assert_eq!(hdr.checksum(), 0);
2150        assert_eq!(hdr.mtu().unwrap(), 1500);
2151    }
2152
2153    #[test]
2154    fn test_icmpv6_parameter_problem_construction() {
2155        // Test creating a Parameter Problem message
2156        let mut hdr = create_test_icmpv6_hdr();
2157
2158        hdr.type_ = 4; // Parameter Problem
2159        hdr.code = 0; // Erroneous header field encountered
2160        hdr.set_checksum(0); // Would be calculated later
2161        hdr.set_pointer(40).unwrap(); // Error at byte offset 40
2162
2163        assert_eq!(hdr.type_, 4);
2164        assert_eq!(hdr.code, 0);
2165        assert_eq!(hdr.checksum(), 0);
2166        assert_eq!(hdr.pointer().unwrap(), 40);
2167    }
2168}