Documentation and code cleanup

pull/9/head
Dennis Schwerdel 2016-06-26 19:18:38 +02:00
parent e31e5c362d
commit 97af7bcef4
5 changed files with 153 additions and 27 deletions

View File

@ -5,8 +5,9 @@
use std::os::unix::io::{AsRawFd, RawFd};
use std::io::{Result as IoResult, Error as IoError, Read, Write};
use std::fs;
use std::fmt;
use super::types::{Error, Type};
use super::types::Error;
extern {
fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32;
@ -14,14 +15,55 @@ extern {
}
/// The type of a tun/tap device
#[derive(RustcDecodable, Debug, Clone, Copy)]
pub enum Type {
/// Tun interface: This interface transports IP packets.
Tun,
/// Tap interface: This insterface transports Ethernet frames.
Tap
}
impl fmt::Display for Type {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Type::Tun => write!(formatter, "tun"),
Type::Tap => write!(formatter, "tap"),
}
}
}
/// Represents a tun/tap device
pub struct Device {
fd: fs::File,
ifname: String
ifname: String,
type_: Type,
}
impl Device {
/// Creates a new tun/tap device
///
/// This method creates a new device of the `type_` kind with the name `ifname`.
///
/// The `ifname` must be an interface name not longer than 31 bytes. It can contain the string
/// `%d` which will be replaced with the next free index number that guarantees that the
/// interface name will be free. In this case, the `ifname()` method can be used to obtain the
/// final interface name.
///
/// # Errors
/// This method will return an error when the underlying system call fails. Common cases are:
/// - The special device file `/dev/net/tun` does not exist or is not accessible by the current
/// user.
/// - The interface name is invalid or already in use.
/// - The current user does not have enough permissions to create tun/tap devices (this
/// requires root permissions).
///
/// # Panics
/// This method panics if the interface name is longer than 31 bytes.
pub fn new(ifname: &str, type_: Type) -> IoResult<Self> {
let fd = try!(fs::OpenOptions::new().read(true).write(true).open("/dev/net/tun"));
// Add trailing \0 to interface name
let mut ifname_string = String::with_capacity(32);
ifname_string.push_str(ifname);
ifname_string.push('\0');
@ -33,30 +75,75 @@ impl Device {
};
match res {
0 => {
// Remove trailing \0 from name
while ifname_c.last() == Some(&0) {
ifname_c.pop();
}
Ok(Device{fd: fd, ifname: String::from_utf8(ifname_c).unwrap()})
Ok(Device{fd: fd, ifname: String::from_utf8(ifname_c).unwrap(), type_: type_})
},
_ => Err(IoError::last_os_error())
}
}
#[allow(dead_code)]
pub fn dummy(ifname: &str, path: &str) -> IoResult<Self> {
Ok(Device{fd: try!(fs::OpenOptions::new().create(true).read(true).write(true).open(path)), ifname: ifname.to_string()})
}
/// Returns the interface name of this device.
#[inline]
pub fn ifname(&self) -> &str {
&self.ifname
}
/// Returns the type of this device
#[allow(dead_code)]
#[inline]
pub fn get_type(&self) -> Type {
self.type_
}
/// Creates a dummy device based on an existing file
///
/// This method opens a regular or special file and reads from it to receive packets and
/// writes to it to send packets. This method does not use a networking device and therefore
/// can be used for testing.
///
/// The parameter `path` is the file that should be used. Special files like `/dev/null`,
/// named pipes and unix sockets can be used with this method.
///
/// Both `ifname` and `type_` parameters have no effect.
///
/// # Errors
/// This method will return an error if the file can not be opened for reading and writing.
#[allow(dead_code)]
pub fn dummy(ifname: &str, path: &str, type_: Type) -> IoResult<Self> {
Ok(Device{
fd: try!(fs::OpenOptions::new().create(true).read(true).write(true).open(path)),
ifname: ifname.to_string(),
type_: type_
})
}
/// Reads a packet/frame from the device
///
/// This method reads one packet or frame (depending on the device type) into the `buffer`.
/// The `buffer` must be large enough to hold a packet/frame of maximum size, otherwise the
/// packet/frame will be split.
/// The method will block until a packet/frame is ready to be read.
/// On success, the method will return the amount of bytes read into the buffer (starting at
/// position 0).
///
/// # Errors
/// This method will return an error if the underlying read call fails.
#[inline]
pub fn read(&mut self, mut buffer: &mut [u8]) -> Result<usize, Error> {
self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error"))
}
/// Writes a packet/frame to the device
///
/// This method writes one packet or frame (depending on the device type) from `data` to the
/// device.
/// The method will block until the packet/frame has been written.
///
/// # Errors
/// This method will return an error if the underlying read call fails.
#[inline]
pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
match self.fd.write_all(data) {

View File

@ -11,9 +11,19 @@ use fnv::FnvHasher;
use super::types::{Error, Table, Protocol, Address};
use super::util::{now, Time, Duration};
/// An ethernet frame dissector
///
/// This dissector is able to extract the source and destination addresses of ethernet frames.
///
/// If the ethernet frame contains a VLAN tag, both addresses will be prefixed with that tag,
/// resulting in 8-byte addresses. Additional nested tags will be ignored.
pub struct Frame;
impl Protocol for Frame {
/// Parses an ethernet frame and extracts the source and destination addresses
///
/// # Errors
/// This method will fail when the given data is not a valid ethernet frame.
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
if data.len() < 14 {
return Err(Error::ParseError("Frame is too short"));
@ -51,19 +61,27 @@ struct SwitchTableValue {
type Hash = BuildHasherDefault<FnvHasher>;
/// A table used to implement a learning switch
///
/// This table is a simple hash map between an address and the destination peer. It learns
/// addresses as they are seen and forgets them after some time.
pub struct SwitchTable {
/// The table storing the actual mapping
table: HashMap<Address, SwitchTableValue, Hash>,
cache: Option<(Address, SocketAddr)>,
/// Timeout period for forgetting learnt addresses
timeout: Duration
}
impl SwitchTable {
/// Creates a new switch table
pub fn new(timeout: Duration) -> Self {
SwitchTable{table: HashMap::default(), cache: None, timeout: timeout}
SwitchTable{table: HashMap::default(), timeout: timeout}
}
}
impl Table for SwitchTable {
/// Forget addresses that have not been seen for the configured timeout
fn housekeep(&mut self) {
let now = now();
let mut del: Vec<Address> = Vec::new();
@ -76,9 +94,9 @@ impl Table for SwitchTable {
info!("Forgot address {}", key);
self.table.remove(&key);
}
self.cache = None;
}
/// Learns the given address, inserting it in the hash map
#[inline]
fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) {
let value = SwitchTableValue{address: addr, timeout: now()+self.timeout as Time};
@ -87,6 +105,7 @@ impl Table for SwitchTable {
}
}
/// Retrieves a peer for an address if it is inside the hash map
#[inline]
fn lookup(&mut self, key: &Address) -> Option<SocketAddr> {
match self.table.get(key) {
@ -95,11 +114,13 @@ impl Table for SwitchTable {
}
}
/// Removes an address from the map and returns whether something has been removed
#[inline]
fn remove(&mut self, key: &Address) -> bool {
self.table.remove(key).is_some()
}
/// Removed all addresses associated with a certain peer
fn remove_all(&mut self, addr: &SocketAddr) {
let mut remove = Vec::new();
for (key, val) in &self.table {

View File

@ -11,10 +11,18 @@ use fnv::FnvHasher;
use super::types::{Protocol, Error, Table, Address};
/// An IP packet dissector
///
/// This dissector is able to extract the source and destination ip addresses of ipv4 packets and
/// ipv6 packets.
#[allow(dead_code)]
pub struct Packet;
impl Protocol for Packet {
/// Parses an ip packet and extracts the source and destination addresses
///
/// # Errors
/// This method will fail when the given data is not a valid ipv4 and ipv6 packet.
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
if data.len() < 1 {
return Err(Error::ParseError("Empty header"));
@ -51,39 +59,56 @@ struct RoutingEntry {
type Hash = BuildHasherDefault<FnvHasher>;
/// A prefix-based routing table
///
/// This table contains a mapping of prefixes associated with peer addresses.
/// To speed up lookup, prefixes are grouped into full bytes and map to a list of prefixes with
/// more fine grained prefixes.
pub struct RoutingTable(HashMap<Vec<u8>, Vec<RoutingEntry>, Hash>);
impl RoutingTable {
/// Creates a new empty routing table
pub fn new() -> Self {
RoutingTable(HashMap::default())
}
}
impl Table for RoutingTable {
/// Learns the given address, inserting it in the hash map
fn learn(&mut self, addr: Address, prefix_len: Option<u8>, address: SocketAddr) {
// If prefix length is not set, treat the whole addess as significant
let prefix_len = match prefix_len {
Some(val) => val,
None => addr.len * 8
};
info!("New routing entry: {}/{} => {}", addr, prefix_len, address);
// Round the prefix length down to the next multiple of 8 and extraxt a prefix of that
// length.
let group_len = prefix_len as usize / 8;
assert!(group_len <= 16);
let mut group_bytes = Vec::with_capacity(group_len);
group_bytes.extend_from_slice(&addr.data[0..group_len]);
// Create an entry
let routing_entry = RoutingEntry{address: address, bytes: addr.data, prefix_len: prefix_len};
// Add the entry to the routing table, creating a new list of the prefix group is empty.
match self.0.entry(group_bytes) {
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(routing_entry),
hash_map::Entry::Vacant(entry) => { entry.insert(vec![routing_entry]); () }
}
}
/// Retrieves a peer for an address if it is inside the routing table
fn lookup(&mut self, addr: &Address) -> Option<SocketAddr> {
let len = addr.len as usize;
let mut found = None;
let mut found_len: isize = -1;
// Iterate over the prefix length from longest prefix group to shortest (empty) prefix
// group
for i in 0..len+1 {
if let Some(group) = self.0.get(&addr.data[0..len-i]) {
// If the group is not empty, check every entry
for entry in group {
// Calculate the match length of the address and the prefix
let mut match_len = 0;
for j in 0..addr.len as usize {
let b = addr.data[j] ^ entry.bytes[j];
@ -94,6 +119,8 @@ impl Table for RoutingTable {
break;
}
}
// If the full prefix matches and the match is longer than the longest prefix
// found so far, remember the peer
if match_len as u8 >= entry.prefix_len && match_len as isize > found_len {
found = Some(entry.address);
found_len = match_len as isize;
@ -101,13 +128,16 @@ impl Table for RoutingTable {
}
}
}
// Return the longest match found (if any).
found
}
/// This method does not do anything.
fn housekeep(&mut self) {
//nothing to do
}
/// Removes an address from the map and returns whether something has been removed
#[inline]
fn remove(&mut self, addr: &Address) -> bool {
let len = addr.len as usize;
@ -139,6 +169,7 @@ impl Table for RoutingTable {
found
}
/// Removed all addresses associated with a certain peer
fn remove_all(&mut self, addr: &SocketAddr) {
for (_key, entry) in &mut self.0 {
entry.retain(|entr| &entr.address != addr);

View File

@ -3,6 +3,7 @@
// This software is licensed under GPL-3 or newer (see LICENSE.md)
#![cfg_attr(feature = "bench", feature(test))]
#[macro_use] extern crate log;
extern crate time;
extern crate docopt;
@ -34,10 +35,10 @@ use std::hash::{Hash, SipHasher, Hasher};
use std::str::FromStr;
use std::process::Command;
use device::Device;
use device::{Device, Type};
use ethernet::SwitchTable;
use ip::RoutingTable;
use types::{Mode, Type, Range, Table, Protocol};
use types::{Mode, Range, Table, Protocol};
use cloud::GenericCloud;
use udpmessage::VERSION;
use crypto::{Crypto, CryptoMethod};

View File

@ -192,20 +192,6 @@ impl fmt::Debug for Range {
}
#[derive(RustcDecodable, Debug, Clone, Copy)]
pub enum Type {
Tun, Tap
}
impl fmt::Display for Type {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Type::Tun => write!(formatter, "tun"),
Type::Tap => write!(formatter, "tap"),
}
}
}
#[derive(RustcDecodable, Debug, Clone, Copy)]
pub enum Mode {
Normal, Hub, Switch, Router