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}