use arrayvec::ArrayVec; use err::arp::{ArpHwAddrError, ArpNewError, ArpProtoAddrError}; use crate::*; use core::mem::MaybeUninit; /// "Address Resolution Protocol" Packet. #[derive(Clone)] pub struct ArpPacket { /// Network link protocol type (e.g. `ArpHardwareId::ETHERNET`). pub hw_addr_type: ArpHardwareId, /// Protocol for which the ARP request is intended (e.g. `EtherType::IPV4`). pub proto_addr_type: EtherType, /// Length (in octets) of a hardware address (e.g. 6 for Ethernet). hw_addr_size: u8, /// Length (in octets) of internetwork addresses (e.g. 4 for IPv4 or 16 for IPv6). proto_addr_size: u8, /// Specifies the operation that the sender is performing pub operation: ArpOperation, /// Buffer containing the sender hardware address (e.g. MAC address). sender_hw_addr_buf: [MaybeUninit; 0xff], /// Buffer containing the sender protocol address (e.g. IPv4 address). sender_protocol_addr_buf: [MaybeUninit; 0xff], /// Buffer containing the target hardware address (e.g. MAC address). target_hw_addr_buf: [MaybeUninit; 0xff], /// Buffer containing the target protocol address (e.g. IPv4 address).. target_protocol_addr_buf: [MaybeUninit; 0xff], } impl ArpPacket { /// Maximum length of an ARP packet in bytes/octets. /// /// This number is calculated by taking the maximum values /// that `hw_addr_size`(255/u8::MAX) & `proto_addr_size` (255/u8::MAX) /// can take and calculate the maximum packet size from that. pub const MAX_LEN: usize = 8 + 2 * 255 + 2 * 255; /// Create a new ARP packet with the given values. pub const fn new( hw_addr_type: ArpHardwareId, proto_addr_type: EtherType, operation: ArpOperation, sender_hw_addr: &[u8], sender_protocol_addr: &[u8], target_hw_addr: &[u8], target_protocol_addr: &[u8], ) -> Result { if sender_hw_addr.len() != target_hw_addr.len() { return Err(ArpNewError::HwAddr(ArpHwAddrError::LenNonMatching( sender_hw_addr.len(), target_hw_addr.len(), ))); } if sender_protocol_addr.len() != target_protocol_addr.len() { return Err(ArpNewError::ProtoAddr(ArpProtoAddrError::LenNonMatching( sender_protocol_addr.len(), target_protocol_addr.len(), ))); } if sender_hw_addr.len() > 255 { return Err(ArpNewError::HwAddr(ArpHwAddrError::LenTooBig( sender_hw_addr.len(), ))); } if sender_protocol_addr.len() > 255 { return Err(ArpNewError::ProtoAddr(ArpProtoAddrError::LenTooBig( sender_protocol_addr.len(), ))); } Ok(ArpPacket { hw_addr_type, proto_addr_type, // cast ok as we verfied the len to be less equal then 255. hw_addr_size: sender_hw_addr.len() as u8, // cast ok as we verfied the len to be less equal then 255. proto_addr_size: sender_protocol_addr.len() as u8, operation, sender_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * sender_hw_addr.len() is guranteed to be <= 255 (checked in if above) // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_hw_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, sender_hw_addr.len(), ); } buf }, sender_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * sender_protocol_addr.len() is guranteed to be <= 255 (checked in if above) // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_protocol_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, sender_protocol_addr.len(), ); } buf }, target_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * target_hw_addr.len() is guranteed to be <= 255 (checked in if above) // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_hw_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, target_hw_addr.len(), ); } buf }, target_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * target_protocol_addr.len() is guranteed to be <= 255 (checked in if above) // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_protocol_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, target_protocol_addr.len(), ); } buf }, }) } /// Create a new ARP packet with the given values without checking /// hardware & protocol address sizes. /// /// # Safety /// /// The caller must gurantee that /// /// * `sender_hw_addr` & `target_hw_addr` have the same length and the length must be smaller or equal than 255. /// * `sender_protocol_addr` & `target_protocol_addr` have the same length and the length must be smaller or equal than 255. /// /// The gurantees the caller must fullfill are equal to the following /// preconditions: /// /// * `sender_hw_addr.len() == target_hw_addr.len()` /// * `sender_hw_addr.len() <= 255` /// * `target_hw_addr.len() <= 255` /// * `sender_protocol_addr.len() == target_protocol_addr.len()` /// * `sender_protocol_addr.len() <= 255` /// * `target_protocol_addr.len() <= 255` pub const unsafe fn new_unchecked( hw_addr_type: ArpHardwareId, proto_addr_type: EtherType, operation: ArpOperation, sender_hw_addr: &[u8], sender_protocol_addr: &[u8], target_hw_addr: &[u8], target_protocol_addr: &[u8], ) -> ArpPacket { debug_assert!(sender_hw_addr.len() == target_hw_addr.len()); debug_assert!(sender_protocol_addr.len() == target_protocol_addr.len()); debug_assert!(sender_hw_addr.len() <= 255); debug_assert!(sender_protocol_addr.len() <= 255); ArpPacket { hw_addr_type, proto_addr_type, // cast ok as we verfied the len to be less equal then 255. hw_addr_size: sender_hw_addr.len() as u8, // cast ok as we verfied the len to be less equal then 255. proto_addr_size: sender_protocol_addr.len() as u8, operation, sender_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * the caller must gurantee that sender_hw_addr.len() is <= 255 // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_hw_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, sender_hw_addr.len(), ); } buf }, sender_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * the caller must gurantee that sender_protocol_addr.len() is <= 255 // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_protocol_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, sender_protocol_addr.len(), ); } buf }, target_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * the caller must gurantee that target_hw_addr.len() is <= 255 // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_hw_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, target_hw_addr.len(), ); } buf }, target_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as // * the caller must gurantee that target_protocol_addr.len() is <= 255 // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_protocol_addr.as_ptr(), buf.as_mut_ptr() as *mut u8, target_protocol_addr.len(), ); } buf }, } } /// Reads an ARP packet from a slice. pub fn from_slice(slice: &[u8]) -> Result { ArpPacketSlice::from_slice(slice).map(|v| v.to_packet()) } /// Length (in octets) of a hardware address (e.g. 6 for Ethernet). #[inline] pub const fn hw_addr_size(&self) -> u8 { self.hw_addr_size } /// Length (in octets) of internetwork addresses (e.g. 4 for IPv4 or 16 for IPv6). #[inline] pub const fn protocol_addr_size(&self) -> u8 { self.proto_addr_size } /// Sender hardware address (e.g. MAC address). #[inline] pub const fn sender_hw_addr(&self) -> &[u8] { unsafe { core::slice::from_raw_parts( self.sender_hw_addr_buf.as_ptr() as *const u8, self.hw_addr_size as usize, ) } } /// Sender protocol address (e.g. IPv4 address). #[inline] pub const fn sender_protocol_addr(&self) -> &[u8] { unsafe { core::slice::from_raw_parts( self.sender_protocol_addr_buf.as_ptr() as *const u8, self.proto_addr_size as usize, ) } } /// Target hardware address (e.g. MAC address). #[inline] pub const fn target_hw_addr(&self) -> &[u8] { unsafe { core::slice::from_raw_parts( self.target_hw_addr_buf.as_ptr() as *const u8, self.hw_addr_size as usize, ) } } /// Target protocol address (e.g. IPv4 address). #[inline] pub const fn target_protocol_addr(&self) -> &[u8] { unsafe { core::slice::from_raw_parts( self.target_protocol_addr_buf.as_ptr() as *const u8, self.proto_addr_size as usize, ) } } /// Set the sender & target hardware addresses (e.g. MAC address). #[inline] pub const fn set_hw_addrs( &mut self, sender_hw_addr: &[u8], target_hw_addr: &[u8], ) -> Result<(), ArpHwAddrError> { if sender_hw_addr.len() != target_hw_addr.len() { return Err(ArpHwAddrError::LenNonMatching( sender_hw_addr.len(), target_hw_addr.len(), )); } if sender_hw_addr.len() > 255 { return Err(ArpHwAddrError::LenTooBig(sender_hw_addr.len())); } { // SAFETY: Safe as // * the caller must gurantee that sender_hw_addr.len() is <= 255 // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_hw_addr.as_ptr(), self.sender_hw_addr_buf.as_mut_ptr() as *mut u8, sender_hw_addr.len(), ); } } { // SAFETY: Safe as // * the caller must gurantee that target_hw_addr.len() is <= 255 // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_hw_addr.as_ptr(), self.target_hw_addr_buf.as_mut_ptr() as *mut u8, target_hw_addr.len(), ); } } self.hw_addr_size = sender_hw_addr.len() as u8; Ok(()) } /// Set the sender & target protocol addresses (e.g. IPv4 address). #[inline] pub const fn set_protocol_addrs( &mut self, sender_protocol_addr: &[u8], target_protocol_addr: &[u8], ) -> Result<(), ArpProtoAddrError> { if sender_protocol_addr.len() != target_protocol_addr.len() { return Err(ArpProtoAddrError::LenNonMatching( sender_protocol_addr.len(), target_protocol_addr.len(), )); } if sender_protocol_addr.len() > 255 { return Err(ArpProtoAddrError::LenTooBig(sender_protocol_addr.len())); } { // SAFETY: Safe as // * sender_protocol_addr.len() is guranteed to be <= 255 (checked in if above) // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_protocol_addr.as_ptr(), self.sender_protocol_addr_buf.as_mut_ptr() as *mut u8, sender_protocol_addr.len(), ); } } { // SAFETY: Safe as // * target_protocol_addr.len() is guranteed to be <= 255 (checked in if above) // * memory areas guranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_protocol_addr.as_ptr(), self.target_protocol_addr_buf.as_mut_ptr() as *mut u8, target_protocol_addr.len(), ); } } self.proto_addr_size = sender_protocol_addr.len() as u8; Ok(()) } /// Serialized length of this ARP packet. #[inline] pub fn packet_len(&self) -> usize { 8 + usize::from(self.hw_addr_size) * 2 + usize::from(self.proto_addr_size) * 2 } /// Returns the serialized header. #[inline] pub fn to_bytes(&self) -> ArrayVec { let hw_addr_type = self.hw_addr_type.0.to_be_bytes(); let proto_addr_type = self.proto_addr_type.0.to_be_bytes(); let operation = self.operation.0.to_be_bytes(); let mut result = ArrayVec::::new_const(); result.extend([ hw_addr_type[0], hw_addr_type[1], proto_addr_type[0], proto_addr_type[1], self.hw_addr_size, self.proto_addr_size, operation[0], operation[1], ]); result.try_extend_from_slice(self.sender_hw_addr()).unwrap(); result .try_extend_from_slice(self.sender_protocol_addr()) .unwrap(); result.try_extend_from_slice(self.target_hw_addr()).unwrap(); result .try_extend_from_slice(self.target_protocol_addr()) .unwrap(); result } /// Writes the header to the given writer. #[cfg(feature = "std")] pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { writer.write_all(&self.to_bytes())?; Ok(()) } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn read( reader: &mut T, ) -> Result { let mut start = [0u8; 8]; reader.read_exact(&mut start[..])?; let mut result = ArpPacket { hw_addr_type: ArpHardwareId(u16::from_be_bytes([start[0], start[1]])), proto_addr_type: EtherType(u16::from_be_bytes([start[2], start[3]])), hw_addr_size: start[4], proto_addr_size: start[5], operation: ArpOperation(u16::from_be_bytes([start[6], start[7]])), sender_hw_addr_buf: [const { MaybeUninit::uninit() }; 255], sender_protocol_addr_buf: [const { MaybeUninit::uninit() }; 255], target_hw_addr_buf: [const { MaybeUninit::uninit() }; 255], target_protocol_addr_buf: [const { MaybeUninit::uninit() }; 255], }; { // SAFETY: Safe as the maximum u8 value is equal to the array size 255. let sender_hw_addr_slice = unsafe { core::slice::from_raw_parts_mut( result.sender_hw_addr_buf.as_mut_ptr() as *mut u8, result.hw_addr_size as usize, ) }; reader.read_exact(sender_hw_addr_slice)?; } { // SAFETY: Safe as the maximum u8 value is equal to the array size 255. let sender_protocol_addr = unsafe { core::slice::from_raw_parts_mut( result.sender_protocol_addr_buf.as_mut_ptr() as *mut u8, result.proto_addr_size as usize, ) }; reader.read_exact(sender_protocol_addr)?; } { // SAFETY: Safe as the maximum u8 value is equal to the array size 255. let target_hw_addr = unsafe { core::slice::from_raw_parts_mut( result.target_hw_addr_buf.as_mut_ptr() as *mut u8, result.hw_addr_size as usize, ) }; reader.read_exact(target_hw_addr)?; } { // SAFETY: Safe as the maximum u8 value is equal to the array size 255. let target_protocol_addr = unsafe { core::slice::from_raw_parts_mut( result.target_protocol_addr_buf.as_mut_ptr() as *mut u8, result.proto_addr_size as usize, ) }; reader.read_exact(target_protocol_addr)?; } Ok(result) } /// Returns an [`ArpEthIpv4Packet`] if the current packet /// is an ethernet & IPv4 ARP packet. pub fn try_eth_ipv4(&self) -> Result { use err::arp::ArpEthIpv4FromError::*; if self.hw_addr_type != ArpHardwareId::ETHERNET { return Err(NonMatchingHwType(self.hw_addr_type)); } if self.proto_addr_type != EtherType::IPV4 { return Err(NonMatchingProtocolType(self.proto_addr_type)); } if self.hw_addr_size != 6 { return Err(NonMatchingHwAddrSize(self.hw_addr_size)); } if self.proto_addr_size != 4 { return Err(NonMatchingProtoAddrSize(self.proto_addr_size)); } Ok(ArpEthIpv4Packet { operation: self.operation, sender_mac: unsafe { // SAFE as we check above that hw_addr_size is 6 [ self.sender_hw_addr_buf[0].assume_init(), self.sender_hw_addr_buf[1].assume_init(), self.sender_hw_addr_buf[2].assume_init(), self.sender_hw_addr_buf[3].assume_init(), self.sender_hw_addr_buf[4].assume_init(), self.sender_hw_addr_buf[5].assume_init(), ] }, sender_ipv4: unsafe { // SAFE as we check above that proto_addr_size is 6 [ self.sender_protocol_addr_buf[0].assume_init(), self.sender_protocol_addr_buf[1].assume_init(), self.sender_protocol_addr_buf[2].assume_init(), self.sender_protocol_addr_buf[3].assume_init(), ] }, target_mac: unsafe { // SAFE as we check above that hw_addr_size is 6 [ self.target_hw_addr_buf[0].assume_init(), self.target_hw_addr_buf[1].assume_init(), self.target_hw_addr_buf[2].assume_init(), self.target_hw_addr_buf[3].assume_init(), self.target_hw_addr_buf[4].assume_init(), self.target_hw_addr_buf[5].assume_init(), ] }, target_ipv4: unsafe { // SAFE as we check above that proto_addr_size is 6 [ self.target_protocol_addr_buf[0].assume_init(), self.target_protocol_addr_buf[1].assume_init(), self.target_protocol_addr_buf[2].assume_init(), self.target_protocol_addr_buf[3].assume_init(), ] }, }) } } impl core::fmt::Debug for ArpPacket { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ArpPacket") .field("hw_addr_type", &self.hw_addr_type) .field("proto_addr_type", &self.proto_addr_type) .field("hw_addr_size", &self.hw_addr_size) .field("proto_addr_size", &self.proto_addr_size) .field("operation", &self.operation) .field("sender_hw_addr", &self.sender_hw_addr()) .field("sender_protocol_addr", &self.sender_protocol_addr()) .field("target_hw_addr", &self.target_hw_addr()) .field("target_protocol_addr", &self.target_protocol_addr()) .finish() } } impl core::cmp::PartialEq for ArpPacket { fn eq(&self, other: &Self) -> bool { self.hw_addr_type == other.hw_addr_type && self.proto_addr_type == other.proto_addr_type && self.hw_addr_size == other.hw_addr_size && self.proto_addr_size == other.proto_addr_size && self.operation == other.operation && self.sender_hw_addr() == other.sender_hw_addr() && self.sender_protocol_addr() == other.sender_protocol_addr() && self.target_hw_addr() == other.target_hw_addr() && self.target_protocol_addr() == other.target_protocol_addr() } } impl core::cmp::Eq for ArpPacket {} impl core::hash::Hash for ArpPacket { fn hash(&self, state: &mut H) { self.hw_addr_type.hash(state); self.proto_addr_type.hash(state); self.hw_addr_size.hash(state); self.proto_addr_size.hash(state); self.operation.hash(state); self.sender_hw_addr().hash(state); self.sender_protocol_addr().hash(state); self.target_hw_addr().hash(state); self.target_protocol_addr().hash(state); } } #[cfg(test)] mod tests { use crate::{test_gens::*, *}; use err::arp::{ArpHwAddrError, ArpNewError, ArpProtoAddrError}; use proptest::prelude::*; #[test] fn new() { // ok case { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1, 2, 3], &[4, 5, 6, 7, 8], &[9, 10, 11], &[12, 13, 14, 15, 16], ) .unwrap(); assert_eq!(3, actual.hw_addr_size()); assert_eq!(5, actual.protocol_addr_size()); assert_eq!(ArpHardwareId::ASH, actual.hw_addr_type); assert_eq!(EtherType::PROVIDER_BRIDGING, actual.proto_addr_type); assert_eq!(ArpOperation::REQUEST, actual.operation); assert_eq!(&[1, 2, 3], actual.sender_hw_addr()); assert_eq!(&[4, 5, 6, 7, 8], actual.sender_protocol_addr()); assert_eq!(&[9, 10, 11], actual.target_hw_addr()); assert_eq!(&[12, 13, 14, 15, 16], actual.target_protocol_addr()); } // ok case (upper hw size) { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1; 255], &[4, 5, 6, 7, 8], &[2; 255], &[12, 13, 14, 15, 16], ) .unwrap(); assert_eq!(255, actual.hw_addr_size()); assert_eq!(5, actual.protocol_addr_size()); assert_eq!(ArpHardwareId::ASH, actual.hw_addr_type); assert_eq!(EtherType::PROVIDER_BRIDGING, actual.proto_addr_type); assert_eq!(ArpOperation::REQUEST, actual.operation); assert_eq!(&[1; 255], actual.sender_hw_addr()); assert_eq!(&[4, 5, 6, 7, 8], actual.sender_protocol_addr()); assert_eq!(&[2; 255], actual.target_hw_addr()); assert_eq!(&[12, 13, 14, 15, 16], actual.target_protocol_addr()); } // ok case (protocol hw size) { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[3, 4, 5], &[1; 255], &[6, 7, 8], &[2; 255], ) .unwrap(); assert_eq!(3, actual.hw_addr_size()); assert_eq!(255, actual.protocol_addr_size()); assert_eq!(ArpHardwareId::ASH, actual.hw_addr_type); assert_eq!(EtherType::PROVIDER_BRIDGING, actual.proto_addr_type); assert_eq!(ArpOperation::REQUEST, actual.operation); assert_eq!(&[3, 4, 5], actual.sender_hw_addr()); assert_eq!(&[1; 255], actual.sender_protocol_addr()); assert_eq!(&[6, 7, 8], actual.target_hw_addr()); assert_eq!(&[2; 255], actual.target_protocol_addr()); } // hw slice len differ error { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1, 2, 3], &[], &[4, 5, 6, 7], &[], ); assert_eq!( Err(ArpNewError::HwAddr(ArpHwAddrError::LenNonMatching(3, 4))), actual ); } // protocol slice len differ error { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[], &[1, 2, 3], &[], &[4, 5, 6, 7], ); assert_eq!( Err(ArpNewError::ProtoAddr(ArpProtoAddrError::LenNonMatching( 3, 4 ))), actual ); } // hardware length error { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[0; 256], &[1, 2, 3, 4], &[0; 256], &[5, 6, 7, 8], ); assert_eq!( Err(ArpNewError::HwAddr(ArpHwAddrError::LenTooBig(256))), actual ); } // protocol length error { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1, 2, 3, 4], &[0; 256], &[5, 6, 7, 8], &[0; 256], ); assert_eq!( Err(ArpNewError::ProtoAddr(ArpProtoAddrError::LenTooBig(256))), actual ); } } #[test] fn new_unchecked() { // ok case { let actual = unsafe { ArpPacket::new_unchecked( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1, 2, 3], &[4, 5, 6, 7, 8], &[9, 10, 11], &[12, 13, 14, 15, 16], ) }; assert_eq!(3, actual.hw_addr_size()); assert_eq!(5, actual.protocol_addr_size()); assert_eq!(ArpHardwareId::ASH, actual.hw_addr_type); assert_eq!(EtherType::PROVIDER_BRIDGING, actual.proto_addr_type); assert_eq!(ArpOperation::REQUEST, actual.operation); assert_eq!(&[1, 2, 3], actual.sender_hw_addr()); assert_eq!(&[4, 5, 6, 7, 8], actual.sender_protocol_addr()); assert_eq!(&[9, 10, 11], actual.target_hw_addr()); assert_eq!(&[12, 13, 14, 15, 16], actual.target_protocol_addr()); } // ok case (upper hw size) { let actual = unsafe { ArpPacket::new_unchecked( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[0; 255], &[4, 5, 6, 7, 8], &[0; 255], &[12, 13, 14, 15, 16], ) }; assert_eq!(255, actual.hw_addr_size()); assert_eq!(5, actual.protocol_addr_size()); assert_eq!(ArpHardwareId::ASH, actual.hw_addr_type); assert_eq!(EtherType::PROVIDER_BRIDGING, actual.proto_addr_type); assert_eq!(ArpOperation::REQUEST, actual.operation); assert_eq!(&[0; 255], actual.sender_hw_addr()); assert_eq!(&[4, 5, 6, 7, 8], actual.sender_protocol_addr()); assert_eq!(&[0; 255], actual.target_hw_addr()); assert_eq!(&[12, 13, 14, 15, 16], actual.target_protocol_addr()); } // ok case (protocol hw size) { let actual = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1, 2, 3], &[0; 255], &[9, 10, 11], &[0; 255], ) .unwrap(); assert_eq!(3, actual.hw_addr_size()); assert_eq!(255, actual.protocol_addr_size()); assert_eq!(ArpHardwareId::ASH, actual.hw_addr_type); assert_eq!(EtherType::PROVIDER_BRIDGING, actual.proto_addr_type); assert_eq!(ArpOperation::REQUEST, actual.operation); assert_eq!(&[1, 2, 3], actual.sender_hw_addr()); assert_eq!(&[0; 255], actual.sender_protocol_addr()); assert_eq!(&[9, 10, 11], actual.target_hw_addr()); assert_eq!(&[0; 255], actual.target_protocol_addr()); } } proptest! { #[test] fn debug(arp in arp_packet_any()) { use std::format; assert_eq!( format!("{:?}", arp), format!( "ArpPacket {{ hw_addr_type: {:?}, proto_addr_type: {:?}, hw_addr_size: {:?}, proto_addr_size: {:?}, operation: {:?}, sender_hw_addr: {:?}, sender_protocol_addr: {:?}, target_hw_addr: {:?}, target_protocol_addr: {:?} }}", arp.hw_addr_type, arp.proto_addr_type, arp.hw_addr_size(), arp.protocol_addr_size(), arp.operation, arp.sender_hw_addr(), arp.sender_protocol_addr(), arp.target_hw_addr(), arp.target_protocol_addr() ) ); } } proptest! { #[test] fn clone_eq(arp in arp_packet_any()) { assert_eq!(&arp.clone(), &arp); } } proptest! { #[test] fn hash(arp in arp_packet_any()) { use core::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; let expected_hash = { let mut s = DefaultHasher::new(); arp.hw_addr_type.hash(&mut s); arp.proto_addr_type.hash(&mut s); arp.hw_addr_size().hash(&mut s); arp.protocol_addr_size().hash(&mut s); arp.operation.hash(&mut s); arp.sender_hw_addr().hash(&mut s); arp.sender_protocol_addr().hash(&mut s); arp.target_hw_addr().hash(&mut s); arp.target_protocol_addr().hash(&mut s); s.finish() }; let actual_hash = { let mut s = DefaultHasher::new(); arp.hash(&mut s); s.finish() }; assert_eq!(expected_hash, actual_hash); } } #[test] fn arp_packet_works() { let bytes = [ 0, 1, // hardware type 8, 0, // proto type 6, 4, // sizes 0, 1, // arp operation 0x00, 0x1b, 0x21, 0x0f, 0x91, 0x9b, // src mac 10, 10, 1, 135, // src ip 0xde, 0xad, 0xc0, 0x00, 0xff, 0xee, // dest mac 192, 168, 1, 253, // dest ip ]; let expected = ArpPacket::new( ArpHardwareId::ETHERNET, EtherType::IPV4, ArpOperation::REQUEST, &[0x00, 0x1b, 0x21, 0x0f, 0x91, 0x9b], &[10, 10, 1, 135], &[0xde, 0xad, 0xc0, 0x00, 0xff, 0xee], &[192, 168, 1, 253], ) .unwrap(); let actual = ArpPacket::from_slice(&bytes).unwrap(); assert_eq!(expected, actual); } proptest! { #[test] fn read( arp in arp_packet_any() ) { use std::vec::Vec; use std::io::Cursor; // ok case let mut buf = Vec::with_capacity(arp.packet_len()); arp.write(&mut buf).unwrap(); { let mut cursor = Cursor::new(&buf); let actual = ArpPacket::read(&mut cursor).unwrap(); assert_eq!(arp, actual); } // len io error for len in 0..arp.packet_len() { let mut cursor = Cursor::new(&buf[..len]); let actual = ArpPacket::read(&mut cursor); assert!(actual.is_err()); } } } proptest! { #[test] fn write_error( arp in arp_packet_any() ) { use std::vec::Vec; use std::io::Cursor; let mut buf = Vec::with_capacity(arp.packet_len()); buf.resize(arp.packet_len(), 0u8); // check that the write produces an error if not enough memory is present for len in 0..arp.packet_len() { let mut cursor = Cursor::new(&mut buf[..len]); let actual = arp.write(&mut cursor); assert!(actual.is_err()); } } } #[test] fn set_hw_addrs() { let start = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1, 2, 3], &[4, 5, 6, 7, 8], &[9, 10, 11], &[12, 13, 14, 15, 16], ) .unwrap(); // ok case { let mut arp = start.clone(); arp.set_hw_addrs(&[17, 18], &[19, 20]).unwrap(); assert_eq!(2, arp.hw_addr_size()); assert_eq!(&[17, 18], arp.sender_hw_addr()); assert_eq!(&[19, 20], arp.target_hw_addr()); } // non matching error { let mut arp = start.clone(); assert_eq!( arp.set_hw_addrs(&[17, 18], &[19]), Err(ArpHwAddrError::LenNonMatching(2, 1)) ); } // above 255 error { let mut arp = start.clone(); assert_eq!( arp.set_hw_addrs(&[0; 260], &[0; 260]), Err(ArpHwAddrError::LenTooBig(260)) ); } } #[test] fn set_proto_addrs() { let start = ArpPacket::new( ArpHardwareId::ASH, EtherType::PROVIDER_BRIDGING, ArpOperation::REQUEST, &[1, 2, 3], &[4, 5, 6, 7, 8], &[9, 10, 11], &[12, 13, 14, 15, 16], ) .unwrap(); // ok case { let mut arp = start.clone(); arp.set_protocol_addrs(&[17, 18], &[19, 20]).unwrap(); assert_eq!(2, arp.protocol_addr_size()); assert_eq!(&[17, 18], arp.sender_protocol_addr()); assert_eq!(&[19, 20], arp.target_protocol_addr()); } // non matching error { let mut arp = start.clone(); assert_eq!( arp.set_protocol_addrs(&[17, 18], &[19]), Err(ArpProtoAddrError::LenNonMatching(2, 1)) ); } // above 255 error { let mut arp = start.clone(); assert_eq!( arp.set_protocol_addrs(&[0; 260], &[0; 260]), Err(ArpProtoAddrError::LenTooBig(260)) ); } } proptest! { #[test] fn try_eth_ipv4( arp_eth_ipv4 in arp_eth_ipv4_packet_any() ) { use err::arp::ArpEthIpv4FromError::*; // ok case { let arp: ArpPacket = arp_eth_ipv4.clone().into(); assert_eq!(arp.try_eth_ipv4(), Ok(arp_eth_ipv4.clone())); } // hw type error { let mut arp: ArpPacket = arp_eth_ipv4.clone().into(); arp.hw_addr_type = ArpHardwareId::AX25; assert_eq!( arp.try_eth_ipv4(), Err(NonMatchingHwType(ArpHardwareId::AX25)) ); } // proto type error { let mut arp: ArpPacket = arp_eth_ipv4.clone().into(); arp.proto_addr_type = EtherType::IPV6; assert_eq!( arp.try_eth_ipv4(), Err(NonMatchingProtocolType(EtherType::IPV6)) ); } // hw address size error { let mut arp: ArpPacket = arp_eth_ipv4.clone().into(); arp.set_hw_addrs(&[1], &[2]).unwrap(); assert_eq!( arp.try_eth_ipv4(), Err(NonMatchingHwAddrSize(1)) ); } // protocol address size error { let mut arp: ArpPacket = arp_eth_ipv4.clone().into(); arp.set_protocol_addrs(&[1], &[2]).unwrap(); assert_eq!( arp.try_eth_ipv4(), Err(NonMatchingProtoAddrSize(1)) ); } } } }