Explorar o código

Implement ARP replies.

whitequark %!s(int64=8) %!d(string=hai) anos
pai
achega
2fbf820de8
Modificáronse 11 ficheiros con 429 adicións e 130 borrados
  1. 38 2
      README.md
  2. 54 0
      examples/smoltcpserver.rs
  3. 105 12
      src/iface/ethernet.rs
  4. 30 2
      src/iface/mod.rs
  5. 26 1
      src/lib.rs
  6. 8 5
      src/phy/mod.rs
  7. 23 13
      src/phy/raw_socket.rs
  8. 24 14
      src/phy/tap_interface.rs
  9. 49 46
      src/wire/arp.rs
  10. 38 35
      src/wire/ethernet.rs
  11. 34 0
      src/wire/ipv4.rs

+ 38 - 2
README.md

@@ -49,10 +49,46 @@ smoltcp = "0.1"
 Usage example
 -------------
 
-```rust
-TBD
+_smoltcp_, being a userspace networking stack, needs to be able to send and receive raw frames.
+This normally requires superuser privileges, but on Linux it is possible to create
+a _persistent tap interface_ that can be manipulated by a specific user:
+
+```sh
+sudo ip tuntap add name tap0 mode tap user $USER
+sudo ip link set tap0 up
+sudo ip addr add 192.168.69.100 dev tap0
 ```
 
+### smoltcpdump
+
+_smoltcpdump_ is a tiny clone of the _tcpdump_ utility.
+
+Unlike the rest of the examples, it uses raw sockets, and so it can be used on regular interfaces,
+e.g. `eth0` or `wlan0`, as well as the `tap0` interface we've created above.
+
+Read its [source code](/examples/smoltcpdump.rs), then run it as:
+
+```sh
+cargo build --example smoltcpdump
+sudo ./target/debug/smoltcpdump eth0
+```
+
+### smoltcpserver
+
+_smoltcpserver_ emulates a network host that can serve requests.
+
+The host is assigned the hardware address `02-00-00-00-00-01` and IPv4 address `192.168.69.1`.
+
+Read its [source code](/examples/smoltcpserver.rs), then run it as:
+
+```sh
+cargo run --example smoltcpserver -- tap0
+```
+
+It responds to:
+
+  * pings (`ping 192.168.69.1`) (actually not yet).
+
 License
 -------
 

+ 54 - 0
examples/smoltcpserver.rs

@@ -0,0 +1,54 @@
+#![feature(associated_consts)]
+extern crate smoltcp;
+
+use std::env;
+use smoltcp::phy::{Device, TapInterface};
+use smoltcp::wire::{PrettyPrinter, 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();
+
+    let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
+    let protocol_addrs = [ProtocolAddress::ipv4([192, 168, 69, 1])];
+
+    let device = TapInterface::new(ifname.as_ref()).unwrap();
+    let device = TracingDevice(device);
+
+    let mut arp_cache_data = [Default::default(); 8];
+    let arp_cache = SliceArpCache::new(&mut arp_cache_data);
+    let mut iface = EthernetInterface::new(device, arp_cache);
+
+    iface.set_hardware_addr(hardware_addr);
+    iface.set_protocol_addrs(&protocol_addrs);
+
+    loop {
+        match iface.poll() {
+            Ok(()) => (),
+            Err(e) => println!("{}", e)
+        }
+    }
+}

+ 105 - 12
src/iface/ethernet.rs

@@ -1,25 +1,29 @@
+use Error;
 use phy::Device;
-use wire::EthernetAddress;
+use wire::{EthernetAddress, EthernetProtocolType, EthernetFrame};
+use wire::{ArpPacket, ArpRepr, ArpOperation};
 use super::{ProtocolAddress, ArpCache};
 
 /// An Ethernet network interface.
 #[derive(Debug)]
-pub struct Interface<DeviceT: Device, ArpCacheT: ArpCache> {
-    device:        DeviceT,
-    arp_cache:     ArpCacheT,
-    hardware_addr: EthernetAddress,
+pub struct Interface<'a, DeviceT: Device, ArpCacheT: ArpCache> {
+    device:         DeviceT,
+    arp_cache:      ArpCacheT,
+    hardware_addr:  EthernetAddress,
+    protocol_addrs: &'a [ProtocolAddress]
 }
 
-impl<DeviceT: Device, ArpCacheT: ArpCache> Interface<DeviceT, ArpCacheT> {
+impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT> {
     /// Create a network interface using the provided network device.
     ///
     /// The newly created interface uses hardware address `00-00-00-00-00-00` and
     /// has no assigned protocol addresses.
-    pub fn new(device: DeviceT, arp_cache: ArpCacheT) -> Interface<DeviceT, ArpCacheT> {
+    pub fn new(device: DeviceT, arp_cache: ArpCacheT) -> Interface<'a, DeviceT, ArpCacheT> {
         Interface {
-            device:        device,
-            arp_cache:     arp_cache,
-            hardware_addr: EthernetAddress([0x00; 6])
+            device:         device,
+            arp_cache:      arp_cache,
+            hardware_addr:  EthernetAddress([0x00; 6]),
+            protocol_addrs: &[]
         }
     }
 
@@ -31,9 +35,98 @@ impl<DeviceT: Device, ArpCacheT: ArpCache> Interface<DeviceT, ArpCacheT> {
     /// Set the hardware address of the interface.
     ///
     /// # Panics
-    /// This function panics if `addr` is not unicast.
+    /// This function panics if the address is not unicast.
     pub fn set_hardware_addr(&mut self, addr: EthernetAddress) {
-        if addr.is_multicast() { panic!("hardware address should be unicast") }
+        if addr.is_multicast() {
+            panic!("hardware address {} is not unicast", addr)
+        }
+
         self.hardware_addr = addr
     }
+
+    /// Get the protocol addresses of the interface.
+    pub fn protocol_addrs(&self) -> &'a [ProtocolAddress] {
+        self.protocol_addrs
+    }
+
+    /// Set the protocol addresses of the interface.
+    ///
+    /// # Panics
+    /// This function panics if any of the addresses is not unicast.
+    pub fn set_protocol_addrs(&mut self, addrs: &'a [ProtocolAddress]) {
+        for addr in addrs {
+            if !addr.is_unicast() {
+                panic!("protocol address {} is not unicast", addr)
+            }
+        }
+
+        self.protocol_addrs = addrs
+    }
+
+    /// Checks whether the interface has the given protocol address assigned.
+    pub fn has_protocol_addr<T: Into<ProtocolAddress>>(&self, addr: T) -> bool {
+        let addr = addr.into();
+        self.protocol_addrs.iter().any(|&probe| probe == addr)
+    }
+
+    /// Receive and process a packet, if available.
+    pub fn poll(&mut self) -> Result<(), Error> {
+        enum Response {
+            Nop,
+            Arp(ArpRepr)
+        }
+
+        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();
+
+        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 mut packet = try!(ArpPacket::new(frame.payload_mut()));
+                    repr.emit(&mut packet);
+
+                    Ok(())
+                })
+            }
+        }
+    }
 }

+ 30 - 2
src/iface/mod.rs

@@ -2,6 +2,7 @@
 //!
 //! The `iface` module deals with the *network interfaces*. It filters incoming frames,
 //! provides lookup and caching of hardware addresses, and handles management packets.
+use core::fmt;
 use wire;
 
 mod arp_cache;
@@ -14,13 +15,25 @@ pub use self::ethernet::Interface as EthernetInterface;
 /// An internetworking protocol address.
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 pub enum ProtocolAddress {
+    /// An invalid address.
+    /// May be used as a placeholder for storage where the address is not assigned yet.
     Invalid,
+    /// An IPv4 address.
     Ipv4(wire::Ipv4Address)
 }
 
 impl ProtocolAddress {
-    pub const fn ipv4(bytes: [u8; 4]) -> ProtocolAddress {
-        ProtocolAddress::Ipv4(wire::Ipv4Address(bytes))
+    /// Create a protocol address wrapping an IPv4 address with the given octets.
+    pub const fn ipv4(octets: [u8; 4]) -> ProtocolAddress {
+        ProtocolAddress::Ipv4(wire::Ipv4Address(octets))
+    }
+
+    /// Query whether the address is a valid unicast address.
+    pub fn is_unicast(&self) -> bool {
+        match self {
+            &ProtocolAddress::Invalid    => false,
+            &ProtocolAddress::Ipv4(addr) => addr.is_unicast()
+        }
     }
 }
 
@@ -29,3 +42,18 @@ impl Default for ProtocolAddress {
         ProtocolAddress::Invalid
     }
 }
+
+impl From<wire::Ipv4Address> for ProtocolAddress {
+    fn from(addr: wire::Ipv4Address) -> Self {
+        ProtocolAddress::Ipv4(addr)
+    }
+}
+
+impl fmt::Display for ProtocolAddress {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &ProtocolAddress::Invalid    => write!(f, "(invalid)"),
+            &ProtocolAddress::Ipv4(addr) => write!(f, "{}", addr)
+        }
+    }
+}

+ 26 - 1
src/lib.rs

@@ -1,4 +1,4 @@
-#![feature(range_contains, associated_consts, const_fn)]
+#![feature(associated_consts, const_fn)]
 #![no_std]
 
 extern crate byteorder;
@@ -9,6 +9,31 @@ extern crate std;
 #[cfg(feature = "std")]
 extern crate libc;
 
+use core::fmt;
+
 pub mod phy;
 pub mod wire;
 pub mod iface;
+
+/// The error type for the networking stack.
+#[derive(Debug)]
+pub enum Error {
+    /// A packet could not be parsed or emitted because a field was out of bounds
+    /// for the underlying buffer.
+    Truncated,
+    /// A packet could not be recognized and was dropped.
+    Unrecognized,
+
+    #[doc(hidden)]
+    __Nonexhaustive
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &Error::Truncated    => write!(f, "truncated packet"),
+            &Error::Unrecognized => write!(f, "unrecognized packet"),
+            &Error::__Nonexhaustive => unreachable!()
+        }
+    }
+}

+ 8 - 5
src/phy/mod.rs

@@ -27,15 +27,18 @@ pub trait Device {
     /// Maximum transmission unit.
     ///
     /// The network device is unable to send or receive frames larger than the MTU.
-    /// In practice, MTU will fall between 64 and 9216 octets.
-    const MTU: usize;
+    /// In practice, MTU will fall between 576 (for IPv4) or 1280 (for IPv6) and 9216 octets.
+    fn mtu(&self) -> usize;
 
     /// Receives a frame.
     ///
     /// It is expected that a `recv` 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.
-    fn recv<F: FnOnce(&[u8])>(&mut self, handler: F);
+    ///
+    /// # Panics
+    /// This function may panic if called recursively.
+    fn recv<R, F: FnOnce(&[u8]) -> R>(&self, handler: F) -> R;
 
     /// Transmits a frame.
     ///
@@ -44,6 +47,6 @@ pub trait Device {
     /// memory by the network device.
     ///
     /// # Panics
-    /// This function may panic if `len` is larger than `MTU`.
-    fn send<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F);
+    /// 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;
 }

+ 23 - 13
src/phy/raw_socket.rs

@@ -1,11 +1,13 @@
-use std::{vec, io};
+use std::cell::RefCell;
+use std::vec::Vec;
+use std::io;
 use super::{sys, Device};
 
 /// A socket that captures or transmits the complete frame.
 #[derive(Debug)]
 pub struct RawSocket {
-    lower:  sys::RawSocketDesc,
-    buffer: vec::Vec<u8>
+    lower:  RefCell<sys::RawSocketDesc>,
+    buffer: RefCell<Vec<u8>>
 }
 
 impl RawSocket {
@@ -17,25 +19,33 @@ impl RawSocket {
         let mut lower = try!(sys::RawSocketDesc::new(name));
         try!(lower.bind_interface());
 
-        let mut buffer = vec::Vec::new();
+        let mut buffer = Vec::new();
         buffer.resize(try!(lower.interface_mtu()), 0);
         Ok(RawSocket {
-            lower:  lower,
-            buffer: buffer
+            lower:  RefCell::new(lower),
+            buffer: RefCell::new(buffer)
         })
     }
 }
 
 impl Device for RawSocket {
-    const MTU: usize = 1536;
+    fn mtu(&self) -> usize {
+        let mut lower = self.lower.borrow_mut();
+        lower.interface_mtu().unwrap()
+    }
 
-    fn recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
-        let len = self.lower.recv(&mut self.buffer[..]).unwrap();
-        handler(&self.buffer[..len])
+    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<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F) {
-        handler(&mut self.buffer[..len]);
-        self.lower.send(&self.buffer[..len]).unwrap();
+    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
     }
 }

+ 24 - 14
src/phy/tap_interface.rs

@@ -1,11 +1,13 @@
-use std::{vec, io};
+use std::cell::RefCell;
+use std::vec::Vec;
+use std::io;
 use super::{sys, Device};
 
 /// A virtual Ethernet interface.
 #[derive(Debug)]
 pub struct TapInterface {
-    lower:  sys::TapInterfaceDesc,
-    buffer: vec::Vec<u8>
+    lower:  RefCell<sys::TapInterfaceDesc>,
+    buffer: RefCell<Vec<u8>>
 }
 
 impl TapInterface {
@@ -18,25 +20,33 @@ impl TapInterface {
         let mut lower = try!(sys::TapInterfaceDesc::new(name));
         try!(lower.attach_interface());
 
-        let mut buffer = vec::Vec::new();
-        buffer.resize(Self::MTU, 0);
+        let mut buffer = Vec::new();
+        buffer.resize(1536, 0);
         Ok(TapInterface {
-            lower:  lower,
-            buffer: buffer
+            lower:  RefCell::new(lower),
+            buffer: RefCell::new(buffer)
         })
     }
 }
 
 impl Device for TapInterface {
-    const MTU: usize = 1536;
+    fn mtu(&self) -> usize {
+        let buffer = self.buffer.borrow();
+        buffer.len()
+    }
 
-    fn recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
-        let len = self.lower.recv(&mut self.buffer[..]).unwrap();
-        handler(&self.buffer[..len])
+    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<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F) {
-        handler(&mut self.buffer[..len]);
-        self.lower.send(&self.buffer[..len]).unwrap();
+    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
     }
 }

+ 49 - 46
src/wire/arp.rs

@@ -1,5 +1,6 @@
 use core::fmt;
 use byteorder::{ByteOrder, NetworkEndian};
+use Error;
 
 pub use super::EthernetProtocolType as ProtocolType;
 
@@ -20,7 +21,9 @@ enum_with_unknown! {
 
 /// A read/write wrapper around an Address Resolution Protocol packet.
 #[derive(Debug)]
-pub struct Packet<T: AsRef<[u8]>>(T);
+pub struct Packet<T: AsRef<[u8]>> {
+    buffer: T
+}
 
 mod field {
     #![allow(non_snake_case)]
@@ -61,14 +64,14 @@ mod field {
 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();
+    pub fn new(buffer: T) -> Result<Packet<T>, Error> {
+        let len = buffer.as_ref().len();
         if len < field::OPER.end {
-            Err(())
+            Err(Error::Truncated)
         } else {
-            let packet = Packet(storage);
+            let packet = Packet { buffer: buffer };
             if len < field::TPA(packet.hardware_len(), packet.protocol_len()).end {
-                Err(())
+                Err(Error::Truncated)
             } else {
                 Ok(packet)
             }
@@ -77,96 +80,96 @@ impl<T: AsRef<[u8]>> Packet<T> {
 
     /// Consumes the packet, returning the underlying buffer.
     pub fn into_inner(self) -> T {
-        self.0
+        self.buffer
     }
 
     /// 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]);
+        let data = self.buffer.as_ref();
+        let raw = NetworkEndian::read_u16(&data[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]);
+        let data = self.buffer.as_ref();
+        let raw = NetworkEndian::read_u16(&data[field::PTYPE]);
         ProtocolType::from(raw)
     }
 
     /// Return the hardware length field.
     pub fn hardware_len(&self) -> u8 {
-        let bytes = self.0.as_ref();
-        bytes[field::HLEN]
+        let data = self.buffer.as_ref();
+        data[field::HLEN]
     }
 
     /// Return the protocol length field.
     pub fn protocol_len(&self) -> u8 {
-        let bytes = self.0.as_ref();
-        bytes[field::PLEN]
+        let data = self.buffer.as_ref();
+        data[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]);
+        let data = self.buffer.as_ref();
+        let raw = NetworkEndian::read_u16(&data[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_len(), self.protocol_len())]
+        let data = self.buffer.as_ref();
+        &data[field::SHA(self.hardware_len(), self.protocol_len())]
     }
 
     /// Return the source protocol address field.
     pub fn source_protocol_addr(&self) -> &[u8] {
-        let bytes = self.0.as_ref();
-        &bytes[field::SPA(self.hardware_len(), self.protocol_len())]
+        let data = self.buffer.as_ref();
+        &data[field::SPA(self.hardware_len(), self.protocol_len())]
     }
 
     /// Return the target hardware address field.
     pub fn target_hardware_addr(&self) -> &[u8] {
-        let bytes = self.0.as_ref();
-        &bytes[field::THA(self.hardware_len(), self.protocol_len())]
+        let data = self.buffer.as_ref();
+        &data[field::THA(self.hardware_len(), self.protocol_len())]
     }
 
     /// Return the target protocol address field.
     pub fn target_protocol_addr(&self) -> &[u8] {
-        let bytes = self.0.as_ref();
-        &bytes[field::TPA(self.hardware_len(), self.protocol_len())]
+        let data = self.buffer.as_ref();
+        &data[field::TPA(self.hardware_len(), self.protocol_len())]
     }
 }
 
 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())
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[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())
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::PTYPE], value.into())
     }
 
     /// Set the hardware length field.
     pub fn set_hardware_len(&mut self, value: u8) {
-        let bytes = self.0.as_mut();
-        bytes[field::HLEN] = value
+        let data = self.buffer.as_mut();
+        data[field::HLEN] = value
     }
 
     /// Set the protocol length field.
     pub fn set_protocol_len(&mut self, value: u8) {
-        let bytes = self.0.as_mut();
-        bytes[field::PLEN] = value
+        let data = self.buffer.as_mut();
+        data[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())
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::OPER], value.into())
     }
 
     /// Set the source hardware address field.
@@ -175,8 +178,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
     /// The function panics if `value` is not `self.hardware_len()` long.
     pub fn set_source_hardware_addr(&mut self, value: &[u8]) {
         let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
-        let bytes = self.0.as_mut();
-        bytes[field::SHA(hardware_len, protocol_len)].copy_from_slice(value)
+        let data = self.buffer.as_mut();
+        data[field::SHA(hardware_len, protocol_len)].copy_from_slice(value)
     }
 
     /// Set the source protocol address field.
@@ -185,8 +188,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
     /// The function panics if `value` is not `self.protocol_len()` long.
     pub fn set_source_protocol_addr(&mut self, value: &[u8]) {
         let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
-        let bytes = self.0.as_mut();
-        bytes[field::SPA(hardware_len, protocol_len)].copy_from_slice(value)
+        let data = self.buffer.as_mut();
+        data[field::SPA(hardware_len, protocol_len)].copy_from_slice(value)
     }
 
     /// Set the target hardware address field.
@@ -195,8 +198,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
     /// The function panics if `value` is not `self.hardware_len()` long.
     pub fn set_target_hardware_addr(&mut self, value: &[u8]) {
         let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
-        let bytes = self.0.as_mut();
-        bytes[field::THA(hardware_len, protocol_len)].copy_from_slice(value)
+        let data = self.buffer.as_mut();
+        data[field::THA(hardware_len, protocol_len)].copy_from_slice(value)
     }
 
     /// Set the target protocol address field.
@@ -205,8 +208,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
     /// The function panics if `value` is not `self.protocol_len()` long.
     pub fn set_target_protocol_addr(&mut self, value: &[u8]) {
         let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
-        let bytes = self.0.as_mut();
-        bytes[field::TPA(hardware_len, protocol_len)].copy_from_slice(value)
+        let data = self.buffer.as_mut();
+        data[field::TPA(hardware_len, protocol_len)].copy_from_slice(value)
     }
 }
 
@@ -230,7 +233,7 @@ pub enum Repr {
 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, ()> {
+    pub fn parse<T: AsRef<[u8]>>(packet: &Packet<T>) -> Result<Repr, Error> {
         match (packet.hardware_type(), packet.protocol_type(),
                packet.hardware_len(), packet.protocol_len()) {
             (HardwareType::Ethernet, ProtocolType::Ipv4, 6, 4) => {
@@ -246,7 +249,7 @@ impl Repr {
                         Ipv4Address::from_bytes(packet.target_protocol_addr())
                 })
             },
-            _ => Err(())
+            _ => Err(Error::Unrecognized)
         }
     }
 
@@ -315,7 +318,7 @@ impl<T: AsRef<[u8]>> PrettyPrint<T> for Packet<T> {
     fn pretty_print(buffer: T, f: &mut fmt::Formatter,
                     indent: &mut PrettyIndent) -> fmt::Result {
         match Packet::new(buffer) {
-            Err(())   => write!(f, "{}(truncated)\n", indent),
+            Err(err)  => write!(f, "{}({})\n", indent, err),
             Ok(frame) => write!(f, "{}{}\n", indent, frame)
         }
     }

+ 38 - 35
src/wire/ethernet.rs

@@ -1,5 +1,6 @@
 use core::fmt;
 use byteorder::{ByteOrder, NetworkEndian};
+use Error;
 
 enum_with_unknown! {
     /// Ethernet protocol type.
@@ -57,20 +58,22 @@ impl Address {
 impl fmt::Display for Address {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let bytes = self.0;
-        write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+        write!(f, "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5])
     }
 }
 
 /// A read/write wrapper around an Ethernet II frame.
 #[derive(Debug)]
-pub struct Frame<T: AsRef<[u8]>>(T);
+pub struct Frame<T: AsRef<[u8]>> {
+    buffer: T
+}
 
 mod field {
     use ::wire::field::*;
 
-    pub const SOURCE:      Field     =  0..6;
-    pub const DESTINATION: Field     =  6..12;
+    pub const DESTINATION: Field     =  0..6;
+    pub const SOURCE:      Field     =  6..12;
     pub const ETHERTYPE:   Field     = 12..14;
     pub const PAYLOAD:     FieldFrom = 14..;
 }
@@ -78,77 +81,77 @@ mod field {
 impl<T: AsRef<[u8]>> Frame<T> {
     /// Wrap a buffer with an Ethernet frame. Returns an error if the buffer
     /// is too small or too large to contain one.
-    pub fn new(storage: T) -> Result<Frame<T>, ()> {
-        let len = storage.as_ref().len();
-        if !(14..1518).contains(len) {
-            Err(()) // TODO: error type?
+    pub fn new(buffer: T) -> Result<Frame<T>, Error> {
+        let len = buffer.as_ref().len();
+        if len < field::PAYLOAD.start {
+            Err(Error::Truncated)
         } else {
-            Ok(Frame(storage))
+            Ok(Frame { buffer: buffer })
         }
     }
 
     /// Consumes the frame, returning the underlying buffer.
     pub fn into_inner(self) -> T {
-        self.0
+        self.buffer
     }
 
-    /// Return the source address field.
+    /// Return the destination address field.
     #[inline(always)]
-    pub fn source(&self) -> Address {
-        let bytes = self.0.as_ref();
-        Address::from_bytes(&bytes[field::SOURCE])
+    pub fn destination(&self) -> Address {
+        let data = self.buffer.as_ref();
+        Address::from_bytes(&data[field::DESTINATION])
     }
 
-    /// Return the destination address field.
+    /// Return the source address field.
     #[inline(always)]
-    pub fn destination(&self) -> Address {
-        let bytes = self.0.as_ref();
-        Address::from_bytes(&bytes[field::DESTINATION])
+    pub fn source(&self) -> Address {
+        let data = self.buffer.as_ref();
+        Address::from_bytes(&data[field::SOURCE])
     }
 
     /// Return the EtherType field, without checking for 802.1Q.
     #[inline(always)]
     pub fn ethertype(&self) -> EtherType {
-        let bytes = self.0.as_ref();
-        let raw = NetworkEndian::read_u16(&bytes[field::ETHERTYPE]);
+        let data = self.buffer.as_ref();
+        let raw = NetworkEndian::read_u16(&data[field::ETHERTYPE]);
         EtherType::from(raw)
     }
 
     /// Return a pointer to the payload, without checking for 802.1Q.
     #[inline(always)]
     pub fn payload(&self) -> &[u8] {
-        let bytes = self.0.as_ref();
-        &bytes[field::PAYLOAD]
+        let data = self.buffer.as_ref();
+        &data[field::PAYLOAD]
     }
 }
 
 impl<T: AsRef<[u8]> + AsMut<[u8]>> Frame<T> {
-    /// Set the source address field.
+    /// Set the destination address field.
     #[inline(always)]
-    pub fn set_source(&mut self, value: Address) {
-        let bytes = self.0.as_mut();
-        bytes[field::SOURCE].copy_from_slice(value.as_bytes())
+    pub fn set_destination(&mut self, value: Address) {
+        let data = self.buffer.as_mut();
+        data[field::DESTINATION].copy_from_slice(value.as_bytes())
     }
 
-    /// Set the destination address field.
+    /// Set the source address field.
     #[inline(always)]
-    pub fn set_destination(&mut self, value: Address) {
-        let bytes = self.0.as_mut();
-        bytes[field::DESTINATION].copy_from_slice(value.as_bytes())
+    pub fn set_source(&mut self, value: Address) {
+        let data = self.buffer.as_mut();
+        data[field::SOURCE].copy_from_slice(value.as_bytes())
     }
 
     /// Set the EtherType field.
     #[inline(always)]
     pub fn set_ethertype(&mut self, value: EtherType) {
-        let bytes = self.0.as_mut();
-        NetworkEndian::write_u16(&mut bytes[field::ETHERTYPE], value.into())
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::ETHERTYPE], value.into())
     }
 
     /// Return a mutable pointer to the payload.
     #[inline(always)]
     pub fn payload_mut(&mut self) -> &mut [u8] {
-        let bytes = self.0.as_mut();
-        &mut bytes[field::PAYLOAD]
+        let data = self.buffer.as_mut();
+        &mut data[field::PAYLOAD]
     }
 }
 
@@ -165,7 +168,7 @@ impl<T: AsRef<[u8]>> PrettyPrint<T> for Frame<T> {
     fn pretty_print(buffer: T, f: &mut fmt::Formatter,
                     indent: &mut PrettyIndent) -> fmt::Result {
         let frame = match Frame::new(buffer) {
-            Err(())   => return write!(f, "{}(truncated)\n", indent),
+            Err(err)  => return write!(f, "{}({})\n", indent, err),
             Ok(frame) => frame
         };
         try!(write!(f, "{}{}\n", indent, frame));

+ 34 - 0
src/wire/ipv4.rs

@@ -5,6 +5,8 @@ use core::fmt;
 pub struct Address(pub [u8; 4]);
 
 impl Address {
+    pub const BROADCAST: Address = Address([255; 4]);
+
     /// Construct an IPv4 address from a sequence of octets, in big-endian.
     ///
     /// # Panics
@@ -19,6 +21,38 @@ impl Address {
     pub fn as_bytes(&self) -> &[u8] {
         &self.0
     }
+
+    /// Query whether the address is an unicast address.
+    pub fn is_unicast(&self) -> bool {
+        !(self.is_broadcast() ||
+          self.is_multicast() ||
+          self.is_unspecified())
+    }
+
+    /// Query whether the address is the broadcast address.
+    pub fn is_broadcast(&self) -> bool {
+        self.0[0..4] == [255; 4]
+    }
+
+    /// Query whether the address is a multicast address.
+    pub fn is_multicast(&self) -> bool {
+        self.0[0] & 0xf0 == 224
+    }
+
+    /// Query whether the address falls into the "unspecified" range.
+    pub fn is_unspecified(&self) -> bool {
+        self.0[0] == 0
+    }
+
+    /// Query whether the address falls into the "link-local" range.
+    pub fn is_link_local(&self) -> bool {
+        self.0[0..2] == [169, 254]
+    }
+
+    /// Query whether the address falls into the "loopback" range.
+    pub fn is_loopback(&self) -> bool {
+        self.0[0] == 127
+    }
 }
 
 impl fmt::Display for Address {