Implement ARP replies.
parent
d862512582
commit
4421b2fe27
40
README.md
40
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
|
||||
-------
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
27
src/lib.rs
27
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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
|
||||
let len = self.lower.recv(&mut self.buffer[..]).unwrap();
|
||||
handler(&self.buffer[..len])
|
||||
fn mtu(&self) -> usize {
|
||||
let mut lower = self.lower.borrow_mut();
|
||||
lower.interface_mtu().unwrap()
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
|
||||
let len = self.lower.recv(&mut self.buffer[..]).unwrap();
|
||||
handler(&self.buffer[..len])
|
||||
fn mtu(&self) -> usize {
|
||||
let buffer = self.buffer.borrow();
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/// Return the source address field.
|
||||
#[inline(always)]
|
||||
pub fn source(&self) -> Address {
|
||||
let bytes = self.0.as_ref();
|
||||
Address::from_bytes(&bytes[field::SOURCE])
|
||||
self.buffer
|
||||
}
|
||||
|
||||
/// Return the destination address field.
|
||||
#[inline(always)]
|
||||
pub fn destination(&self) -> Address {
|
||||
let bytes = self.0.as_ref();
|
||||
Address::from_bytes(&bytes[field::DESTINATION])
|
||||
let data = self.buffer.as_ref();
|
||||
Address::from_bytes(&data[field::DESTINATION])
|
||||
}
|
||||
|
||||
/// Return the source address field.
|
||||
#[inline(always)]
|
||||
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.
|
||||
#[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())
|
||||
}
|
||||
|
||||
/// Set the destination 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())
|
||||
let data = self.buffer.as_mut();
|
||||
data[field::DESTINATION].copy_from_slice(value.as_bytes())
|
||||
}
|
||||
|
||||
/// Set the source address field.
|
||||
#[inline(always)]
|
||||
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));
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue