From d52d7e3aafc01ea515affff98291a05e8bc96109 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Thu, 30 Jun 2016 10:05:37 +0200 Subject: [PATCH] Added pluggable polling system --- CHANGELOG.md | 1 + Cargo.lock | 46 +++++++------------- Cargo.toml | 4 +- src/benches.rs | 12 +++--- src/cloud.rs | 30 ++++++------- src/main.rs | 3 +- src/poll/epoll.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++++ src/poll/mod.rs | 6 +++ 8 files changed, 148 insertions(+), 58 deletions(-) create mode 100644 src/poll/epoll.rs create mode 100644 src/poll/mod.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e2f46ac..dd07273 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project follows [semantic versioning](http://semver.org). ### UNRELEASED +- [added] Added pluggable polling system - [added] Added documentation - [changed] Code cleanup - [changed] Updated dependencies diff --git a/Cargo.lock b/Cargo.lock index e493385..b627c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,11 +3,11 @@ name = "vpncloud" version = "0.6.0" dependencies = [ "aligned_alloc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.81 (registry+https://github.com/rust-lang/crates.io-index)", - "epoll 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -32,7 +32,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -41,6 +41,11 @@ name = "bitflags" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitflags" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.0" @@ -56,25 +61,6 @@ dependencies = [ "strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "epoll" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "errno 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "errno" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "fnv" version = "1.0.2" @@ -96,7 +82,7 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -109,7 +95,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -119,7 +105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -131,7 +117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -147,7 +133,7 @@ name = "rand" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -190,7 +176,7 @@ name = "signal" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -205,7 +191,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -222,7 +208,7 @@ version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index e8af1a2..bac3aab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,14 +15,14 @@ time = "0.1" docopt = "0.6" rustc-serialize = "0.3" log = "0.3" -epoll = "0.3" -signal = ">=0.1.4" +signal = "0.2" nix = "0.6" libc = "0.2" aligned_alloc = "0.1" rand = "0.3" fnv = "1" net2 = "0.2" +bitflags = "0.7" [build-dependencies] gcc = "0.3" diff --git a/src/benches.rs b/src/benches.rs index fa6fff0..5394bb1 100644 --- a/src/benches.rs +++ b/src/benches.rs @@ -7,6 +7,7 @@ use test::Bencher; use std::str::FromStr; use std::net::{UdpSocket, ToSocketAddrs, Ipv4Addr, SocketAddr, SocketAddrV4}; use std::os::unix::io::AsRawFd; +use std::mem; use super::cloud::GenericCloud; use super::device::{Device, Type}; @@ -16,8 +17,7 @@ use super::ethernet::{Frame, SwitchTable}; use super::types::{Address, Table, Protocol}; use super::ip::Packet; use super::util::now as util_now; - -use epoll; +use super::poll::{self, Poll}; #[bench] fn crypto_salsa20(b: &mut Bencher) { @@ -141,13 +141,11 @@ fn now(b: &mut Bencher) { #[bench] fn epoll_wait(b: &mut Bencher) { let socket = UdpSocket::bind("0.0.0.0:0").unwrap(); - let epoll_handle = epoll::create1(0).unwrap(); + let mut poll_handle = Poll::new(1).unwrap(); let fd = socket.as_raw_fd(); - let mut event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLOUT, data: 0}; - epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, fd, &mut event).unwrap(); - let mut events = [epoll::EpollEvent{events: 0, data: 0}; 1]; + poll_handle.register(fd, poll::WRITE).unwrap(); b.iter(|| { - epoll::wait(epoll_handle, &mut events, 1000).unwrap() + assert_eq!(poll_handle.wait(1000).unwrap().len(), 1) }); b.bytes = 1400; } diff --git a/src/cloud.rs b/src/cloud.rs index 8cfbb83..45eeb4e 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -14,7 +14,6 @@ use std::time::Instant; use std::cmp::{min, max}; use fnv::FnvHasher; -use epoll; use nix::sys::signal::{SIGTERM, SIGQUIT, SIGINT}; use signal::trap::Trap; use rand::{random, sample, thread_rng}; @@ -25,6 +24,7 @@ use super::device::Device; use super::udpmessage::{encode, decode, Options, Message}; use super::crypto::Crypto; use super::util::{now, Time, Duration, resolve}; +use super::poll::{self, Poll}; type Hash = BuildHasherDefault; @@ -569,34 +569,28 @@ impl GenericCloud

{ pub fn run(&mut self) { let dummy_time = Instant::now(); let trap = Trap::trap(&[SIGINT, SIGTERM, SIGQUIT]); - let epoll_handle = try_fail!(epoll::create1(0), "Failed to create epoll handle: {}"); + let mut poll_handle = try_fail!(Poll::new(3), "Failed to create poll handle: {}"); let socket4_fd = self.socket4.as_raw_fd(); let socket6_fd = self.socket6.as_raw_fd(); let device_fd = self.device.as_raw_fd(); - let mut socket4_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 0}; - let mut socket6_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 1}; - let mut device_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 2}; - try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket4_fd, &mut socket4_event), "Failed to add ipv4 socket to epoll handle: {}"); - try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket6_fd, &mut socket6_event), "Failed to add ipv6 socket to epoll handle: {}"); - try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, device_fd, &mut device_event), "Failed to add device to epoll handle: {}"); - let mut events = [epoll::EpollEvent{events: 0, data: 0}; 3]; + try_fail!(poll_handle.register(socket4_fd, poll::READ), "Failed to add ipv4 socket to poll handle: {}"); + try_fail!(poll_handle.register(socket6_fd, poll::READ), "Failed to add ipv4 socket to poll handle: {}"); + try_fail!(poll_handle.register(device_fd, poll::READ), "Failed to add ipv4 socket to poll handle: {}"); let mut buffer = [0; 64*1024]; loop { - let count = try_fail!(epoll::wait(epoll_handle, &mut events, 1000), "Epoll wait failed: {}") as usize; - // Process events - for evt in events.iter().take(count) { - match evt.data { - 0 | 1 => { - let (size, src) = match evt.data { - 0 => try_fail!(self.socket4.recv_from(&mut buffer), "Failed to read from ipv4 network socket: {}"), - 1 => try_fail!(self.socket6.recv_from(&mut buffer), "Failed to read from ipv6 network socket: {}"), + for evt in try_fail!(poll_handle.wait(1000), "Poll wait failed: {}") { + match evt.fd() { + fd if (fd == socket4_fd || fd == socket6_fd) => { + let (size, src) = match evt.fd() { + fd if fd == socket4_fd => try_fail!(self.socket4.recv_from(&mut buffer), "Failed to read from ipv4 network socket: {}"), + fd if fd == socket6_fd => try_fail!(self.socket6.recv_from(&mut buffer), "Failed to read from ipv6 network socket: {}"), _ => unreachable!() }; if let Err(e) = decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) { error!("Error: {}, from: {}", e, src); } }, - 2 => { + fd if (fd == device_fd) => { let start = 64; let size = try_fail!(self.device.read(&mut buffer[start..]), "Failed to read from tap device: {}"); if let Err(e) = self.handle_interface_data(&mut buffer, start, start+size) { diff --git a/src/main.rs b/src/main.rs index 86be4ca..257f8c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,10 @@ #![cfg_attr(feature = "bench", feature(test))] #[macro_use] extern crate log; +#[macro_use] extern crate bitflags; extern crate time; extern crate docopt; extern crate rustc_serialize; -extern crate epoll; extern crate signal; extern crate nix; extern crate libc; @@ -26,6 +26,7 @@ pub mod ethernet; pub mod ip; pub mod cloud; pub mod device; +pub mod poll; #[cfg(test)] mod tests; #[cfg(feature = "bench")] mod benches; diff --git a/src/poll/epoll.rs b/src/poll/epoll.rs new file mode 100644 index 0000000..2f1c3f4 --- /dev/null +++ b/src/poll/epoll.rs @@ -0,0 +1,104 @@ +use libc; + +use std::os::unix::io::RawFd; +use std::io; +use std::ops::{Deref, DerefMut}; + +bitflags!{ + pub flags Flags: u32 { + const READ = libc::EPOLLIN as u32, + const WRITE = libc::EPOLLOUT as u32, + const ERROR = libc::EPOLLERR as u32, + } +} + +#[derive(Clone, Copy)] +pub struct Event(libc::epoll_event); + +impl Event { + #[inline] + pub fn fd(&self) -> RawFd { + self.0.u64 as RawFd + } + + #[inline] + pub fn flags(&self) -> Flags { + Flags::from_bits(self.0.events).expect("Invalid flags set") + } + + #[inline] + fn new(fd: RawFd, flags: Flags) -> Self { + Event(libc::epoll_event{u64: fd as u64, events: flags.bits}) + } +} + +impl Deref for Event { + type Target = libc::epoll_event; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Event { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + + +pub struct Poll { + fd: RawFd, + events: Vec +} + +impl Poll { + #[inline] + pub fn new(max_events: usize) -> io::Result { + let mut events = Vec::with_capacity(max_events); + events.resize(max_events, Event::new(0, Flags::empty())); + let fd = unsafe { libc::epoll_create(max_events as i32) }; + if fd == -1 { + return Err(io::Error::last_os_error()); + } + Ok(Poll{fd: fd, events: events}) + } + + #[inline] + pub fn register(&mut self, fd: RawFd, flags: Flags) -> io::Result<()> { + let mut ev = Event::new(fd, flags); + let res = unsafe { libc::epoll_ctl(self.fd, libc::EPOLL_CTL_ADD, fd, &mut ev as &mut libc::epoll_event) }; + if res == -1 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + #[inline] + pub fn unregister(&mut self, fd: RawFd) -> io::Result<()> { + let mut ev = Event::new(fd, Flags::empty()); + let res = unsafe { libc::epoll_ctl(self.fd, libc::EPOLL_CTL_DEL, fd, &mut ev as &mut libc::epoll_event) }; + if res == -1 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + #[inline] + pub fn wait(&mut self, timeout_millis: u32) -> io::Result<&[Event]> { + let res = unsafe { libc::epoll_wait(self.fd, &mut self.events[0] as &mut libc::epoll_event, self.events.len() as i32, timeout_millis as i32) }; + if res == -1 { + return Err(io::Error::last_os_error()); + } + Ok(&self.events[0..res as usize]) + } +} + +impl Drop for Poll { + #[inline] + fn drop(&mut self) { + unsafe { libc::close(self.fd) }; + } +} diff --git a/src/poll/mod.rs b/src/poll/mod.rs new file mode 100644 index 0000000..b84cc88 --- /dev/null +++ b/src/poll/mod.rs @@ -0,0 +1,6 @@ + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod epoll; + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub use self::epoll::*;