vpncloud/src/main.rs

302 lines
9.5 KiB
Rust
Raw Normal View History

// VpnCloud - Peer-to-Peer VPN
2017-07-22 14:49:53 +00:00
// Copyright (C) 2015-2017 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
#![cfg_attr(feature = "bench", feature(test))]
2016-06-26 17:18:38 +00:00
2015-11-19 15:34:20 +00:00
#[macro_use] extern crate log;
2016-06-30 08:05:37 +00:00
#[macro_use] extern crate bitflags;
2015-11-19 15:34:20 +00:00
extern crate time;
extern crate docopt;
#[macro_use] extern crate serde_derive;
extern crate serde;
extern crate serde_yaml;
2015-11-25 13:31:05 +00:00
extern crate signal;
2015-11-25 20:55:30 +00:00
extern crate libc;
extern crate rand;
2016-03-29 08:45:54 +00:00
extern crate fnv;
2016-05-02 06:35:11 +00:00
extern crate net2;
extern crate yaml_rust;
extern crate igd;
extern crate siphasher;
2016-11-23 14:21:22 +00:00
extern crate daemonize;
2019-01-04 12:53:12 +00:00
extern crate ring;
#[cfg(feature = "bench")] extern crate test;
2015-11-19 15:34:20 +00:00
2016-06-27 13:43:30 +00:00
#[macro_use] pub mod util;
pub mod types;
pub mod crypto;
pub mod udpmessage;
pub mod ethernet;
pub mod ip;
pub mod cloud;
pub mod device;
2016-06-30 08:05:37 +00:00
pub mod poll;
pub mod config;
pub mod port_forwarding;
2019-01-09 16:45:12 +00:00
pub mod traffic;
2015-11-26 21:16:51 +00:00
#[cfg(test)] mod tests;
#[cfg(feature = "bench")] mod benches;
2015-11-19 15:34:20 +00:00
use docopt::Docopt;
2016-11-23 14:21:22 +00:00
use std::sync::Mutex;
2015-11-23 00:40:47 +00:00
use std::str::FromStr;
2015-11-23 18:06:25 +00:00
use std::process::Command;
2016-11-23 14:21:22 +00:00
use std::fs::File;
use std::path::Path;
use std::io::{self, Write};
2015-11-20 17:40:23 +00:00
2016-06-26 17:18:38 +00:00
use device::{Device, Type};
2015-11-23 00:40:47 +00:00
use ethernet::SwitchTable;
use ip::RoutingTable;
use types::{Mode, Range, Protocol, HeaderMagic, Error};
use cloud::GenericCloud;
2015-11-30 16:27:50 +00:00
use crypto::{Crypto, CryptoMethod};
use port_forwarding::PortForwarding;
use util::Duration;
use config::Config;
const VERSION: u8 = 1;
const MAGIC: HeaderMagic = *b"vpn\x01";
2015-11-19 15:34:20 +00:00
static USAGE: &'static str = include_str!("usage.txt");
2019-01-01 23:35:14 +00:00
#[derive(Deserialize, Debug, Default)]
pub struct Args {
flag_config: Option<String>,
flag_type: Option<Type>,
flag_mode: Option<Mode>,
flag_shared_key: Option<String>,
flag_crypto: Option<CryptoMethod>,
flag_subnet: Vec<String>,
flag_device: Option<String>,
flag_listen: Option<u16>,
flag_network_id: Option<String>,
flag_magic: Option<String>,
flag_connect: Vec<String>,
flag_peer_timeout: Option<Duration>,
2019-01-10 18:36:50 +00:00
flag_keepalive: Option<Duration>,
flag_dst_timeout: Option<Duration>,
flag_verbose: bool,
flag_quiet: bool,
flag_ifup: Option<String>,
flag_ifdown: Option<String>,
flag_version: bool,
2016-11-23 14:21:22 +00:00
flag_no_port_forwarding: bool,
flag_daemon: bool,
flag_pid_file: Option<String>,
2019-01-09 16:45:12 +00:00
flag_stats_file: Option<String>,
2016-11-23 14:21:22 +00:00
flag_user: Option<String>,
flag_group: Option<String>,
flag_log_file: Option<String>
}
2016-11-23 14:21:22 +00:00
struct DualLogger {
file: Mutex<Option<File>>
}
impl DualLogger {
pub fn new<P: AsRef<Path>>(path: Option<P>) -> Result<Self, io::Error> {
if let Some(path) = path {
let file = try!(File::create(path));
Ok(DualLogger{file: Mutex::new(Some(file))})
} else {
Ok(DualLogger{file: Mutex::new(None)})
}
}
}
2015-11-19 15:34:20 +00:00
2016-11-23 14:21:22 +00:00
impl log::Log for DualLogger {
2016-06-11 14:08:57 +00:00
#[inline]
2019-01-01 23:35:14 +00:00
fn enabled(&self, _metadata: &log::Metadata) -> bool {
2015-11-19 15:34:20 +00:00
true
}
2016-06-11 14:08:57 +00:00
#[inline]
2019-01-01 23:35:14 +00:00
fn log(&self, record: &log::Record) {
2015-11-19 15:34:20 +00:00
if self.enabled(record.metadata()) {
2016-11-23 14:21:22 +00:00
println!("{} - {}", record.level(), record.args());
let mut file = self.file.lock().expect("Lock poisoned");
if let Some(ref mut file) = *file {
2016-11-23 14:21:22 +00:00
let time = time::strftime("%F %T", &time::now()).expect("Failed to format timestamp");
writeln!(file, "{} - {} - {}", time, record.level(), record.args()).expect("Failed to write to logfile");
}
2015-11-19 15:34:20 +00:00
}
}
2019-01-01 23:35:14 +00:00
#[inline]
fn flush(&self) {
let mut file = self.file.lock().expect("Lock poisoned");
if let Some(ref mut file) = *file {
try_fail!(file.flush(), "Logging error: {}");
}
}
2015-11-19 15:34:20 +00:00
}
2017-05-04 05:26:21 +00:00
fn run_script(script: &str, ifname: &str) {
2015-11-23 18:06:25 +00:00
let mut cmd = Command::new("sh");
cmd.arg("-c").arg(&script).env("IFNAME", ifname);
debug!("Running script: {:?}", cmd);
match cmd.status() {
2019-01-01 23:35:14 +00:00
Ok(status) => if !status.success() {
2016-06-11 14:08:57 +00:00
error!("Script returned with error: {:?}", status.code())
2015-11-23 18:06:25 +00:00
},
Err(e) => error!("Failed to execute script {:?}: {}", script, e)
}
2015-11-19 15:34:20 +00:00
}
enum AnyTable {
Switch(SwitchTable),
Routing(RoutingTable)
}
enum AnyCloud<P: Protocol> {
Switch(GenericCloud<P, SwitchTable>),
Routing(GenericCloud<P, RoutingTable>)
}
impl<P: Protocol> AnyCloud<P> {
2019-01-01 23:35:14 +00:00
#[allow(unknown_lints,clippy::too_many_arguments)]
2019-01-10 18:36:50 +00:00
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(
2019-01-10 18:36:50 +00:00
config, device,t, learning, broadcast, addresses, crypto, port_forwarding
)),
AnyTable::Routing(t) => AnyCloud::Routing(GenericCloud::<P, RoutingTable>::new(
2019-01-10 18:36:50 +00:00
config, device,t, learning, broadcast, addresses, crypto, port_forwarding
))
}
}
fn ifname(&self) -> &str {
match *self {
AnyCloud::Switch(ref c) => c.ifname(),
AnyCloud::Routing(ref c) => c.ifname()
}
}
fn run(&mut self) {
match *self {
AnyCloud::Switch(ref mut c) => c.run(),
AnyCloud::Routing(ref mut c) => c.run()
}
}
fn connect(&mut self, a: &str) -> Result<(), Error> {
match *self {
AnyCloud::Switch(ref mut c) => c.connect(a),
AnyCloud::Routing(ref mut c) => c.connect(a)
}
}
fn add_reconnect_peer(&mut self, a: String) {
match *self {
AnyCloud::Switch(ref mut c) => c.add_reconnect_peer(a),
AnyCloud::Routing(ref mut c) => c.add_reconnect_peer(a)
}
}
}
fn run<P: Protocol> (config: Config) {
let device = try_fail!(Device::new(&config.device_name, config.device_type),
"Failed to open virtual {} interface {}: {}", config.device_type, config.device_name);
2015-11-23 00:40:47 +00:00
info!("Opened device {}", device.ifname());
let mut ranges = Vec::with_capacity(config.subnets.len());
for s in &config.subnets {
ranges.push(try_fail!(Range::from_str(s), "Invalid subnet format: {} ({})", s));
2015-11-22 19:02:02 +00:00
}
let dst_timeout = config.dst_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))),
2019-01-10 18:36:50 +00:00
Type::Tun => (false, false, AnyTable::Routing(RoutingTable::new())),
Type::Dummy => (false, false, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
2015-11-23 00:40:47 +00:00
},
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)))
2015-11-23 00:40:47 +00:00
};
2015-11-29 20:53:08 +00:00
Crypto::init();
let crypto = match config.shared_key {
2019-01-10 18:36:50 +00:00
Some(ref key) => Crypto::from_shared_key(config.crypto, key),
2015-11-23 14:40:04 +00:00
None => Crypto::None
};
let port_forwarding = if config.port_forwarding {
PortForwarding::new(config.port)
} else {
None
};
2019-01-10 18:36:50 +00:00
let mut cloud = AnyCloud::<P>::new(&config, device, table, learning,broadcasting,ranges, crypto, port_forwarding);
if let Some(script) = config.ifup {
2017-05-04 05:26:21 +00:00
run_script(&script, cloud.ifname());
}
for addr in config.peers {
try_fail!(cloud.connect(&addr as &str), "Failed to send message to {}: {}", &addr);
cloud.add_reconnect_peer(addr);
}
2016-11-23 14:21:22 +00:00
if config.daemonize {
info!("Running process as daemon");
let mut daemonize = daemonize::Daemonize::new();
if let Some(user) = config.user {
daemonize = daemonize.user(&user as &str);
}
if let Some(group) = config.group {
daemonize = daemonize.group(&group as &str);
}
if let Some(pid_file) = config.pid_file {
daemonize = daemonize.pid_file(pid_file).chown_pid_file(true);
}
try_fail!(daemonize.start(), "Failed to daemonize: {}");
}
cloud.run();
if let Some(script) = config.ifdown {
2017-05-04 05:26:21 +00:00
run_script(&script, cloud.ifname());
}
}
fn main() {
let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
if args.flag_version {
2015-11-30 22:04:24 +00:00
Crypto::init();
println!("VpnCloud v{}, protocol version {}, libsodium {} (AES256: {})",
env!("CARGO_PKG_VERSION"),
VERSION,
Crypto::sodium_version(),
Crypto::aes256_available()
);
return;
}
2019-01-01 23:35:14 +00:00
let logger = try_fail!(DualLogger::new(args.flag_log_file.as_ref()), "Failed to open logfile: {}");
log::set_boxed_logger(Box::new(logger)).unwrap();
assert!(!args.flag_verbose || !args.flag_quiet);
log::set_max_level(
if args.flag_verbose {
2019-01-01 23:35:14 +00:00
log::LevelFilter::Debug
} else if args.flag_quiet {
2019-01-01 23:35:14 +00:00
log::LevelFilter::Error
} else {
2019-01-01 23:35:14 +00:00
log::LevelFilter::Info
2015-11-23 00:40:47 +00:00
}
2019-01-01 23:35:14 +00:00
);
let mut config = Config::default();
if let Some(ref file) = args.flag_config {
info!("Reading config file '{}'", file);
let f = try_fail!(File::open(file), "Failed to open config file: {:?}");
let config_file = try_fail!(serde_yaml::from_reader(f), "Failed to load config file: {:?}");
config.merge_file(config_file)
}
config.merge_args(args);
debug!("Config: {:?}", config);
match config.device_type {
Type::Tap => run::<ethernet::Frame>(config),
2019-01-10 18:36:50 +00:00
Type::Tun => run::<ip::Packet>(config),
Type::Dummy => run::<ethernet::Frame>(config)
}
2015-11-22 19:02:02 +00:00
}