Kaynağa Gözat

Implement ARP representation parsing and emission.

whitequark 8 yıl önce
ebeveyn
işleme
68d9c86a5b
3 değiştirilmiş dosya ile 126 ekleme ve 0 silme
  1. 92 0
      src/arp.rs
  2. 30 0
      src/ipv4.rs
  3. 4 0
      src/lib.rs

+ 92 - 0
src/arp.rs

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

+ 4 - 0
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;