diff --git a/Cargo.toml b/Cargo.toml index b70d077..3510d08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ log = "0.3" epoll = "0.2" signal = "0.1" nix = "*" +libc = "*" libsodium-sys = {version = "0.0.9", optional = true} [build-dependencies] diff --git a/performance.md b/performance.md index f13e4b7..22bfcf1 100644 --- a/performance.md +++ b/performance.md @@ -41,10 +41,10 @@ $> iperf -c 10.2.1.2 -t 60 **Results:** * Throughput without VpnCloud: 938 Mbits/sec - * Throughput via VpnCloud (MTU=1400): 363 Mbits/sec + * Throughput via VpnCloud (MTU=1400): 461 Mbits/sec * CPU usage for VpnCloud (MTU=1400): maxed out at ~105% of one core - * Throughput via VpnCloud (MTU=16384): 946 Mbits/sec (no idea why this is higher) - * CPU usage for VpnCloud (MTU=16384): ~73% of one core + * Throughput via VpnCloud (MTU=16384): 949 Mbits/sec (no idea why this is higher) + * CPU usage for VpnCloud (MTU=16384): ~68% of one core ### Test 2: Unencrypted ping @@ -86,6 +86,6 @@ SIZE: 1400 bytes ### Conclusion -* VpnCloud achieves about 360 MBit/s with default MTU settings. +* VpnCloud achieves about 460 MBit/s with default MTU settings. * At increased MTU, VpnCloud is able to saturate a Gigabit link. * VpnCloud adds about 120µs to the round trip times, i.e. 60µs latency increase. diff --git a/src/benches.rs b/src/benches.rs index a3d1ad1..3413e15 100644 --- a/src/benches.rs +++ b/src/benches.rs @@ -1,6 +1,6 @@ use test::Bencher; -use time::{SteadyTime, Duration}; +use time::Duration; use std::str::FromStr; use std::net::ToSocketAddrs; @@ -61,7 +61,8 @@ fn switch_lookup(b: &mut Bencher) { #[bench] fn ethernet_parse(b: &mut Bencher) { - let data = [0; 1500]; + let mut data = [0; 1500]; + data[5] = 45; b.iter(|| { Frame::parse(&data).unwrap() }) diff --git a/src/cloud.rs b/src/cloud.rs index 95764d4..5107f37 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -7,19 +7,20 @@ use std::fmt; use std::os::unix::io::AsRawFd; use std::marker::PhantomData; -use time::{Duration, SteadyTime, precise_time_ns}; use epoll; use nix::sys::signal::{SIGTERM, SIGQUIT, SIGINT}; use signal::trap::Trap; +use time::SteadyTime; use super::types::{Table, Protocol, Range, Error, NetworkId}; use super::device::Device; use super::udpmessage::{encode, decode, Options, Message}; use super::crypto::Crypto; +use super::util::{now, Time, Duration, time_rand}; struct PeerList { timeout: Duration, - peers: HashMap + peers: HashMap } impl PeerList { @@ -28,7 +29,7 @@ impl PeerList { } fn timeout(&mut self) -> Vec { - let now = SteadyTime::now(); + let now = now(); let mut del: Vec = Vec::new(); for (&addr, &timeout) in &self.peers { if timeout < now { @@ -49,7 +50,7 @@ impl PeerList { #[inline] fn add(&mut self, addr: &SocketAddr) { - if self.peers.insert(*addr, SteadyTime::now()+self.timeout).is_none() { + if self.peers.insert(*addr, now()+self.timeout as Time).is_none() { info!("New peer: {:?}", addr); } } @@ -96,10 +97,10 @@ pub struct GenericCloud { device: Device, options: Options, crypto: Crypto, - next_peerlist: SteadyTime, + next_peerlist: Time, update_freq: Duration, buffer_out: [u8; 64*1024], - next_housekeep: SteadyTime, + next_housekeep: Time, _dummy_p: PhantomData

, } @@ -124,10 +125,10 @@ impl GenericCloud

{ device: device, options: options, crypto: crypto, - next_peerlist: SteadyTime::now(), + next_peerlist: now(), update_freq: peer_timeout/2, buffer_out: [0; 64*1024], - next_housekeep: SteadyTime::now(), + next_housekeep: now(), _dummy_p: PhantomData, } } @@ -169,7 +170,7 @@ impl GenericCloud

{ fn housekeep(&mut self) -> Result<(), Error> { self.peers.timeout(); self.table.housekeep(); - if self.next_peerlist <= SteadyTime::now() { + if self.next_peerlist <= now() { debug!("Send peer list to all peers"); let mut peer_num = self.peers.len(); if peer_num > 10 { @@ -178,12 +179,12 @@ impl GenericCloud

{ peer_num = 10; } } - let peers = self.peers.subset(peer_num, precise_time_ns() as u32); + let peers = self.peers.subset(peer_num, time_rand() as u32); let msg = Message::Peers(peers); for addr in &self.peers.as_vec() { try!(self.send_msg(addr, &msg)); } - self.next_peerlist = SteadyTime::now() + self.update_freq; + self.next_peerlist = now() + self.update_freq as Time; } for addr in self.reconnect_peers.clone() { try!(self.connect(addr, false)); @@ -272,6 +273,7 @@ impl GenericCloud

{ } pub fn run(&mut self) { + let dummy_time = SteadyTime::now(); let trap = Trap::trap(&[SIGINT, SIGTERM, SIGQUIT]); let epoll_handle = try_fail!(epoll::create1(0), "Failed to create epoll handle: {}"); let socket_fd = self.socket.as_raw_fd(); @@ -284,10 +286,6 @@ impl GenericCloud

{ let mut buffer = [0; 64*1024]; loop { let count = try_fail!(epoll::wait(epoll_handle, &mut events, 1000), "Epoll wait failed: {}"); - // Check for signals - if trap.wait(SteadyTime::now()).is_some() { - break; - } // Process events for i in 0..count { match &events[i as usize].data { @@ -308,13 +306,17 @@ impl GenericCloud

{ _ => unreachable!() } } - // Do the housekeeping - if self.next_housekeep < SteadyTime::now() { + if self.next_housekeep < now() { + // Check for signals + if trap.wait(dummy_time).is_some() { + break; + } + // Do the housekeeping match self.housekeep() { Ok(_) => (), Err(e) => error!("Error: {:?}", e) } - self.next_housekeep = SteadyTime::now() + Duration::seconds(1) + self.next_housekeep = now() + 1 } } info!("Shutting down..."); diff --git a/src/ethernet.rs b/src/ethernet.rs index 014216d..fca5cb6 100644 --- a/src/ethernet.rs +++ b/src/ethernet.rs @@ -3,9 +3,7 @@ use std::net::SocketAddr; use std::collections::HashMap; use super::types::{Error, Table, Protocol, Address}; - -use time::{Duration, SteadyTime}; - +use super::util::{now, Time, Duration}; #[derive(PartialEq)] pub struct Frame; @@ -49,7 +47,7 @@ impl Protocol for Frame { struct SwitchTableValue { address: SocketAddr, - timeout: SteadyTime + timeout: Time } pub struct SwitchTable { @@ -66,7 +64,7 @@ impl SwitchTable { impl Table for SwitchTable { fn housekeep(&mut self) { - let now = SteadyTime::now(); + let now = now(); let mut del: Vec

= Vec::new(); for (key, val) in &self.table { if val.timeout < now { @@ -82,7 +80,7 @@ impl Table for SwitchTable { #[inline] fn learn(&mut self, key: Address, _prefix_len: Option, addr: SocketAddr) { - let value = SwitchTableValue{address: addr, timeout: SteadyTime::now()+self.timeout}; + let value = SwitchTableValue{address: addr, timeout: now()+self.timeout as Time}; if self.table.insert(key.clone(), value).is_none() { info!("Learned address {:?} => {}", key, addr); } diff --git a/src/main.rs b/src/main.rs index a8a9f23..f2a8b65 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ extern crate rustc_serialize; extern crate epoll; extern crate signal; extern crate nix; +extern crate libc; #[cfg(feature = "crypto")] extern crate libsodium_sys; #[cfg(feature = "bench")] extern crate test; @@ -19,7 +20,6 @@ mod cloud; mod device; #[cfg(feature = "bench")] mod benches; -use time::Duration; use docopt::Docopt; use std::hash::{Hash, SipHasher, Hasher}; @@ -33,6 +33,7 @@ use types::{Error, Mode, Type, Range, Table, Protocol}; use cloud::GenericCloud; use udpmessage::VERSION; use crypto::Crypto; +use util::Duration; struct SimpleLogger; @@ -63,8 +64,8 @@ struct Args { flag_listen: String, flag_network_id: Option, flag_connect: Vec, - flag_peer_timeout: usize, - flag_dst_timeout: usize, + flag_peer_timeout: Duration, + flag_dst_timeout: Duration, flag_verbose: bool, flag_quiet: bool, flag_ifup: Option, @@ -93,8 +94,8 @@ fn run (args: Args) { for s in args.flag_subnet { ranges.push(try_fail!(Range::from_str(&s), "Invalid subnet format: {} ({})", s)); } - let dst_timeout = Duration::seconds(args.flag_dst_timeout as i64); - let peer_timeout = Duration::seconds(args.flag_peer_timeout as i64); + let dst_timeout = args.flag_dst_timeout; + let peer_timeout = args.flag_peer_timeout; let (learning, broadcasting, table): (bool, bool, Box) = match args.flag_mode { Mode::Normal => match args.flag_type { Type::Tap => (true, true, Box::new(SwitchTable::new(dst_timeout))), diff --git a/src/util.rs b/src/util.rs index 3ff87a8..9f586c5 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,23 @@ use std::{mem, slice}; +use libc; + +pub type Duration = u32; +pub type Time = i64; + +#[inline] +pub fn now() -> Time { + let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + unsafe { libc::clock_gettime(6, &mut tv); } + tv.tv_sec +} + +#[inline] +pub fn time_rand() -> i64 { + let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut tv); } + tv.tv_sec ^ tv.tv_nsec +} + #[inline(always)] pub unsafe fn as_bytes(obj: &T) -> &[u8] {