From d5c8a9c5e708069eda1c680334d50d76caafea24 Mon Sep 17 00:00:00 2001 From: vad Date: Thu, 5 Feb 2026 16:21:09 +0100 Subject: [PATCH 1/3] chore(icmp): Remove the `IcmpDataUn` and `IcmpV6DataUn` unions All the variants carried by them have the same size (4 bytes). Given than using C-style unions in Rust makes it difficult to use serialization libraries, remove them and instead define the `data` fields as `[u8; 4]`, then implement our own (safe) abstraction that allow users to check and access the appropriate variants (depending on the ICMP type). Keep the unsafe variants public as well. --- src/icmp.rs | 967 +++++++++++++++++++--------------------------------- 1 file changed, 349 insertions(+), 618 deletions(-) diff --git a/src/icmp.rs b/src/icmp.rs index 3a40963..4a6fbf0 100644 --- a/src/icmp.rs +++ b/src/icmp.rs @@ -51,7 +51,7 @@ pub struct IcmpHdr { pub type_: u8, pub code: u8, pub check: [u8; 2], - pub data: IcmpDataUn, + pub data: [u8; 4], } impl IcmpHdr { @@ -72,393 +72,314 @@ impl IcmpHdr { unsafe { setter_be!(self, check, checksum) } } - /// Returns the identification field from ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. + /// Checks whether the ICMP header carries the Echo/Timestamp/Info/Mask + /// message. #[inline] - pub fn echo_id(&self) -> Result { - if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) { - return Err(IcmpError::InvalidIcmpType); + fn check_echo(&self) -> Result<(), IcmpError> { + if matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - Ok(unsafe { self.echo_id_unchecked() }) } - /// Sets the identification field for ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. + /// Returns a reference the ICMP Echo/Timestamp/Info/Mask message. + /// Only valid for ICMP types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. Using + /// this method on a header of an another type results in an error. #[inline] - pub fn set_echo_id(&mut self, id: u16) -> Result<(), IcmpError> { - if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_echo_id_unchecked(id); - } - Ok(()) + pub fn echo(&self) -> Result<&IcmpEcho, IcmpError> { + self.check_echo().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.echo_unchecked() } + }) } - /// Returns the sequence number from ICMP Echo/Timestamp/Info/Mask messages. + /// Returns a reference the ICMP Echo/Timestamp/Info/Mask message. + /// Only valid for ICMP types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. Using + /// this method on a header of an another type results in an error. #[inline] - pub fn echo_sequence(&self) -> Result { - if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.echo_sequence_unchecked() }) + pub fn echo_mut(&mut self) -> Result<&mut IcmpEcho, IcmpError> { + self.check_echo().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.echo_mut_unchecked() } + }) } - /// Sets the sequence number for ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. + /// Checks whether the ICMP header carries the Redirect message. #[inline] - pub fn set_echo_sequence(&mut self, sequence: u16) -> Result<(), IcmpError> { - if !matches!(self.type_, 0 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38) { - return Err(IcmpError::InvalidIcmpType); + fn check_redirect(&self) -> Result<(), IcmpError> { + if self.type_ == 5 { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - unsafe { - self.set_echo_sequence_unchecked(sequence); - } - Ok(()) } /// Returns the gateway internet address from an ICMP Redirect message (Type 5) #[inline] pub fn gateway_address(&self) -> Result { - if self.type_ != 5 { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.gateway_address_unchecked() }) + self.check_redirect().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.gateway_address_unchecked() } + }) } /// Sets the gateway internet address for an ICMP Redirect message (Type 5) #[inline] pub fn set_gateway_address(&mut self, addr: net::Ipv4Addr) -> Result<(), IcmpError> { - if self.type_ != 5 { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_gateway_address_unchecked(addr); - } - Ok(()) + self.check_redirect().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { + self.set_gateway_address_unchecked(addr); + } + }) } - /// Returns the Next-Hop MTU field from a Destination Unreachable message - /// in host byte order. Used for Path MTU Discovery (RFC 1191). + /// Checks whether the ICMP header carries the Destination Unreachable message. #[inline] - pub fn next_hop_mtu(&self) -> Result { - if self.type_ != 3 { - return Err(IcmpError::InvalidIcmpType); + fn check_destination_unreachable(&self) -> Result<(), IcmpError> { + if self.type_ == 3 { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - Ok(unsafe { self.next_hop_mtu_unchecked() }) } - /// Sets the Next-Hop MTU field for a Destination Unreachable message. + /// Returns a reference to the Destination Unreachable message. /// Used for Path MTU Discovery (RFC 1191). #[inline] - pub fn set_next_hop_mtu(&mut self, mtu: u16) -> Result<(), IcmpError> { - if self.type_ != 3 { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_next_hop_mtu_unchecked(mtu); - } - Ok(()) + pub fn destination_unreachable(&self) -> Result<&IcmpDstUnreachable, IcmpError> { + self.check_destination_unreachable().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.destination_unreachable_unchecked() } + }) } - /// Returns the pointer to the errored byte from a Parameter Problem message (Type 12) + /// Returns a mutable reference to the Destination Unreachable message. #[inline] - pub fn parameter_pointer(&self) -> Result { - if self.type_ != 12 { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.parameter_pointer_unchecked() }) + pub fn destination_unreachable_mut(&mut self) -> Result<&mut IcmpDstUnreachable, IcmpError> { + self.check_destination_unreachable().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.destination_unreachable_mut_unchecked() } + }) } - /// Sets the pointer to the errored byte for a Parameter Problem message (Type 12) + /// Checks whether the ICMP header carries the Parameter Problem message. #[inline] - pub fn set_parameter_pointer(&mut self, pointer: u8) -> Result<(), IcmpError> { - if self.type_ != 12 { - return Err(IcmpError::InvalidIcmpType); + fn check_parameter_problem(&self) -> Result<(), IcmpError> { + if self.type_ == 12 { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - unsafe { - self.set_parameter_pointer_unchecked(pointer); - } - Ok(()) } - /// Returns the ID Number field from a Traceroute message (Type 30). - /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages. - /// This is only valid for ICMP Type 30 (Traceroute Request) and Type 31 (Traceroute Reply). + /// Returns a reference to the Parameter Problem message (Type 12). #[inline] - pub fn traceroute_id(&self) -> Result { - if !matches!(self.type_, 30 | 31) { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.traceroute_id_unchecked() }) + pub fn parameter_problem(&self) -> Result<&IcmpParamProblem, IcmpError> { + self.check_parameter_problem().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.parameter_problem_unchecked() } + }) } - /// Sets the ID Number field for a Traceroute message (Type 30). - /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages. + /// Returns a mutable reference to the Parameter Problem message (Type 12). #[inline] - pub fn set_traceroute_id(&mut self, id: u16) -> Result<(), IcmpError> { - if !matches!(self.type_, 30 | 31) { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_traceroute_id_unchecked(id); - } - Ok(()) + pub fn parameter_problem_mut(&mut self) -> Result<&mut IcmpParamProblem, IcmpError> { + self.check_parameter_problem().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.parameter_problem_mut_unchecked() } + }) } - /// Returns the Security Parameters Index (SPI) from a PHOTURIS message (Type 40). - /// The SPI identifies a security association between two peers. #[inline] - pub fn photuris_spi(&self) -> Result { - if self.type_ != 40 { - return Err(IcmpError::InvalidIcmpType); + fn check_traceroute(&self) -> Result<(), IcmpError> { + if matches!(self.type_, 30 | 31) { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - Ok(unsafe { self.photuris_spi_unchecked() }) } - /// Sets the Security Parameters Index (SPI) for a PHOTURIS message (Type 40). - /// The SPI identifies a security association between two peers. + /// Returns the ID Number field from a Traceroute message (Type 30). + /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages. + /// This is only valid for ICMP Type 30 (Traceroute Request) and Type 31 (Traceroute Reply). #[inline] - pub fn set_photuris_spi(&mut self, spi: u16) -> Result<(), IcmpError> { - if self.type_ != 40 { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_photuris_spi_unchecked(spi); - } - Ok(()) + pub fn traceroute(&self) -> Result<&IcmpTraceroute, IcmpError> { + self.check_traceroute().map(|_| { + // SAFETY: We checked the ICMP type + unsafe { self.traceroute_unchecked() } + }) } - /// Returns the pointer to the byte where an error was detected in a PHOTURIS message (Type 40). - /// Used to identify the location of errors during PHOTURIS protocol processing. + /// Sets the ID Number field for a Traceroute message (Type 30). + /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages. #[inline] - pub fn photuris_pointer(&self) -> Result { - if self.type_ != 40 { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.photuris_pointer_unchecked() }) + pub fn traceroute_mut(&mut self) -> Result<&mut IcmpTraceroute, IcmpError> { + self.check_traceroute().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.traceroute_mut_unchecked() } + }) } - /// Sets the pointer to the byte where an error was detected in a PHOTURIS message (Type 40). - /// Used to identify the location of errors during PHOTURIS protocol processing. #[inline] - pub fn set_photuris_pointer(&mut self, pointer: u16) -> Result<(), IcmpError> { - if self.type_ != 40 { - return Err(IcmpError::InvalidIcmpType); + fn check_photuris(&self) -> Result<(), IcmpError> { + if self.type_ == 40 { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - unsafe { - self.set_photuris_pointer_unchecked(pointer); - } - Ok(()) } -} -/// These are the unsafe alternatives to the safe functions on `IcmpHdr` that do prevent undefined behavior. -impl IcmpHdr { - /// Returns the identification field from ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. - /// - /// # Safety - /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38 - /// before calling this function. Accessing the echo fields with other ICMP types may result - /// in undefined behavior. + /// Returns the PHOTURIS message (Type 40). #[inline] - pub unsafe fn echo_id_unchecked(&self) -> u16 { - self.data.echo.id_unchecked() + pub fn photuris(&self) -> Result<&IcmpHdrPhoturis, IcmpError> { + self.check_photuris().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.photuris_unchecked() } + }) } - /// Sets the identification field for ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. - /// - /// # Safety - /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38 - /// before calling this function. Accessing the echo fields with other ICMP types may result - /// in undefined behavior. + /// Sets the Security Parameters Index (SPI) for a PHOTURIS message (Type 40). + /// The SPI identifies a security association between two peers. #[inline] - pub unsafe fn set_echo_id_unchecked(&mut self, id: u16) { - self.data.echo.set_id_unchecked(id); + pub fn photuris_mut(&mut self) -> Result<&mut IcmpHdrPhoturis, IcmpError> { + self.check_photuris().map(|_| { + // SAFETY: We checked the ICMP type. + unsafe { self.photuris_mut_unchecked() } + }) } +} - /// Returns the sequence number from ICMP Echo/Timestamp/Info/Mask messages. +/// These are the unsafe alternatives to the safe functions on `IcmpHdr` that do prevent undefined behavior. +impl IcmpHdr { + /// Returns a reference to the ICMP Echo/Timestamp/Info/Mask message. /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. /// /// # Safety /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38 - /// before calling this function. Accessing the echo fields with other ICMP types may result + /// before calling this function. Calling this method with other ICMP types may result /// in undefined behavior. #[inline] - pub unsafe fn echo_sequence_unchecked(&self) -> u16 { - self.data.echo.sequence_unchecked() + pub unsafe fn echo_unchecked(&self) -> &IcmpEcho { + &*self.data.as_ptr().cast() } - /// Sets the sequence number for ICMP Echo/Timestamp/Info/Mask messages. + /// Returns a mutable reference to the ICMP Echo/Timestamp/Info/Mask message. /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. /// /// # Safety /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38 - /// before calling this function. Accessing the echo fields with other ICMP types may result + /// before calling this function. Calling this method with other ICMP types may result /// in undefined behavior. #[inline] - pub unsafe fn set_echo_sequence_unchecked(&mut self, sequence: u16) { - self.data.echo.set_sequence_unchecked(sequence); + pub unsafe fn echo_mut_unchecked(&mut self) -> &mut IcmpEcho { + &mut *self.data.as_mut_ptr().cast() } - /// Returns the gateway internet address from an ICMP Redirect message (Type 5) + /// Returns a reference to the gateway internet address from an ICMP + /// Redirect message (Type 5). /// /// # Safety /// Caller must ensure ICMP type is 5 (Redirect) before calling this function. - /// Accessing the redirect field with other ICMP types may result in undefined behavior. + /// Calling this method with other ICMP types may result in undefined behavior. #[inline] pub unsafe fn gateway_address_unchecked(&self) -> net::Ipv4Addr { - net::Ipv4Addr::from(self.data.redirect) + net::Ipv4Addr::from(self.data) } - /// Sets the gateway internet address for an ICMP Redirect message (Type 5) + /// Returns a mutable the gateway internet address for an ICMP Redirect message (Type 5) /// /// # Safety /// Caller must ensure ICMP type is 5 (Redirect) before calling this function. /// Accessing the redirect field with other ICMP types may result in undefined behavior. #[inline] pub unsafe fn set_gateway_address_unchecked(&mut self, addr: net::Ipv4Addr) { - self.data.redirect = addr.octets(); + self.data = addr.octets(); } - /// Returns the Next-Hop MTU field from a Destination Unreachable message - /// in host byte order. Used for Path MTU Discovery (RFC 1191). + /// Returns a reference to the Destination Unreachable message. + /// Used for Path MTU Discovery (RFC 1191). /// /// # Safety /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function. /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn next_hop_mtu_unchecked(&self) -> u16 { - self.data.dst_unreachable.mtu_unchecked() + pub unsafe fn destination_unreachable_unchecked(&self) -> &IcmpDstUnreachable { + &*self.data.as_ptr().cast() } - /// Sets the Next-Hop MTU field for a Destination Unreachable message. + /// Returns a mutable reference to the Destination Unreachable message. /// Used for Path MTU Discovery (RFC 1191). /// /// # Safety /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function. /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn set_next_hop_mtu_unchecked(&mut self, mtu: u16) { - self.data.dst_unreachable.set_mtu_unchecked(mtu) + pub unsafe fn destination_unreachable_mut_unchecked(&mut self) -> &mut IcmpDstUnreachable { + &mut *self.data.as_mut_ptr().cast() } - /// Returns the pointer to the errored byte from a Parameter Problem message (Type 12) + /// Returns a reference to the Parameter Problem message (Type 12) /// /// # Safety /// Caller must ensure ICMP type is 12 (Parameter Problem) before calling this function. /// Accessing the param_problem field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn parameter_pointer_unchecked(&self) -> u8 { - self.data.param_problem.pointer + pub unsafe fn parameter_problem_unchecked(&self) -> &IcmpParamProblem { + &*self.data.as_ptr().cast() } - /// Sets the pointer to the errored byte for a Parameter Problem message (Type 12) + /// Returns a reference to the Parameter Problem message (Type 12) /// /// # Safety /// Caller must ensure ICMP type is 12 (Parameter Problem) before calling this function. /// Accessing the param_problem field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn set_parameter_pointer_unchecked(&mut self, pointer: u8) { - self.data.param_problem.pointer = pointer; + pub unsafe fn parameter_problem_mut_unchecked(&mut self) -> &mut IcmpParamProblem { + &mut *self.data.as_mut_ptr().cast() } - /// Returns the ID Number field from a Traceroute message (Type 30). - /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages. + /// Returns a reference the Traceroute message (Type 30). /// This is only valid for ICMP Type 30 (Traceroute Request) and Type 31 (Traceroute Reply). /// /// # Safety /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn traceroute_id_unchecked(&self) -> u16 { - self.data.traceroute.id_unchecked() + pub unsafe fn traceroute_unchecked(&self) -> &IcmpTraceroute { + &*self.data.as_ptr().cast() } - /// Sets the ID Number field for a Traceroute message (Type 30). - /// The ID Number is used to match Reply messages (Type 31) to their corresponding Request messages. + /// Returns a mutable reference the Traceroute message (Type 30). + /// This is only valid for ICMP Type 30 (Traceroute Request) and Type 31 (Traceroute Reply). /// /// # Safety /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn set_traceroute_id_unchecked(&mut self, id: u16) { - self.data.traceroute.set_id_unchecked(id); + pub unsafe fn traceroute_mut_unchecked(&mut self) -> &mut IcmpTraceroute { + &mut *self.data.as_mut_ptr().cast() } - /// Returns the Security Parameters Index (SPI) from a PHOTURIS message (Type 40). - /// The SPI identifies a security association between two peers. + /// Returns a reference to the PHOTURIS message (Type 40). /// /// # Safety /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function. /// Accessing the photuris field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn photuris_spi_unchecked(&self) -> u16 { - self.data.photuris.reserved_spi_unchecked() + pub unsafe fn photuris_unchecked(&self) -> &IcmpHdrPhoturis { + &*self.data.as_ptr().cast() } - /// Sets the Security Parameters Index (SPI) for a PHOTURIS message (Type 40). - /// The SPI identifies a security association between two peers. + /// Returns a mutable reference to the PHOTURIS message (Type 40). /// /// # Safety /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function. /// Accessing the photuris field with other ICMP types may result in undefined behavior. #[inline] - pub unsafe fn set_photuris_spi_unchecked(&mut self, spi: u16) { - self.data.photuris.set_reserved_spi_unckecked(spi); - } - - /// Returns the pointer to the byte where an error was detected in a PHOTURIS message (Type 40). - /// Used to identify the location of errors during PHOTURIS protocol processing. - /// - /// # Safety - /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function. - /// Accessing the photuris field with other ICMP types may result in undefined behavior. - #[inline] - pub unsafe fn photuris_pointer_unchecked(&self) -> u16 { - self.data.photuris.pointer_unchecked() - } - - /// Sets the pointer to the byte where an error was detected in a PHOTURIS message (Type 40). - /// Used to identify the location of errors during PHOTURIS protocol processing. - /// - /// # Safety - /// Caller must ensure ICMP type is 40 (PHOTURIS) before calling this function. - /// Accessing the photuris field with other ICMP types may result in undefined behavior. - #[inline] - pub unsafe fn set_photuris_pointer_unchecked(&mut self, pointer: u16) { - self.data.photuris.set_pointer_unchecked(pointer); - } -} - -/// Union holding the variable 4-byte field after the first 4 bytes of an ICMP header. -/// The meaning of this field depends on the ICMP type: -/// - `echo`: Used for Echo Request/Reply and other messages with ID/sequence numbers (Types: 0,8,13,14,15,16,17,18,37,38) -/// - `redirect`: Used for Redirect messages (Type 5) to hold gateway IPv4 address -/// - `dst_unreachable`: Used for Destination Unreachable messages (Type 3) to hold Next-Hop MTU -/// - `param_problem`: Used for Parameter Problem messages (Type 12) to point to error location -/// - `traceroute`: Used for Traceroute messages (Type 30) to hold ID number -/// - `photuris`: Used for PHOTURIS security messages (Type 40) to hold SPI and error pointer -/// - `reserved`: Generic 4-byte field for types not covered by other variants -#[repr(C, packed)] -#[derive(Copy, Clone)] -pub union IcmpDataUn { - pub echo: IcmpEcho, - pub redirect: [u8; 4], - pub dst_unreachable: IcmpDstUnreachable, - pub param_problem: IcmpParamProblem, - pub traceroute: IcmpTraceroute, - pub photuris: IcmpHdrPhoturis, - pub reserved: [u8; 4], // Generic 4-byte data, also for "Unused" fields -} - -impl core::fmt::Debug for IcmpDataUn { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - // Safe approach: just show the raw 4 bytes - let bytes = unsafe { self.reserved }; - write!(f, "IcmpDataUn({bytes:#04x?})") + pub unsafe fn photuris_mut_unchecked(&mut self) -> &mut IcmpHdrPhoturis { + &mut *self.data.as_mut_ptr().cast() } } @@ -484,39 +405,21 @@ pub struct IcmpEcho { impl IcmpEcho { /// Returns the identification field from ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. - /// - /// # Safety - /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38 - /// before calling this function. Accessing the echo fields with other ICMP types may result - /// in undefined behavior. #[inline] - unsafe fn id_unchecked(&self) -> u16 { - getter_be!(self, id, u16) + fn id(&self) -> u16 { + unsafe { getter_be!(self, id, u16) } } /// Sets the identification field for ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. - /// - /// # Safety - /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38 - /// before calling this function. Accessing the echo fields with other ICMP types may result - /// in undefined behavior. #[inline] - unsafe fn set_id_unchecked(&mut self, id: u16) { - setter_be!(self, id, id) + pub fn set_id(&mut self, id: u16) { + unsafe { setter_be!(self, id, id) } } /// Returns the sequence number from ICMP Echo/Timestamp/Info/Mask messages. - /// Only valid for ICMP Types: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38. - /// - /// # Safety - /// Caller must ensure that the ICMP type is one of: 0, 8, 13, 14, 15, 16, 17, 18, 37, 38 - /// before calling this function. Accessing the echo fields with other ICMP types may result - /// in undefined behavior. #[inline] - unsafe fn sequence_unchecked(&self) -> u16 { - getter_be!(self, sequence, u16) + pub fn sequence(&self) -> u16 { + unsafe { getter_be!(self, sequence, u16) } } /// Sets the sequence number for ICMP Echo/Timestamp/Info/Mask messages. @@ -527,8 +430,8 @@ impl IcmpEcho { /// before calling this function. Accessing the echo fields with other ICMP types may result /// in undefined behavior. #[inline] - unsafe fn set_sequence_unchecked(&mut self, sequence: u16) { - setter_be!(self, sequence, sequence) + pub fn set_sequence(&mut self, sequence: u16) { + unsafe { setter_be!(self, sequence, sequence) } } } @@ -551,13 +454,13 @@ impl IcmpDstUnreachable { /// Caller must ensure ICMP type is 3 (Destination Unreachable) before calling this function. /// Accessing the dst_unreachable field with other ICMP types may result in undefined behavior. #[inline] - unsafe fn mtu_unchecked(&self) -> u16 { - getter_be!(self, mtu, u16) + pub fn mtu(&self) -> u16 { + unsafe { getter_be!(self, mtu, u16) } } #[inline] - unsafe fn set_mtu_unchecked(&mut self, mtu: u16) { - setter_be!(self, mtu, mtu) + pub fn set_mtu(&mut self, mtu: u16) { + unsafe { setter_be!(self, mtu, mtu) } } } @@ -572,6 +475,18 @@ pub struct IcmpParamProblem { pub _unused: [u8; 3], // To make up 4 bytes } +impl IcmpParamProblem { + #[inline] + pub fn pointer(&self) -> u8 { + self.pointer + } + + #[inline] + pub fn set_pointer(&mut self, pointer: u8) { + self.pointer = pointer; + } +} + /// For ICMP Type 40 (PHOTURIS) Message (RFC 2521) /// Contains 2 "Reserved" bytes followed by the Security Parameters Index used /// for a security association between two peers. Also includes a 2-byte pointer @@ -586,23 +501,23 @@ pub struct IcmpHdrPhoturis { impl IcmpHdrPhoturis { #[inline] - unsafe fn reserved_spi_unchecked(&self) -> u16 { - getter_be!(self, reserved_spi, u16) + pub fn reserved_spi(&self) -> u16 { + unsafe { getter_be!(self, reserved_spi, u16) } } #[inline] - unsafe fn set_reserved_spi_unckecked(&mut self, spi: u16) { - setter_be!(self, reserved_spi, spi) + pub fn set_reserved_spi(&mut self, spi: u16) { + unsafe { setter_be!(self, reserved_spi, spi) } } #[inline] - unsafe fn pointer_unchecked(&self) -> u16 { - getter_be!(self, pointer, u16) + pub fn pointer(&self) -> u16 { + unsafe { getter_be!(self, pointer, u16) } } #[inline] - unsafe fn set_pointer_unchecked(&mut self, pointer: u16) { - setter_be!(self, pointer, pointer) + pub fn set_pointer(&mut self, pointer: u16) { + unsafe { setter_be!(self, pointer, pointer) } } } @@ -627,8 +542,8 @@ impl IcmpTraceroute { /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior. #[inline] - unsafe fn id_unchecked(&self) -> u16 { - getter_be!(self, id, u16) + pub fn id(&self) -> u16 { + unsafe { getter_be!(self, id, u16) } } /// Sets the ID Number field for a Traceroute message (Type 30). @@ -638,8 +553,8 @@ impl IcmpTraceroute { /// Caller must ensure ICMP type is 30 (Traceroute Request) or 31 (Traceroute Reply) before calling /// this function. Accessing the traceroute field with other ICMP types may result in undefined behavior. #[inline] - unsafe fn set_id_unchecked(&mut self, id: u16) { - setter_be!(self, id, id) + pub fn set_id(&mut self, id: u16) { + unsafe { setter_be!(self, id, id) } } } @@ -919,57 +834,7 @@ pub struct IcmpV6Hdr { pub type_: u8, pub code: u8, pub check: [u8; 2], - pub data: IcmpV6DataUn, -} - -/// Union holding the variable 4-byte field after the first 4 bytes of an ICMPv6 header. -/// The meaning of this field depends on the ICMPv6 type: -/// - `echo`: Used for Echo Request/Reply messages (Types: 128, 129) -/// - `packet_too_big_mtu`: Used in Packet Too Big messages (Type 2) to indicate next-hop MTU -/// - `param_problem_pointer`: Used in Parameter Problem messages (Type 4) to point to error location -/// - `reserved`: Generic 4-byte field for unused/reserved data in other message types, -/// including the reserved field of Redirect messages (Type 137) -#[repr(C, packed)] -#[derive(Copy, Clone)] -pub union IcmpV6DataUn { - pub echo: IcmpEcho, - pub packet_too_big_mtu: [u8; 4], - pub param_problem_pointer: [u8; 4], - pub reserved: [u8; 4], -} - -impl IcmpV6DataUn { - #[inline] - unsafe fn mtu_unchecked(&self) -> u32 { - getter_be!(self, packet_too_big_mtu, u32) - } - - #[inline] - unsafe fn set_mtu_unchecked(&mut self, mtu: u32) { - setter_be!(self, packet_too_big_mtu, mtu) - } - - #[inline] - unsafe fn pointer_unchecked(&self) -> u32 { - getter_be!(self, param_problem_pointer, u32) - } - - #[inline] - unsafe fn set_pointer_unchecked(&mut self, pointer: u32) { - setter_be!(self, param_problem_pointer, pointer) - } -} - -impl core::fmt::Debug for IcmpV6DataUn { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - // Safe approach: just show the raw 4 bytes - let bytes = unsafe { self.reserved }; - write!( - f, - "IcmpV6DataUn([{:#04x}, {:#04x}, {:#04x}, {:#04x}])", - bytes[0], bytes[1], bytes[2], bytes[3] - ) - } + pub data: [u8; 4], } /// Full ICMPv6 Redirect message as defined in RFC 4443 section 4.5. @@ -1049,165 +914,156 @@ impl IcmpV6Hdr { unsafe { setter_be!(self, check, checksum) } } + #[inline] + fn check_echo(&self) -> Result<(), IcmpError> { + if matches!(self.type_, 128 | 129) { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) + } + } + + #[inline] + pub fn echo(&self) -> Result<&IcmpEcho, IcmpError> { + self.check_echo().map(|_| { + // SAFETY: We verified the ICMPv6 type before reinterpreting the bytes. + unsafe { self.echo_unchecked() } + }) + } + + #[inline] + pub fn echo_mut(&mut self) -> Result<&mut IcmpEcho, IcmpError> { + self.check_echo().map(|_| { + // SAFETY: We verified the ICMPv6 type before reinterpreting the bytes. + unsafe { self.echo_mut_unchecked() } + }) + } + /// Returns the identification field from ICMPv6 Echo Request/Reply messages. /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). #[inline] pub fn echo_id(&self) -> Result { - if !matches!(self.type_, 128 | 129) { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.echo_id_unchecked() }) + self.echo().map(|echo| echo.id()) } /// Sets the identification field for ICMPv6 Echo Request/Reply messages. /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). #[inline] pub fn set_echo_id(&mut self, id: u16) -> Result<(), IcmpError> { - if !matches!(self.type_, 128 | 129) { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_echo_id_unchecked(id); - } - Ok(()) + self.echo_mut().map(|echo| echo.set_id(id)) } /// Returns the sequence number from ICMPv6 Echo Request/Reply messages. /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). #[inline] pub fn echo_sequence(&self) -> Result { - if !matches!(self.type_, 128 | 129) { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.echo_sequence_unchecked() }) + self.echo().map(|echo| echo.sequence()) } /// Sets the sequence number for ICMPv6 Echo Request/Reply messages. /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). #[inline] pub fn set_echo_sequence(&mut self, sequence: u16) -> Result<(), IcmpError> { - if !matches!(self.type_, 128 | 129) { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_echo_sequence_unchecked(sequence); + self.echo_mut().map(|echo| echo.set_sequence(sequence)) + } + + #[inline] + fn check_packet_too_big(&self) -> Result<(), IcmpError> { + if self.type_ == 2 { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - Ok(()) } /// Returns the MTU field from an ICMPv6 Packet Too Big message (Type 2). /// This value indicates the maximum packet size that can be handled by the next hop. #[inline] pub fn mtu(&self) -> Result { - if self.type_ != 2 { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.mtu_unchecked() }) + self.check_packet_too_big() + .map(|_| unsafe { self.mtu_unchecked() }) } /// Sets the MTU field for an ICMPv6 Packet Too Big message (Type 2). /// This should be set to the maximum packet size that can be handled by the next hop. #[inline] pub fn set_mtu(&mut self, mtu: u32) -> Result<(), IcmpError> { - if self.type_ != 2 { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_mtu_unchecked(mtu); + self.check_packet_too_big() + .map(|_| unsafe { self.set_mtu_unchecked(mtu) }) + } + + #[inline] + fn check_parameter_problem(&self) -> Result<(), IcmpError> { + if self.type_ == 4 { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - Ok(()) } /// Returns the pointer field from an ICMPv6 Parameter Problem message (Type 4). /// The pointer indicates the offset within the invoking packet where the error was detected. #[inline] pub fn pointer(&self) -> Result { - if self.type_ != 4 { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.pointer_unchecked() }) + self.check_parameter_problem() + .map(|_| unsafe { self.pointer_unchecked() }) } /// Sets the pointer field for an ICMPv6 Parameter Problem message (Type 4). /// The pointer should indicate the offset within the invoking packet where the error was detected. #[inline] pub fn set_pointer(&mut self, pointer: u32) -> Result<(), IcmpError> { - if self.type_ != 4 { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_pointer_unchecked(pointer); + self.check_parameter_problem() + .map(|_| unsafe { self.set_pointer_unchecked(pointer) }) + } + + #[inline] + fn check_redirect(&self) -> Result<(), IcmpError> { + if self.type_ == 137 { + Ok(()) + } else { + Err(IcmpError::InvalidIcmpType) } - Ok(()) } /// Returns the 4-byte reserved field from an ICMPv6 Redirect message (Type 137). /// This field is currently unused and MUST be initialized to zeros by the sender. #[inline] pub fn redirect_reserved(&self) -> Result<[u8; 4], IcmpError> { - if self.type_ != 137 { - return Err(IcmpError::InvalidIcmpType); - } - Ok(unsafe { self.redirect_reserved_unchecked() }) + self.check_redirect() + .map(|_| unsafe { self.redirect_reserved_unchecked() }) } /// Sets the 4-byte reserved field for an ICMPv6 Redirect message (Type 137). /// This field is currently unused and MUST be set to zeros. #[inline] pub fn set_redirect_reserved(&mut self, reserved: [u8; 4]) -> Result<(), IcmpError> { - if self.type_ != 137 { - return Err(IcmpError::InvalidIcmpType); - } - unsafe { - self.set_redirect_reserved_unchecked(reserved); - } - Ok(()) + self.check_redirect() + .map(|_| unsafe { self.set_redirect_reserved_unchecked(reserved) }) } } impl IcmpV6Hdr { - /// Returns the identification field from ICMPv6 Echo Request/Reply messages. - /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). + /// Returns a reference to the ICMPv6 Echo Request/Reply data. /// /// # Safety - /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling. - /// Accessing echo fields with other types may result in undefined behavior. + /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling + /// this function. Accessing the echo fields with other ICMPv6 types may result in undefined + /// behavior. #[inline] - pub unsafe fn echo_id_unchecked(&self) -> u16 { - self.data.echo.id_unchecked() + pub unsafe fn echo_unchecked(&self) -> &IcmpEcho { + &*self.data.as_ptr().cast() } - /// Sets the identification field for ICMPv6 Echo Request/Reply messages. - /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). + /// Returns a mutable reference to the ICMPv6 Echo Request/Reply data. /// /// # Safety - /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling. - /// Accessing echo fields with other types may result in undefined behavior. + /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling + /// this function. Accessing the echo fields with other ICMPv6 types may result in undefined + /// behavior. #[inline] - pub unsafe fn set_echo_id_unchecked(&mut self, id: u16) { - self.data.echo.set_id_unchecked(id); - } - - /// Returns the sequence number from ICMPv6 Echo Request/Reply messages. - /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). - /// - /// # Safety - /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling. - /// Accessing echo fields with other types may result in undefined behavior. - #[inline] - pub unsafe fn echo_sequence_unchecked(&self) -> u16 { - self.data.echo.sequence_unchecked() - } - - /// Sets the sequence number for ICMPv6 Echo Request/Reply messages. - /// Only valid for ICMPv6 Types: 128 (Echo Request), 129 (Echo Reply). - /// - /// # Safety - /// Caller must ensure ICMPv6 type is 128 (Echo Request) or 129 (Echo Reply) before calling. - /// Accessing echo fields with other types may result in undefined behavior. - #[inline] - pub unsafe fn set_echo_sequence_unchecked(&mut self, sequence: u16) { - self.data.echo.set_sequence_unchecked(sequence); + pub unsafe fn echo_mut_unchecked(&mut self) -> &mut IcmpEcho { + &mut *self.data.as_mut_ptr().cast() } /// Returns the MTU field from an ICMPv6 Packet Too Big message (Type 2). @@ -1218,7 +1074,7 @@ impl IcmpV6Hdr { /// Accessing MTU field with other types may result in undefined behavior. #[inline] pub unsafe fn mtu_unchecked(&self) -> u32 { - self.data.mtu_unchecked() + u32::from_be_bytes(self.data) } /// Sets the MTU field for an ICMPv6 Packet Too Big message (Type 2). @@ -1229,7 +1085,7 @@ impl IcmpV6Hdr { /// Accessing MTU field with other types may result in undefined behavior. #[inline] pub unsafe fn set_mtu_unchecked(&mut self, mtu: u32) { - self.data.set_mtu_unchecked(mtu); + self.data = mtu.to_be_bytes(); } /// Returns the pointer field from an ICMPv6 Parameter Problem message (Type 4). @@ -1240,7 +1096,7 @@ impl IcmpV6Hdr { /// Accessing pointer field with other types may result in undefined behavior. #[inline] pub unsafe fn pointer_unchecked(&self) -> u32 { - self.data.pointer_unchecked() + u32::from_be_bytes(self.data) } /// Sets the pointer field for an ICMPv6 Parameter Problem message (Type 4). @@ -1251,7 +1107,7 @@ impl IcmpV6Hdr { /// Accessing pointer field with other types may result in undefined behavior. #[inline] pub unsafe fn set_pointer_unchecked(&mut self, pointer: u32) { - self.data.set_pointer_unchecked(pointer); + self.data = pointer.to_be_bytes(); } /// Returns the 4-byte reserved field from an ICMPv6 Redirect message (Type 137). @@ -1262,7 +1118,7 @@ impl IcmpV6Hdr { /// Accessing redirect fields with other types may result in undefined behavior. #[inline] pub unsafe fn redirect_reserved_unchecked(&self) -> [u8; 4] { - self.data.reserved + self.data } /// Sets the 4-byte reserved field for an ICMPv6 Redirect message (Type 137). @@ -1273,53 +1129,7 @@ impl IcmpV6Hdr { /// Accessing redirect fields with other types may result in undefined behavior. #[inline] pub unsafe fn set_redirect_reserved_unchecked(&mut self, reserved: [u8; 4]) { - self.data.reserved = reserved; - } -} - -#[cfg(feature = "serde")] -mod serde_impl { - use super::{IcmpDataUn, IcmpV6DataUn}; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - impl Serialize for IcmpDataUn { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = unsafe { self.reserved }; - <[u8; 4]>::serialize(&bytes, serializer) - } - } - - impl<'de> Deserialize<'de> for IcmpDataUn { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let bytes = <[u8; 4]>::deserialize(deserializer)?; - Ok(IcmpDataUn { reserved: bytes }) - } - } - - impl Serialize for IcmpV6DataUn { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = unsafe { self.reserved }; - <[u8; 4]>::serialize(&bytes, serializer) - } - } - - impl<'de> Deserialize<'de> for IcmpV6DataUn { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let bytes = <[u8; 4]>::deserialize(deserializer)?; - Ok(IcmpV6DataUn { reserved: bytes }) - } + self.data = reserved; } } @@ -1342,9 +1152,7 @@ mod tests { type_: 0, code: 0, check: [0, 0], - data: IcmpDataUn { - reserved: [0, 0, 0, 0], - }, + data: [0, 0, 0, 0], } } @@ -1374,23 +1182,19 @@ mod tests { // Test echo ID let test_id: u16 = 0x4321; - hdr.set_echo_id(test_id).unwrap(); - assert_eq!(hdr.echo_id().unwrap(), test_id); + hdr.echo_mut().unwrap().set_id(test_id); + assert_eq!(hdr.echo().unwrap().id(), test_id); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.echo.id, test_id.to_be_bytes()); - } + assert_eq!(hdr.data[..2], test_id.to_be_bytes()); // Test echo sequence let test_seq: u16 = 0x8765; - hdr.set_echo_sequence(test_seq).unwrap(); - assert_eq!(hdr.echo_sequence().unwrap(), test_seq); + hdr.echo_mut().unwrap().set_sequence(test_seq); + assert_eq!(hdr.echo().unwrap().sequence(), test_seq); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.echo.sequence, test_seq.to_be_bytes()); - } + assert_eq!(hdr.data[2..], test_seq.to_be_bytes()); } #[test] @@ -1404,9 +1208,7 @@ mod tests { assert_eq!(hdr.gateway_address().unwrap(), test_addr); // Verify the raw bytes - unsafe { - assert_eq!(hdr.data.redirect, [192, 168, 1, 1]); - } + assert_eq!(hdr.data, [192, 168, 1, 1]); } #[test] @@ -1416,13 +1218,11 @@ mod tests { hdr.type_ = 3; let test_mtu: u16 = 1500; - hdr.set_next_hop_mtu(test_mtu).unwrap(); - assert_eq!(hdr.next_hop_mtu().unwrap(), test_mtu); + hdr.destination_unreachable_mut().unwrap().set_mtu(test_mtu); + assert_eq!(hdr.destination_unreachable().unwrap().mtu(), test_mtu); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.dst_unreachable.mtu, test_mtu.to_be_bytes()); - } + assert_eq!(hdr.data[2..], test_mtu.to_be_bytes()); } #[test] @@ -1432,13 +1232,13 @@ mod tests { hdr.type_ = 12; let test_pointer: u8 = 42; - hdr.set_parameter_pointer(test_pointer).unwrap(); - assert_eq!(hdr.parameter_pointer().unwrap(), test_pointer); + hdr.parameter_problem_mut() + .unwrap() + .set_pointer(test_pointer); + assert_eq!(hdr.parameter_problem().unwrap().pointer(), test_pointer); // Verify the raw byte - unsafe { - assert_eq!(hdr.data.param_problem.pointer, test_pointer); - } + assert_eq!(hdr.data[0], test_pointer); } #[test] @@ -1448,13 +1248,11 @@ mod tests { hdr.type_ = 30; let test_id: u16 = 0x9876; - hdr.set_traceroute_id(test_id).unwrap(); - assert_eq!(hdr.traceroute_id().unwrap(), test_id); + hdr.traceroute_mut().unwrap().set_id(test_id); + assert_eq!(hdr.traceroute().unwrap().id(), test_id); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.traceroute.id, test_id.to_be_bytes()); - } + assert_eq!(hdr.data[..2], test_id.to_be_bytes()); } #[test] @@ -1464,13 +1262,11 @@ mod tests { hdr.type_ = 40; let test_spi: u16 = 0xFEDC; - hdr.set_photuris_spi(test_spi).unwrap(); - assert_eq!(hdr.photuris_spi().unwrap(), test_spi); + hdr.photuris_mut().unwrap().set_reserved_spi(test_spi); + assert_eq!(hdr.photuris().unwrap().reserved_spi(), test_spi); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.photuris.reserved_spi, test_spi.to_be_bytes()); - } + assert_eq!(hdr.data[..2], test_spi.to_be_bytes()); } #[test] @@ -1480,13 +1276,11 @@ mod tests { hdr.type_ = 40; let test_pointer: u16 = 0x1A2B; - hdr.set_photuris_pointer(test_pointer).unwrap(); - assert_eq!(hdr.photuris_pointer().unwrap(), test_pointer); + hdr.photuris_mut().unwrap().set_pointer(test_pointer); + assert_eq!(hdr.photuris().unwrap().pointer(), test_pointer); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.photuris.pointer, test_pointer.to_be_bytes()); - } + assert_eq!(hdr.data[2..], test_pointer.to_be_bytes()); } #[test] @@ -1507,34 +1301,6 @@ mod tests { assert_eq!(hdr.code, 1); } - #[test] - fn test_union_field_access_safety() { - // This test demonstrates that different union field accesses - // manipulate the same memory - let mut hdr = create_test_icmp_hdr(); - - // Set type to Echo Reply (0) which is valid for echo_id - hdr.type_ = 0; - - // Set echo ID and verify the memory is shared with redirect - hdr.set_echo_id(0xABCD).unwrap(); - - unsafe { - assert_eq!(hdr.data.redirect[0], 0xAB); - assert_eq!(hdr.data.redirect[1], 0xCD); - } - - // Set type to Redirect (5) which is valid for gateway_address - hdr.type_ = 5; - - // Set gateway address and verify it affects the echo ID - hdr.set_gateway_address(Ipv4Addr::new(1, 2, 3, 4)).unwrap(); - - // Set type back to Echo Reply (0) to check echo_id - hdr.type_ = 0; - assert_eq!(hdr.echo_id().unwrap(), 0x0102); // First two bytes of the IP address - } - #[test] fn test_icmp_hdr_bitwise_operations() { // This test covers operations that might be common in packet processing @@ -1582,14 +1348,16 @@ mod tests { hdr.set_checksum(0); // Would be calculated later based on the entire message // Echo Request (type 8) is valid for echo_id and echo_sequence - hdr.set_echo_id(0x1234).unwrap(); - hdr.set_echo_sequence(0x5678).unwrap(); + let echo = hdr.echo_mut().unwrap(); + echo.set_id(0x1234); + echo.set_sequence(0x5678); assert_eq!(hdr.type_, 8); assert_eq!(hdr.code, 0); assert_eq!(hdr.checksum(), 0); - assert_eq!(hdr.echo_id().unwrap(), 0x1234); - assert_eq!(hdr.echo_sequence().unwrap(), 0x5678); + let echo = hdr.echo().unwrap(); + assert_eq!(echo.id(), 0x1234); + assert_eq!(echo.sequence(), 0x5678); } #[test] @@ -1602,12 +1370,12 @@ mod tests { hdr.set_checksum(0); // Would be calculated later // Destination Unreachable (type 3) is valid for next_hop_mtu - hdr.set_next_hop_mtu(1400).unwrap(); // Example MTU value + hdr.destination_unreachable_mut().unwrap().set_mtu(1400); // Example MTU value assert_eq!(hdr.type_, 3); assert_eq!(hdr.code, 4); assert_eq!(hdr.checksum(), 0); - assert_eq!(hdr.next_hop_mtu().unwrap(), 1400); + assert_eq!(hdr.destination_unreachable().unwrap().mtu(), 1400); } #[test] @@ -1620,12 +1388,12 @@ mod tests { hdr.set_checksum(0); // Would be calculated later // Parameter Problem (type 12) is valid for parameter_pointer - hdr.set_parameter_pointer(20).unwrap(); // Error at byte offset 20 + hdr.parameter_problem_mut().unwrap().set_pointer(20); // Error at byte offset 20 assert_eq!(hdr.type_, 12); assert_eq!(hdr.code, 0); assert_eq!(hdr.checksum(), 0); - assert_eq!(hdr.parameter_pointer().unwrap(), 20); + assert_eq!(hdr.parameter_problem().unwrap().pointer(), 20); } #[test] @@ -1933,9 +1701,7 @@ mod tests { type_: 0, code: 0, check: [0, 0], - data: IcmpV6DataUn { - reserved: [0, 0, 0, 0], - }, + data: [0, 0, 0, 0], } } @@ -1969,9 +1735,8 @@ mod tests { assert_eq!(hdr.echo_id().unwrap(), test_id); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.echo.id, test_id.to_be_bytes()); - } + let test_id_bytes = test_id.to_be_bytes(); + assert_eq!(&hdr.data[..2], &test_id_bytes); // Test echo sequence let test_seq: u16 = 0x8765; @@ -1979,9 +1744,8 @@ mod tests { assert_eq!(hdr.echo_sequence().unwrap(), test_seq); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.echo.sequence, test_seq.to_be_bytes()); - } + let test_seq_bytes = test_seq.to_be_bytes(); + assert_eq!(&hdr.data[2..], &test_seq_bytes); } #[test] @@ -1995,23 +1759,17 @@ mod tests { assert_eq!(hdr.mtu().unwrap(), test_mtu); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.packet_too_big_mtu, test_mtu.to_be_bytes()); - } + assert_eq!(hdr.data, test_mtu.to_be_bytes()); // Test with zero hdr.set_mtu(0).unwrap(); assert_eq!(hdr.mtu().unwrap(), 0); - unsafe { - assert_eq!(hdr.data.packet_too_big_mtu, [0, 0, 0, 0]); - } + assert_eq!(hdr.data, [0, 0, 0, 0]); // Test with max value hdr.set_mtu(u32::MAX).unwrap(); assert_eq!(hdr.mtu().unwrap(), u32::MAX); - unsafe { - assert_eq!(hdr.data.packet_too_big_mtu, [0xFF, 0xFF, 0xFF, 0xFF]); - } + assert_eq!(hdr.data, [0xFF, 0xFF, 0xFF, 0xFF]); } #[test] @@ -2025,23 +1783,17 @@ mod tests { assert_eq!(hdr.pointer().unwrap(), test_pointer); // Verify byte order in raw storage - unsafe { - assert_eq!(hdr.data.param_problem_pointer, test_pointer.to_be_bytes()); - } + assert_eq!(hdr.data, test_pointer.to_be_bytes()); // Test with zero hdr.set_pointer(0).unwrap(); assert_eq!(hdr.pointer().unwrap(), 0); - unsafe { - assert_eq!(hdr.data.param_problem_pointer, [0, 0, 0, 0]); - } + assert_eq!(hdr.data, [0, 0, 0, 0]); // Test with max value hdr.set_pointer(u32::MAX).unwrap(); assert_eq!(hdr.pointer().unwrap(), u32::MAX); - unsafe { - assert_eq!(hdr.data.param_problem_pointer, [0xFF, 0xFF, 0xFF, 0xFF]); - } + assert_eq!(hdr.data, [0xFF, 0xFF, 0xFF, 0xFF]); } #[test] @@ -2053,9 +1805,7 @@ mod tests { type_: 137, code: 0, check: [0, 0], - data: IcmpV6DataUn { - reserved: [0, 0, 0, 0], - }, + data: [0, 0, 0, 0], }, target_address: [0; 16], destination_address: [0; 16], @@ -2211,10 +1961,17 @@ mod serde_prop_tests { type_, code, check, - data: IcmpDataUn { reserved: data }, + data, }) } + fn echo_bytes(id: [u8; 2], seq: [u8; 2]) -> [u8; 4] { + let mut bytes = [0u8; 4]; + bytes[..2].copy_from_slice(&id); + bytes[2..].copy_from_slice(&seq); + bytes + } + fn icmpv6_hdr_strategy() -> impl Strategy { let echo = ( Just(128u8), @@ -2227,9 +1984,7 @@ mod serde_prop_tests { type_, code, check, - data: IcmpV6DataUn { - echo: IcmpEcho { id, sequence: seq }, - }, + data: echo_bytes(id, seq), }); let echo_reply = ( @@ -2243,9 +1998,7 @@ mod serde_prop_tests { type_, code, check, - data: IcmpV6DataUn { - echo: IcmpEcho { id, sequence: seq }, - }, + data: echo_bytes(id, seq), }); let packet_too_big = ( @@ -2258,9 +2011,7 @@ mod serde_prop_tests { type_, code, check, - data: IcmpV6DataUn { - packet_too_big_mtu: bytes, - }, + data: bytes, }); let param_problem = ( @@ -2273,9 +2024,7 @@ mod serde_prop_tests { type_, code, check, - data: IcmpV6DataUn { - param_problem_pointer: bytes, - }, + data: bytes, }); let redirect = ( @@ -2288,7 +2037,7 @@ mod serde_prop_tests { type_, code, check, - data: IcmpV6DataUn { reserved }, + data: reserved, }); let fallback = ( @@ -2303,7 +2052,7 @@ mod serde_prop_tests { type_, code, check, - data: IcmpV6DataUn { reserved: bytes }, + data: bytes, }); prop_oneof![ @@ -2329,7 +2078,7 @@ mod serde_prop_tests { type_: 137, code, check, - data: IcmpV6DataUn { reserved }, + data: reserved, }, target_address: target, destination_address: dest, @@ -2348,13 +2097,13 @@ mod serde_prop_tests { prop_assert_eq!(decoded.type_, hdr.type_); prop_assert_eq!(decoded.code, hdr.code); prop_assert_eq!(decoded.check, hdr.check); - unsafe { prop_assert_eq!(decoded.data.reserved, hdr.data.reserved); } + prop_assert_eq!(decoded.data, hdr.data); let decoded_cbor = round_trip_cbor(&hdr); prop_assert_eq!(decoded_cbor.type_, hdr.type_); prop_assert_eq!(decoded_cbor.code, hdr.code); prop_assert_eq!(decoded_cbor.check, hdr.check); - unsafe { prop_assert_eq!(decoded_cbor.data.reserved, hdr.data.reserved); } + prop_assert_eq!(decoded_cbor.data, hdr.data); } #[test] @@ -2363,35 +2112,13 @@ mod serde_prop_tests { prop_assert_eq!(decoded.type_, hdr.type_); prop_assert_eq!(decoded.code, hdr.code); prop_assert_eq!(decoded.check, hdr.check); + prop_assert_eq!(decoded.data, hdr.data); let decoded_cbor = round_trip_cbor(&hdr); prop_assert_eq!(decoded_cbor.type_, hdr.type_); prop_assert_eq!(decoded_cbor.code, hdr.code); prop_assert_eq!(decoded_cbor.check, hdr.check); - - match hdr.type_ { - 128 | 129 => unsafe { - prop_assert_eq!(decoded.data.echo.id, hdr.data.echo.id); - prop_assert_eq!(decoded.data.echo.sequence, hdr.data.echo.sequence); - prop_assert_eq!(decoded_cbor.data.echo.id, hdr.data.echo.id); - prop_assert_eq!(decoded_cbor.data.echo.sequence, hdr.data.echo.sequence); - }, - 2 => unsafe { - prop_assert_eq!(decoded.data.packet_too_big_mtu, hdr.data.packet_too_big_mtu); - prop_assert_eq!(decoded_cbor.data.packet_too_big_mtu, hdr.data.packet_too_big_mtu); - }, - 4 => unsafe { - prop_assert_eq!(decoded.data.param_problem_pointer, hdr.data.param_problem_pointer); - prop_assert_eq!( - decoded_cbor.data.param_problem_pointer, - hdr.data.param_problem_pointer - ); - }, - _ => unsafe { - prop_assert_eq!(decoded.data.reserved, hdr.data.reserved); - prop_assert_eq!(decoded_cbor.data.reserved, hdr.data.reserved); - }, - } + prop_assert_eq!(decoded_cbor.data, hdr.data); } #[test] @@ -2400,13 +2127,17 @@ mod serde_prop_tests { prop_assert_eq!(decoded.hdr.type_, msg.hdr.type_); prop_assert_eq!(decoded.hdr.code, msg.hdr.code); prop_assert_eq!(decoded.hdr.check, msg.hdr.check); - unsafe { prop_assert_eq!(decoded.hdr.data.reserved, msg.hdr.data.reserved); } + prop_assert_eq!(decoded.hdr.data, msg.hdr.data); + prop_assert_eq!(decoded.target_address, msg.target_address); + prop_assert_eq!(decoded.destination_address, msg.destination_address); let decoded_cbor = round_trip_cbor(&msg); prop_assert_eq!(decoded_cbor.hdr.type_, msg.hdr.type_); prop_assert_eq!(decoded_cbor.hdr.code, msg.hdr.code); prop_assert_eq!(decoded_cbor.hdr.check, msg.hdr.check); - unsafe { prop_assert_eq!(decoded_cbor.hdr.data.reserved, msg.hdr.data.reserved); } + prop_assert_eq!(decoded_cbor.hdr.data, msg.hdr.data); + prop_assert_eq!(decoded_cbor.target_address, msg.target_address); + prop_assert_eq!(decoded_cbor.destination_address, msg.destination_address); } } } From e1902523e549dac8dcec494943fe4bb4cf5a393f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20DEROIDE?= Date: Tue, 7 Oct 2025 16:25:09 +0200 Subject: [PATCH 2/3] Align all headers or L3 and higher protocols --- src/arp.rs | 2 +- src/geneve.rs | 2 +- src/icmp.rs | 20 ++++++++++---------- src/ip.rs | 4 ++-- src/llc.rs | 2 +- src/mpls.rs | 2 +- src/sctp.rs | 2 +- src/tcp.rs | 2 +- src/udp.rs | 2 +- src/vxlan.rs | 2 +- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/arp.rs b/src/arp.rs index 68cf78b..5de3cb4 100644 --- a/src/arp.rs +++ b/src/arp.rs @@ -7,7 +7,7 @@ use crate::{getter_be, setter_be}; /// The ARP header is typically found after the Ethernet header and is used to /// map a network protocol address (like an IPv4 address) to a hardware /// address (like a MAC address). -#[repr(C)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ArpHdr { diff --git a/src/geneve.rs b/src/geneve.rs index c62b811..4f1286a 100644 --- a/src/geneve.rs +++ b/src/geneve.rs @@ -3,7 +3,7 @@ use crate::{getter_be, setter_be}; /// Represents a Geneve (Generic Network Virtualization Encapsulation) header, according to RFC 8926. /// Geneve is an encapsulation protocol designed for network virtualization. /// -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone, Default)] pub struct GeneveHdr { diff --git a/src/icmp.rs b/src/icmp.rs index 4a6fbf0..2426330 100644 --- a/src/icmp.rs +++ b/src/icmp.rs @@ -44,7 +44,7 @@ pub enum IcmpError { /// /// The `data` field contains type-specific data such as echo identifiers/sequence numbers, /// redirect gateway addresses, or pointers to errors in received packets. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpHdr { @@ -395,7 +395,7 @@ impl IcmpHdr { /// - 18: Address Mask Reply (deprecated) /// - 37: Domain Name Request (deprecated) /// - 38: Domain Name Reply (deprecated) -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpEcho { @@ -438,7 +438,7 @@ impl IcmpEcho { /// For ICMP Type 3 "Destination Unreachable" Message (RFC 792) with support for PMTUD (RFC 1191) /// Contains 2 unused bytes followed by a Next-Hop MTU field indicating the maximum transmission unit /// of the next-hop network on which fragmentation is required. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpDstUnreachable { @@ -467,7 +467,7 @@ impl IcmpDstUnreachable { /// For ICMP Type 12 "Parameter Problem" Message (RFC 792) /// Contains a pointer to the byte in the original datagram that caused the error /// and 3 bytes of unused padding to make the field a total of 4 bytes. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpParamProblem { @@ -491,7 +491,7 @@ impl IcmpParamProblem { /// Contains 2 "Reserved" bytes followed by the Security Parameters Index used /// for a security association between two peers. Also includes a 2-byte pointer /// field indicating where in the message the error was detected. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpHdrPhoturis { @@ -525,7 +525,7 @@ impl IcmpHdrPhoturis { /// Contains a 16-bit ID Number field used by the source to match responses to outgoing requests /// followed by 2 unused bytes to make a total of 4 bytes. The ID Number helps match Reply messages /// (type 31) to their corresponding Requests. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpTraceroute { @@ -630,7 +630,7 @@ impl IcmpTraceroute { /// Ok(0) /// } /// ``` -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpTimestampMsgPart { @@ -738,7 +738,7 @@ impl IcmpTimestampMsgPart { /// Ok(0) /// } /// ``` -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpTracerouteMsgPart { @@ -827,7 +827,7 @@ impl IcmpTracerouteMsgPart { /// /// The `data` field contains type-specific data such as echo identifiers/sequence numbers, /// MTU values, or pointers to errors in received packets. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpV6Hdr { @@ -839,7 +839,7 @@ pub struct IcmpV6Hdr { /// Full ICMPv6 Redirect message as defined in RFC 4443 section 4.5. /// Combines the base ICMPv6 header with the target and destination addresses. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IcmpV6RedirectMsg { diff --git a/src/ip.rs b/src/ip.rs index 3eb3569..402a7f3 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -10,7 +10,7 @@ pub enum IpHdr { } /// IPv4 header, which is present after the Ethernet header. -#[repr(C)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Ipv4Hdr { @@ -166,7 +166,7 @@ impl Ipv4Hdr { } /// IPv6 header, which is present after the Ethernet header. -#[repr(C)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Ipv6Hdr { diff --git a/src/llc.rs b/src/llc.rs index b62b641..5770654 100644 --- a/src/llc.rs +++ b/src/llc.rs @@ -1,7 +1,7 @@ use core::mem; /// Represents Logical Link Control according to ISO/IEC 8802-2 Definition -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LlcHdr { diff --git a/src/mpls.rs b/src/mpls.rs index e29e39d..d006f0a 100644 --- a/src/mpls.rs +++ b/src/mpls.rs @@ -4,7 +4,7 @@ use core::mem; /// https://www.rfc-editor.org/rfc/rfc3032.html. /// This header format applies to all MPLS messages. /// 20 bits for Label - 3 for TC - 1 for S - 8 for TTL -#[repr(C, packed)] +#[repr(C)] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Mpls { diff --git a/src/sctp.rs b/src/sctp.rs index 57c9b05..c15ec4d 100644 --- a/src/sctp.rs +++ b/src/sctp.rs @@ -1,6 +1,6 @@ use core::mem; -#[repr(C)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SctpHdr { diff --git a/src/tcp.rs b/src/tcp.rs index ae72a2f..7ec2c41 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -5,7 +5,7 @@ use crate::bitfield::BitfieldUnit; pub const TCP_HDR_LEN: usize = mem::size_of::(); /// TCP header, which is present after the IP header. -#[repr(C)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TcpHdr { diff --git a/src/udp.rs b/src/udp.rs index b99db4d..f2e22a6 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -24,7 +24,7 @@ use crate::{getter_be, setter_be}; /// udp_header.set_len(28); // 8 bytes header + 20 bytes payload /// udp_header.set_checksum(0); // Checksum calculation would be done separately /// ``` -#[repr(C)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct UdpHdr { diff --git a/src/vxlan.rs b/src/vxlan.rs index b89ead2..27429fd 100644 --- a/src/vxlan.rs +++ b/src/vxlan.rs @@ -6,7 +6,7 @@ use core::mem; /// Uses a 24-bit VXLAN Network Identifier (VNI) for traffic segregation. /// Header length: 8 bytes. /// Reference: RFC 7348. -#[repr(C, packed)] +#[repr(C, align(4))] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct VxlanHdr { From ac41cfccfd46fd7c4b3e76e76daa2fdad3819b4f Mon Sep 17 00:00:00 2001 From: vad Date: Fri, 6 Feb 2026 13:48:40 +0100 Subject: [PATCH 3/3] perf: Don't call `read_unaligned` in `getter_be` It unnecessarily adds a redundant copy, especially on little-endian targets, where call to `swap_bytes` (that calls the `bswap` intrinsic) already makes an another copy. --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a5ee901..d30ce06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,14 +29,14 @@ pub mod vxlan; #[macro_export] macro_rules! getter_be { ($self:expr, $field:ident, $ty:ty) => { - core::ptr::read_unaligned(core::ptr::addr_of!($self.$field).cast::<$ty>()) + *core::ptr::addr_of!($self.$field).cast::<$ty>() }; } #[cfg(target_endian = "little")] #[macro_export] macro_rules! getter_be { ($self:expr, $field:ident, $ty:ty) => { - core::ptr::read_unaligned(core::ptr::addr_of!($self.$field).cast::<$ty>()).swap_bytes() + (&*core::ptr::addr_of!($self.$field).cast::<$ty>()).swap_bytes() }; }