Added feature to disable special NAT support

This commit is contained in:
Dennis Schwerdel 2019-12-22 23:21:47 +01:00
parent 54a2240f34
commit eb620781a8
3 changed files with 143 additions and 119 deletions

View File

@ -4,6 +4,7 @@ This project follows [semantic versioning](http://semver.org).
### Unreleased ### Unreleased
- [added] Added feature to disable special NAT support
- [changed] Improved port forwarding on quirky routers - [changed] Improved port forwarding on quirky routers
- [changed] Reduced peer timeout to 5min to work better with NAT - [changed] Reduced peer timeout to 5min to work better with NAT

View File

@ -24,7 +24,7 @@ rand = "0.7"
fnv = "1" fnv = "1"
net2 = "0.2" net2 = "0.2"
yaml-rust = "0.4" yaml-rust = "0.4"
igd = "0.9" igd = { version = "0.9", optional = true }
siphasher = "0.3" siphasher = "0.3"
daemonize = "0.4" daemonize = "0.4"
ring = "0.16" ring = "0.16"
@ -38,8 +38,9 @@ pkg-config = "0.3"
tempfile = "3" tempfile = "3"
[features] [features]
default = [] default = ["nat"]
bench = [] bench = []
nat = ["igd"]
[profile.release] [profile.release]
lto = true lto = true

View File

@ -2,152 +2,174 @@
// Copyright (C) 2015-2019 Dennis Schwerdel // Copyright (C) 2015-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md) // This software is licensed under GPL-3 or newer (see LICENSE.md)
use std::{io, net::SocketAddrV4}; #[cfg(feature = "nat")]
mod internal {
use igd::{search_gateway, AddAnyPortError, AddPortError, Gateway, PortMappingProtocol, SearchError}; use std::{io, net::SocketAddrV4};
use super::util::{get_internal_ip, SystemTimeSource, Time, TimeSource}; use igd::{search_gateway, AddAnyPortError, AddPortError, Gateway, PortMappingProtocol, SearchError};
const LEASE_TIME: u32 = 1800; use crate::util::{get_internal_ip, SystemTimeSource, Time, TimeSource};
const DESCRIPTION: &str = "VpnCloud";
const LEASE_TIME: u32 = 1800;
pub struct PortForwarding { const DESCRIPTION: &str = "VpnCloud";
pub internal_addr: SocketAddrV4,
pub external_addr: SocketAddrV4,
pub gateway: Gateway,
pub next_extension: Option<Time>
}
impl PortForwarding { pub struct PortForwarding {
pub fn new(port: u16) -> Option<Self> { pub internal_addr: SocketAddrV4,
// Get the gateway pub external_addr: SocketAddrV4,
let gateway = match search_gateway(Default::default()) { gateway: Gateway,
Ok(gateway) => gateway, pub next_extension: Option<Time>
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);
let internal_addr = SocketAddrV4::new(get_internal_ip(), port);
// 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
}
};
if let Ok((port, timeout)) = Self::get_any_forwarding(&gateway, internal_addr, port) {
info!("Port-forwarding: external IP is {}", external_ip);
let external_addr = SocketAddrV4::new(external_ip, port);
info!("Port-forwarding: sucessfully activated port forward on {}, timeout: {}", external_addr, timeout);
let next_extension =
if timeout > 0 { Some(SystemTimeSource::now() + Time::from(timeout) - 60) } else { None };
Some(PortForwarding { internal_addr, external_addr, gateway, next_extension })
} else {
None
}
} }
fn get_any_forwarding(gateway: &Gateway, addr: SocketAddrV4, port: u16) -> Result<(u16, u32), ()> { impl PortForwarding {
if let Ok(a) = Self::get_forwarding(gateway, addr, port) { pub fn new(port: u16) -> Option<Self> {
return Ok(a) // Get the gateway
} let gateway = match search_gateway(Default::default()) {
if let Ok(a) = Self::get_forwarding(gateway, addr, 0) { Ok(gateway) => gateway,
return Ok(a) Err(err) => {
} if let SearchError::IoError(ref err) = err {
for i in 1..5 { if err.kind() == io::ErrorKind::WouldBlock {
if let Ok(a) = Self::get_forwarding(gateway, addr, port + i) { // Why this code?
return Ok(a) warn!("Port-forwarding: no router found");
} return None
}
for _ in 0..5 {
if let Ok(a) = Self::get_forwarding(gateway, addr, rand::random()) {
return Ok(a)
}
}
Err(())
}
fn get_forwarding(gateway: &Gateway, addr: SocketAddrV4, port: u16) -> Result<(u16, u32), ()> {
info!("Trying external port {}", port);
if port == 0 {
match gateway.add_any_port(PortMappingProtocol::UDP, addr, LEASE_TIME, DESCRIPTION) {
Ok(port) => Ok((port, LEASE_TIME)),
Err(AddAnyPortError::OnlyPermanentLeasesSupported) => {
match gateway.add_any_port(PortMappingProtocol::UDP, addr, 0, DESCRIPTION) {
Ok(port) => Ok((port, 0)),
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
Err(())
} }
} }
error!("Port-forwarding: failed to find router: {}", err);
return None
} }
};
info!("Port-forwarding: found router at {}", gateway.addr);
let internal_addr = SocketAddrV4::new(get_internal_ip(), port);
// Query the external address
let external_ip = match gateway.get_external_ip() {
Ok(ip) => ip,
Err(err) => { Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err); error!("Port-forwarding: failed to obtain external IP: {}", err);
Err(()) return None
}
};
if let Ok((port, timeout)) = Self::get_any_forwarding(&gateway, internal_addr, port) {
info!("Port-forwarding: external IP is {}", external_ip);
let external_addr = SocketAddrV4::new(external_ip, port);
info!("Port-forwarding: sucessfully activated port forward on {}, timeout: {}", external_addr, timeout);
let next_extension =
if timeout > 0 { Some(SystemTimeSource::now() + Time::from(timeout) - 60) } else { None };
Some(PortForwarding { internal_addr, external_addr, gateway, next_extension })
} else {
None
}
}
fn get_any_forwarding(gateway: &Gateway, addr: SocketAddrV4, port: u16) -> Result<(u16, u32), ()> {
if let Ok(a) = Self::get_forwarding(gateway, addr, port) {
return Ok(a)
}
if let Ok(a) = Self::get_forwarding(gateway, addr, 0) {
return Ok(a)
}
for i in 1..5 {
if let Ok(a) = Self::get_forwarding(gateway, addr, port + i) {
return Ok(a)
} }
} }
} else { for _ in 0..5 {
match gateway.add_port(PortMappingProtocol::UDP, port, addr, LEASE_TIME, DESCRIPTION) { if let Ok(a) = Self::get_forwarding(gateway, addr, rand::random()) {
Ok(()) => Ok((port, LEASE_TIME)), return Ok(a)
Err(AddPortError::OnlyPermanentLeasesSupported) => { }
match gateway.add_port(PortMappingProtocol::UDP, port, addr, 0, DESCRIPTION) { }
Ok(()) => Ok((port, 0)), Err(())
Err(err) => { }
error!("Port-forwarding: failed to activate port forwarding: {}", err);
Err(()) fn get_forwarding(gateway: &Gateway, addr: SocketAddrV4, port: u16) -> Result<(u16, u32), ()> {
info!("Trying external port {}", port);
if port == 0 {
match gateway.add_any_port(PortMappingProtocol::UDP, addr, LEASE_TIME, DESCRIPTION) {
Ok(port) => Ok((port, LEASE_TIME)),
Err(AddAnyPortError::OnlyPermanentLeasesSupported) => {
match gateway.add_any_port(PortMappingProtocol::UDP, addr, 0, DESCRIPTION) {
Ok(port) => Ok((port, 0)),
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
Err(())
}
} }
} }
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
Err(())
}
} }
Err(err) => { } else {
error!("Port-forwarding: failed to activate port forwarding: {}", err); match gateway.add_port(PortMappingProtocol::UDP, port, addr, LEASE_TIME, DESCRIPTION) {
Err(()) Ok(()) => Ok((port, LEASE_TIME)),
Err(AddPortError::OnlyPermanentLeasesSupported) => {
match gateway.add_port(PortMappingProtocol::UDP, port, addr, 0, DESCRIPTION) {
Ok(()) => Ok((port, 0)),
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
Err(())
}
}
}
Err(err) => {
error!("Port-forwarding: failed to activate port forwarding: {}", err);
Err(())
}
} }
} }
} }
}
pub fn check_extend(&mut self) { pub fn check_extend(&mut self) {
if let Some(deadline) = self.next_extension { if let Some(deadline) = self.next_extension {
if deadline > SystemTimeSource::now() { if deadline > SystemTimeSource::now() {
return
}
} else {
return return
} }
} else { match self.gateway.add_port(
return 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(SystemTimeSource::now() + Time::from(LEASE_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)
}
} }
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(SystemTimeSource::now() + Time::from(LEASE_TIME) - 60);
} }
fn deactivate(&self) { impl Drop for PortForwarding {
match self.gateway.remove_port(PortMappingProtocol::UDP, self.external_addr.port()) { fn drop(&mut self) {
Ok(()) => info!("Port-forwarding: successfully deactivated port forwarding"), self.deactivate()
Err(err) => error!("Port-forwarding: failed to deactivate port forwarding: {}", err)
} }
} }
} }
impl Drop for PortForwarding { #[cfg(not(feature = "nat"))]
fn drop(&mut self) { mod internal {
self.deactivate() pub struct PortForwarding;
impl PortForwarding {
pub fn new(_port: u16) -> Option<Self> {
warn!("Compiled without feature 'nat', skipping port forwarding.");
None
}
pub fn check_extend(&mut self) {
unreachable!()
}
} }
} }
pub use internal::*;