More generic

This commit is contained in:
Dennis Schwerdel 2015-11-23 01:40:47 +01:00
parent a559a10155
commit 30fab51be6
6 changed files with 92 additions and 172 deletions

View File

@ -6,16 +6,14 @@ use std::io::Read;
use std::fmt; use std::fmt;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::str::FromStr;
use time::{Duration, SteadyTime, precise_time_ns}; use time::{Duration, SteadyTime, precise_time_ns};
use epoll; use epoll;
use super::types::{Table, Protocol, VirtualInterface, Range, Error, NetworkId, Behavior}; use super::types::{Table, Protocol, VirtualInterface, Range, Error, NetworkId};
use super::device::{TunDevice, TapDevice}; use super::device::Device;
use super::udpmessage::{encode, decode, Options, Message}; use super::udpmessage::{encode, decode, Options, Message};
use super::ethernet::{Frame, MacTable}; use super::{ethernet, ip};
use super::ip::{InternetProtocol, RoutingTable};
struct PeerList { struct PeerList {
timeout: Duration, timeout: Duration,
@ -85,25 +83,25 @@ impl PeerList {
} }
pub struct GenericCloud<T: Table, P: Protocol, I: VirtualInterface> { pub struct GenericCloud<P: Protocol> {
peers: PeerList, peers: PeerList,
addresses: Vec<Range>, addresses: Vec<Range>,
learning: bool, learning: bool,
broadcast: bool, broadcast: bool,
reconnect_peers: Vec<SocketAddr>, reconnect_peers: Vec<SocketAddr>,
table: T, table: Box<Table>,
socket: UdpSocket, socket: UdpSocket,
device: I, device: Device,
network_id: Option<NetworkId>, network_id: Option<NetworkId>,
next_peerlist: SteadyTime, next_peerlist: SteadyTime,
update_freq: Duration, update_freq: Duration,
buffer_out: [u8; 64*1024], buffer_out: [u8; 64*1024],
next_housekeep: SteadyTime, next_housekeep: SteadyTime,
_dummy_m: PhantomData<P>, _dummy_p: PhantomData<P>,
} }
impl<T: Table, P: Protocol, I: VirtualInterface> GenericCloud<T, P, I> { impl<P: Protocol> GenericCloud<P> {
pub fn new(device: I, listen: String, network_id: Option<NetworkId>, table: T, pub fn new(device: Device, listen: String, network_id: Option<NetworkId>, table: Box<Table>,
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>) -> Self { peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>) -> Self {
let socket = match UdpSocket::bind(&listen as &str) { let socket = match UdpSocket::bind(&listen as &str) {
Ok(socket) => socket, Ok(socket) => socket,
@ -123,7 +121,7 @@ impl<T: Table, P: Protocol, I: VirtualInterface> GenericCloud<T, P, I> {
update_freq: peer_timeout/2, update_freq: peer_timeout/2,
buffer_out: [0; 64*1024], buffer_out: [0; 64*1024],
next_housekeep: SteadyTime::now(), next_housekeep: SteadyTime::now(),
_dummy_m: PhantomData, _dummy_p: PhantomData,
} }
} }
@ -311,47 +309,5 @@ impl<T: Table, P: Protocol, I: VirtualInterface> GenericCloud<T, P, I> {
} }
pub type TapCloud = GenericCloud<MacTable, Frame, TapDevice>; pub type TapCloud = GenericCloud<ethernet::Frame>;
pub type TunCloud = GenericCloud<ip::Packet>;
impl TapCloud {
pub fn new_tap_cloud(device: &str, listen: String, behavior: Behavior, network_id: Option<NetworkId>, mac_timeout: Duration, peer_timeout: Duration) -> Self {
let device = match TapDevice::new(device) {
Ok(device) => device,
_ => panic!("Failed to open tap device")
};
info!("Opened tap device {}", device.ifname());
let table = MacTable::new(mac_timeout);
let (learning, broadcasting) = match behavior {
Behavior::Normal => (true, true),
Behavior::Switch => (true, true),
Behavior::Hub => (false, true),
Behavior::Router => (false, false)
};
Self::new(device, listen, network_id, table, peer_timeout, learning, broadcasting, vec![])
}
}
pub type TunCloud = GenericCloud<RoutingTable, InternetProtocol, TunDevice>;
impl TunCloud {
pub fn new_tun_cloud(device: &str, listen: String, behavior: Behavior, network_id: Option<NetworkId>, range_strs: Vec<String>, peer_timeout: Duration) -> Self {
let device = match TunDevice::new(device) {
Ok(device) => device,
_ => panic!("Failed to open tun device")
};
info!("Opened tun device {}", device.ifname());
let table = RoutingTable::new();
let mut ranges = Vec::with_capacity(range_strs.len());
for s in range_strs {
ranges.push(Range::from_str(&s).expect("Invalid subnet"));
}
let (learning, broadcasting) = match behavior {
Behavior::Normal => (false, false),
Behavior::Switch => (true, true),
Behavior::Hub => (false, true),
Behavior::Router => (false, false)
};
Self::new(device, listen, network_id, table, peer_timeout, learning, broadcasting, ranges)
}
}

View File

@ -1,9 +1,8 @@
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::io::{Result as IoResult, Error as IoError, Read, Write}; use std::io::{Result as IoResult, Error as IoError, Read, Write};
use std::marker::PhantomData;
use std::fs; use std::fs;
use super::types::{Error, VirtualInterface}; use super::types::{Error, VirtualInterface, Type};
extern { extern {
fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32; fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32;
@ -11,56 +10,26 @@ extern {
} }
trait DeviceSetup { pub struct Device {
fn setup_device(RawFd, &str) -> IoResult<String>;
}
#[allow(dead_code)]
struct TapSetup;
impl DeviceSetup for TapSetup {
fn setup_device(fd: RawFd, ifname: &str) -> IoResult<String> {
let mut ifname_string = String::with_capacity(32);
ifname_string.push_str(ifname);
ifname_string.push('\0');
let mut ifname_c = ifname_string.into_bytes();
let res = unsafe { setup_tap_device(fd, ifname_c.as_mut_ptr()) };
match res {
0 => Ok(String::from_utf8(ifname_c).unwrap()),
_ => Err(IoError::last_os_error())
}
}
}
#[allow(dead_code)]
struct TunSetup;
impl DeviceSetup for TunSetup {
fn setup_device(fd: RawFd, ifname: &str) -> IoResult<String> {
let mut ifname_string = String::with_capacity(32);
ifname_string.push_str(ifname);
ifname_string.push('\0');
let mut ifname_c = ifname_string.into_bytes();
let res = unsafe { setup_tun_device(fd, ifname_c.as_mut_ptr()) };
match res {
0 => Ok(String::from_utf8(ifname_c).unwrap()),
_ => Err(IoError::last_os_error())
}
}
}
pub struct Device<T> {
fd: fs::File, fd: fs::File,
ifname: String, ifname: String
_dummy_t: PhantomData<T>
} }
impl<T: DeviceSetup> Device<T> { impl Device {
pub fn new(ifname: &str) -> IoResult<Self> { pub fn new(ifname: &str, type_: Type) -> IoResult<Self> {
let fd = try!(fs::OpenOptions::new().read(true).write(true).open("/dev/net/tun")); let fd = try!(fs::OpenOptions::new().read(true).write(true).open("/dev/net/tun"));
let ifname = try!(T::setup_device(fd.as_raw_fd(), ifname)); let mut ifname_string = String::with_capacity(32);
Ok(Device{fd: fd, ifname: ifname, _dummy_t: PhantomData}) ifname_string.push_str(ifname);
ifname_string.push('\0');
let mut ifname_c = ifname_string.into_bytes();
let res = match type_ {
Type::Tun => unsafe { setup_tun_device(fd.as_raw_fd(), ifname_c.as_mut_ptr()) },
Type::Tap => unsafe { setup_tap_device(fd.as_raw_fd(), ifname_c.as_mut_ptr()) }
};
match res {
0 => Ok(Device{fd: fd, ifname: String::from_utf8(ifname_c).unwrap()}),
_ => Err(IoError::last_os_error())
}
} }
#[inline(always)] #[inline(always)]
@ -69,13 +38,13 @@ impl<T: DeviceSetup> Device<T> {
} }
} }
impl<T> AsRawFd for Device<T> { impl AsRawFd for Device {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd() self.fd.as_raw_fd()
} }
} }
impl<T> VirtualInterface for Device<T> { impl VirtualInterface for Device {
fn read(&mut self, mut buffer: &mut [u8]) -> Result<usize, Error> { fn read(&mut self, mut buffer: &mut [u8]) -> Result<usize, Error> {
self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error")) self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error"))
} }
@ -87,6 +56,3 @@ impl<T> VirtualInterface for Device<T> {
} }
} }
} }
pub type TapDevice = Device<TapSetup>;
pub type TunDevice = Device<TunSetup>;

View File

@ -51,24 +51,23 @@ impl Protocol for Frame {
} }
struct MacTableValue { struct SwitchTableValue {
address: SocketAddr, address: SocketAddr,
timeout: SteadyTime timeout: SteadyTime
} }
pub struct SwitchTable {
pub struct MacTable { table: HashMap<Address, SwitchTableValue>,
table: HashMap<Address, MacTableValue>,
timeout: Duration timeout: Duration
} }
impl MacTable { impl SwitchTable {
pub fn new(timeout: Duration) -> MacTable { pub fn new(timeout: Duration) -> Self {
MacTable{table: HashMap::new(), timeout: timeout} SwitchTable{table: HashMap::new(), timeout: timeout}
} }
} }
impl Table for MacTable { impl Table for SwitchTable {
fn housekeep(&mut self) { fn housekeep(&mut self) {
let now = SteadyTime::now(); let now = SteadyTime::now();
let mut del: Vec<Address> = Vec::new(); let mut del: Vec<Address> = Vec::new();
@ -84,7 +83,7 @@ impl Table for MacTable {
} }
fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) { fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) {
let value = MacTableValue{address: addr, timeout: SteadyTime::now()+self.timeout}; let value = SwitchTableValue{address: addr, timeout: SteadyTime::now()+self.timeout};
if self.table.insert(key.clone(), value).is_none() { if self.table.insert(key.clone(), value).is_none() {
info!("Learned address {:?} => {}", key, addr); info!("Learned address {:?} => {}", key, addr);
} }

View File

@ -6,9 +6,9 @@ use super::types::{Protocol, Error, Table, Address};
use super::util::to_vec; use super::util::to_vec;
#[allow(dead_code)] #[allow(dead_code)]
pub struct InternetProtocol; pub struct Packet;
impl Protocol for InternetProtocol { impl Protocol for Packet {
fn parse(data: &[u8]) -> Result<(Address, Address), Error> { fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
if data.len() < 1 { if data.len() < 1 {
return Err(Error::ParseError("Empty header")); return Err(Error::ParseError("Empty header"));

View File

@ -16,13 +16,16 @@ use time::Duration;
use docopt::Docopt; use docopt::Docopt;
use std::hash::{Hash, SipHasher, Hasher}; use std::hash::{Hash, SipHasher, Hasher};
use std::str::FromStr;
use types::{Error, Behavior}; use device::Device;
use ethernet::SwitchTable;
use ip::RoutingTable;
use types::{Error, Behavior, Type, Range, Table};
use cloud::{TapCloud, TunCloud}; use cloud::{TapCloud, TunCloud};
//TODO: hub behavior //TODO: L2 routing
//TODO: L2 routing/L3 switching
//TODO: Implement IPv6 //TODO: Implement IPv6
//TODO: Encryption //TODO: Encryption
//TODO: Call close //TODO: Call close
@ -54,18 +57,13 @@ Options:
-c <connect>, --connect <connect> List of peers (addr:port) to connect to -c <connect>, --connect <connect> List of peers (addr:port) to connect to
--network-id <network_id> Optional token that identifies the network --network-id <network_id> Optional token that identifies the network
--peer-timeout <peer_timeout> Peer timeout in seconds [default: 1800] --peer-timeout <peer_timeout> Peer timeout in seconds [default: 1800]
--subnet <subnet>... The local subnets to use (only for tun) --subnet <subnet>... The local subnets to use
--mac-timeout <mac_timeout> Mac table entry timeout in seconds (only for tap) [default: 300] --dst-timeout <dst_timeout> Switch table entry timeout in seconds [default: 300]
-v, --verbose Log verbosely -v, --verbose Log verbosely
-q, --quiet Only print error messages -q, --quiet Only print error messages
-h, --help Display the help -h, --help Display the help
"; ";
#[derive(RustcDecodable, Debug)]
enum Type {
Tun, Tap
}
#[derive(RustcDecodable, Debug)] #[derive(RustcDecodable, Debug)]
struct Args { struct Args {
flag_type: Type, flag_type: Type,
@ -76,49 +74,11 @@ struct Args {
flag_network_id: Option<String>, flag_network_id: Option<String>,
flag_connect: Vec<String>, flag_connect: Vec<String>,
flag_peer_timeout: usize, flag_peer_timeout: usize,
flag_mac_timeout: usize, flag_dst_timeout: usize,
flag_verbose: bool, flag_verbose: bool,
flag_quiet: bool flag_quiet: bool
} }
fn tap_cloud(args: Args) {
let mut tapcloud = TapCloud::new_tap_cloud(
&args.flag_device,
args.flag_listen,
args.flag_behavior,
args.flag_network_id.map(|name| {
let mut s = SipHasher::new();
name.hash(&mut s);
s.finish()
}),
Duration::seconds(args.flag_mac_timeout as i64),
Duration::seconds(args.flag_peer_timeout as i64)
);
for addr in args.flag_connect {
tapcloud.connect(&addr as &str, true).expect("Failed to send");
}
tapcloud.run()
}
fn tun_cloud(args: Args) {
let mut tuncloud = TunCloud::new_tun_cloud(
&args.flag_device,
args.flag_listen,
args.flag_behavior,
args.flag_network_id.map(|name| {
let mut s = SipHasher::new();
name.hash(&mut s);
s.finish()
}),
args.flag_subnet,
Duration::seconds(args.flag_peer_timeout as i64)
);
for addr in args.flag_connect {
tuncloud.connect(&addr as &str, true).expect("Failed to send");
}
tuncloud.run()
}
fn main() { fn main() {
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
log::set_logger(|max_log_level| { log::set_logger(|max_log_level| {
@ -133,8 +93,42 @@ fn main() {
Box::new(SimpleLogger) Box::new(SimpleLogger)
}).unwrap(); }).unwrap();
debug!("Args: {:?}", args); debug!("Args: {:?}", args);
match args.flag_type { let device = Device::new(&args.flag_device, args.flag_type).expect("Failed to open virtual interface");
Type::Tap => tap_cloud(args), info!("Opened device {}", device.ifname());
Type::Tun => tun_cloud(args) let mut ranges = Vec::with_capacity(args.flag_subnet.len());
for s in args.flag_subnet {
ranges.push(Range::from_str(&s).expect("Invalid subnet"));
} }
let dst_timeout = Duration::seconds(args.flag_dst_timeout as i64);
let peer_timeout = Duration::seconds(args.flag_peer_timeout as i64);
let (learning, broadcasting, table): (bool, bool, Box<Table>) = match args.flag_behavior {
Behavior::Normal => match args.flag_type {
Type::Tap => (true, true, Box::new(SwitchTable::new(dst_timeout))),
Type::Tun => (false, false, Box::new(RoutingTable::new()))
},
Behavior::Router => (false, false, Box::new(RoutingTable::new())),
Behavior::Switch => (true, true, Box::new(SwitchTable::new(dst_timeout))),
Behavior::Hub => (false, true, Box::new(SwitchTable::new(dst_timeout)))
};
let network_id = args.flag_network_id.map(|name| {
let mut s = SipHasher::new();
name.hash(&mut s);
s.finish()
});
match args.flag_type {
Type::Tap => {
let mut cloud = TapCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges);
for addr in args.flag_connect {
cloud.connect(&addr as &str, true).expect("Failed to send");
}
cloud.run()
},
Type::Tun => {
let mut cloud = TunCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges);
for addr in args.flag_connect {
cloud.connect(&addr as &str, true).expect("Failed to send");
}
cloud.run()
}
};
} }

View File

@ -80,6 +80,11 @@ impl FromStr for Range {
} }
#[derive(RustcDecodable, Debug, Clone, Copy)]
pub enum Type {
Tun, Tap
}
#[derive(RustcDecodable, Debug)] #[derive(RustcDecodable, Debug)]
pub enum Behavior { pub enum Behavior {
Normal, Hub, Switch, Router Normal, Hub, Switch, Router