Browse Source

Implement ARP snooping.

whitequark 8 years ago
parent
commit
a4e36053f9

+ 2 - 3
examples/smoltcpdump.rs

@@ -8,8 +8,7 @@ fn main() {
     let ifname = env::args().nth(1).unwrap();
     let mut socket = RawSocket::new(ifname.as_ref()).unwrap();
     loop {
-        socket.recv(|buffer| {
-            print!("{}", PrettyPrinter::<EthernetFrame<_>>::new("", &buffer))
-        })
+        let buffer = socket.receive().unwrap();
+        print!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer))
     }
 }

+ 3 - 26
examples/smoltcpserver.rs

@@ -2,33 +2,10 @@
 extern crate smoltcp;
 
 use std::env;
-use smoltcp::phy::{Device, TapInterface};
-use smoltcp::wire::{PrettyPrinter, EthernetFrame, EthernetAddress};
+use smoltcp::phy::{Tracer, TapInterface};
+use smoltcp::wire::{EthernetFrame, EthernetAddress};
 use smoltcp::iface::{ProtocolAddress, SliceArpCache, EthernetInterface};
 
-struct TracingDevice<T: Device>(T);
-
-impl<T: Device> Device for TracingDevice<T> {
-    fn mtu(&self) -> usize {
-        self.0.mtu()
-    }
-
-    fn recv<R, F: FnOnce(&[u8]) -> R>(&self, handler: F) -> R {
-        self.0.recv(|buffer| {
-            print!("{}", PrettyPrinter::<EthernetFrame<_>>::new("<- ", &buffer));
-            handler(buffer)
-        })
-    }
-
-    fn send<R, F: FnOnce(&mut [u8]) -> R>(&self, len: usize, handler: F) -> R {
-        self.0.send(len, |buffer| {
-            let result = handler(buffer);
-            print!("{}", PrettyPrinter::<EthernetFrame<_>>::new("-> ", &buffer));
-            result
-        })
-    }
-}
-
 fn main() {
     let ifname = env::args().nth(1).unwrap();
 
@@ -36,7 +13,7 @@ fn main() {
     let protocol_addrs = [ProtocolAddress::ipv4([192, 168, 69, 1])];
 
     let device = TapInterface::new(ifname.as_ref()).unwrap();
-    let device = TracingDevice(device);
+    let device = Tracer::<_, EthernetFrame<&[u8]>>::new(device);
 
     let mut arp_cache_data = [Default::default(); 8];
     let arp_cache = SliceArpCache::new(&mut arp_cache_data);

+ 40 - 44
src/iface/ethernet.rs

@@ -75,57 +75,53 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
             Nop,
             Arp(ArpRepr)
         }
+        let mut response = Response::Nop;
 
-        let response = try!(self.device.recv(|buffer| {
-            let frame = try!(EthernetFrame::new(buffer));
-            match frame.ethertype() {
-                EthernetProtocolType::Arp => {
-                    let packet = try!(ArpPacket::new(frame.payload()));
-                    let repr = try!(ArpRepr::parse(&packet));
-                    match repr {
-                        ArpRepr::EthernetIpv4 {
-                            operation: ArpOperation::Request,
-                            source_hardware_addr, source_protocol_addr,
-                            target_protocol_addr, ..
-                        } => {
-                            if self.has_protocol_addr(target_protocol_addr) {
-                                Ok(Response::Arp(ArpRepr::EthernetIpv4 {
-                                    operation: ArpOperation::Reply,
-                                    source_hardware_addr: self.hardware_addr,
-                                    source_protocol_addr: target_protocol_addr,
-                                    target_hardware_addr: source_hardware_addr,
-                                    target_protocol_addr: source_protocol_addr
-                                }))
-                            } else {
-                                Ok(Response::Nop)
-                            }
-                        },
-                        _ => Err(Error::Unrecognized)
-                    }
-                },
-                _ => Err(Error::Unrecognized)
-            }
-        }));
-
-        // TODO: accurately calculate the outgoing packet size?
-        let size = self.device.mtu();
+        let rx_buffer = try!(self.device.receive());
+        let frame = try!(EthernetFrame::new(rx_buffer));
+        match frame.ethertype() {
+            EthernetProtocolType::Arp => {
+                let packet = try!(ArpPacket::new(frame.payload()));
+                let repr = try!(ArpRepr::parse(&packet));
+                match repr {
+                    ArpRepr::EthernetIpv4 {
+                        operation: ArpOperation::Request,
+                        source_hardware_addr, source_protocol_addr,
+                        target_protocol_addr, ..
+                    } => {
+                        if self.has_protocol_addr(target_protocol_addr) {
+                            response = Response::Arp(ArpRepr::EthernetIpv4 {
+                                operation: ArpOperation::Reply,
+                                source_hardware_addr: self.hardware_addr,
+                                source_protocol_addr: target_protocol_addr,
+                                target_hardware_addr: source_hardware_addr,
+                                target_protocol_addr: source_protocol_addr
+                            })
+                        }
+                    },
+                    _ => return Err(Error::Unrecognized)
+                }
+            },
+            _ => return Err(Error::Unrecognized)
+        }
 
         match response {
             Response::Nop => Ok(()),
             Response::Arp(repr) => {
-                self.device.send(size, |buffer| {
-                    let mut frame = try!(EthernetFrame::new(buffer));
-                    frame.set_source(self.hardware_addr);
-                    frame.set_destination(match repr {
-                        ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr,
-                        _ => unreachable!()
-                    });
+                let tx_size = self.device.mtu();
+                let tx_buffer = try!(self.device.transmit(tx_size));
+                let mut frame = try!(EthernetFrame::new(tx_buffer));
+                frame.set_source(self.hardware_addr);
+                frame.set_destination(match repr {
+                    ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr,
+                    _ => unreachable!()
+                });
+                frame.set_ethertype(EthernetProtocolType::Arp);
 
-                    let mut packet = try!(ArpPacket::new(frame.payload_mut()));
-                    repr.emit(&mut packet);
+                let mut packet = try!(ArpPacket::new(frame.payload_mut()));
+                repr.emit(&mut packet);
 
-                    Ok(())
-                })
+                Ok(())
             }
         }
     }

+ 18 - 16
src/phy/mod.rs

@@ -4,15 +4,20 @@
 //! for transmitting and receiving frames, [Device](trait.Device.html),
 //! as well as an implementations of that trait that uses the host OS,
 //! [RawSocket](struct.RawSocket.html) and [TapInterface](struct.TapInterface.html).
+use Error;
 
 #[cfg(feature = "std")]
 mod sys;
 
+#[cfg(feature = "std")]
+mod tracer;
 #[cfg(feature = "std")]
 mod raw_socket;
 #[cfg(all(feature = "std", target_os = "linux"))]
 mod tap_interface;
 
+#[cfg(feature = "std")]
+pub use self::tracer::Tracer;
 #[cfg(feature = "std")]
 pub use self::raw_socket::RawSocket;
 #[cfg(all(feature = "std", target_os = "linux"))]
@@ -24,29 +29,26 @@ pub use self::tap_interface::TapInterface;
 /// and receiving packets from memory pools; hence, the stack borrows the buffer for a packet
 /// that it is about to receive, as well for a packet that it is about to send, from the device.
 pub trait Device {
-    /// Maximum transmission unit.
+    type RxBuffer: AsRef<[u8]>;
+    type TxBuffer: AsRef<[u8]> + AsMut<[u8]>;
+
+    /// Get maximum transmission unit.
     ///
     /// The network device is unable to send or receive frames larger than the MTU.
     /// In practice, MTU will fall between 576 (for IPv4) or 1280 (for IPv6) and 9216 octets.
     fn mtu(&self) -> usize;
 
-    /// Receives a frame.
+    /// Receive a frame.
     ///
-    /// It is expected that a `recv` implementation, once a packet is written to memory
+    /// It is expected that a `receive` implementation, once a packet is written to memory
     /// through DMA, would gain ownership of the underlying buffer, provide it for parsing,
-    /// and then return it to the network device.
-    ///
-    /// # Panics
-    /// This function may panic if called recursively.
-    fn recv<R, F: FnOnce(&[u8]) -> R>(&self, handler: F) -> R;
+    /// and return it to the network device once it is dropped.
+    fn receive(&mut self) -> Result<Self::RxBuffer, Error>;
 
-    /// Transmits a frame.
-    ///
-    /// It is expected that a `send` implementation would gain ownership of a buffer with
-    /// the requested length, provide it for emission, and then schedule it to be read from
-    /// memory by the network device.
+    /// Transmit a frame.
     ///
-    /// # Panics
-    /// This function may panic if `len` is larger than `MTU`, or if called recursively.
-    fn send<R, F: FnOnce(&mut [u8]) -> R>(&self, len: usize, handler: F) -> R;
+    /// It is expected that a `transmit` implementation would gain ownership of a buffer with
+    /// the requested length, provide it for emission, and schedule it to be read from
+    /// memory by the network device once it is dropped.
+    fn transmit(&mut self, len: usize) -> Result<Self::TxBuffer, Error>;
 }

+ 41 - 20
src/phy/raw_socket.rs

@@ -1,13 +1,16 @@
 use std::cell::RefCell;
 use std::vec::Vec;
+use std::rc::Rc;
 use std::io;
+
+use Error;
 use super::{sys, Device};
 
 /// A socket that captures or transmits the complete frame.
 #[derive(Debug)]
 pub struct RawSocket {
-    lower:  RefCell<sys::RawSocketDesc>,
-    buffer: RefCell<Vec<u8>>
+    lower:  Rc<RefCell<sys::RawSocketDesc>>,
+    mtu:    usize
 }
 
 impl RawSocket {
@@ -18,34 +21,52 @@ impl RawSocket {
     pub fn new(name: &str) -> io::Result<RawSocket> {
         let mut lower = try!(sys::RawSocketDesc::new(name));
         try!(lower.bind_interface());
-
-        let mut buffer = Vec::new();
-        buffer.resize(try!(lower.interface_mtu()), 0);
+        let mtu = try!(lower.interface_mtu());
         Ok(RawSocket {
-            lower:  RefCell::new(lower),
-            buffer: RefCell::new(buffer)
+            lower: Rc::new(RefCell::new(lower)),
+            mtu:   mtu
         })
     }
 }
 
 impl Device for RawSocket {
-    fn mtu(&self) -> usize {
+    type RxBuffer = Vec<u8>;
+    type TxBuffer = TxBuffer;
+
+    fn mtu(&self) -> usize { self.mtu }
+
+    fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
         let mut lower = self.lower.borrow_mut();
-        lower.interface_mtu().unwrap()
+        let mut buffer = vec![0; self.mtu];
+        lower.recv(&mut buffer[..]).unwrap();
+        Ok(buffer)
     }
 
-    fn recv<R, F: FnOnce(&[u8]) -> R>(&self, handler: F) -> R {
-        let mut lower  = self.lower.borrow_mut();
-        let mut buffer = self.buffer.borrow_mut();
-        let len = lower.recv(&mut buffer[..]).unwrap();
-        handler(&buffer[..len])
+    fn transmit(&mut self, len: usize) -> Result<Self::TxBuffer, Error> {
+        Ok(TxBuffer {
+            lower:  self.lower.clone(),
+            buffer: vec![0; len]
+        })
     }
+}
 
-    fn send<R, F: FnOnce(&mut [u8]) -> R>(&self, len: usize, handler: F) -> R {
-        let mut lower  = self.lower.borrow_mut();
-        let mut buffer = self.buffer.borrow_mut();
-        let result = handler(&mut buffer[..len]);
-        lower.send(&buffer[..len]).unwrap();
-        result
+#[doc(hidden)]
+pub struct TxBuffer {
+    lower:  Rc<RefCell<sys::RawSocketDesc>>,
+    buffer: Vec<u8>
+}
+
+impl AsRef<[u8]> for TxBuffer {
+    fn as_ref(&self) -> &[u8] { self.buffer.as_ref() }
+}
+
+impl AsMut<[u8]> for TxBuffer {
+    fn as_mut(&mut self) -> &mut [u8] { self.buffer.as_mut() }
+}
+
+impl Drop for TxBuffer {
+    fn drop(&mut self) {
+        let mut lower = self.lower.borrow_mut();
+        lower.send(&mut self.buffer[..]).unwrap();
     }
 }

+ 41 - 21
src/phy/tap_interface.rs

@@ -1,13 +1,16 @@
 use std::cell::RefCell;
 use std::vec::Vec;
+use std::rc::Rc;
 use std::io;
+
+use Error;
 use super::{sys, Device};
 
 /// A virtual Ethernet interface.
 #[derive(Debug)]
 pub struct TapInterface {
-    lower:  RefCell<sys::TapInterfaceDesc>,
-    buffer: RefCell<Vec<u8>>
+    lower:  Rc<RefCell<sys::TapInterfaceDesc>>,
+    mtu:    usize
 }
 
 impl TapInterface {
@@ -19,34 +22,51 @@ impl TapInterface {
     pub fn new(name: &str) -> io::Result<TapInterface> {
         let mut lower = try!(sys::TapInterfaceDesc::new(name));
         try!(lower.attach_interface());
-
-        let mut buffer = Vec::new();
-        buffer.resize(1536, 0);
         Ok(TapInterface {
-            lower:  RefCell::new(lower),
-            buffer: RefCell::new(buffer)
+            lower: Rc::new(RefCell::new(lower)),
+            mtu:   1536 // FIXME: get the real value somehow
         })
     }
 }
 
 impl Device for TapInterface {
-    fn mtu(&self) -> usize {
-        let buffer = self.buffer.borrow();
-        buffer.len()
+    type RxBuffer = Vec<u8>;
+    type TxBuffer = TxBuffer;
+
+    fn mtu(&self) -> usize { self.mtu }
+
+    fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
+        let mut lower = self.lower.borrow_mut();
+        let mut buffer = vec![0; self.mtu];
+        lower.recv(&mut buffer[..]).unwrap();
+        Ok(buffer)
     }
 
-    fn recv<R, F: FnOnce(&[u8]) -> R>(&self, handler: F) -> R {
-        let mut lower  = self.lower.borrow_mut();
-        let mut buffer = self.buffer.borrow_mut();
-        let len = lower.recv(&mut buffer[..]).unwrap();
-        handler(&buffer[..len])
+    fn transmit(&mut self, len: usize) -> Result<Self::TxBuffer, Error> {
+        Ok(TxBuffer {
+            lower:  self.lower.clone(),
+            buffer: vec![0; len]
+        })
     }
+}
+
+#[doc(hidden)]
+pub struct TxBuffer {
+    lower:  Rc<RefCell<sys::TapInterfaceDesc>>,
+    buffer: Vec<u8>
+}
+
+impl AsRef<[u8]> for TxBuffer {
+    fn as_ref(&self) -> &[u8] { self.buffer.as_ref() }
+}
+
+impl AsMut<[u8]> for TxBuffer {
+    fn as_mut(&mut self) -> &mut [u8] { self.buffer.as_mut() }
+}
 
-    fn send<R, F: FnOnce(&mut [u8]) -> R>(&self, len: usize, handler: F) -> R {
-        let mut lower  = self.lower.borrow_mut();
-        let mut buffer = self.buffer.borrow_mut();
-        let result = handler(&mut buffer[..len]);
-        lower.send(&buffer[..len]).unwrap();
-        result
+impl Drop for TxBuffer {
+    fn drop(&mut self) {
+        let mut lower = self.lower.borrow_mut();
+        lower.send(&mut self.buffer[..]).unwrap();
     }
 }

+ 72 - 0
src/phy/tracer.rs

@@ -0,0 +1,72 @@
+use core::marker::PhantomData;
+
+use Error;
+use wire::pretty_print::{PrettyPrint, PrettyPrinter};
+use super::Device;
+
+/// A tracer device.
+///
+/// A tracer is a device that prints all packets traversing it
+/// to the standard output, and delegates to another device otherwise.
+pub struct Tracer<T: Device, U: PrettyPrint> {
+    lower:   T,
+    phantom: PhantomData<U>
+}
+
+impl<T: Device, U: PrettyPrint> Tracer<T, U> {
+    /// Create a tracer device.
+    pub fn new(lower: T) -> Tracer<T, U> {
+        Tracer {
+            lower:   lower,
+            phantom: PhantomData
+        }
+    }
+
+    /// Return the underlying device, consuming the tracer.
+    pub fn into_lower(self) -> T {
+        self.lower
+    }
+}
+
+impl<T: Device, U: PrettyPrint> Device for Tracer<T, U> {
+    type RxBuffer = T::RxBuffer;
+    type TxBuffer = TxBuffer<T::TxBuffer, U>;
+
+    fn mtu(&self) -> usize { self.lower.mtu() }
+
+    fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
+        let buffer = try!(self.lower.receive());
+        print!("{}", PrettyPrinter::<U>::new("<- ", &buffer));
+        Ok(buffer)
+    }
+
+    fn transmit(&mut self, len: usize) -> Result<Self::TxBuffer, Error> {
+        let buffer = try!(self.lower.transmit(len));
+        Ok(TxBuffer {
+            buffer:  buffer,
+            phantom: PhantomData
+        })
+    }
+}
+
+#[doc(hidden)]
+pub struct TxBuffer<T: AsRef<[u8]>, U: PrettyPrint> {
+    buffer:  T,
+    phantom: PhantomData<U>
+}
+
+impl<T: AsRef<[u8]>, U: PrettyPrint> AsRef<[u8]>
+        for TxBuffer<T, U> {
+    fn as_ref(&self) -> &[u8] { self.buffer.as_ref() }
+}
+
+impl<T: AsRef<[u8]> + AsMut<[u8]>, U: PrettyPrint> AsMut<[u8]>
+        for TxBuffer<T, U> {
+    fn as_mut(&mut self) -> &mut [u8] { self.buffer.as_mut() }
+}
+
+impl<T: AsRef<[u8]>, U: PrettyPrint> Drop for TxBuffer<T, U> {
+    fn drop(&mut self) {
+        print!("{}", PrettyPrinter::<U>::new("-> ", &self.buffer));
+    }
+}

+ 2 - 2
src/wire/arp.rs

@@ -314,8 +314,8 @@ impl fmt::Display for Repr {
 
 use super::pretty_print::{PrettyPrint, PrettyIndent};
 
-impl<T: AsRef<[u8]>> PrettyPrint<T> for Packet<T> {
-    fn pretty_print(buffer: T, f: &mut fmt::Formatter,
+impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
+    fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
                     indent: &mut PrettyIndent) -> fmt::Result {
         match Packet::new(buffer) {
             Err(err)  => write!(f, "{}({})\n", indent, err),

+ 7 - 7
src/wire/ethernet.rs

@@ -164,8 +164,8 @@ impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
 
 use super::pretty_print::{PrettyPrint, PrettyIndent};
 
-impl<T: AsRef<[u8]>> PrettyPrint<T> for Frame<T> {
-    fn pretty_print(buffer: T, f: &mut fmt::Formatter,
+impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
+    fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
                     indent: &mut PrettyIndent) -> fmt::Result {
         let frame = match Frame::new(buffer) {
             Err(err)  => return write!(f, "{}({})\n", indent, err),
@@ -176,7 +176,7 @@ impl<T: AsRef<[u8]>> PrettyPrint<T> for Frame<T> {
 
         match frame.ethertype() {
             EtherType::Arp =>
-                super::ArpPacket::pretty_print(frame.payload(), f, indent),
+                super::ArpPacket::<&[u8]>::pretty_print(&frame.payload(), f, indent),
             _ => Ok(())
         }
     }
@@ -210,8 +210,8 @@ mod test {
     #[test]
     fn test_deconstruct() {
         let frame = Frame::new(&FRAME_BYTES[..]).unwrap();
-        assert_eq!(frame.source(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
-        assert_eq!(frame.destination(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
+        assert_eq!(frame.destination(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
+        assert_eq!(frame.source(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
         assert_eq!(frame.ethertype(), EtherType::Ipv4);
         assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
     }
@@ -220,8 +220,8 @@ mod test {
     fn test_construct() {
         let mut bytes = vec![0; 64];
         let mut frame = Frame::new(&mut bytes).unwrap();
-        frame.set_source(Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
-        frame.set_destination(Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
+        frame.set_destination(Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
+        frame.set_source(Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
         frame.set_ethertype(EtherType::Ipv4);
         frame.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
         assert_eq!(&frame.into_inner()[..], &FRAME_BYTES[..]);

+ 7 - 6
src/wire/pretty_print.rs

@@ -45,23 +45,24 @@ impl fmt::Display for PrettyIndent {
 }
 
 /// Interface for printing listings.
-pub trait PrettyPrint<T: AsRef<[u8]>> {
+pub trait PrettyPrint {
     /// Write a concise, formatted representation of a packet contained in the provided
     /// buffer, and any nested packets it may contain.
     ///
     /// `pretty_print` accepts a buffer and not a packet wrapper because the packet might
     /// be truncated, and so it might not be possible to create the packet wrapper.
-    fn pretty_print(buffer: T, fmt: &mut fmt::Formatter, indent: &mut PrettyIndent) -> fmt::Result;
+    fn pretty_print(buffer: &AsRef<[u8]>, fmt: &mut fmt::Formatter,
+                    indent: &mut PrettyIndent) -> fmt::Result;
 }
 
 /// Wrapper for using a `PrettyPrint` where a `Display` is expected.
-pub struct PrettyPrinter<'a, T: PrettyPrint<&'a AsRef<[u8]>>> {
+pub struct PrettyPrinter<'a, T: PrettyPrint> {
     prefix:  &'static str,
     buffer:  &'a AsRef<[u8]>,
     phantom: PhantomData<T>
 }
 
-impl<'a, T: PrettyPrint<&'a AsRef<[u8]>>> PrettyPrinter<'a, T> {
+impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
     /// Format the listing with the recorded parameters when Display::fmt is called.
     pub fn new(prefix: &'static str, buffer: &'a AsRef<[u8]>) -> PrettyPrinter<'a, T> {
         PrettyPrinter {
@@ -72,8 +73,8 @@ impl<'a, T: PrettyPrint<&'a AsRef<[u8]>>> PrettyPrinter<'a, T> {
     }
 }
 
-impl<'a, T: PrettyPrint<&'a AsRef<[u8]>>> fmt::Display for PrettyPrinter<'a, T> {
+impl<'a, T: PrettyPrint> fmt::Display for PrettyPrinter<'a, T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        T::pretty_print(self.buffer, f, &mut PrettyIndent::new(self.prefix))
+        T::pretty_print(&self.buffer, f, &mut PrettyIndent::new(self.prefix))
     }
 }