2015-11-19 15:34:20 +00:00
|
|
|
#[macro_use] extern crate log;
|
|
|
|
extern crate time;
|
|
|
|
extern crate docopt;
|
|
|
|
extern crate rustc_serialize;
|
2015-11-20 08:11:54 +00:00
|
|
|
extern crate epoll;
|
2015-11-23 14:40:04 +00:00
|
|
|
#[cfg(feature = "crypto")] extern crate sodiumoxide;
|
2015-11-19 15:34:20 +00:00
|
|
|
|
2015-11-19 18:29:42 +00:00
|
|
|
mod util;
|
2015-11-23 00:04:30 +00:00
|
|
|
mod types;
|
2015-11-24 19:55:14 +00:00
|
|
|
mod crypto;
|
2015-11-19 15:34:20 +00:00
|
|
|
mod udpmessage;
|
|
|
|
mod ethernet;
|
2015-11-21 15:50:50 +00:00
|
|
|
mod ip;
|
2015-11-22 16:28:04 +00:00
|
|
|
mod cloud;
|
2015-11-22 18:00:56 +00:00
|
|
|
mod device;
|
2015-11-19 15:34:20 +00:00
|
|
|
|
|
|
|
use time::Duration;
|
|
|
|
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
|
|
|
|
2015-11-23 00:40:47 +00:00
|
|
|
use device::Device;
|
|
|
|
use ethernet::SwitchTable;
|
|
|
|
use ip::RoutingTable;
|
2015-11-23 10:55:37 +00:00
|
|
|
use types::{Error, Mode, Type, Range, Table};
|
2015-11-23 00:04:30 +00:00
|
|
|
use cloud::{TapCloud, TunCloud};
|
2015-11-24 10:19:27 +00:00
|
|
|
use udpmessage::VERSION;
|
2015-11-24 19:55:14 +00:00
|
|
|
use crypto::Crypto;
|
2015-11-19 15:34:20 +00:00
|
|
|
|
2015-11-19 18:29:42 +00:00
|
|
|
//TODO: Implement IPv6
|
2015-11-19 19:51:53 +00:00
|
|
|
//TODO: Call close
|
2015-11-24 19:55:14 +00:00
|
|
|
//FIXME: The crypto method should be signaled as part of the protocol
|
|
|
|
//FIXME: The HMAC should also include the header
|
|
|
|
//FIXME: Encryption should also include all following additional headers
|
2015-11-19 15:34:20 +00:00
|
|
|
|
|
|
|
struct SimpleLogger;
|
|
|
|
|
|
|
|
impl log::Log for SimpleLogger {
|
|
|
|
fn enabled(&self, _metadata: &log::LogMetadata) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
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-22 22:34:54 +00:00
|
|
|
flag_subnet: Vec<String>,
|
2015-11-19 16:23:36 +00:00
|
|
|
flag_device: String,
|
|
|
|
flag_listen: String,
|
2015-11-20 17:40:23 +00:00
|
|
|
flag_network_id: Option<String>,
|
2015-11-23 16:10:42 +00:00
|
|
|
flag_connect: Vec<String>,
|
2015-11-19 16:23:36 +00:00
|
|
|
flag_peer_timeout: usize,
|
2015-11-23 00:40:47 +00:00
|
|
|
flag_dst_timeout: usize,
|
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() {
|
|
|
|
Ok(status) => match status.success() {
|
|
|
|
true => (),
|
|
|
|
false => error!("Script returned with error: {:?}", status.code())
|
|
|
|
},
|
|
|
|
Err(e) => error!("Failed to execute script {:?}: {}", script, e)
|
|
|
|
}
|
2015-11-19 15:34:20 +00:00
|
|
|
}
|
|
|
|
|
2015-11-22 19:02:02 +00:00
|
|
|
fn main() {
|
|
|
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
2015-11-24 10:19:27 +00:00
|
|
|
if args.flag_version {
|
|
|
|
println!("VpnCloud v{} ({}, protocol version {})", env!("CARGO_PKG_VERSION"),
|
|
|
|
if cfg!(feature = "crypto") { "with crypto support" } else { "without crypto support" },
|
|
|
|
VERSION
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
2015-11-22 19:02:02 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
Box::new(SimpleLogger)
|
|
|
|
}).unwrap();
|
|
|
|
debug!("Args: {:?}", args);
|
2015-11-23 00:40:47 +00:00
|
|
|
let device = Device::new(&args.flag_device, args.flag_type).expect("Failed to open virtual interface");
|
|
|
|
info!("Opened device {}", device.ifname());
|
|
|
|
let mut ranges = Vec::with_capacity(args.flag_subnet.len());
|
|
|
|
for s in args.flag_subnet {
|
|
|
|
ranges.push(Range::from_str(&s).expect("Invalid subnet"));
|
2015-11-22 19:02:02 +00:00
|
|
|
}
|
2015-11-23 00:40:47 +00:00
|
|
|
let dst_timeout = Duration::seconds(args.flag_dst_timeout as i64);
|
|
|
|
let peer_timeout = Duration::seconds(args.flag_peer_timeout as i64);
|
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
|
|
|
};
|
|
|
|
let network_id = args.flag_network_id.map(|name| {
|
|
|
|
let mut s = SipHasher::new();
|
|
|
|
name.hash(&mut s);
|
|
|
|
s.finish()
|
|
|
|
});
|
2015-11-23 14:40:04 +00:00
|
|
|
let crypto = match args.flag_shared_key {
|
|
|
|
Some(key) => Crypto::from_shared_key(&key),
|
|
|
|
None => Crypto::None
|
|
|
|
};
|
2015-11-23 00:40:47 +00:00
|
|
|
match args.flag_type {
|
|
|
|
Type::Tap => {
|
2015-11-23 14:40:04 +00:00
|
|
|
let mut cloud = TapCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
|
2015-11-23 18:06:25 +00:00
|
|
|
if let Some(script) = args.flag_ifup {
|
|
|
|
run_script(script, cloud.ifname());
|
|
|
|
}
|
2015-11-23 16:10:42 +00:00
|
|
|
for addr in &args.flag_connect {
|
2015-11-23 00:40:47 +00:00
|
|
|
cloud.connect(&addr as &str, true).expect("Failed to send");
|
|
|
|
}
|
2015-11-23 18:06:25 +00:00
|
|
|
cloud.run();
|
|
|
|
if let Some(script) = args.flag_ifdown {
|
|
|
|
run_script(script, cloud.ifname());
|
|
|
|
}
|
2015-11-23 00:40:47 +00:00
|
|
|
},
|
|
|
|
Type::Tun => {
|
2015-11-23 14:40:04 +00:00
|
|
|
let mut cloud = TunCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
|
2015-11-23 18:06:25 +00:00
|
|
|
if let Some(script) = args.flag_ifup {
|
|
|
|
run_script(script, cloud.ifname());
|
|
|
|
}
|
2015-11-23 16:10:42 +00:00
|
|
|
for addr in &args.flag_connect {
|
2015-11-23 00:40:47 +00:00
|
|
|
cloud.connect(&addr as &str, true).expect("Failed to send");
|
|
|
|
}
|
2015-11-23 18:06:25 +00:00
|
|
|
if let Some(script) = args.flag_ifdown {
|
|
|
|
run_script(script, cloud.ifname());
|
|
|
|
}
|
2015-11-23 00:40:47 +00:00
|
|
|
cloud.run()
|
|
|
|
}
|
|
|
|
};
|
2015-11-22 19:02:02 +00:00
|
|
|
}
|