Compare commits

..

4 Commits

Author SHA1 Message Date
Dennis Schwerdel 8c55e6c076 Finish advertise address & exchange own addresses 2021-04-09 22:01:35 +02:00
Dennis Schwerdel 7427be31c8 Merge branch 'master' of github.com:dswd/vpncloud 2021-04-09 20:31:16 +02:00
Dennis Schwerdel 5609a61ddb Add performance for 2.2.0 2021-04-09 20:31:13 +02:00
Jeffrey Schiller 0f9a0d8f91
Add ability to configure “own” addresses (#185)
* Add ability to configure “own” addresses

This configuration option permits the declaration of external or public
addresses instead of attempting to learn them from port forwarding or
interfaces. This is useful in situations where it isn’t possible to
accurately obtain the correct external addresses that peers should use.

* Update args and use better parse listen address

Add the --advertise_addresses control argument to accompany the new
configuration option. Also parse the listen address/port to extract the
port to advertise with advertise_addresses instead of assuming it is
just a port.
2021-04-09 20:30:16 +02:00
8 changed files with 235 additions and 18 deletions

View File

@ -0,0 +1,165 @@
{
"meta": {
"region": "eu-central-1",
"instance_type": "m5.large",
"ami": "ami-0db9040eb3ab74509",
"version": "2.2.0",
"duration": 623.0307722091675
},
"native": {
"iperf": {
"throughput": 9680235000.0,
"cpu_sender": 12.015535,
"cpu_receiver": 71.982452
},
"ping_100": {
"rtt_min": 0.049,
"rtt_max": 0.219,
"rtt_avg": 0.058,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.053,
"rtt_max": 0.247,
"rtt_avg": 0.059,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.053,
"rtt_max": 0.189,
"rtt_avg": 0.06,
"pkt_loss": 0.0
}
},
"plain": {
"iperf": {
"throughput": 5790600000.0,
"cpu_sender": 14.109763,
"cpu_receiver": 69.727033
},
"ping_100": {
"rtt_min": 0.079,
"rtt_max": 0.291,
"rtt_avg": 0.094,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.079,
"rtt_max": 0.304,
"rtt_avg": 0.096,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.082,
"rtt_max": 0.367,
"rtt_avg": 0.097,
"pkt_loss": 0.0
}
},
"aes256": {
"iperf": {
"throughput": 3917767000.0,
"cpu_sender": 6.439156,
"cpu_receiver": 64.267206
},
"ping_100": {
"rtt_min": 0.081,
"rtt_max": 0.206,
"rtt_avg": 0.097,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.088,
"rtt_max": 0.206,
"rtt_avg": 0.1,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.089,
"rtt_max": 0.319,
"rtt_avg": 0.103,
"pkt_loss": 0.0
}
},
"aes128": {
"iperf": {
"throughput": 3697142000.0,
"cpu_sender": 7.417808,
"cpu_receiver": 59.433831
},
"ping_100": {
"rtt_min": 0.083,
"rtt_max": 0.265,
"rtt_avg": 0.097,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.081,
"rtt_max": 0.369,
"rtt_avg": 0.102,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.086,
"rtt_max": 0.448,
"rtt_avg": 0.102,
"pkt_loss": 0.0
}
},
"chacha20": {
"iperf": {
"throughput": 3194412000.0,
"cpu_sender": 6.12856,
"cpu_receiver": 61.223349
},
"ping_100": {
"rtt_min": 0.081,
"rtt_max": 0.28,
"rtt_avg": 0.098,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.088,
"rtt_max": 0.264,
"rtt_avg": 0.103,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.092,
"rtt_max": 0.204,
"rtt_avg": 0.106,
"pkt_loss": 0.0
}
},
"results": {
"throughput_mbits": {
"native": 9680.235,
"plain": 5790.6,
"aes256": 3917.767,
"aes128": 3697.142,
"chacha20": 3194.412
},
"latency_us": {
"plain": {
"100": 18.0,
"500": 18.500000000000004,
"1000": 18.500000000000004
},
"aes256": {
"100": 19.5,
"500": 20.500000000000004,
"1000": 21.5
},
"aes128": {
"100": 19.5,
"500": 21.5,
"1000": 20.999999999999996
},
"chacha20": {
"100": 20.0,
"500": 22.0,
"1000": 23.0
}
}
}
}

View File

@ -7,8 +7,8 @@ from datetime import date
# Note: this script will run for ~8 minutes and incur costs of about $ 0.02 # Note: this script will run for ~8 minutes and incur costs of about $ 0.02
FILE = "../target/release/vpncloud" FILE = "../../target/release/vpncloud"
VERSION = "2.1.0" VERSION = "2.2.0"
REGION = "eu-central-1" REGION = "eu-central-1"
env = EC2Environment( env = EC2Environment(
@ -113,4 +113,4 @@ name = "measurements/{date}_{version}_perf.json".format(date=date.today().strfti
eprint('Storing results in {}'.format(name)) eprint('Storing results in {}'.format(name))
with open(name, 'w') as fp: with open(name, 'w') as fp:
json.dump(results, fp, indent=2) json.dump(results, fp, indent=2)
eprint("done.") eprint("done.")

View File

@ -29,7 +29,7 @@ use crate::{
AddrList, NodeInfo, PeerInfo, MESSAGE_TYPE_CLOSE, MESSAGE_TYPE_DATA, MESSAGE_TYPE_KEEPALIVE, AddrList, NodeInfo, PeerInfo, MESSAGE_TYPE_CLOSE, MESSAGE_TYPE_DATA, MESSAGE_TYPE_KEEPALIVE,
MESSAGE_TYPE_NODE_INFO, MESSAGE_TYPE_NODE_INFO,
}, },
net::{mapped_addr, Socket}, net::{mapped_addr, parse_listen, Socket},
payload::Protocol, payload::Protocol,
poll::{WaitImpl, WaitResult}, poll::{WaitImpl, WaitResult},
port_forwarding::PortForwarding, port_forwarding::PortForwarding,
@ -222,7 +222,14 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
pub fn reset_own_addresses(&mut self) -> io::Result<()> { pub fn reset_own_addresses(&mut self) -> io::Result<()> {
self.own_addresses.clear(); self.own_addresses.clear();
self.own_addresses.push(self.socket.address().map(mapped_addr)?); let socket_addr = self.socket.address().map(mapped_addr)?;
// 1) Specified advertise addresses
for addr in &self.config.advertise_addresses {
self.own_addresses.push(parse_listen(addr, socket_addr.port()));
}
// 2) Address of UDP socket
self.own_addresses.push(socket_addr);
// 3) Addresses from port forwarding
if let Some(ref pfw) = self.port_forwarding { if let Some(ref pfw) = self.port_forwarding {
self.own_addresses.push(pfw.get_internal_ip().into()); self.own_addresses.push(pfw.get_internal_ip().into());
self.own_addresses.push(pfw.get_external_ip().into()); self.own_addresses.push(pfw.get_external_ip().into());
@ -435,11 +442,6 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
pfw.check_extend(); pfw.check_extend();
} }
let now = TS::now(); let now = TS::now();
// Periodically reset own peers
if self.next_own_address_reset <= now {
self.reset_own_addresses().map_err(|err| Error::SocketIo("Failed to get own addresses", err))?;
self.next_own_address_reset = now + OWN_ADDRESS_RESET_INTERVAL;
}
// Periodically send peer list to peers // Periodically send peer list to peers
if self.next_peers <= now { if self.next_peers <= now {
debug!("Send peer list to all peers"); debug!("Send peer list to all peers");
@ -470,6 +472,11 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
self.load_beacon()?; self.load_beacon()?;
self.next_beacon = now + Time::from(self.config.beacon_interval); self.next_beacon = now + Time::from(self.config.beacon_interval);
} }
// Periodically reset own peers
if self.next_own_address_reset <= now {
self.reset_own_addresses().map_err(|err| Error::SocketIo("Failed to get own addresses", err))?;
self.next_own_address_reset = now + OWN_ADDRESS_RESET_INTERVAL;
}
Ok(()) Ok(())
} }
@ -691,6 +698,12 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
} }
if let Some(node_id) = peer.node_id { if let Some(node_id) = peer.node_id {
if self.node_id == node_id { if self.node_id == node_id {
// Check addresses and add addresses that we don't know to own addresses
for addr in &peer.addrs {
if !self.own_addresses.contains(addr) {
self.own_addresses.push(*addr)
}
}
continue 'outer; continue 'outer;
} }
for p in self.peers.values() { for p in self.peers.values() {
@ -707,7 +720,17 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
fn update_peer_info(&mut self, addr: SocketAddr, info: Option<NodeInfo>) -> Result<(), Error> { fn update_peer_info(&mut self, addr: SocketAddr, info: Option<NodeInfo>) -> Result<(), Error> {
if let Some(peer) = self.peers.get_mut(&addr) { if let Some(peer) = self.peers.get_mut(&addr) {
peer.last_seen = TS::now(); peer.last_seen = TS::now();
peer.timeout = TS::now() + self.config.peer_timeout as Time peer.timeout = TS::now() + self.config.peer_timeout as Time;
if let Some(info) = &info {
// Update peer addresses, always add seen address
peer.addrs.clear();
peer.addrs.push(addr);
for addr in &info.addrs {
if !peer.addrs.contains(addr) {
peer.addrs.push(*addr);
}
}
}
} else { } else {
error!("Received peer update from non peer {}", addr_nice(addr)); error!("Received peer update from non peer {}", addr_nice(addr));
return Ok(()); return Ok(());

View File

@ -19,6 +19,7 @@ pub struct Config {
pub fix_rp_filter: bool, pub fix_rp_filter: bool,
pub ip: Option<String>, pub ip: Option<String>,
pub advertise_addresses: Vec<String>,
pub ifup: Option<String>, pub ifup: Option<String>,
pub ifdown: Option<String>, pub ifdown: Option<String>,
@ -56,6 +57,7 @@ impl Default for Config {
device_path: None, device_path: None,
fix_rp_filter: false, fix_rp_filter: false,
ip: None, ip: None,
advertise_addresses: vec![],
ifup: None, ifup: None,
ifdown: None, ifdown: None,
crypto: CryptoConfig::default(), crypto: CryptoConfig::default(),
@ -105,6 +107,9 @@ impl Config {
if let Some(val) = file.ip { if let Some(val) = file.ip {
self.ip = Some(val); self.ip = Some(val);
} }
if let Some(mut val) = file.advertise_addresses {
self.advertise_addresses.append(&mut val);
}
if let Some(val) = file.ifup { if let Some(val) = file.ifup {
self.ifup = Some(val); self.ifup = Some(val);
} }
@ -212,6 +217,7 @@ impl Config {
if let Some(val) = args.ifup { if let Some(val) = args.ifup {
self.ifup = Some(val); self.ifup = Some(val);
} }
self.advertise_addresses.append(&mut args.advertise_addresses);
if let Some(val) = args.ifdown { if let Some(val) = args.ifdown {
self.ifdown = Some(val); self.ifdown = Some(val);
} }
@ -318,6 +324,7 @@ impl Config {
ifup: self.ifup, ifup: self.ifup,
ifdown: self.ifdown, ifdown: self.ifdown,
ip: self.ip, ip: self.ip,
advertise_addresses: Some(self.advertise_addresses),
keepalive: self.keepalive, keepalive: self.keepalive,
listen: Some(self.listen), listen: Some(self.listen),
mode: Some(self.mode), mode: Some(self.mode),
@ -467,6 +474,10 @@ pub struct Args {
#[structopt(long)] #[structopt(long)]
pub ip: Option<String>, pub ip: Option<String>,
/// A list of IP Addresses to advertise as our external address(s)
#[structopt(long = "advertise_addresses", use_delimiter = true)]
pub advertise_addresses: Vec<String>,
/// A command to setup the network interface /// A command to setup the network interface
#[structopt(long)] #[structopt(long)]
pub ifup: Option<String>, pub ifup: Option<String>,
@ -606,6 +617,7 @@ pub struct ConfigFile {
pub device: Option<ConfigFileDevice>, pub device: Option<ConfigFileDevice>,
pub ip: Option<String>, pub ip: Option<String>,
pub advertise_addresses: Option<Vec<String>>,
pub ifup: Option<String>, pub ifup: Option<String>,
pub ifdown: Option<String>, pub ifdown: Option<String>,
@ -638,6 +650,9 @@ device:
name: vpncloud%d name: vpncloud%d
path: /dev/net/tun path: /dev/net/tun
ip: 10.0.1.1/16 ip: 10.0.1.1/16
advertise-addresses:
- 192.168.0.1
- 192.168.1.1
ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up
ifdown: 'true' ifdown: 'true'
peers: peers:
@ -673,6 +688,7 @@ statsd:
fix_rp_filter: None fix_rp_filter: None
}), }),
ip: Some("10.0.1.1/16".to_string()), ip: Some("10.0.1.1/16".to_string()),
advertise_addresses: Some(vec!["192.168.0.1".to_string(), "192.168.1.1".to_string()]),
ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()),
ifdown: Some("true".to_string()), ifdown: Some("true".to_string()),
crypto: CryptoConfig::default(), crypto: CryptoConfig::default(),
@ -721,6 +737,7 @@ fn config_merge() {
fix_rp_filter: None, fix_rp_filter: None,
}), }),
ip: None, ip: None,
advertise_addresses: Some(vec![]),
ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()),
ifdown: Some("true".to_string()), ifdown: Some("true".to_string()),
crypto: CryptoConfig::default(), crypto: CryptoConfig::default(),
@ -757,6 +774,7 @@ fn config_merge() {
device_name: "vpncloud%d".to_string(), device_name: "vpncloud%d".to_string(),
device_path: None, device_path: None,
ip: None, ip: None,
advertise_addresses: vec![],
ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()),
ifdown: Some("true".to_string()), ifdown: Some("true".to_string()),
listen: "3210".to_string(), listen: "3210".to_string(),
@ -816,6 +834,8 @@ fn config_merge() {
device_path: Some("/dev/null".to_string()), device_path: Some("/dev/null".to_string()),
fix_rp_filter: false, fix_rp_filter: false,
ip: None, ip: None,
advertise_addresses: vec![],
ifup: Some("ifconfig $IFNAME 10.0.1.2/16 mtu 1400 up".to_string()), ifup: Some("ifconfig $IFNAME 10.0.1.2/16 mtu 1400 up".to_string()),
ifdown: Some("ifconfig $IFNAME down".to_string()), ifdown: Some("ifconfig $IFNAME down".to_string()),
crypto: CryptoConfig { password: Some("anothersecret".to_string()), ..CryptoConfig::default() }, crypto: CryptoConfig { password: Some("anothersecret".to_string()), ..CryptoConfig::default() },

View File

@ -11,7 +11,7 @@ use std::{
}; };
use super::util::{MockTimeSource, MsgBuffer, Time, TimeSource}; use super::util::{MockTimeSource, MsgBuffer, Time, TimeSource};
use crate::port_forwarding::PortForwarding; use crate::{config::DEFAULT_PORT, port_forwarding::PortForwarding};
pub fn mapped_addr(addr: SocketAddr) -> SocketAddr { pub fn mapped_addr(addr: SocketAddr) -> SocketAddr {
// HOT PATH // HOT PATH
@ -35,21 +35,23 @@ pub trait Socket: AsRawFd + Sized {
fn create_port_forwarding(&self) -> Option<PortForwarding>; fn create_port_forwarding(&self) -> Option<PortForwarding>;
} }
pub fn parse_listen(addr: &str) -> SocketAddr { pub fn parse_listen(addr: &str, default_port: u16) -> SocketAddr {
if let Some(addr) = addr.strip_prefix("*:") { if let Some(addr) = addr.strip_prefix("*:") {
let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}"); let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}");
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port) SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
} else if addr.contains(':') { } else if addr.contains(':') {
try_fail!(addr.parse::<SocketAddr>(), "Invalid address: {}: {}", addr) try_fail!(addr.parse::<SocketAddr>(), "Invalid address: {}: {}", addr)
} else { } else if let Ok(port) = addr.parse::<u16>() {
let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}");
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port) SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
} else {
let ip = try_fail!(addr.parse::<IpAddr>(), "Invalid addr: {}");
SocketAddr::new(ip, default_port)
} }
} }
impl Socket for UdpSocket { impl Socket for UdpSocket {
fn listen(addr: &str) -> Result<Self, io::Error> { fn listen(addr: &str) -> Result<Self, io::Error> {
let addr = parse_listen(addr); let addr = parse_listen(addr, DEFAULT_PORT);
UdpSocket::bind(addr) UdpSocket::bind(addr)
} }
@ -134,7 +136,7 @@ impl AsRawFd for MockSocket {
impl Socket for MockSocket { impl Socket for MockSocket {
fn listen(addr: &str) -> Result<Self, io::Error> { fn listen(addr: &str) -> Result<Self, io::Error> {
Ok(Self::new(parse_listen(addr))) Ok(Self::new(parse_listen(addr, DEFAULT_PORT)))
} }
fn receive(&mut self, buffer: &mut MsgBuffer) -> Result<SocketAddr, io::Error> { fn receive(&mut self, buffer: &mut MsgBuffer) -> Result<SocketAddr, io::Error> {

View File

@ -109,6 +109,7 @@ impl OldConfigFile {
ifdown: self.ifdown, ifdown: self.ifdown,
ifup: self.ifup, ifup: self.ifup,
ip: None, ip: None,
advertise_addresses: None,
keepalive: self.keepalive, keepalive: self.keepalive,
listen: self.listen.or(self.port.map(|p| format!("{}", p))), listen: self.listen.or(self.port.map(|p| format!("{}", p))),
mode: self.mode, mode: self.mode,

View File

@ -41,6 +41,12 @@ fn configure_connectivity(config: &mut Config, mode: usize, theme: &ColorfulThem
.interact()?; .interact()?;
} }
if mode == MODE_EXPERT { if mode == MODE_EXPERT {
config.advertise_addresses = str_list(
Input::with_theme(theme)
.with_prompt("Advertise addresses (comma separated)")
.default(config.advertise_addresses.join(","))
.interact_text()?,
);
config.peer_timeout = Input::with_theme(theme) config.peer_timeout = Input::with_theme(theme)
.with_prompt("Peer timeout (in seconds)") .with_prompt("Peer timeout (in seconds)")
.default(config.peer_timeout) .default(config.peer_timeout)

View File

@ -92,7 +92,7 @@ fn serve_proxy_connection(stream: TcpStream) -> Result<(), io::Error> {
} }
pub fn run_proxy(listen: &str) -> Result<(), io::Error> { pub fn run_proxy(listen: &str) -> Result<(), io::Error> {
let addr = parse_listen(listen); let addr = parse_listen(listen, 8080);
let server = TcpListener::bind(addr)?; let server = TcpListener::bind(addr)?;
info!("Listening on ws://{}", server.local_addr()?); info!("Listening on ws://{}", server.local_addr()?);
for stream in server.incoming() { for stream in server.incoming() {