diff --git a/src/arp.rs b/src/arp.rs index f0e879e..c6d386e 100644 --- a/src/arp.rs +++ b/src/arp.rs @@ -209,6 +209,69 @@ impl + AsMut<[u8]>> Packet { } } +/// A high-level representation of an Address Resolution Protocol packet. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Repr { + /// An Ethernet and IPv4 Address Resolution Protocol packet. + EthernetIpv4 { + operation: Operation, + source_hardware_addr: ::EthernetAddress, + source_protocol_addr: ::Ipv4Address, + target_hardware_addr: ::EthernetAddress, + target_protocol_addr: ::Ipv4Address + }, + #[doc(hidden)] + __Nonexhaustive +} + +impl Repr { + /// Parse an Address Resolution Packet and return a high-level representation, + /// or return `Err(())` if the packet is not recognized. + pub fn parse>(packet: &Packet) -> Result { + match (packet.hardware_type(), packet.protocol_type(), + packet.hardware_length(), packet.protocol_length()) { + (HardwareType::Ethernet, ProtocolType::Ipv4, 6, 4) => { + Ok(Repr::EthernetIpv4 { + operation: packet.operation(), + source_hardware_addr: + ::EthernetAddress::from_bytes(packet.source_hardware_addr()), + source_protocol_addr: + ::Ipv4Address::from_bytes(packet.source_protocol_addr()), + target_hardware_addr: + ::EthernetAddress::from_bytes(packet.target_hardware_addr()), + target_protocol_addr: + ::Ipv4Address::from_bytes(packet.target_protocol_addr()) + }) + }, + _ => Err(()) + } + } + + /// Emit a high-level representation into an Address Resolution Packet. + pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet) { + match self { + &Repr::EthernetIpv4 { + operation, + source_hardware_addr, + source_protocol_addr, + target_hardware_addr, + target_protocol_addr + } => { + packet.set_hardware_type(HardwareType::Ethernet); + packet.set_protocol_type(ProtocolType::Ipv4); + packet.set_hardware_length(6); + packet.set_protocol_length(4); + packet.set_operation(operation); + packet.set_source_hardware_addr(source_hardware_addr.as_bytes()); + packet.set_source_protocol_addr(source_protocol_addr.as_bytes()); + packet.set_target_hardware_addr(target_hardware_addr.as_bytes()); + packet.set_target_protocol_addr(target_protocol_addr.as_bytes()); + }, + &Repr::__Nonexhaustive => unreachable!() + } + } +} + #[cfg(test)] mod test { use super::*; @@ -253,4 +316,33 @@ mod test { packet.set_target_protocol_addr(&[0x41, 0x42, 0x43, 0x44]); assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); } + + fn packet_repr() -> Repr { + Repr::EthernetIpv4 { + operation: Operation::Request, + source_hardware_addr: + ::EthernetAddress::from_bytes(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]), + source_protocol_addr: + ::Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]), + target_hardware_addr: + ::EthernetAddress::from_bytes(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]), + target_protocol_addr: + ::Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]) + } + } + + #[test] + fn test_parse() { + let packet = Packet::new(&PACKET_BYTES[..]).unwrap(); + let repr = Repr::parse(&packet).unwrap(); + assert_eq!(repr, packet_repr()); + } + + #[test] + fn test_emit() { + let mut bytes = vec![0; 28]; + let mut packet = Packet::new(&mut bytes).unwrap(); + packet_repr().emit(&mut packet); + assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); + } } diff --git a/src/ipv4.rs b/src/ipv4.rs new file mode 100644 index 0000000..c39ffb0 --- /dev/null +++ b/src/ipv4.rs @@ -0,0 +1,30 @@ +use core::fmt; + +/// A four-octet IPv4 address. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +pub struct Address(pub [u8; 4]); + +impl Address { + /// Construct an IPv4 address from a sequence of octets, in big-endian. + /// + /// # Panics + /// The function panics if `data` is not four octets long. + pub fn from_bytes(data: &[u8]) -> Address { + let mut bytes = [0; 4]; + bytes.copy_from_slice(data); + Address(bytes) + } + + /// Return an IPv4 address as a sequence of octets, in big-endian. + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let bytes = self.0; + write!(f, "{:02x}.{:02x}.{:02x}.{:02x}", + bytes[0], bytes[1], bytes[2], bytes[3]) + } +} diff --git a/src/lib.rs b/src/lib.rs index 4a9aa6b..4a87d1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,7 @@ mod field { mod ethernet; mod arp; +mod ipv4; pub use ethernet::ProtocolType as EthernetProtocolType; pub use ethernet::Address as EthernetAddress; @@ -53,3 +54,6 @@ pub use arp::HardwareType as ArpHardwareType; pub use arp::ProtocolType as ArpProtocolType; pub use arp::Operation as ArpOperation; pub use arp::Packet as ArpPacket; +pub use arp::Repr as ArpRepr; + +pub use ipv4::Address as Ipv4Address;