Implement ARP snooping.

This commit is contained in:
whitequark 2016-12-12 12:30:35 +00:00
parent 4421b2fe27
commit 7f1b88ef45
10 changed files with 237 additions and 149 deletions

View File

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

View File

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

View File

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

View File

@ -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.
/// Transmit 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.
///
/// # 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>;
}

View File

@ -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 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
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::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();
}
}

View File

@ -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 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
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() }
}
impl Drop for TxBuffer {
fn drop(&mut self) {
let mut lower = self.lower.borrow_mut();
lower.send(&mut self.buffer[..]).unwrap();
}
}

72
src/phy/tracer.rs Normal file
View File

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

View File

@ -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),

View File

@ -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[..]);

View File

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