vpncloud/src/main.rs

192 lines
5.9 KiB
Rust
Raw Normal View History

// VpnCloud - Peer-to-Peer VPN
// Copyright (C) 2015-2016 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;
extern crate rustc_serialize;
2015-11-25 13:31:05 +00:00
extern crate signal;
extern crate nix;
2015-11-25 20:55:30 +00:00
extern crate libc;
2015-11-30 21:11:37 +00:00
extern crate aligned_alloc;
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;
#[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;
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;
2015-11-20 17:40:23 +00:00
use std::hash::{Hash, SipHasher, Hasher};
2015-11-23 00:40:47 +00:00
use std::str::FromStr;
2015-11-23 18:06:25 +00:00
use std::process::Command;
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, Table, Protocol, HeaderMagic};
use cloud::GenericCloud;
2015-11-30 16:27:50 +00:00
use crypto::{Crypto, CryptoMethod};
use util::{Duration, Encoder};
const VERSION: u8 = 1;
const MAGIC: HeaderMagic = *b"vpn\x01";
2015-11-19 15:34:20 +00:00
struct SimpleLogger;
impl log::Log for SimpleLogger {
2016-06-11 14:08:57 +00:00
#[inline]
2015-11-19 15:34:20 +00:00
fn enabled(&self, _metadata: &log::LogMetadata) -> bool {
true
}
2016-06-11 14:08:57 +00:00
#[inline]
2015-11-19 15:34:20 +00:00
fn log(&self, record: &log::LogRecord) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
}
2015-11-23 10:55:37 +00:00
static USAGE: &'static str = include_str!("usage.txt");
2015-11-19 15:34:20 +00:00
#[derive(RustcDecodable, Debug)]
struct Args {
2015-11-22 19:02:02 +00:00
flag_type: Type,
2015-11-23 10:55:37 +00:00
flag_mode: Mode,
2015-11-23 14:40:04 +00:00
flag_shared_key: Option<String>,
2015-11-30 16:27:50 +00:00
flag_crypto: CryptoMethod,
2015-11-22 22:34:54 +00:00
flag_subnet: Vec<String>,
2015-11-19 16:23:36 +00:00
flag_device: String,
2016-05-02 06:35:11 +00:00
flag_listen: u16,
2015-11-20 17:40:23 +00:00
flag_network_id: Option<String>,
flag_magic: Option<String>,
2015-11-23 16:10:42 +00:00
flag_connect: Vec<String>,
2015-11-25 20:55:30 +00:00
flag_peer_timeout: Duration,
flag_dst_timeout: Duration,
2015-11-19 16:23:36 +00:00
flag_verbose: bool,
2015-11-23 18:06:25 +00:00
flag_quiet: bool,
flag_ifup: Option<String>,
2015-11-24 10:19:27 +00:00
flag_ifdown: Option<String>,
flag_version: bool
2015-11-23 18:06:25 +00:00
}
fn run_script(script: String, ifname: &str) {
let mut cmd = Command::new("sh");
cmd.arg("-c").arg(&script).env("IFNAME", ifname);
debug!("Running script: {:?}", cmd);
match cmd.status() {
2016-06-11 14:08:57 +00:00
Ok(status) => if status.success() {
()
} else {
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
}
fn run<T: Protocol> (mut args: Args) {
let device = try_fail!(Device::new(&args.flag_device, args.flag_type),
"Failed to open virtual {} interface {}: {}", args.flag_type, &args.flag_device);
2015-11-23 00:40:47 +00:00
info!("Opened device {}", device.ifname());
let mut ranges = Vec::with_capacity(args.flag_subnet.len());
for s in args.flag_subnet {
ranges.push(try_fail!(Range::from_str(&s), "Invalid subnet format: {} ({})", s));
2015-11-22 19:02:02 +00:00
}
2015-11-25 20:55:30 +00:00
let dst_timeout = args.flag_dst_timeout;
let peer_timeout = args.flag_peer_timeout;
2015-11-23 10:55:37 +00:00
let (learning, broadcasting, table): (bool, bool, Box<Table>) = match args.flag_mode {
Mode::Normal => match args.flag_type {
2015-11-23 00:40:47 +00:00
Type::Tap => (true, true, Box::new(SwitchTable::new(dst_timeout))),
Type::Tun => (false, false, Box::new(RoutingTable::new()))
},
2015-11-23 10:55:37 +00:00
Mode::Router => (false, false, Box::new(RoutingTable::new())),
Mode::Switch => (true, true, Box::new(SwitchTable::new(dst_timeout))),
Mode::Hub => (false, true, Box::new(SwitchTable::new(dst_timeout)))
2015-11-23 00:40:47 +00:00
};
if let Some(network_id) = args.flag_network_id {
warn!("The --network-id argument is deprecated, please use --magic instead.");
if args.flag_magic.is_none() {
args.flag_magic = Some(network_id);
}
}
let magic = args.flag_magic.map_or(MAGIC, |name| {
if name.starts_with("hash:") {
let mut s = SipHasher::new();
name[6..].hash(&mut s);
let mut data = [0; 4];
Encoder::write_u32((s.finish() & 0xffffffff) as u32, &mut data);
data
} else {
let num = try_fail!(u32::from_str_radix(&name, 16), "Failed to parse header magic: {}");
let mut data = [0; 4];
Encoder::write_u32(num, &mut data);
data
}
2015-11-23 00:40:47 +00:00
});
2015-11-29 20:53:08 +00:00
Crypto::init();
2015-11-23 14:40:04 +00:00
let crypto = match args.flag_shared_key {
2015-11-30 16:27:50 +00:00
Some(key) => Crypto::from_shared_key(args.flag_crypto, &key),
2015-11-23 14:40:04 +00:00
None => Crypto::None
};
let mut cloud = GenericCloud::<T>::new(magic, device, args.flag_listen, table, peer_timeout, learning, broadcasting, ranges, crypto);
if let Some(script) = args.flag_ifup {
run_script(script, cloud.ifname());
}
for addr in args.flag_connect {
try_fail!(cloud.connect(&addr as &str), "Failed to send message to {}: {}", &addr);
cloud.add_reconnect_peer(addr);
}
cloud.run();
if let Some(script) = args.flag_ifdown {
run_script(script, cloud.ifname());
}
}
fn main() {
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).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;
}
log::set_logger(|max_log_level| {
assert!(!args.flag_verbose || !args.flag_quiet);
if args.flag_verbose {
max_log_level.set(log::LogLevelFilter::Debug);
} else if args.flag_quiet {
max_log_level.set(log::LogLevelFilter::Error);
} else {
max_log_level.set(log::LogLevelFilter::Info);
2015-11-23 00:40:47 +00:00
}
Box::new(SimpleLogger)
}).unwrap();
debug!("Args: {:?}", args);
match args.flag_type {
Type::Tap => run::<ethernet::Frame>(args),
Type::Tun => run::<ip::Packet>(args),
}
2015-11-22 19:02:02 +00:00
}