mirror of https://github.com/dswd/vpncloud.git
Many changes for 0.9.0
This commit is contained in:
parent
fe25ecec1d
commit
fecffcce95
|
@ -2,11 +2,16 @@
|
|||
|
||||
This project follows [semantic versioning](http://semver.org).
|
||||
|
||||
### UNRELEASED
|
||||
### v0.9.0 (UNRELEASED)
|
||||
|
||||
- [added] Added keepalive option for nodes behind NAT
|
||||
- [added] Added ability to write out statistics file with peers and traffic info
|
||||
- [added] Added dummy device type that does not allocate an interface
|
||||
- [changed] Using ring instead of libsodium
|
||||
- [changed] Using PBKDF2 for shared keys (**incompatible**)
|
||||
- [fixed] Hashed magics now also consider first character (**incompatible**)
|
||||
|
||||
### v0.8.2 (2018-01-02)
|
||||
### v0.8.2 (2019-01-02)
|
||||
|
||||
- [changed] Using serde instead of rustc_serialize
|
||||
- [changed] Updated libsodium to 1.0.16
|
||||
|
|
|
@ -961,7 +961,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "vpncloud"
|
||||
version = "0.8.2"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "vpncloud"
|
||||
version = "0.8.2"
|
||||
authors = ["Dennis Schwerdel <schwerdel@informatik.uni-kl.de>"]
|
||||
version = "0.9.0"
|
||||
authors = ["Dennis Schwerdel <schwerdel@googlemail.com>"]
|
||||
build = "build.rs"
|
||||
license = "GPL-3.0"
|
||||
description = "Peer-to-peer VPN"
|
||||
|
|
40
src/cloud.rs
40
src/cloud.rs
|
@ -19,8 +19,9 @@ use signal::{trap::Trap, Signal};
|
|||
use rand::{prelude::*, random, thread_rng};
|
||||
use net2::UdpBuilder;
|
||||
|
||||
use super::config::Config;
|
||||
use super::types::{Table, Protocol, Range, Error, HeaderMagic, NodeId};
|
||||
use super::device::Device;
|
||||
use super::device::{Device, Type};
|
||||
use super::udpmessage::{encode, decode, Message};
|
||||
use super::crypto::Crypto;
|
||||
use super::port_forwarding::PortForwarding;
|
||||
|
@ -214,26 +215,25 @@ pub struct GenericCloud<P: Protocol, T: Table> {
|
|||
}
|
||||
|
||||
impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
||||
#[allow(unknown_lints,clippy::too_many_arguments)]
|
||||
pub fn new(magic: HeaderMagic, device: Device, listen: u16, table: T,
|
||||
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||
crypto: Crypto, port_forwarding: Option<PortForwarding>, stats_file: Option<String>
|
||||
pub fn new(config: &Config, device: Device, table: T,
|
||||
learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||
crypto: Crypto, port_forwarding: Option<PortForwarding>
|
||||
) -> Self {
|
||||
let socket4 = match UdpBuilder::new_v4().expect("Failed to obtain ipv4 socket builder")
|
||||
.reuse_address(true).expect("Failed to set so_reuseaddr").bind(("0.0.0.0", listen)) {
|
||||
.reuse_address(true).expect("Failed to set so_reuseaddr").bind(("0.0.0.0", config.port)) {
|
||||
Ok(socket) => socket,
|
||||
Err(err) => fail!("Failed to open ipv4 address 0.0.0.0:{}: {}", listen, err)
|
||||
Err(err) => fail!("Failed to open ipv4 address 0.0.0.0:{}: {}", config.port, err)
|
||||
};
|
||||
let socket6 = match UdpBuilder::new_v6().expect("Failed to obtain ipv6 socket builder")
|
||||
.only_v6(true).expect("Failed to set only_v6")
|
||||
.reuse_address(true).expect("Failed to set so_reuseaddr").bind(("::", listen)) {
|
||||
.reuse_address(true).expect("Failed to set so_reuseaddr").bind(("::", config.port)) {
|
||||
Ok(socket) => socket,
|
||||
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", listen, err)
|
||||
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", config.port, err)
|
||||
};
|
||||
GenericCloud{
|
||||
magic,
|
||||
magic: config.get_magic(),
|
||||
node_id: random(),
|
||||
peers: PeerList::new(peer_timeout),
|
||||
peers: PeerList::new(config.peer_timeout),
|
||||
addresses,
|
||||
learning,
|
||||
broadcast,
|
||||
|
@ -245,13 +245,13 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
device,
|
||||
crypto,
|
||||
next_peerlist: now(),
|
||||
update_freq: peer_timeout/2-60,
|
||||
update_freq: config.get_keepalive(),
|
||||
buffer_out: [0; 64*1024],
|
||||
next_housekeep: now(),
|
||||
next_stats_out: now() + STATS_INTERVAL,
|
||||
port_forwarding,
|
||||
traffic: TrafficStats::new(),
|
||||
stats_file,
|
||||
stats_file: config.stats_file.clone(),
|
||||
_dummy_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -635,7 +635,13 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
let device_fd = self.device.as_raw_fd();
|
||||
try_fail!(poll_handle.register(socket4_fd, Flags::READ), "Failed to add ipv4 socket to poll handle: {}");
|
||||
try_fail!(poll_handle.register(socket6_fd, Flags::READ), "Failed to add ipv6 socket to poll handle: {}");
|
||||
try_fail!(poll_handle.register(device_fd, Flags::READ), "Failed to add device to poll handle: {}");
|
||||
if let Err(err) = poll_handle.register(device_fd, Flags::READ) {
|
||||
if self.device.get_type() != Type::Dummy {
|
||||
fail!("Failed to add device to poll handle: {}", err);
|
||||
} else {
|
||||
warn!("Failed to add device to poll handle: {}", err);
|
||||
}
|
||||
}
|
||||
let mut buffer = [0; 64*1024];
|
||||
let mut poll_error = false;
|
||||
loop {
|
||||
|
@ -658,8 +664,10 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
fd if fd == socket6_fd => try_fail!(self.socket6.recv_from(&mut buffer), "Failed to read from ipv6 network socket: {}"),
|
||||
_ => unreachable!()
|
||||
};
|
||||
self.traffic.count_in_traffic(src, size);
|
||||
if let Err(e) = decode(&mut buffer[..size], self.magic, &mut self.crypto).and_then(|msg| self.handle_net_message(src, msg)) {
|
||||
if let Err(e) = decode(&mut buffer[..size], self.magic, &mut self.crypto).and_then(|msg| {
|
||||
self.traffic.count_in_traffic(src, size);
|
||||
self.handle_net_message(src, msg)
|
||||
}) {
|
||||
error!("Error: {}, from: {}", e, src);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher};
|
|||
use siphasher::sip::SipHasher24;
|
||||
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
#[derive(Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct Config {
|
||||
pub device_type: Type,
|
||||
pub device_name: String,
|
||||
|
@ -25,6 +25,7 @@ pub struct Config {
|
|||
pub port: u16,
|
||||
pub peers: Vec<String>,
|
||||
pub peer_timeout: Duration,
|
||||
pub keepalive: Option<Duration>,
|
||||
pub mode: Mode,
|
||||
pub dst_timeout: Duration,
|
||||
pub subnets: Vec<String>,
|
||||
|
@ -43,7 +44,7 @@ impl Default for Config {
|
|||
ifup: None, ifdown: None,
|
||||
crypto: CryptoMethod::ChaCha20, shared_key: None,
|
||||
magic: None,
|
||||
port: 3210, peers: vec![], peer_timeout: 1800,
|
||||
port: 3210, peers: vec![], peer_timeout: 1800, keepalive: None,
|
||||
mode: Mode::Normal, dst_timeout: 300,
|
||||
subnets: vec![],
|
||||
port_forwarding: true,
|
||||
|
@ -88,6 +89,9 @@ impl Config {
|
|||
if let Some(val) = file.peer_timeout {
|
||||
self.peer_timeout = val;
|
||||
}
|
||||
if let Some(val) = file.keepalive {
|
||||
self.keepalive = Some(val);
|
||||
}
|
||||
if let Some(val) = file.mode {
|
||||
self.mode = val;
|
||||
}
|
||||
|
@ -147,6 +151,9 @@ impl Config {
|
|||
if let Some(val) = args.flag_peer_timeout {
|
||||
self.peer_timeout = val;
|
||||
}
|
||||
if let Some(val) = args.flag_keepalive {
|
||||
self.keepalive = Some(val);
|
||||
}
|
||||
if let Some(val) = args.flag_mode {
|
||||
self.mode = val;
|
||||
}
|
||||
|
@ -192,6 +199,13 @@ impl Config {
|
|||
MAGIC
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_keepalive(&self) -> Duration {
|
||||
match self.keepalive {
|
||||
Some(dur) => dur,
|
||||
None => self.peer_timeout/2-60
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -207,6 +221,7 @@ pub struct ConfigFile {
|
|||
pub port: Option<u16>,
|
||||
pub peers: Option<Vec<String>>,
|
||||
pub peer_timeout: Option<Duration>,
|
||||
pub keepalive: Option<Duration>,
|
||||
pub mode: Option<Mode>,
|
||||
pub dst_timeout: Option<Duration>,
|
||||
pub subnets: Option<Vec<String>>,
|
||||
|
|
|
@ -10,7 +10,7 @@ use ring::digest::*;
|
|||
use super::types::Error;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||
pub enum CryptoMethod {
|
||||
#[serde(rename = "chacha20")]
|
||||
ChaCha20,
|
||||
|
|
|
@ -23,7 +23,10 @@ pub enum Type {
|
|||
Tun,
|
||||
/// Tap interface: This insterface transports Ethernet frames.
|
||||
#[serde(rename = "tap")]
|
||||
Tap
|
||||
Tap,
|
||||
/// Dummy interface: This interface does nothing.
|
||||
#[serde(rename = "dummy")]
|
||||
Dummy
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
|
@ -31,6 +34,7 @@ impl fmt::Display for Type {
|
|||
match *self {
|
||||
Type::Tun => write!(formatter, "tun"),
|
||||
Type::Tap => write!(formatter, "tap"),
|
||||
Type::Dummy => write!(formatter, "dummy")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +68,9 @@ impl Device {
|
|||
/// # Panics
|
||||
/// This method panics if the interface name is longer than 31 bytes.
|
||||
pub fn new(ifname: &str, type_: Type) -> io::Result<Self> {
|
||||
if type_ == Type::Dummy {
|
||||
return Self::dummy(ifname, "/dev/null", type_);
|
||||
}
|
||||
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);
|
||||
|
@ -73,7 +80,8 @@ impl Device {
|
|||
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()) }
|
||||
Type::Tap => unsafe { setup_tap_device(fd.as_raw_fd(), ifname_c.as_mut_ptr()) },
|
||||
Type::Dummy => unreachable!()
|
||||
};
|
||||
match res {
|
||||
0 => {
|
||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -78,6 +78,7 @@ pub struct Args {
|
|||
flag_magic: Option<String>,
|
||||
flag_connect: Vec<String>,
|
||||
flag_peer_timeout: Option<Duration>,
|
||||
flag_keepalive: Option<Duration>,
|
||||
flag_dst_timeout: Option<Duration>,
|
||||
flag_verbose: bool,
|
||||
flag_quiet: bool,
|
||||
|
@ -159,15 +160,15 @@ enum AnyCloud<P: Protocol> {
|
|||
|
||||
impl<P: Protocol> AnyCloud<P> {
|
||||
#[allow(unknown_lints,clippy::too_many_arguments)]
|
||||
fn new(magic: HeaderMagic, device: Device, listen: u16, table: AnyTable,
|
||||
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||
crypto: Crypto, port_forwarding: Option<PortForwarding>, stats_file: Option<String>) -> Self {
|
||||
fn new(config: &Config, device: Device, table: AnyTable,
|
||||
learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||
crypto: Crypto, port_forwarding: Option<PortForwarding>) -> Self {
|
||||
match table {
|
||||
AnyTable::Switch(t) => AnyCloud::Switch(GenericCloud::<P, SwitchTable>::new(
|
||||
magic, device, listen, t, peer_timeout, learning, broadcast, addresses, crypto, port_forwarding, stats_file
|
||||
config, device,t, learning, broadcast, addresses, crypto, port_forwarding
|
||||
)),
|
||||
AnyTable::Routing(t) => AnyCloud::Routing(GenericCloud::<P, RoutingTable>::new(
|
||||
magic, device, listen, t, peer_timeout, learning, broadcast, addresses, crypto, port_forwarding, stats_file
|
||||
config, device,t, learning, broadcast, addresses, crypto, port_forwarding
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -211,20 +212,19 @@ fn run<P: Protocol> (config: Config) {
|
|||
ranges.push(try_fail!(Range::from_str(s), "Invalid subnet format: {} ({})", s));
|
||||
}
|
||||
let dst_timeout = config.dst_timeout;
|
||||
let peer_timeout = config.peer_timeout;
|
||||
let (learning, broadcasting, table) = match config.mode {
|
||||
Mode::Normal => match config.device_type {
|
||||
Type::Tap => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))),
|
||||
Type::Tun => (false, false, AnyTable::Routing(RoutingTable::new()))
|
||||
Type::Tun => (false, false, AnyTable::Routing(RoutingTable::new())),
|
||||
Type::Dummy => (false, false, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
|
||||
},
|
||||
Mode::Router => (false, false, AnyTable::Routing(RoutingTable::new())),
|
||||
Mode::Switch => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))),
|
||||
Mode::Hub => (false, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
|
||||
};
|
||||
let magic = config.get_magic();
|
||||
Crypto::init();
|
||||
let crypto = match config.shared_key {
|
||||
Some(key) => Crypto::from_shared_key(config.crypto, &key),
|
||||
Some(ref key) => Crypto::from_shared_key(config.crypto, key),
|
||||
None => Crypto::None
|
||||
};
|
||||
let port_forwarding = if config.port_forwarding {
|
||||
|
@ -232,7 +232,7 @@ fn run<P: Protocol> (config: Config) {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let mut cloud = AnyCloud::<P>::new(magic, device, config.port, table, peer_timeout, learning, broadcasting, ranges, crypto, port_forwarding, config.stats_file);
|
||||
let mut cloud = AnyCloud::<P>::new(&config, device, table, learning,broadcasting,ranges, crypto, port_forwarding);
|
||||
if let Some(script) = config.ifup {
|
||||
run_script(&script, cloud.ifname());
|
||||
}
|
||||
|
@ -295,6 +295,7 @@ fn main() {
|
|||
debug!("Config: {:?}", config);
|
||||
match config.device_type {
|
||||
Type::Tap => run::<ethernet::Frame>(config),
|
||||
Type::Tun => run::<ip::Packet>(config)
|
||||
Type::Tun => run::<ip::Packet>(config),
|
||||
Type::Dummy => run::<ethernet::Frame>(config)
|
||||
}
|
||||
}
|
||||
|
|
16
src/tests.rs
16
src/tests.rs
|
@ -456,6 +456,7 @@ peers:
|
|||
- remote.machine.foo:3210
|
||||
- remote.machine.bar:3210
|
||||
peer_timeout: 1800
|
||||
keepalive: 840
|
||||
dst_timeout: 300
|
||||
mode: normal
|
||||
subnets:
|
||||
|
@ -464,6 +465,7 @@ port_forwarding: true
|
|||
user: nobody
|
||||
group: nogroup
|
||||
pid_file: /run/vpncloud.run
|
||||
stats_file: /var/log/vpncloud.stats
|
||||
";
|
||||
assert_eq!(serde_yaml::from_str::<ConfigFile>(config_file).unwrap(), ConfigFile{
|
||||
device_type: Some(Type::Tun),
|
||||
|
@ -476,13 +478,15 @@ pid_file: /run/vpncloud.run
|
|||
port: Some(3210),
|
||||
peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]),
|
||||
peer_timeout: Some(1800),
|
||||
keepalive: Some(840),
|
||||
mode: Some(Mode::Normal),
|
||||
dst_timeout: Some(300),
|
||||
subnets: Some(vec!["10.0.1.0/24".to_string()]),
|
||||
port_forwarding: Some(true),
|
||||
user: Some("nobody".to_string()),
|
||||
group: Some("nogroup".to_string()),
|
||||
pid_file: Some("/run/vpncloud.run".to_string())
|
||||
pid_file: Some("/run/vpncloud.run".to_string()),
|
||||
stats_file: Some("/var/log/vpncloud.stats".to_string())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -500,13 +504,15 @@ fn config_merge() {
|
|||
port: Some(3210),
|
||||
peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]),
|
||||
peer_timeout: Some(1800),
|
||||
keepalive: Some(840),
|
||||
mode: Some(Mode::Normal),
|
||||
dst_timeout: Some(300),
|
||||
subnets: Some(vec!["10.0.1.0/24".to_string()]),
|
||||
port_forwarding: Some(true),
|
||||
user: Some("nobody".to_string()),
|
||||
group: Some("nogroup".to_string()),
|
||||
pid_file: Some("/run/vpncloud.run".to_string())
|
||||
pid_file: Some("/run/vpncloud.run".to_string()),
|
||||
stats_file: Some("/var/log/vpncloud.stats".to_string())
|
||||
});
|
||||
assert_eq!(config, Config{
|
||||
device_type: Type::Tun,
|
||||
|
@ -519,6 +525,7 @@ fn config_merge() {
|
|||
port: 3210,
|
||||
peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()],
|
||||
peer_timeout: 1800,
|
||||
keepalive: Some(840),
|
||||
dst_timeout: 300,
|
||||
mode: Mode::Normal,
|
||||
port_forwarding: true,
|
||||
|
@ -526,6 +533,7 @@ fn config_merge() {
|
|||
user: Some("nobody".to_string()),
|
||||
group: Some("nogroup".to_string()),
|
||||
pid_file: Some("/run/vpncloud.run".to_string()),
|
||||
stats_file: Some("/var/log/vpncloud.stats".to_string()),
|
||||
..Default::default()
|
||||
});
|
||||
config.merge_args(Args{
|
||||
|
@ -538,6 +546,7 @@ fn config_merge() {
|
|||
flag_magic: Some("hash:mynet".to_string()),
|
||||
flag_listen: Some(3211),
|
||||
flag_peer_timeout: Some(1801),
|
||||
flag_keepalive: Some(850),
|
||||
flag_dst_timeout: Some(301),
|
||||
flag_mode: Some(Mode::Switch),
|
||||
flag_subnet: vec![],
|
||||
|
@ -545,6 +554,7 @@ fn config_merge() {
|
|||
flag_no_port_forwarding: true,
|
||||
flag_daemon: true,
|
||||
flag_pid_file: Some("/run/vpncloud-mynet.run".to_string()),
|
||||
flag_stats_file: Some("/var/log/vpncloud-mynet.stats".to_string()),
|
||||
flag_user: Some("root".to_string()),
|
||||
flag_group: Some("root".to_string()),
|
||||
..Default::default()
|
||||
|
@ -560,6 +570,7 @@ fn config_merge() {
|
|||
port: 3211,
|
||||
peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string(), "another:3210".to_string()],
|
||||
peer_timeout: 1801,
|
||||
keepalive: Some(850),
|
||||
dst_timeout: 301,
|
||||
mode: Mode::Switch,
|
||||
port_forwarding: false,
|
||||
|
@ -567,6 +578,7 @@ fn config_merge() {
|
|||
user: Some("root".to_string()),
|
||||
group: Some("root".to_string()),
|
||||
pid_file: Some("/run/vpncloud-mynet.run".to_string()),
|
||||
stats_file: Some("/var/log/vpncloud-mynet.stats".to_string()),
|
||||
daemonize: true,
|
||||
..Default::default()
|
||||
});
|
||||
|
|
|
@ -19,6 +19,8 @@ Options:
|
|||
--crypto <method> The encryption method to use ("aes256", or
|
||||
"chacha20").
|
||||
--peer-timeout <secs> Peer timeout in seconds.
|
||||
--keepalive <secs> Periodically send message to keep
|
||||
connections alive.
|
||||
--dst-timeout <secs> Switch table entry timeout in seconds.
|
||||
--ifup <command> A command to setup the network interface.
|
||||
--ifdown <command> A command to bring down the network
|
||||
|
|
Loading…
Reference in New Issue