mirror of https://github.com/dswd/vpncloud.git
Some benchmarks and low-hanging speed improvements
This commit is contained in:
parent
f6b6faf50a
commit
946e384660
|
@ -20,3 +20,4 @@ gcc = "0.3"
|
|||
[features]
|
||||
default = []
|
||||
crypto = ["libsodium-sys"] #not default as it requires external library
|
||||
bench = []
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
use test::Bencher;
|
||||
|
||||
use time::{SteadyTime, Duration};
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
use super::udpmessage::{Options, Message, encode, decode};
|
||||
use super::crypto::Crypto;
|
||||
use super::ethernet::{Frame, SwitchTable};
|
||||
use super::types::{Address, Table, Protocol};
|
||||
use super::ip::Packet;
|
||||
|
||||
|
||||
#[bench]
|
||||
fn message_encode(b: &mut Bencher) {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let payload = [0; 1400];
|
||||
let msg = Message::Data(&payload);
|
||||
let mut buf = [0; 1500];
|
||||
b.iter(|| {
|
||||
encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn message_decode(b: &mut Bencher) {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let payload = [0; 1400];
|
||||
let msg = Message::Data(&payload);
|
||||
let mut buf = [0; 1500];
|
||||
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||
b.iter(|| {
|
||||
decode(&mut buf[..size], &mut crypto).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn switch_learn(b: &mut Bencher) {
|
||||
let mut table = SwitchTable::new(Duration::seconds(10));
|
||||
let addr = Address::from_str("12:34:56:78:90:ab").unwrap();
|
||||
let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap();
|
||||
b.iter(|| {
|
||||
table.learn(addr.clone(), None, peer);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn switch_lookup(b: &mut Bencher) {
|
||||
let mut table = SwitchTable::new(Duration::seconds(10));
|
||||
let addr = Address::from_str("12:34:56:78:90:ab").unwrap();
|
||||
let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap();
|
||||
table.learn(addr.clone(), None, peer);
|
||||
b.iter(|| {
|
||||
table.lookup(&addr);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn ethernet_parse(b: &mut Bencher) {
|
||||
let data = [0; 1500];
|
||||
b.iter(|| {
|
||||
Frame::parse(&data).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn ipv4_parse(b: &mut Bencher) {
|
||||
let mut data = [0; 1500];
|
||||
data[0] = 4*16;
|
||||
b.iter(|| {
|
||||
Packet::parse(&data).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn ipv6_parse(b: &mut Bencher) {
|
||||
let mut data = [0; 1500];
|
||||
data[0] = 6*16;
|
||||
b.iter(|| {
|
||||
Packet::parse(&data).unwrap()
|
||||
})
|
||||
}
|
|
@ -225,7 +225,7 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
return Err(Error::WrongNetwork(options.network_id));
|
||||
}
|
||||
}
|
||||
debug!("Recieved {:?} from {}", msg, peer);
|
||||
debug!("Received {:?} from {}", msg, peer);
|
||||
match msg {
|
||||
Message::Data(payload) => {
|
||||
let (src, _dst) = try!(P::parse(payload));
|
||||
|
@ -237,7 +237,7 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
return Err(Error::TunTapDevError("Failed to write to device"));
|
||||
}
|
||||
}
|
||||
self.peers.add(&peer);
|
||||
// not adding peer to increase performance
|
||||
if self.learning {
|
||||
//learn single address
|
||||
self.table.learn(src, None, peer);
|
||||
|
|
|
@ -38,10 +38,12 @@ impl Device {
|
|||
&self.ifname
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read(&mut self, mut buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
match self.fd.write_all(&data) {
|
||||
Ok(_) => Ok(()),
|
||||
|
@ -51,6 +53,7 @@ impl Device {
|
|||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
#[inline(always)]
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd.as_raw_fd()
|
||||
}
|
||||
|
|
|
@ -25,27 +25,23 @@ impl Protocol for Frame {
|
|||
if data.len() < pos + 2 {
|
||||
return Err(Error::ParseError("Vlan frame is too short"));
|
||||
}
|
||||
let mut src = Vec::with_capacity(8);
|
||||
let mut dst = Vec::with_capacity(8);
|
||||
let mut src = [0; 16];
|
||||
let mut dst = [0; 16];
|
||||
unsafe {
|
||||
src.set_len(8);
|
||||
ptr::copy_nonoverlapping(data[pos..].as_ptr(), src.as_mut_ptr(), 2);
|
||||
ptr::copy_nonoverlapping(src_data.as_ptr(), src[2..].as_mut_ptr(), 6);
|
||||
dst.set_len(8);
|
||||
ptr::copy_nonoverlapping(data[pos..].as_ptr(), dst.as_mut_ptr(), 2);
|
||||
ptr::copy_nonoverlapping(dst_data.as_ptr(), dst[2..].as_mut_ptr(), 6);
|
||||
}
|
||||
Ok((Address(src), Address(dst)))
|
||||
Ok((Address(src, 8), Address(dst, 8)))
|
||||
} else {
|
||||
let mut src = Vec::with_capacity(6);
|
||||
let mut dst = Vec::with_capacity(6);
|
||||
let mut src = [0; 16];
|
||||
let mut dst = [0; 16];
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(src_data.as_ptr(), src.as_mut_ptr(), 6);
|
||||
src.set_len(6);
|
||||
ptr::copy_nonoverlapping(dst_data.as_ptr(), dst.as_mut_ptr(), 6);
|
||||
dst.set_len(6);
|
||||
}
|
||||
Ok((Address(src), Address(dst)))
|
||||
Ok((Address(src, 6), Address(dst, 6)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +80,7 @@ impl Table for SwitchTable {
|
|||
self.cache = None;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) {
|
||||
let value = SwitchTableValue{address: addr, timeout: SteadyTime::now()+self.timeout};
|
||||
if self.table.insert(key.clone(), value).is_none() {
|
||||
|
@ -91,6 +88,7 @@ impl Table for SwitchTable {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lookup(&mut self, key: &Address) -> Option<SocketAddr> {
|
||||
match self.table.get(key) {
|
||||
Some(value) => Some(value.address),
|
||||
|
@ -108,14 +106,14 @@ impl Table for SwitchTable {
|
|||
fn without_vlan() {
|
||||
let data = [6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8];
|
||||
let (src, dst) = Frame::parse(&data).unwrap();
|
||||
assert_eq!(src, Address(vec![1,2,3,4,5,6]));
|
||||
assert_eq!(dst, Address(vec![6,5,4,3,2,1]));
|
||||
assert_eq!(src, Address([1,2,3,4,5,6,0,0,0,0,0,0,0,0,0,0], 6));
|
||||
assert_eq!(dst, Address([6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_vlan() {
|
||||
let data = [6,5,4,3,2,1,1,2,3,4,5,6,0x81,0,4,210,1,2,3,4,5,6,7,8];
|
||||
let (src, dst) = Frame::parse(&data).unwrap();
|
||||
assert_eq!(src, Address(vec![4,210,1,2,3,4,5,6]));
|
||||
assert_eq!(dst, Address(vec![4,210,6,5,4,3,2,1]));
|
||||
assert_eq!(src, Address([4,210,1,2,3,4,5,6,0,0,0,0,0,0,0,0], 8));
|
||||
assert_eq!(dst, Address([4,210,6,5,4,3,2,1,0,0,0,0,0,0,0,0], 8));
|
||||
}
|
||||
|
|
26
src/ip.rs
26
src/ip.rs
|
@ -1,9 +1,10 @@
|
|||
use std::net::SocketAddr;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use std::io::Read;
|
||||
use std::ptr;
|
||||
|
||||
use super::types::{Protocol, Error, Table, Address};
|
||||
use super::util::to_vec;
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Packet;
|
||||
|
@ -14,18 +15,28 @@ impl Protocol for Packet {
|
|||
return Err(Error::ParseError("Empty header"));
|
||||
}
|
||||
let version = data[0] >> 4;
|
||||
let mut src = [0; 16];
|
||||
let mut dst = [0; 16];
|
||||
match version {
|
||||
4 => {
|
||||
if data.len() < 20 {
|
||||
return Err(Error::ParseError("Truncated header"));
|
||||
}
|
||||
Ok((Address(to_vec(&data[12..16])), Address(to_vec(&data[16..20]))))
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(data[12..].as_ptr(), src.as_mut_ptr(), 4);
|
||||
ptr::copy_nonoverlapping(data[16..].as_ptr(), dst.as_mut_ptr(), 4);
|
||||
}
|
||||
Ok((Address(src, 4), Address(dst, 4)))
|
||||
},
|
||||
6 => {
|
||||
if data.len() < 40 {
|
||||
return Err(Error::ParseError("Truncated header"));
|
||||
}
|
||||
Ok((Address(to_vec(&data[8..24])), Address(to_vec(&data[24..40]))))
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(data[8..].as_ptr(), src.as_mut_ptr(), 16);
|
||||
ptr::copy_nonoverlapping(data[24..].as_ptr(), dst.as_mut_ptr(), 16);
|
||||
}
|
||||
Ok((Address(src, 16), Address(dst, 16)))
|
||||
},
|
||||
_ => Err(Error::ParseError("Invalid version"))
|
||||
}
|
||||
|
@ -35,11 +46,11 @@ impl Protocol for Packet {
|
|||
|
||||
struct RoutingEntry {
|
||||
address: SocketAddr,
|
||||
bytes: Vec<u8>,
|
||||
bytes: [u8; 16],
|
||||
prefix_len: u8
|
||||
}
|
||||
|
||||
pub struct RoutingTable(HashMap<Vec<u8>, Vec<RoutingEntry>>);
|
||||
pub struct RoutingTable(HashMap<[u8; 16], Vec<RoutingEntry>>);
|
||||
|
||||
impl RoutingTable {
|
||||
pub fn new() -> Self {
|
||||
|
@ -55,7 +66,10 @@ impl Table for RoutingTable {
|
|||
};
|
||||
info!("New routing entry: {:?}/{} => {}", addr, prefix_len, address);
|
||||
let group_len = (prefix_len as usize / 16) * 2;
|
||||
let group_bytes: Vec<u8> = addr.0[..group_len].iter().map(|b| *b).collect();
|
||||
assert!(group_len <= 16);
|
||||
let mut group_bytes = [0; 16];
|
||||
unsafe { ptr::copy_nonoverlapping(addr.0.as_ptr(), group_bytes.as_mut_ptr(), group_len) };
|
||||
|
||||
let routing_entry = RoutingEntry{address: address, bytes: addr.0, prefix_len: prefix_len};
|
||||
match self.0.entry(group_bytes) {
|
||||
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(routing_entry),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg_attr(feature = "bench", feature(test))]
|
||||
#[macro_use] extern crate log;
|
||||
extern crate time;
|
||||
extern crate docopt;
|
||||
|
@ -6,6 +7,7 @@ extern crate epoll;
|
|||
extern crate signal;
|
||||
extern crate nix;
|
||||
#[cfg(feature = "crypto")] extern crate libsodium_sys;
|
||||
#[cfg(feature = "bench")] extern crate test;
|
||||
|
||||
#[macro_use] mod util;
|
||||
mod types;
|
||||
|
@ -15,6 +17,7 @@ mod ethernet;
|
|||
mod ip;
|
||||
mod cloud;
|
||||
mod device;
|
||||
#[cfg(feature = "bench")] mod benches;
|
||||
|
||||
use time::Duration;
|
||||
use docopt::Docopt;
|
||||
|
@ -35,10 +38,12 @@ use crypto::Crypto;
|
|||
struct SimpleLogger;
|
||||
|
||||
impl log::Log for SimpleLogger {
|
||||
#[inline(always)]
|
||||
fn enabled(&self, _metadata: &log::LogMetadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn log(&self, record: &log::LogRecord) {
|
||||
if self.enabled(record.metadata()) {
|
||||
println!("{} - {}", record.level(), record.args());
|
||||
|
|
45
src/types.rs
45
src/types.rs
|
@ -7,12 +7,27 @@ use super::util::{as_bytes, as_obj};
|
|||
|
||||
pub type NetworkId = u64;
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
|
||||
pub struct Address(pub Vec<u8>);
|
||||
#[derive(PartialOrd, Eq, Ord, Clone, Hash)]
|
||||
pub struct Address(pub [u8; 16], pub u8);
|
||||
|
||||
impl PartialEq for Address {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
if self.1 != rhs.1 {
|
||||
return false;
|
||||
}
|
||||
for i in 0..self.1 as usize {
|
||||
if self.0[i] != rhs.0[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Debug for Address {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match self.0.len() {
|
||||
match self.1 {
|
||||
4 => write!(formatter, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3]),
|
||||
6 => write!(formatter, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
|
||||
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]),
|
||||
|
@ -36,12 +51,11 @@ impl FromStr for Address {
|
|||
fn from_str(text: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(addr) = Ipv4Addr::from_str(text) {
|
||||
let ip = addr.octets();
|
||||
let mut res = Vec::with_capacity(4);
|
||||
let mut res = [0; 16];
|
||||
unsafe {
|
||||
res.set_len(4);
|
||||
ptr::copy_nonoverlapping(ip.as_ptr(), res.as_mut_ptr(), ip.len());
|
||||
}
|
||||
return Ok(Address(res));
|
||||
return Ok(Address(res, 4));
|
||||
}
|
||||
if let Ok(addr) = Ipv6Addr::from_str(text) {
|
||||
let mut segments = addr.segments();
|
||||
|
@ -49,20 +63,19 @@ impl FromStr for Address {
|
|||
segments[i] = segments[i].to_be();
|
||||
}
|
||||
let bytes = unsafe { as_bytes(&segments) };
|
||||
let mut res = Vec::with_capacity(16);
|
||||
let mut res = [0; 16];
|
||||
unsafe {
|
||||
res.set_len(16);
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), res.as_mut_ptr(), bytes.len());
|
||||
}
|
||||
return Ok(Address(res));
|
||||
return Ok(Address(res, 16));
|
||||
}
|
||||
let parts: Vec<&str> = text.split(':').collect();
|
||||
if parts.len() == 6 {
|
||||
let mut bytes = Vec::with_capacity(6);
|
||||
let mut bytes = [0; 16];
|
||||
for i in 0..6 {
|
||||
bytes.push(try!(u8::from_str_radix(&parts[i], 16).map_err(|_| Error::ParseError("Failed to parse mac"))));
|
||||
bytes[i] = try!(u8::from_str_radix(&parts[i], 16).map_err(|_| Error::ParseError("Failed to parse mac")));
|
||||
}
|
||||
return Ok(Address(bytes));
|
||||
return Ok(Address(bytes, 6));
|
||||
}
|
||||
return Err(Error::ParseError("Failed to parse address"))
|
||||
}
|
||||
|
@ -154,8 +167,8 @@ impl fmt::Display for Error {
|
|||
|
||||
#[test]
|
||||
fn address_fmt() {
|
||||
assert_eq!(format!("{:?}", Address(vec![120,45,22,5])), "120.45.22.5");
|
||||
assert_eq!(format!("{:?}", Address(vec![120,45,22,5,1,2])), "78:2d:16:05:01:02");
|
||||
assert_eq!(format!("{:?}", Address(vec![3,56,120,45,22,5,1,2])), "vlan824/78:2d:16:05:01:02");
|
||||
assert_eq!(format!("{:?}", Address(vec![0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])), "0001:0203:0405:0607:0809:0a0b:0c0d:0e0f");
|
||||
assert_eq!(format!("{:?}", Address([120,45,22,5,0,0,0,0,0,0,0,0,0,0,0,0], 4)), "120.45.22.5");
|
||||
assert_eq!(format!("{:?}", Address([120,45,22,5,1,2,0,0,0,0,0,0,0,0,0,0], 6)), "78:2d:16:05:01:02");
|
||||
assert_eq!(format!("{:?}", Address([3,56,120,45,22,5,1,2,0,0,0,0,0,0,0,0], 8)), "vlan824/78:2d:16:05:01:02");
|
||||
assert_eq!(format!("{:?}", Address([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 16)), "0001:0203:0405:0607:0809:0a0b:0c0d:0e0f");
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
|
|||
use std::u16;
|
||||
|
||||
use super::types::{Error, NetworkId, Range, Address};
|
||||
use super::util::{as_obj, as_bytes, to_vec};
|
||||
use super::util::{as_obj, as_bytes};
|
||||
use super::crypto::Crypto;
|
||||
|
||||
const MAGIC: [u8; 3] = [0x76, 0x70, 0x6e];
|
||||
|
@ -166,7 +166,9 @@ pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, M
|
|||
if end < pos + len + 1 {
|
||||
return Err(Error::ParseError("Init data too short"));
|
||||
}
|
||||
let base = Address(to_vec(&data[pos..pos+len]));
|
||||
let mut addr = [0; 16];
|
||||
unsafe { ptr::copy_nonoverlapping(data[pos..].as_ptr(), addr.as_mut_ptr(), len) };
|
||||
let base = Address(addr, len as u8);
|
||||
let prefix_len = data[pos+len];
|
||||
pos += len + 1;
|
||||
addrs.push(Range{base: base, prefix_len: prefix_len});
|
||||
|
@ -260,8 +262,7 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8], crypto: &mut Cry
|
|||
pos += 1;
|
||||
for range in ranges {
|
||||
let base = &range.base;
|
||||
let len = base.0.len();
|
||||
assert!(len <= 255);
|
||||
let len = base.1 as usize;
|
||||
assert!(buf.len() >= pos + 1 + len + 1);
|
||||
buf[pos] = len as u8;
|
||||
pos += 1;
|
||||
|
@ -350,8 +351,8 @@ fn encode_option_network_id() {
|
|||
fn encode_message_init() {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let addrs = vec![Range{base: Address(vec![0,1,2,3]), prefix_len: 24},
|
||||
Range{base: Address(vec![0,1,2,3,4,5]), prefix_len: 16}];
|
||||
let addrs = vec![Range{base: Address([0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0], 4), prefix_len: 24},
|
||||
Range{base: Address([0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0], 6), prefix_len: 16}];
|
||||
let msg = Message::Init(addrs);
|
||||
let mut buf = [0; 1024];
|
||||
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||
|
|
11
src/util.rs
11
src/util.rs
|
@ -1,4 +1,4 @@
|
|||
use std::{mem, slice, ptr};
|
||||
use std::{mem, slice};
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn as_bytes<T>(obj: &T) -> &[u8] {
|
||||
|
@ -11,15 +11,6 @@ pub unsafe fn as_obj<T>(data: &[u8]) -> &T {
|
|||
mem::transmute(data.as_ptr())
|
||||
}
|
||||
|
||||
pub fn to_vec(data: &[u8]) -> Vec<u8> {
|
||||
let mut v = Vec::with_capacity(data.len());
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), v.as_mut_ptr(), data.len());
|
||||
v.set_len(data.len());
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
macro_rules! fail {
|
||||
($format:expr) => ( {
|
||||
use std::process;
|
||||
|
|
Loading…
Reference in New Issue