Implement ARP representation parsing and emission.

This commit is contained in:
whitequark 2016-12-10 11:16:51 +00:00
parent adff3c0069
commit d966c1870b
3 changed files with 126 additions and 0 deletions

View File

@ -209,6 +209,69 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
}
}
/// 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<T: AsRef<[u8]>>(packet: &Packet<T>) -> Result<Repr, ()> {
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<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
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[..]);
}
}

30
src/ipv4.rs Normal file
View File

@ -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])
}
}

View File

@ -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;