From 06fc814b4b066bbaeed6278b4ec2308ebe1075ff Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Mon, 8 Aug 2016 16:30:22 +0200 Subject: [PATCH] Added support for YAML config file (re #8) --- CHANGELOG.md | 3 + Cargo.lock | 7 ++ Cargo.toml | 1 + README.md | 1 + src/config.rs | 163 ++++++++++++++++++++++++ src/configfile.rs | 306 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 122 +++++++++--------- src/usage.txt | 7 +- vpncloud.md | 47 ++++++- 9 files changed, 588 insertions(+), 69 deletions(-) create mode 100644 src/config.rs create mode 100644 src/configfile.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ab3e5a2..7c01f04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ This project follows [semantic versioning](http://semver.org). ### UNRELEASED +- [added] Added `-s` shorthand for `--subnet` +- [added] Added support for YAML config file via `--config` - [changed] Configurable magic header is now used instead of Network-ID (**incompatible**) +- [changed] Clarified documentation on TUN netmasks - [fixed] Fixed documentation of listen parameter - [fixed] Fixed problem with multiple subnets diff --git a/Cargo.lock b/Cargo.lock index 92a6f19..00d4b9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -247,6 +248,11 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "yaml-rust" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] "checksum aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3fb52b09c1710b961acb35390d514be82e4ac96a9969a8e38565a29b878dc9" "checksum aligned_alloc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e970f9697cd6bb78fa6616c04b36bd0d2e9259846b9ebe10562fbedc4823d896" @@ -280,3 +286,4 @@ dependencies = [ "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum yaml-rust 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ebfe12f475ad59be6178ebf004d51e682022496535994f8d23fd7ed31084598c" diff --git a/Cargo.toml b/Cargo.toml index f053106..c91b05d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ rand = "0.3" fnv = "1" net2 = "0.2" bitflags = "0.7" +yaml-rust = "0.3" [build-dependencies] gcc = "0.3" diff --git a/README.md b/README.md index e238e11..bf27515 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ somewhat stable state. VpnCloud features the following functionality: Ethernet networks. * High throughput and low additional latency (see [performance page](https://github.com/dswd/vpncloud.rs/wiki/Performance-Measurements)) * Support for tunneled VLans (TAP device) +* Option to hide protocol header ### Installing diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..92df308 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,163 @@ +use super::{MAGIC, Args}; + +use device::Type; +use types::{Mode, HeaderMagic}; +use crypto::CryptoMethod; +use util::{Encoder, Duration}; + +use std::hash::{Hash, SipHasher, Hasher}; + + +#[derive(RustcDecodable, Debug)] +pub struct Config { + pub device_type: Type, + pub device_name: String, + pub ifup: Option, + pub ifdown: Option, + pub crypto: CryptoMethod, + pub shared_key: Option, + pub magic: Option, + pub port: u16, + pub peers: Vec, + pub peer_timeout: Duration, + pub mode: Mode, + pub dst_timeout: Duration, + pub subnets: Vec, +} + +impl Default for Config { + fn default() -> Self { + Config { + device_type: Type::Tap, device_name: "vpncloud%d".to_string(), + ifup: None, ifdown: None, + crypto: CryptoMethod::ChaCha20, shared_key: None, + magic: None, + port: 3210, peers: vec![], peer_timeout: 1800, + mode: Mode::Normal, dst_timeout: 300, + subnets: vec![] + } + } +} + +impl Config { + pub fn merge_file(&mut self, file: ConfigFile) { + if let Some(val) = file.device_type { + self.device_type = val; + } + if let Some(val) = file.device_name { + self.device_name = val; + } + if let Some(val) = file.ifup { + self.ifup = Some(val); + } + if let Some(val) = file.ifdown { + self.ifdown = Some(val); + } + if let Some(val) = file.crypto { + self.crypto = val; + } + if let Some(val) = file.shared_key { + self.shared_key = Some(val); + } + if let Some(val) = file.magic { + self.magic = Some(val); + } + if let Some(val) = file.port { + self.port = val; + } + if let Some(mut val) = file.peers { + self.peers.append(&mut val); + } + if let Some(val) = file.peer_timeout { + self.peer_timeout = val; + } + if let Some(val) = file.mode { + self.mode = val; + } + if let Some(val) = file.dst_timeout { + self.dst_timeout = val; + } + if let Some(mut val) = file.subnets { + self.subnets.append(&mut val); + } + } + + pub fn merge_args(&mut self, mut args: Args) { + if let Some(val) = args.flag_type { + self.device_type = val; + } + if let Some(val) = args.flag_device { + self.device_name = val; + } + if let Some(val) = args.flag_ifup { + self.ifup = Some(val); + } + if let Some(val) = args.flag_ifdown { + self.ifdown = Some(val); + } + if let Some(val) = args.flag_crypto { + self.crypto = val; + } + if let Some(val) = args.flag_shared_key { + self.shared_key = Some(val); + } + if let Some(val) = args.flag_network_id { + warn!("The --network-id argument is deprecated, please use --magic instead."); + self.magic = Some(val); + } + if let Some(val) = args.flag_magic { + self.magic = Some(val); + } + if let Some(val) = args.flag_listen { + self.port = val; + } + self.peers.append(&mut args.flag_connect); + if let Some(val) = args.flag_peer_timeout { + self.peer_timeout = val; + } + if let Some(val) = args.flag_mode { + self.mode = val; + } + if let Some(val) = args.flag_dst_timeout { + self.dst_timeout = val; + } + self.subnets.append(&mut args.flag_subnet); + } + + pub fn get_magic(&self) -> HeaderMagic { + if let Some(ref name) = self.magic { + 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 + } + } else { + MAGIC + } + } +} + + +#[derive(RustcDecodable, Debug)] +pub struct ConfigFile { + pub device_type: Option, + pub device_name: Option, + pub ifup: Option, + pub ifdown: Option, + pub crypto: Option, + pub shared_key: Option, + pub magic: Option, + pub port: Option, + pub peers: Option>, + pub peer_timeout: Option, + pub mode: Option, + pub dst_timeout: Option, + pub subnets: Option>, +} diff --git a/src/configfile.rs b/src/configfile.rs new file mode 100644 index 0000000..e742b03 --- /dev/null +++ b/src/configfile.rs @@ -0,0 +1,306 @@ +extern crate yaml_rust; +extern crate rustc_serialize; + +use std::fs::File; +use std::io::{self, Read}; +use std::ops::Deref; +use std::ascii::AsciiExt; +use std::fmt; + +use rustc_serialize::{Decodable, Decoder}; +use yaml_rust::{yaml, Yaml, YamlLoader, ScanError}; + +#[derive(Debug)] +pub enum ParseError { + Read(io::Error), + Syntax(ScanError), + NoDocument, + InvalidType(&'static str, Yaml), + InvalidOption(String, Vec), + Unsupported(&'static str), + Other(String) +} + +impl fmt::Display for ParseError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + ParseError::Read(ref err) => write!(formatter, "Failed to read config: {}", err), + ParseError::Syntax(ref err) => write!(formatter, "Invalid config syntax: {}", err), + ParseError::NoDocument => write!(formatter, "Config was empty"), + ParseError::InvalidType(ref exp, ref value) => write!(formatter, "Invalid value encountered for type {}: {:?}", exp, value), + ParseError::InvalidOption(ref opt, ref opts) => write!(formatter, "Invalid option value {}, choices were {:?}", opt, opts), + ParseError::Unsupported(ref reason) => write!(formatter, "Failed to decode config: {}", reason), + ParseError::Other(ref reason) => write!(formatter, "Failed to decode config: {}", reason) + } + } +} + + +pub fn parse(file: &str) -> Result { + let mut file = try!(File::open(file).map_err(ParseError::Read)); + let mut text = String::new(); + try!(file.read_to_string(&mut text).map_err(ParseError::Read)); + parse_str(&text) +} + +pub fn parse_str(text: &str) -> Result { + if let Some(yaml) = try!(YamlLoader::load_from_str(text).map_err(ParseError::Syntax)).pop() { + parse_yaml(yaml) + } else { + Err(ParseError::NoDocument) + } +} + +pub fn parse_yaml(yaml: Yaml) -> Result { + T::decode(&mut YamlDecoder(yaml)) +} + +struct YamlDecoder(Yaml); + +static NULL: Yaml = Yaml::Null; + +impl YamlDecoder { + #[inline] + fn get_field(&self, name: &str) -> Result { + match self.as_hash() { + Some(hash) => match hash.get(&Yaml::String(name.to_string())) { + Some(field) => Ok(field.clone()), + None => Ok(Yaml::Null) + }, + None => Err(ParseError::InvalidType("hash", self.0.clone())) + } + } + + #[inline] + fn get_item(&self, index: usize) -> Result<&Yaml, ParseError> { + match self.as_vec() { + Some(vec) => if vec.len() > index { + Ok(&vec[index]) + } else { + Ok(&NULL) + }, + None => Err(ParseError::InvalidType("hash", self.0.clone())) + } + } + + #[inline] + fn vec(&self) -> Result<&yaml::Array, ParseError> { + match self.as_vec() { + Some(vec) => Ok(vec), + None => Err(ParseError::InvalidType("list", self.0.clone())) + } + } + + #[inline] + fn hash(&self) -> Result<&yaml::Hash, ParseError> { + match self.as_hash() { + Some(hash) => Ok(hash), + None => Err(ParseError::InvalidType("hash", self.0.clone())) + } + } + + #[inline] + fn bool(&self) -> Result { + self.0.as_bool().ok_or_else(|| ParseError::InvalidType("bool", self.0.clone())) + } + + #[inline] + fn num(&self) -> Result { + self.0.as_i64().ok_or_else(|| ParseError::InvalidType("number", self.0.clone())) + } + + #[inline] + fn float(&self) -> Result { + self.0.as_f64().ok_or_else(|| ParseError::InvalidType("float", self.0.clone())) + } + + #[inline] + fn str(&self) -> Result<&str, ParseError> { + self.as_str().ok_or_else(|| ParseError::InvalidType("string", self.0.clone())) + } +} + +impl Deref for YamlDecoder { + type Target = Yaml; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Decoder for YamlDecoder { + type Error = ParseError; + + #[inline] + fn read_nil(&mut self) -> Result<(), ParseError> { + Ok(()) + } + + #[inline] + fn read_bool(&mut self) -> Result { + self.bool() + } + + #[inline] + fn read_f64(&mut self) -> Result { + self.float() + } + + #[inline] + fn read_f32(&mut self) -> Result { + Ok(try!(self.float()) as f32) + } + + #[inline] + fn read_i64(&mut self) -> Result { + self.num() + } + + #[inline] + fn read_i32(&mut self) -> Result { + Ok(try!(self.num()) as i32) + } + + #[inline] + fn read_i16(&mut self) -> Result { + Ok(try!(self.num()) as i16) + } + + #[inline] + fn read_i8(&mut self) -> Result { + Ok(try!(self.num()) as i8) + } + + #[inline] + fn read_isize(&mut self) -> Result { + Ok(try!(self.num()) as isize) + } + + #[inline] + fn read_u64(&mut self) -> Result { + Ok(try!(self.num()) as u64) + } + + #[inline] + fn read_u32(&mut self) -> Result { + Ok(try!(self.read_u64()) as u32) + } + + #[inline] + fn read_u16(&mut self) -> Result { + Ok(try!(self.read_u64()) as u16) + } + + #[inline] + fn read_u8(&mut self) -> Result { + Ok(try!(self.read_u64()) as u8) + } + + #[inline] + fn read_usize(&mut self) -> Result { + Ok(try!(self.read_u64()) as usize) + } + + #[inline] + fn read_str(&mut self) -> Result { + self.str().map(|val| val.to_string()) + } + + #[inline] + fn read_char(&mut self) -> Result { + let string = try!(self.str()); + if string.len() == 1 { + Ok(string.chars().next().unwrap()) + } else { + Err(ParseError::InvalidType("char", self.clone())) + } + } + + fn read_enum(&mut self, _name: &str, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(self) + } + + fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> Result where F: FnMut(&mut Self, usize) -> Result { + let name = try!(self.read_str()); + for (i, n) in names.iter().enumerate() { + if n == &name { + return f(self, i) + } + } + for (i, n) in names.iter().enumerate() { + if n.eq_ignore_ascii_case(&name) { + return f(self, i) + } + } + Err(ParseError::InvalidOption(name, names.iter().map(|s| s.to_string()).collect())) + } + + fn read_enum_variant_arg(&mut self, _a_idx: usize, _f: F) -> Result where F: FnOnce(&mut Self) -> Result { + Err(ParseError::Unsupported("Enum variants with aruments are not supported")) + } + + fn read_enum_struct_variant(&mut self, _names: &[&str], _f: F) -> Result where F: FnMut(&mut Self, usize) -> Result { + Err(ParseError::Unsupported("Enum variants with aruments are not supported")) + } + + fn read_enum_struct_variant_field(&mut self, _f_name: &str, _f_idx: usize, _f: F) -> Result where F: FnOnce(&mut Self) -> Result { + Err(ParseError::Unsupported("Enum variants with aruments are not supported")) + } + + fn read_struct(&mut self, _s_name: &str, _len: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(self) + } + + fn read_struct_field(&mut self, f_name: &str, _f_idx: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(&mut YamlDecoder(try!(self.get_field(f_name)))) + } + + fn read_tuple(&mut self, _len: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(self) + } + + fn read_tuple_arg(&mut self, a_idx: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(&mut YamlDecoder(try!(self.get_item(a_idx)).clone())) + } + + fn read_tuple_struct(&mut self, _s_name: &str, _len: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(self) + } + + fn read_tuple_struct_arg(&mut self, a_idx: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(&mut YamlDecoder(try!(self.get_item(a_idx)).clone())) + } + + fn read_option(&mut self, mut f: F) -> Result where F: FnMut(&mut Self, bool) -> Result { + let isset = !self.is_null(); + f(self, isset) + } + + fn read_seq(&mut self, f: F) -> Result where F: FnOnce(&mut Self, usize) -> Result { + let len = try!(self.vec()).len(); + f(self, len) + } + + fn read_seq_elt(&mut self, idx: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(&mut YamlDecoder(try!(self.vec())[idx].clone())) + } + + fn read_map(&mut self, f: F) -> Result where F: FnOnce(&mut Self, usize) -> Result { + let len = try!(self.hash()).len(); + f(self, len) + } + + fn read_map_elt_key(&mut self, idx: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(&mut YamlDecoder(try!(self.hash()).into_iter().nth(idx).unwrap().0.clone())) + } + + fn read_map_elt_val(&mut self, idx: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result { + f(&mut YamlDecoder(try!(self.hash()).into_iter().nth(idx).unwrap().1.clone())) + } + + fn error(&mut self, err: &str) -> Self::Error { + ParseError::Other(err.to_string()) + } + +} diff --git a/src/main.rs b/src/main.rs index b695ff0..34545f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ extern crate aligned_alloc; extern crate rand; extern crate fnv; extern crate net2; +extern crate yaml_rust; #[cfg(feature = "bench")] extern crate test; #[macro_use] pub mod util; @@ -27,12 +28,13 @@ pub mod ip; pub mod cloud; pub mod device; pub mod poll; +pub mod config; +pub mod configfile; #[cfg(test)] mod tests; #[cfg(feature = "bench")] mod benches; use docopt::Docopt; -use std::hash::{Hash, SipHasher, Hasher}; use std::str::FromStr; use std::process::Command; @@ -42,12 +44,37 @@ use ip::RoutingTable; use types::{Mode, Range, Table, Protocol, HeaderMagic}; use cloud::GenericCloud; use crypto::{Crypto, CryptoMethod}; -use util::{Duration, Encoder}; +use util::Duration; +use config::Config; const VERSION: u8 = 1; const MAGIC: HeaderMagic = *b"vpn\x01"; +static USAGE: &'static str = include_str!("usage.txt"); + +#[derive(RustcDecodable, Debug)] +pub struct Args { + flag_config: Option, + flag_type: Option, + flag_mode: Option, + flag_shared_key: Option, + flag_crypto: Option, + flag_subnet: Vec, + flag_device: Option, + flag_listen: Option, + flag_network_id: Option, + flag_magic: Option, + flag_connect: Vec, + flag_peer_timeout: Option, + flag_dst_timeout: Option, + flag_verbose: bool, + flag_quiet: bool, + flag_ifup: Option, + flag_ifdown: Option, + flag_version: bool +} + struct SimpleLogger; @@ -65,29 +92,6 @@ impl log::Log for SimpleLogger { } } -static USAGE: &'static str = include_str!("usage.txt"); - -#[derive(RustcDecodable, Debug)] -struct Args { - flag_type: Type, - flag_mode: Mode, - flag_shared_key: Option, - flag_crypto: CryptoMethod, - flag_subnet: Vec, - flag_device: String, - flag_listen: u16, - flag_network_id: Option, - flag_magic: Option, - flag_connect: Vec, - flag_peer_timeout: Duration, - flag_dst_timeout: Duration, - flag_verbose: bool, - flag_quiet: bool, - flag_ifup: Option, - flag_ifdown: Option, - flag_version: bool -} - fn run_script(script: String, ifname: &str) { let mut cmd = Command::new("sh"); cmd.arg("-c").arg(&script).env("IFNAME", ifname); @@ -102,18 +106,18 @@ fn run_script(script: String, ifname: &str) { } } -fn run (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); +fn run (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); 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)); + 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)); } - let dst_timeout = args.flag_dst_timeout; - let peer_timeout = args.flag_peer_timeout; - let (learning, broadcasting, table): (bool, bool, Box) = match args.flag_mode { - Mode::Normal => match args.flag_type { + let dst_timeout = config.dst_timeout; + let peer_timeout = config.peer_timeout; + let (learning, broadcasting, table): (bool, bool, Box
) = match config.mode { + Mode::Normal => match config.device_type { Type::Tap => (true, true, Box::new(SwitchTable::new(dst_timeout))), Type::Tun => (false, false, Box::new(RoutingTable::new())) }, @@ -121,41 +125,22 @@ fn run (mut args: Args) { Mode::Switch => (true, true, Box::new(SwitchTable::new(dst_timeout))), Mode::Hub => (false, true, Box::new(SwitchTable::new(dst_timeout))) }; - 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 - } - }); + let magic = config.get_magic(); Crypto::init(); - let crypto = match args.flag_shared_key { - Some(key) => Crypto::from_shared_key(args.flag_crypto, &key), + let crypto = match config.shared_key { + Some(key) => Crypto::from_shared_key(config.crypto, &key), None => Crypto::None }; - let mut cloud = GenericCloud::::new(magic, device, args.flag_listen, table, peer_timeout, learning, broadcasting, ranges, crypto); - if let Some(script) = args.flag_ifup { + let mut cloud = GenericCloud::::new(magic, device, config.port, table, peer_timeout, learning, broadcasting, ranges, crypto); + if let Some(script) = config.ifup { run_script(script, cloud.ifname()); } - for addr in args.flag_connect { + for addr in config.peers { 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 { + if let Some(script) = config.ifdown { run_script(script, cloud.ifname()); } } @@ -183,9 +168,16 @@ fn main() { } Box::new(SimpleLogger) }).unwrap(); - debug!("Args: {:?}", args); - match args.flag_type { - Type::Tap => run::(args), - Type::Tun => run::(args), + let mut config = Config::default(); + if let Some(ref file) = args.flag_config { + info!("Reading config file '{}'", file); + let config_file = try_fail!(configfile::parse(file), "Failed to load config file: {:?}"); + config.merge_file(config_file) + } + config.merge_args(args); + debug!("Config: {:?}", config); + match config.device_type { + Type::Tap => run::(config), + Type::Tun => run::(config) } } diff --git a/src/usage.txt b/src/usage.txt index 3b6dec5..c8b183c 100644 --- a/src/usage.txt +++ b/src/usage.txt @@ -1,7 +1,9 @@ Usage: - vpncloud [options] [-t ] [-d ] [-l ] [-c ...] [-s ...] + vpncloud [options] [--config ] [-t ] [-d ] [-l ] [-c ...] [-s ...] Options: + --config Read configuration options from the + specified file. -t , --type Set the type of network ("tap" or "tun"). [default: tap] -d , --device Name of the virtual device. @@ -19,9 +21,8 @@ Options: --shared-key The shared key to encrypt all traffic. --crypto The encryption method to use ("aes256", or "chacha20"). [default: chacha20] - --peer-timeout Peer timeout in seconds. [default: 1800] + --peer-timeout Peer timeout in seconds. --dst-timeout Switch table entry timeout in seconds. - [default: 300] --ifup A command to setup the network interface. --ifdown A command to bring down the network interface. diff --git a/vpncloud.md b/vpncloud.md index de16cf7..998aa2a 100644 --- a/vpncloud.md +++ b/vpncloud.md @@ -3,11 +3,18 @@ vpncloud(1) -- Peer-to-peer VPN ## SYNOPSIS -`vpncloud [options] [-t ] [-d ] [-l ] [-c ...]` +`vpncloud [options] [--config ] [-t ] [-d ] [-l ] [-c ...]` ## OPTIONS + * `--config `: + + Read configuration options from the specified file. Please see the section + **CONFIG FILES** for documentation on the file format. + If the same option is defined in the config file and as a parameter, the + parameter overrides the config file. + * `-t `, `--type `: Set the type of network. There are two options: **tap** devices process @@ -213,6 +220,44 @@ interface is configured with (/16 in this example). messages, reorder them, or duplicate them). +## CONFIG FILES + +The config file is a YAML file that contains configuration values. All entries +are optional and override the defaults. Please see the section **OPTIONS** for +detailed descriptions of the options. + +* `device_type`: Set the type of network. Same as `--type` +* `device_name`: Name of the virtual device. Same as `--device` +* `ifup`: A command to setup the network interface. Same as `--ifup` +* `ifup`: A command to bring down the network interface. Same as `--ifdown` +* `crypto`: The encryption method to use. Same as `--crypto` +* `shared_key`: The shared key to encrypt all traffic. Same as `--shared-key` +* `magic`: Override the 4-byte magic header of each packet. Same as `--magic` +* `port`: The port number on which to listen for data. Same as `--listen` +* `peers`: A list of addresses to connect to. See `--connect` +* `peer_timeout`: Peer timeout in seconds. Same as`--peer-timeout` +* `mode`: The mode of the VPN. Same as `--mode` +* `dst_timeout`: Switch table entry timeout in seconds. Same as `--dst-timeout` +* `subnets`: A list of local subnets to use. See `--subnet` + + +### Example + +device_type: tun +device_name: vpncloud%d +ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up +crypto: aes256 +shared_key: mysecret +port: 3210 +peers: + - remote.machine.foo:3210 + - remote.machine.bar:3210 +peer_timeout: 1800 +mode: normal +subnets: + - 10.0.1.0/24 + + ## NETWORK PROTOCOL The protocol of VpnCloud is kept as simple as possible to allow other