Browse Source

Imlpement ARP packet support.

whitequark 8 years ago
parent
commit
183bfa274f
3 changed files with 263 additions and 1 deletions
  1. 1 1
      README.md
  2. 256 0
      src/arp.rs
  3. 6 0
      src/lib.rs

+ 1 - 1
README.md

@@ -21,7 +21,7 @@ The only supported medium is Ethernet.
   * 802.1Q is not supported.
   * Jumbo frames are not supported.
   * CRC calculation is not supported.
-  * ARP packets are not supported.
+  * ARP packets are supported.
   * ARP probes or announcements are not supported.
 
 ### IP layer

+ 256 - 0
src/arp.rs

@@ -0,0 +1,256 @@
+use byteorder::{ByteOrder, NetworkEndian};
+
+pub use ::ethernet::ProtocolType as ProtocolType;
+
+enum_with_unknown! {
+    /// ARP network protocol type.
+    pub enum HardwareType(u16) {
+        Ethernet = 1
+    }
+}
+
+enum_with_unknown! {
+    /// ARP operation type.
+    pub enum Operation(u16) {
+        Request = 1,
+        Reply = 2
+    }
+}
+
+/// A read/write wrapper around an Address Resolution Protocol packet.
+#[derive(Debug)]
+pub struct Packet<T: AsRef<[u8]>>(T);
+
+mod field {
+    #![allow(non_snake_case)]
+
+    use ::field::*;
+
+    pub const HTYPE: Field = 0..2;
+    pub const PTYPE: Field = 2..4;
+    pub const HLEN:  usize = 4;
+    pub const PLEN:  usize = 5;
+    pub const OPER:  Field = 6..8;
+
+    #[inline(always)]
+    pub fn SHA(hardware_length: u8, _protocol_length: u8) -> Field {
+        let start = OPER.end;
+        start..(start + hardware_length as usize)
+    }
+
+    #[inline(always)]
+    pub fn SPA(hardware_length: u8, protocol_length: u8) -> Field {
+        let start = SHA(hardware_length, protocol_length).end;
+        start..(start + protocol_length as usize)
+    }
+
+    #[inline(always)]
+    pub fn THA(hardware_length: u8, protocol_length: u8) -> Field {
+        let start = SPA(hardware_length, protocol_length).end;
+        start..(start + hardware_length as usize)
+    }
+
+    #[inline(always)]
+    pub fn TPA(hardware_length: u8, protocol_length: u8) -> Field {
+        let start = THA(hardware_length, protocol_length).end;
+        start..(start + protocol_length as usize)
+    }
+}
+
+impl<T: AsRef<[u8]>> Packet<T> {
+    /// Wrap a buffer with an ARP packet. Returns an error if the buffer
+    /// is too small to contain one.
+    pub fn new(storage: T) -> Result<Packet<T>, ()> {
+        let len = storage.as_ref().len();
+        if len < field::OPER.end {
+            Err(())
+        } else {
+            let packet = Packet(storage);
+            if len < field::TPA(packet.hardware_length(), packet.protocol_length()).end {
+                Err(())
+            } else {
+                Ok(packet)
+            }
+        }
+    }
+
+    /// Consumes the packet, returning the underlying buffer.
+    pub fn into_inner(self) -> T {
+        self.0
+    }
+
+    /// Return the hardware type field.
+    pub fn hardware_type(&self) -> HardwareType {
+        let bytes = self.0.as_ref();
+        let raw = NetworkEndian::read_u16(&bytes[field::HTYPE]);
+        HardwareType::from(raw)
+    }
+
+    /// Return the protocol type field.
+    pub fn protocol_type(&self) -> ProtocolType {
+        let bytes = self.0.as_ref();
+        let raw = NetworkEndian::read_u16(&bytes[field::PTYPE]);
+        ProtocolType::from(raw)
+    }
+
+    /// Return the hardware length field.
+    pub fn hardware_length(&self) -> u8 {
+        let bytes = self.0.as_ref();
+        bytes[field::HLEN]
+    }
+
+    /// Return the protocol length field.
+    pub fn protocol_length(&self) -> u8 {
+        let bytes = self.0.as_ref();
+        bytes[field::PLEN]
+    }
+
+    /// Return the operation field.
+    pub fn operation(&self) -> Operation {
+        let bytes = self.0.as_ref();
+        let raw = NetworkEndian::read_u16(&bytes[field::OPER]);
+        Operation::from(raw)
+    }
+
+    /// Return the source hardware address field.
+    pub fn source_hardware_addr(&self) -> &[u8] {
+        let bytes = self.0.as_ref();
+        &bytes[field::SHA(self.hardware_length(), self.protocol_length())]
+    }
+
+    /// Return the source protocol address field.
+    pub fn source_protocol_addr(&self) -> &[u8] {
+        let bytes = self.0.as_ref();
+        &bytes[field::SPA(self.hardware_length(), self.protocol_length())]
+    }
+
+    /// Return the target hardware address field.
+    pub fn target_hardware_addr(&self) -> &[u8] {
+        let bytes = self.0.as_ref();
+        &bytes[field::THA(self.hardware_length(), self.protocol_length())]
+    }
+
+    /// Return the target protocol address field.
+    pub fn target_protocol_addr(&self) -> &[u8] {
+        let bytes = self.0.as_ref();
+        &bytes[field::TPA(self.hardware_length(), self.protocol_length())]
+    }
+}
+
+impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
+    /// Set the hardware type field.
+    pub fn set_hardware_type(&mut self, value: HardwareType) {
+        let bytes = self.0.as_mut();
+        NetworkEndian::write_u16(&mut bytes[field::HTYPE], value.into())
+    }
+
+    /// Set the protocol type field.
+    pub fn set_protocol_type(&mut self, value: ProtocolType) {
+        let bytes = self.0.as_mut();
+        NetworkEndian::write_u16(&mut bytes[field::PTYPE], value.into())
+    }
+
+    /// Set the hardware length field.
+    pub fn set_hardware_length(&mut self, value: u8) {
+        let bytes = self.0.as_mut();
+        bytes[field::HLEN] = value
+    }
+
+    /// Set the protocol length field.
+    pub fn set_protocol_length(&mut self, value: u8) {
+        let bytes = self.0.as_mut();
+        bytes[field::PLEN] = value
+    }
+
+    /// Set the operation field.
+    pub fn set_operation(&mut self, value: Operation) {
+        let bytes = self.0.as_mut();
+        NetworkEndian::write_u16(&mut bytes[field::OPER], value.into())
+    }
+
+    /// Set the source hardware address field.
+    ///
+    /// # Panics
+    /// The function panics if `value` is not `self.hardware_length()` long.
+    pub fn set_source_hardware_addr(&mut self, value: &[u8]) {
+        let (hardware_length, protocol_length) = (self.hardware_length(), self.protocol_length());
+        let bytes = self.0.as_mut();
+        bytes[field::SHA(hardware_length, protocol_length)].copy_from_slice(value)
+    }
+
+    /// Set the source protocol address field.
+    ///
+    /// # Panics
+    /// The function panics if `value` is not `self.protocol_length()` long.
+    pub fn set_source_protocol_addr(&mut self, value: &[u8]) {
+        let (hardware_length, protocol_length) = (self.hardware_length(), self.protocol_length());
+        let bytes = self.0.as_mut();
+        bytes[field::SPA(hardware_length, protocol_length)].copy_from_slice(value)
+    }
+
+    /// Set the target hardware address field.
+    ///
+    /// # Panics
+    /// The function panics if `value` is not `self.hardware_length()` long.
+    pub fn set_target_hardware_addr(&mut self, value: &[u8]) {
+        let (hardware_length, protocol_length) = (self.hardware_length(), self.protocol_length());
+        let bytes = self.0.as_mut();
+        bytes[field::THA(hardware_length, protocol_length)].copy_from_slice(value)
+    }
+
+    /// Set the target protocol address field.
+    ///
+    /// # Panics
+    /// The function panics if `value` is not `self.protocol_length()` long.
+    pub fn set_target_protocol_addr(&mut self, value: &[u8]) {
+        let (hardware_length, protocol_length) = (self.hardware_length(), self.protocol_length());
+        let bytes = self.0.as_mut();
+        bytes[field::TPA(hardware_length, protocol_length)].copy_from_slice(value)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    static PACKET_BYTES: [u8; 28] =
+        [0x00, 0x01,
+         0x08, 0x00,
+         0x06,
+         0x04,
+         0x00, 0x01,
+         0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+         0x21, 0x22, 0x23, 0x24,
+         0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+         0x41, 0x42, 0x43, 0x44];
+
+    #[test]
+    fn test_deconstruct() {
+        let packet = Packet::new(&PACKET_BYTES[..]).unwrap();
+        assert_eq!(packet.hardware_type(), HardwareType::Ethernet);
+        assert_eq!(packet.protocol_type(), ProtocolType::Ipv4);
+        assert_eq!(packet.hardware_length(), 6);
+        assert_eq!(packet.protocol_length(), 4);
+        assert_eq!(packet.operation(), Operation::Request);
+        assert_eq!(packet.source_hardware_addr(), &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
+        assert_eq!(packet.source_protocol_addr(), &[0x21, 0x22, 0x23, 0x24]);
+        assert_eq!(packet.target_hardware_addr(), &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]);
+        assert_eq!(packet.target_protocol_addr(), &[0x41, 0x42, 0x43, 0x44]);
+    }
+
+    #[test]
+    fn test_construct() {
+        let mut bytes = vec![0; 28];
+        let mut packet = Packet::new(&mut bytes).unwrap();
+        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::Request);
+        packet.set_source_hardware_addr(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
+        packet.set_source_protocol_addr(&[0x21, 0x22, 0x23, 0x24]);
+        packet.set_target_hardware_addr(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]);
+        packet.set_target_protocol_addr(&[0x41, 0x42, 0x43, 0x44]);
+        assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
+    }
+}

+ 6 - 0
src/lib.rs

@@ -43,7 +43,13 @@ mod field {
 }
 
 mod ethernet;
+mod arp;
 
 pub use ethernet::ProtocolType as EthernetProtocolType;
 pub use ethernet::Address as EthernetAddress;
 pub use ethernet::Frame as EthernetFrame;
+
+pub use arp::HardwareType as ArpHardwareType;
+pub use arp::ProtocolType as ArpProtocolType;
+pub use arp::Operation as ArpOperation;
+pub use arp::Packet as ArpPacket;