Support for port forwarding (closes #6)

This commit is contained in:
Dennis Schwerdel 2016-08-10 11:34:13 +02:00
parent ed9f464dbf
commit 0243859d5a
12 changed files with 352 additions and 10 deletions

View File

@ -4,12 +4,13 @@ This project follows [semantic versioning](http://semver.org).
### UNRELEASED
- [added] Support for automatic port forwarding via UPnP
- [added] Added `-s` shorthand for `--subnet`
- [added] Added support for YAML config file via `--config`
- [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
- [fixed] Fixed problem with multiple subnets
- [fixed] Fixed problem with interrupted poll after suspend to ram
### v0.7.0 (2016-08-05)

175
Cargo.lock generated
View File

@ -7,6 +7,7 @@ dependencies = [
"docopt 0.6.82 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
@ -52,6 +53,15 @@ name = "cfg-if"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cookie"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "docopt"
version = "0.6.82"
@ -73,6 +83,61 @@ name = "gcc"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hpack"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "httparse"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "idna"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "igd"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)",
"xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -82,6 +147,11 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "0.2.1"
@ -97,6 +167,11 @@ name = "log"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "matches"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "0.1.11"
@ -105,6 +180,14 @@ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "net2"
version = "0.2.26"
@ -130,6 +213,14 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num_cpus"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pkg-config"
version = "0.3.8"
@ -187,6 +278,15 @@ dependencies = [
"nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "solicit"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.3.0"
@ -219,6 +319,46 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "traitobject"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typeable"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicase"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-bidi"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-normalization"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "0.1.3"
@ -248,6 +388,22 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "xml-rs"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "xmltree"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "yaml-rust"
version = "0.3.3"
@ -259,16 +415,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e3d6405328b6edb412158b3b7710e2634e23f3614b9bb1c412df7952489a626"
"checksum docopt 0.6.82 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20016093b4e545dccf6ad4a01099de0b695f9bc99b08210e68f6425db2d37d"
"checksum fnv 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d3d4285d5aa1cf04504b7d8c2d1fdccf4586b56739499a04cc58663b2543cd30"
"checksum gcc 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "dcb000abd6df9df4c637f75190297ebe56c1d7e66b56bbf3b4aa7aece15f61a2"
"checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
"checksum hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "eb27e8a3e8f17ac43ffa41bbda9cf5ad3f9f13ef66fa4873409d4902310275f7"
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
"checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
"checksum mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c93a4bd787ddc6e7833c519b73a50883deb5863d76d9b71eb8216fb7f94e66"
"checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2"
"checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2"
"checksum num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "cee7e88156f3f9e19bdd598f8d6c9db7bf4078f99f8381f43a55b09648d1a6e3"
"checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa"
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
"checksum regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)" = "56b7ee9f764ecf412c6e2fff779bca4b22980517ae335a21aeaf4e32625a5df2"
@ -277,13 +443,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
"checksum signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "beb615e58999635b6063277cf520f2d88824955c1056cf4f166b0f55b218512d"
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
"checksum strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d73a2c36a4d095ed1a6df5cbeac159863173447f7a82b3f4757426844ab825"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55dd963dbaeadc08aa7266bf7f91c3154a7805e32bb94b820b769d2ef3b4744d"
"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af"
"checksum traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07eaeb7689bb7fca7ce15628319635758eda769fed481ecfe6686ddef2600616"
"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764"
"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
"checksum url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afe9ec54bc4db14bc8744b7fed060d785ac756791450959b2248443319d5b119"
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"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 xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef"
"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082"
"checksum yaml-rust 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ebfe12f475ad59be6178ebf004d51e682022496535994f8d23fd7ed31084598c"

View File

@ -24,6 +24,7 @@ fnv = "1"
net2 = "0.2"
bitflags = "0.7"
yaml-rust = "0.3"
igd = "0.5"
[build-dependencies]
gcc = "0.3"

View File

@ -35,6 +35,7 @@ somewhat stable state. VpnCloud features the following functionality:
* 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
* Automatic port forwarding via UPnP
### Installing

View File

@ -152,7 +152,7 @@ fn epoll_wait(b: &mut Bencher) {
fn handle_interface_data(b: &mut Bencher) {
let mut node = GenericCloud::<Frame>::new(
MAGIC, Device::dummy("vpncloud0", "/dev/null", Type::Tap).unwrap(), 0,
Box::new(SwitchTable::new(300)), 1800, true, true, vec![], Crypto::None
Box::new(SwitchTable::new(300)), 1800, true, true, vec![], Crypto::None, None
);
let mut data = [0; 1500];
data[105] = 45;
@ -166,7 +166,7 @@ fn handle_interface_data(b: &mut Bencher) {
fn handle_net_message(b: &mut Bencher) {
let mut node = GenericCloud::<Frame>::new(
MAGIC, Device::dummy("vpncloud0", "/dev/null", Type::Tap).unwrap(), 0,
Box::new(SwitchTable::new(300)), 1800, true, true, vec![], Crypto::None
Box::new(SwitchTable::new(300)), 1800, true, true, vec![], Crypto::None, None
);
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1));
let mut data = [0; 1500];

View File

@ -23,6 +23,7 @@ use super::types::{Table, Protocol, Range, Error, HeaderMagic, NodeId};
use super::device::Device;
use super::udpmessage::{encode, decode, Message};
use super::crypto::Crypto;
use super::port_forwarding::PortForwarding;
use super::util::{now, Time, Duration, resolve};
use super::poll::{self, Poll};
@ -170,6 +171,7 @@ pub struct GenericCloud<P: Protocol> {
update_freq: Duration,
buffer_out: [u8; 64*1024],
next_housekeep: Time,
port_forwarding: Option<PortForwarding>,
_dummy_p: PhantomData<P>,
}
@ -178,7 +180,7 @@ impl<P: Protocol> GenericCloud<P> {
#[allow(too_many_arguments)]
pub fn new(magic: HeaderMagic, device: Device, listen: u16, table: Box<Table>,
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>,
crypto: Crypto) -> Self {
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)) {
Ok(socket) => socket,
@ -208,6 +210,7 @@ impl<P: Protocol> GenericCloud<P> {
update_freq: peer_timeout/2-60,
buffer_out: [0; 64*1024],
next_housekeep: now(),
port_forwarding: port_forwarding,
_dummy_p: PhantomData,
}
}
@ -347,6 +350,10 @@ impl<P: Protocol> GenericCloud<P> {
self.table.remove_all(&peer);
}
self.table.housekeep();
// Periodically extend the port-forwarding
if let Some(ref mut pfw) = self.port_forwarding {
pfw.check_extend();
}
// Periodically send peer list to peers
let now = now();
if self.next_peerlist <= now {

View File

@ -23,6 +23,7 @@ pub struct Config {
pub mode: Mode,
pub dst_timeout: Duration,
pub subnets: Vec<String>,
pub port_forwarding: bool,
}
impl Default for Config {
@ -34,7 +35,8 @@ impl Default for Config {
magic: None,
port: 3210, peers: vec![], peer_timeout: 1800,
mode: Mode::Normal, dst_timeout: 300,
subnets: vec![]
subnets: vec![],
port_forwarding: true
}
}
}
@ -80,6 +82,9 @@ impl Config {
if let Some(mut val) = file.subnets {
self.subnets.append(&mut val);
}
if let Some(val) = file.port_forwarding {
self.port_forwarding = val;
}
}
pub fn merge_args(&mut self, mut args: Args) {
@ -122,6 +127,9 @@ impl Config {
self.dst_timeout = val;
}
self.subnets.append(&mut args.flag_subnet);
if args.flag_no_port_forwarding {
self.port_forwarding = false;
}
}
pub fn get_magic(&self) -> HeaderMagic {
@ -160,4 +168,5 @@ pub struct ConfigFile {
pub mode: Option<Mode>,
pub dst_timeout: Option<Duration>,
pub subnets: Option<Vec<String>>,
pub port_forwarding: Option<bool>
}

View File

@ -17,6 +17,7 @@ extern crate rand;
extern crate fnv;
extern crate net2;
extern crate yaml_rust;
extern crate igd;
#[cfg(feature = "bench")] extern crate test;
#[macro_use] pub mod util;
@ -30,6 +31,7 @@ pub mod device;
pub mod poll;
pub mod config;
pub mod configfile;
pub mod port_forwarding;
#[cfg(test)] mod tests;
#[cfg(feature = "bench")] mod benches;
@ -44,6 +46,7 @@ use ip::RoutingTable;
use types::{Mode, Range, Table, Protocol, HeaderMagic};
use cloud::GenericCloud;
use crypto::{Crypto, CryptoMethod};
use port_forwarding::PortForwarding;
use util::Duration;
use config::Config;
@ -72,7 +75,8 @@ pub struct Args {
flag_quiet: bool,
flag_ifup: Option<String>,
flag_ifdown: Option<String>,
flag_version: bool
flag_version: bool,
flag_no_port_forwarding: bool
}
@ -131,7 +135,12 @@ fn run<T: Protocol> (config: Config) {
Some(key) => Crypto::from_shared_key(config.crypto, &key),
None => Crypto::None
};
let mut cloud = GenericCloud::<T>::new(magic, device, config.port, table, peer_timeout, learning, broadcasting, ranges, crypto);
let port_forwarding = if config.port_forwarding {
PortForwarding::new(config.port)
} else {
None
};
let mut cloud = GenericCloud::<T>::new(magic, device, config.port, table, peer_timeout, learning, broadcasting, ranges, crypto, port_forwarding);
if let Some(script) = config.ifup {
run_script(script, cloud.ifname());
}

128
src/port_forwarding.rs Normal file
View File

@ -0,0 +1,128 @@
use std::net::{SocketAddrV4, UdpSocket, SocketAddr};
use std::io;
use igd::*;
use super::util::{Time, now};
const LEASE_TIME: u32 = 300;
const DESCRIPTION: &'static str = "VpnCloud";
pub struct PortForwarding {
pub internal_addr: SocketAddrV4,
pub external_addr: SocketAddrV4,
pub gateway: Gateway,
pub next_extension: Option<Time>,
}
impl PortForwarding {
pub fn new(port: u16) -> Option<Self> {
// Get the gateway
let gateway = match search_gateway() {
Ok(gateway) => gateway,
Err(err) => {
if let SearchError::IoError(ref err) = err {
if err.kind() == io::ErrorKind::WouldBlock { // Why this code?
warn!("Port-forwarding: no router found");
return None
}
}
error!("Port-forwarding: failed to find router: {}", err);
return None
}
};
info!("Port-forwarding: found router at {}", gateway.addr);
// Get the internal address (this trick gets the address by opening a UDP connection which
// does not really open anything but returns the correct address)
let dummy_sock = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind");
dummy_sock.connect(gateway.addr).expect("Failed to connect");
let internal_addr;
if let SocketAddr::V4(addr) = dummy_sock.local_addr().expect("Failed to get local address") {
internal_addr = SocketAddrV4::new(*addr.ip(), port);
} else {
unreachable!()
}
// Query the external address
let external_ip = match gateway.get_external_ip() {
Ok(ip) => ip,
Err(err) => {
error!("Port-forwarding: failed to obtain external IP: {}", err);
return None
}
};
// Try to activate the port forwarding
// - First with external port = internal port and timeout
// - If the port is used, request any port
// - If timeout is denied, try permanent forwarding
info!("Port-forwarding: external IP is {}", external_ip);
let (external_addr, timeout) = match gateway.add_port(PortMappingProtocol::UDP, internal_addr.port(), internal_addr, LEASE_TIME, DESCRIPTION) {
Ok(()) => (SocketAddrV4::new(external_ip, internal_addr.port()), LEASE_TIME),
Err(AddPortError::PortInUse) => match gateway.add_any_port(PortMappingProtocol::UDP, internal_addr, LEASE_TIME, DESCRIPTION) {
Ok(port) => (SocketAddrV4::new(external_ip, port), LEASE_TIME),
Err(AddAnyPortError::OnlyPermanentLeasesSupported) => match gateway.add_any_port(PortMappingProtocol::UDP, internal_addr, 0, DESCRIPTION) {
Ok(port) => (SocketAddrV4::new(external_ip, port), 0),
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
return None
}
},
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
return None
}
},
Err(AddPortError::OnlyPermanentLeasesSupported) => match gateway.add_port(PortMappingProtocol::UDP, internal_addr.port(), internal_addr, 0, DESCRIPTION) {
Ok(()) => (SocketAddrV4::new(external_ip, internal_addr.port()), 0),
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
return None
}
},
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
return None
}
};
info!("Port-forwarding: sucessfully activated port forward on {}, timeout: {}", external_addr, timeout);
let next_extension = if timeout > 0 {
Some(now() + timeout as Time - 60)
} else {
None
};
Some(PortForwarding {
internal_addr: internal_addr,
external_addr: external_addr,
gateway: gateway,
next_extension: next_extension
})
}
pub fn check_extend(&mut self) {
if let Some(deadline) = self.next_extension {
if deadline > now() {
return
}
} else {
return
}
match self.gateway.add_port(PortMappingProtocol::UDP, self.external_addr.port(), self.internal_addr, LEASE_TIME, DESCRIPTION) {
Ok(()) => debug!("Port-forwarding: extended port forwarding"),
Err(err) => error!("Port-forwarding: failed to extend port forwarding: {}", err)
};
self.next_extension = Some(now() + LEASE_TIME as Time - 60);
}
fn deactivate(&self) {
match self.gateway.remove_port(PortMappingProtocol::UDP, self.external_addr.port()) {
Ok(()) => info!("Port-forwarding: successfully deactivated port forwarding"),
Err(err) => error!("Port-forwarding: failed to deactivate port forwarding: {}", err)
}
}
}
impl Drop for PortForwarding {
fn drop(&mut self) {
self.deactivate()
}
}

View File

@ -411,6 +411,7 @@ peer_timeout: 1800
mode: normal
subnets:
- 10.0.1.0/24
port_forwarding: true
";
assert_eq!(configfile::parse_str::<ConfigFile>(config_file).unwrap(), ConfigFile{
device_type: Some(Type::Tun),
@ -425,7 +426,8 @@ subnets:
peer_timeout: Some(1800),
mode: Some(Mode::Normal),
dst_timeout: None,
subnets: Some(vec!["10.0.1.0/24".to_string()])
subnets: Some(vec!["10.0.1.0/24".to_string()]),
port_forwarding: Some(true)
})
}
@ -445,7 +447,8 @@ fn config_merge() {
peer_timeout: Some(1800),
mode: Some(Mode::Normal),
dst_timeout: None,
subnets: Some(vec!["10.0.1.0/24".to_string()])
subnets: Some(vec!["10.0.1.0/24".to_string()]),
port_forwarding: None
});
assert_eq!(config, Config{
device_type: Type::Tun,

View File

@ -23,6 +23,7 @@ Options:
--ifup <command> A command to setup the network interface.
--ifdown <command> A command to bring down the network
interface.
--no-port-forwarding Disable automatic port forward.
-v, --verbose Print debug information.
-q, --quiet Only print errors and warnings.
-h, --help Display the help.

View File

@ -96,6 +96,11 @@ vpncloud(1) -- Peer-to-peer VPN
The name of the allocated device will be available via the environment
variable `IFNAME`.
* `--no-port-forwarding`:
Disable automatic port forward. If this option is not set, VpnCloud tries to
detect a NAT router and automatically add a port forwarding to it.
* `-v`, `--verbose`:
Print debug information, including information for data being received and
@ -239,6 +244,7 @@ detailed descriptions of the options.
* `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`
* `port_forwarding`: Whether to activate port forwardig. See `--no-port-forwarding`
### Example
@ -256,6 +262,7 @@ peer_timeout: 1800
mode: normal
subnets:
- 10.0.1.0/24
port_forwarding: true
## NETWORK PROTOCOL