Add listen option, switched to dual-stack

This commit is contained in:
Dennis Schwerdel 2020-02-20 16:25:35 +01:00
parent fba72882a6
commit 390f217a23
12 changed files with 102 additions and 151 deletions

View File

@ -2,6 +2,11 @@
This project follows [semantic versioning](http://semver.org). This project follows [semantic versioning](http://semver.org).
### UNRELEASED
- [added] Added option to listen on specified IP
- [changed] No longer using two sockets for ipv4 and ipv6
### v1.3.0 (2020-01-25) ### v1.3.0 (2020-01-25)
- [added] Building for aarch64 aka arm64 (thanks to Ivan) - [added] Building for aarch64 aka arm64 (thanks to Ivan)

View File

@ -130,10 +130,9 @@ fn now(b: &mut Bencher) {
#[bench] #[bench]
fn epoll_wait(b: &mut Bencher) { fn epoll_wait(b: &mut Bencher) {
let socketv4 = UdpSocket::bind("0.0.0.0:0").unwrap(); let socket = UdpSocket::bind("[::]:0").unwrap();
let socketv6 = UdpSocket::bind("[::]:0").unwrap();
let device = TunTapDevice::dummy("dummy", "/dev/zero", Type::Dummy).unwrap(); let device = TunTapDevice::dummy("dummy", "/dev/zero", Type::Dummy).unwrap();
let mut waiter = WaitImpl::testing(&socketv4, &socketv6, &device, 1000).unwrap(); let mut waiter = WaitImpl::testing(&socket, &device, 1000).unwrap();
b.iter(|| assert!(waiter.next().is_some())); b.iter(|| assert!(waiter.next().is_some()));
b.bytes = 1400; b.bytes = 1400;
} }

View File

@ -222,8 +222,7 @@ pub struct GenericCloud<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSou
reconnect_peers: Vec<ReconnectEntry>, reconnect_peers: Vec<ReconnectEntry>,
own_addresses: Vec<SocketAddr>, own_addresses: Vec<SocketAddr>,
table: T, table: T,
socket4: S, socket: S,
socket6: S,
device: D, device: D,
crypto: Crypto, crypto: Crypto,
next_peerlist: Time, next_peerlist: Time,
@ -248,13 +247,9 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
port_forwarding: Option<PortForwarding>, stats_file: Option<File> port_forwarding: Option<PortForwarding>, stats_file: Option<File>
) -> Self ) -> Self
{ {
let socket4 = match S::listen_v4("0.0.0.0", config.port) { let socket = match S::listen(config.listen) {
Ok(socket) => socket, Ok(socket) => socket,
Err(err) => fail!("Failed to open ipv4 address 0.0.0.0:{}: {}", config.port, err) Err(err) => fail!("Failed to open socket {}: {}", config.listen, err)
};
let socket6 = match S::listen_v6("::", config.port) {
Ok(socket) => socket,
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", config.port, err)
}; };
let now = TS::now(); let now = TS::now();
let update_freq = config.get_keepalive() as u16; let update_freq = config.get_keepalive() as u16;
@ -269,8 +264,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
own_addresses: Vec::new(), own_addresses: Vec::new(),
peer_timeout_publish: config.peer_timeout as u16, peer_timeout_publish: config.peer_timeout as u16,
table, table,
socket4, socket,
socket6,
device, device,
next_peerlist: now, next_peerlist: now,
update_freq, update_freq,
@ -309,11 +303,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
let msg_data = encode(msg, &mut self.buffer_out, self.magic, &mut self.crypto); let msg_data = encode(msg, &mut self.buffer_out, self.magic, &mut self.crypto);
for addr in self.peers.peers.keys() { for addr in self.peers.peers.keys() {
self.traffic.count_out_traffic(*addr, msg_data.len()); self.traffic.count_out_traffic(*addr, msg_data.len());
let socket = match *addr { match self.socket.send(msg_data, *addr) {
SocketAddr::V4(_) => &mut self.socket4,
SocketAddr::V6(_) => &mut self.socket6
};
match socket.send(msg_data, *addr) {
Ok(written) if written == msg_data.len() => Ok(()), Ok(written) if written == msg_data.len() => Ok(()),
Ok(_) => { Ok(_) => {
Err(Error::Socket("Sent out truncated packet", io::Error::new(io::ErrorKind::Other, "truncated"))) Err(Error::Socket("Sent out truncated packet", io::Error::new(io::ErrorKind::Other, "truncated")))
@ -335,11 +325,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
// Encrypt and encode // Encrypt and encode
let msg_data = encode(msg, &mut self.buffer_out, self.magic, &mut self.crypto); let msg_data = encode(msg, &mut self.buffer_out, self.magic, &mut self.crypto);
self.traffic.count_out_traffic(addr, msg_data.len()); self.traffic.count_out_traffic(addr, msg_data.len());
let socket = match addr { match self.socket.send(msg_data, addr) {
SocketAddr::V4(_) => &mut self.socket4,
SocketAddr::V6(_) => &mut self.socket6
};
match socket.send(msg_data, addr) {
Ok(written) if written == msg_data.len() => Ok(()), Ok(written) if written == msg_data.len() => Ok(()),
Ok(_) => Err(Error::Socket("Sent out truncated packet", io::Error::new(io::ErrorKind::Other, "truncated"))), Ok(_) => Err(Error::Socket("Sent out truncated packet", io::Error::new(io::ErrorKind::Other, "truncated"))),
Err(e) => Err(Error::Socket("IOError when sending", e)) Err(e) => Err(Error::Socket("IOError when sending", e))
@ -354,8 +340,8 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
/// # Errors /// # Errors
/// Returns an IOError if the underlying system call fails /// Returns an IOError if the underlying system call fails
#[allow(dead_code)] #[allow(dead_code)]
pub fn address(&self) -> io::Result<(SocketAddr, SocketAddr)> { pub fn address(&self) -> io::Result<SocketAddr> {
Ok((self.socket4.address()?, self.socket6.address()?)) Ok(self.socket.address()?)
} }
/// Returns the number of peers /// Returns the number of peers
@ -747,10 +733,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
fn initialize(&mut self) { fn initialize(&mut self) {
match self.address() { match self.address() {
Err(err) => error!("Failed to obtain local addresses: {}", err), Err(err) => error!("Failed to obtain local addresses: {}", err),
Ok((v4, v6)) => { Ok(addr) => self.own_addresses.push(addr)
self.own_addresses.push(v4);
self.own_addresses.push(v6);
}
} }
} }
@ -764,13 +747,8 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
} }
} }
fn handle_socket_v4_event(&mut self, buffer: &mut [u8]) { fn handle_socket_event(&mut self, buffer: &mut [u8]) {
let (size, src) = try_fail!(self.socket4.receive(buffer), "Failed to read from ipv4 network socket: {}"); let (size, src) = try_fail!(self.socket.receive(buffer), "Failed to read from network socket: {}");
self.handle_socket_data(src, &mut buffer[..size])
}
fn handle_socket_v6_event(&mut self, buffer: &mut [u8]) {
let (size, src) = try_fail!(self.socket6.receive(buffer), "Failed to read from ipv6 network socket: {}");
self.handle_socket_data(src, &mut buffer[..size]) self.handle_socket_data(src, &mut buffer[..size])
} }
@ -792,8 +770,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
/// Also, this method will call `housekeep` every second. /// Also, this method will call `housekeep` every second.
pub fn run(&mut self) { pub fn run(&mut self) {
let ctrlc = CtrlC::new(); let ctrlc = CtrlC::new();
let waiter = let waiter = try_fail!(WaitImpl::new(&self.socket, &self.device, 1000), "Failed to setup poll: {}");
try_fail!(WaitImpl::new(&self.socket4, &self.socket6, &self.device, 1000), "Failed to setup poll: {}");
let mut buffer = [0; 64 * 1024]; let mut buffer = [0; 64 * 1024];
let mut poll_error = false; let mut poll_error = false;
for evt in waiter { for evt in waiter {
@ -806,8 +783,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
poll_error = true; poll_error = true;
} }
WaitResult::Timeout => {} WaitResult::Timeout => {}
WaitResult::SocketV4 => self.handle_socket_v4_event(&mut buffer), WaitResult::Socket => self.handle_socket_event(&mut buffer),
WaitResult::SocketV6 => self.handle_socket_v6_event(&mut buffer),
WaitResult::Device => self.handle_device_event(&mut buffer) WaitResult::Device => self.handle_device_event(&mut buffer)
} }
if self.next_housekeep < TS::now() { if self.next_housekeep < TS::now() {
@ -842,26 +818,17 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
#[cfg(test)] #[cfg(test)]
impl<P: Protocol, T: Table> GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource> { impl<P: Protocol, T: Table> GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource> {
pub fn socket4(&mut self) -> &mut MockSocket { pub fn socket(&mut self) -> &mut MockSocket {
&mut self.socket4 &mut self.socket
}
pub fn socket6(&mut self) -> &mut MockSocket {
&mut self.socket6
} }
pub fn device(&mut self) -> &mut MockDevice { pub fn device(&mut self) -> &mut MockDevice {
&mut self.device &mut self.device
} }
pub fn trigger_socket_v4_event(&mut self) { pub fn trigger_socket_event(&mut self) {
let mut buffer = [0; 64 * 1024]; let mut buffer = [0; 64 * 1024];
self.handle_socket_v4_event(&mut buffer); self.handle_socket_event(&mut buffer);
}
pub fn trigger_socket_v6_event(&mut self) {
let mut buffer = [0; 64 * 1024];
self.handle_socket_v6_event(&mut buffer);
} }
pub fn trigger_device_event(&mut self) { pub fn trigger_device_event(&mut self) {

View File

@ -12,13 +12,28 @@ use super::{
}; };
use siphasher::sip::SipHasher24; use siphasher::sip::SipHasher24;
use std::hash::{Hash, Hasher}; use std::{
hash::{Hash, Hasher},
net::{IpAddr, Ipv6Addr, SocketAddr}
};
const HASH_PREFIX: &str = "hash:"; const HASH_PREFIX: &str = "hash:";
pub const DEFAULT_PEER_TIMEOUT: u16 = 600; pub const DEFAULT_PEER_TIMEOUT: u16 = 600;
fn parse_listen(addr: &str) -> SocketAddr {
if addr.starts_with("*:") {
let port = try_fail!(addr[2..].parse::<u16>(), "Invalid port: {}");
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
} else if addr.contains(':') {
try_fail!(addr.parse::<SocketAddr>(), "Invalid address: {}: {}", addr)
} else {
let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}");
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
}
}
#[derive(Deserialize, Debug, PartialEq, Clone)] #[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct Config { pub struct Config {
pub device_type: Type, pub device_type: Type,
@ -29,7 +44,7 @@ pub struct Config {
pub crypto: CryptoMethod, pub crypto: CryptoMethod,
pub shared_key: Option<String>, pub shared_key: Option<String>,
pub magic: Option<String>, pub magic: Option<String>,
pub port: u16, pub listen: SocketAddr,
pub peers: Vec<String>, pub peers: Vec<String>,
pub peer_timeout: Duration, pub peer_timeout: Duration,
pub keepalive: Option<Duration>, pub keepalive: Option<Duration>,
@ -58,7 +73,7 @@ impl Default for Config {
crypto: CryptoMethod::ChaCha20, crypto: CryptoMethod::ChaCha20,
shared_key: None, shared_key: None,
magic: None, magic: None,
port: 3210, listen: "[::]:3210".parse::<SocketAddr>().unwrap(),
peers: vec![], peers: vec![],
peer_timeout: DEFAULT_PEER_TIMEOUT as Duration, peer_timeout: DEFAULT_PEER_TIMEOUT as Duration,
keepalive: None, keepalive: None,
@ -79,6 +94,7 @@ impl Default for Config {
} }
impl Config { impl Config {
#[allow(clippy::cognitive_complexity)]
pub fn merge_file(&mut self, file: ConfigFile) { pub fn merge_file(&mut self, file: ConfigFile) {
if let Some(val) = file.device_type { if let Some(val) = file.device_type {
self.device_type = val; self.device_type = val;
@ -105,7 +121,11 @@ impl Config {
self.magic = Some(val); self.magic = Some(val);
} }
if let Some(val) = file.port { if let Some(val) = file.port {
self.port = val; self.listen = parse_listen(&format!("{}", &val));
warn!("The config option 'port' is deprecated, use 'listen' instead.");
}
if let Some(val) = file.listen {
self.listen = parse_listen(&val);
} }
if let Some(mut val) = file.peers { if let Some(mut val) = file.peers {
self.peers.append(&mut val); self.peers.append(&mut val);
@ -181,7 +201,7 @@ impl Config {
self.magic = Some(val); self.magic = Some(val);
} }
if let Some(val) = args.flag_listen { if let Some(val) = args.flag_listen {
self.port = val; self.listen = parse_listen(&val);
} }
self.peers.append(&mut args.flag_connect); self.peers.append(&mut args.flag_connect);
if let Some(val) = args.flag_peer_timeout { if let Some(val) = args.flag_peer_timeout {
@ -265,6 +285,7 @@ pub struct ConfigFile {
pub shared_key: Option<String>, pub shared_key: Option<String>,
pub magic: Option<String>, pub magic: Option<String>,
pub port: Option<u16>, pub port: Option<u16>,
pub listen: Option<String>,
pub peers: Option<Vec<String>>, pub peers: Option<Vec<String>>,
pub peer_timeout: Option<Duration>, pub peer_timeout: Option<Duration>,
pub keepalive: Option<Duration>, pub keepalive: Option<Duration>,
@ -322,6 +343,7 @@ stats_file: /var/log/vpncloud.stats
shared_key: Some("mysecret".to_string()), shared_key: Some("mysecret".to_string()),
magic: Some("0123ABCD".to_string()), magic: Some("0123ABCD".to_string()),
port: Some(3210), port: Some(3210),
listen: None,
peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]), peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]),
peer_timeout: Some(600), peer_timeout: Some(600),
keepalive: Some(840), keepalive: Some(840),
@ -352,6 +374,7 @@ fn config_merge() {
shared_key: Some("mysecret".to_string()), shared_key: Some("mysecret".to_string()),
magic: Some("0123ABCD".to_string()), magic: Some("0123ABCD".to_string()),
port: Some(3210), port: Some(3210),
listen: None,
peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]), peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]),
peer_timeout: Some(600), peer_timeout: Some(600),
keepalive: Some(840), keepalive: Some(840),
@ -376,7 +399,7 @@ fn config_merge() {
magic: Some("0123ABCD".to_string()), magic: Some("0123ABCD".to_string()),
crypto: CryptoMethod::AES256, crypto: CryptoMethod::AES256,
shared_key: Some("mysecret".to_string()), shared_key: Some("mysecret".to_string()),
port: 3210, listen: "[::]:3210".parse::<SocketAddr>().unwrap(),
peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()], peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()],
peer_timeout: 600, peer_timeout: 600,
keepalive: Some(840), keepalive: Some(840),
@ -402,7 +425,7 @@ fn config_merge() {
flag_crypto: Some(CryptoMethod::ChaCha20), flag_crypto: Some(CryptoMethod::ChaCha20),
flag_shared_key: Some("anothersecret".to_string()), flag_shared_key: Some("anothersecret".to_string()),
flag_magic: Some("hash:mynet".to_string()), flag_magic: Some("hash:mynet".to_string()),
flag_listen: Some(3211), flag_listen: Some("3211".to_string()),
flag_peer_timeout: Some(1801), flag_peer_timeout: Some(1801),
flag_keepalive: Some(850), flag_keepalive: Some(850),
flag_dst_timeout: Some(301), flag_dst_timeout: Some(301),
@ -429,7 +452,7 @@ fn config_merge() {
magic: Some("hash:mynet".to_string()), magic: Some("hash:mynet".to_string()),
crypto: CryptoMethod::ChaCha20, crypto: CryptoMethod::ChaCha20,
shared_key: Some("anothersecret".to_string()), shared_key: Some("anothersecret".to_string()),
port: 3211, listen: "[::]:3211".parse::<SocketAddr>().unwrap(),
peers: vec![ peers: vec![
"remote.machine.foo:3210".to_string(), "remote.machine.foo:3210".to_string(),
"remote.machine.bar:3210".to_string(), "remote.machine.bar:3210".to_string(),

View File

@ -72,7 +72,7 @@ pub struct Args {
flag_crypto: Option<CryptoMethod>, flag_crypto: Option<CryptoMethod>,
flag_subnet: Vec<String>, flag_subnet: Vec<String>,
flag_device: Option<String>, flag_device: Option<String>,
flag_listen: Option<u16>, flag_listen: Option<String>,
flag_network_id: Option<String>, flag_network_id: Option<String>,
flag_magic: Option<String>, flag_magic: Option<String>,
flag_connect: Vec<String>, flag_connect: Vec<String>,
@ -270,7 +270,7 @@ fn run<P: Protocol>(config: Config) {
Some(ref key) => Crypto::from_shared_key(config.crypto, key), Some(ref key) => Crypto::from_shared_key(config.crypto, key),
None => Crypto::None None => Crypto::None
}; };
let port_forwarding = if config.port_forwarding { PortForwarding::new(config.port) } else { None }; let port_forwarding = if config.port_forwarding { PortForwarding::new(config.listen.port()) } else { None };
let stats_file = match config.stats_file { let stats_file = match config.stats_file {
None => None, None => None,
Some(ref name) => { Some(ref name) => {

View File

@ -1,34 +1,24 @@
use std::{ use std::{
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
io::{self, ErrorKind}, io::{self, ErrorKind},
net::{SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket}, net::{SocketAddr, UdpSocket},
os::unix::io::{AsRawFd, RawFd}, os::unix::io::{AsRawFd, RawFd},
sync::atomic::{AtomicBool, Ordering} sync::atomic::{AtomicBool, Ordering}
}; };
use super::util::{MockTimeSource, Time, TimeSource}; use super::util::{MockTimeSource, Time, TimeSource};
use net2::UdpBuilder;
pub trait Socket: AsRawFd + Sized { pub trait Socket: AsRawFd + Sized {
fn listen_v4(host: &str, port: u16) -> Result<Self, io::Error>; fn listen(addr: SocketAddr) -> Result<Self, io::Error>;
fn listen_v6(host: &str, port: u16) -> Result<Self, io::Error>;
fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error>; fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error>;
fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error>; fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error>;
fn address(&self) -> Result<SocketAddr, io::Error>; fn address(&self) -> Result<SocketAddr, io::Error>;
} }
impl Socket for UdpSocket { impl Socket for UdpSocket {
fn listen_v4(host: &str, port: u16) -> Result<Self, io::Error> { fn listen(addr: SocketAddr) -> Result<Self, io::Error> {
UdpBuilder::new_v4().expect("Failed to obtain ipv4 socket builder").bind((host, port)) UdpSocket::bind(addr)
}
fn listen_v6(host: &str, port: u16) -> Result<Self, io::Error> {
UdpBuilder::new_v6()
.expect("Failed to obtain ipv4 socket builder")
.only_v6(true)
.expect("Failed to set only_v6")
.bind((host, port))
} }
fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error> { fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error> {
self.recv_from(buffer) self.recv_from(buffer)
@ -99,13 +89,8 @@ impl AsRawFd for MockSocket {
} }
impl Socket for MockSocket { impl Socket for MockSocket {
fn listen_v4(host: &str, port: u16) -> Result<Self, io::Error> { fn listen(addr: SocketAddr) -> Result<Self, io::Error> {
let ip = try_fail!(host.parse(), "Failed to parse IPv4 address: {}"); Ok(Self::new(addr))
Ok(Self::new(SocketAddr::V4(SocketAddrV4::new(ip, port))))
}
fn listen_v6(host: &str, port: u16) -> Result<Self, io::Error> {
let ip = try_fail!(host.parse(), "Failed to parse IPv6 address: {}");
Ok(Self::new(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0))))
} }
fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error> { fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error> {
if let Some((addr, data)) = self.inbound.pop_front() { if let Some((addr, data)) = self.inbound.pop_front() {

View File

@ -13,33 +13,30 @@ use crate::{device::Type, net::Socket};
pub struct EpollWait { pub struct EpollWait {
poll_fd: RawFd, poll_fd: RawFd,
event: libc::epoll_event, event: libc::epoll_event,
socketv4: RawFd, socket: RawFd,
socketv6: RawFd,
device: RawFd, device: RawFd,
timeout: u32 timeout: u32
} }
impl EpollWait { impl EpollWait {
pub fn new<S: Socket>(socketv4: &S, socketv6: &S, device: &dyn Device, timeout: u32) -> io::Result<Self> { pub fn new<S: Socket>(socket: &S, device: &dyn Device, timeout: u32) -> io::Result<Self> {
Self::create(socketv4, socketv6, device, timeout, libc::EPOLLIN as u32) Self::create(socket, device, timeout, libc::EPOLLIN as u32)
} }
pub fn testing<S: Socket>(socketv4: &S, socketv6: &S, device: &dyn Device, timeout: u32) -> io::Result<Self> { pub fn testing<S: Socket>(socket: &S, device: &dyn Device, timeout: u32) -> io::Result<Self> {
Self::create(socketv4, socketv6, device, timeout, (libc::EPOLLIN | libc::EPOLLOUT) as u32) Self::create(socket, device, timeout, (libc::EPOLLIN | libc::EPOLLOUT) as u32)
} }
fn create<S: Socket>( fn create<S: Socket>(socket: &S, device: &dyn Device, timeout: u32, flags: u32) -> io::Result<Self> {
socketv4: &S, socketv6: &S, device: &dyn Device, timeout: u32, flags: u32
) -> io::Result<Self> {
let mut event = libc::epoll_event { u64: 0, events: 0 }; let mut event = libc::epoll_event { u64: 0, events: 0 };
let poll_fd = unsafe { libc::epoll_create(3) }; let poll_fd = unsafe { libc::epoll_create(3) };
if poll_fd == -1 { if poll_fd == -1 {
return Err(io::Error::last_os_error()) return Err(io::Error::last_os_error())
} }
let raw_fds = if device.get_type() != Type::Dummy { let raw_fds = if device.get_type() != Type::Dummy {
vec![socketv4.as_raw_fd(), socketv6.as_raw_fd(), device.as_raw_fd()] vec![socket.as_raw_fd(), device.as_raw_fd()]
} else { } else {
vec![socketv4.as_raw_fd(), socketv6.as_raw_fd()] vec![socket.as_raw_fd()]
}; };
for fd in raw_fds { for fd in raw_fds {
event.u64 = fd as u64; event.u64 = fd as u64;
@ -52,8 +49,7 @@ impl EpollWait {
Ok(Self { Ok(Self {
poll_fd, poll_fd,
event, event,
socketv4: socketv4.as_raw_fd(), socket: socket.as_raw_fd(),
socketv6: socketv6.as_raw_fd(),
device: device.as_raw_fd(), device: device.as_raw_fd(),
timeout timeout
}) })
@ -74,10 +70,8 @@ impl Iterator for EpollWait {
-1 => WaitResult::Error(io::Error::last_os_error()), -1 => WaitResult::Error(io::Error::last_os_error()),
0 => WaitResult::Timeout, 0 => WaitResult::Timeout,
1 => { 1 => {
if self.event.u64 == self.socketv4 as u64 { if self.event.u64 == self.socket as u64 {
WaitResult::SocketV4 WaitResult::Socket
} else if self.event.u64 == self.socketv6 as u64 {
WaitResult::SocketV6
} else if self.event.u64 == self.device as u64 { } else if self.event.u64 == self.device as u64 {
WaitResult::Device WaitResult::Device
} else { } else {

View File

@ -13,8 +13,7 @@ use std::io;
pub enum WaitResult { pub enum WaitResult {
Timeout, Timeout,
SocketV4, Socket,
SocketV6,
Device, Device,
Error(io::Error) Error(io::Error)
} }

View File

@ -1,8 +1,7 @@
macro_rules! assert_clean { macro_rules! assert_clean {
($($node: expr),*) => { ($($node: expr),*) => {
$( $(
assert_eq!($node.socket4().pop_outbound().map(|(addr, mut msg)| (addr, $node.decode_message(&mut msg).unwrap().without_data())), None); assert_eq!($node.socket().pop_outbound().map(|(addr, mut msg)| (addr, $node.decode_message(&mut msg).unwrap().without_data())), None);
assert_eq!($node.socket6().pop_outbound().map(|(addr, mut msg)| (addr, $node.decode_message(&mut msg).unwrap().without_data())), None);
assert_eq!($node.device().pop_outbound(), None); assert_eq!($node.device().pop_outbound(), None);
)* )*
}; };
@ -10,13 +9,13 @@ macro_rules! assert_clean {
macro_rules! assert_message4 { macro_rules! assert_message4 {
($from: expr, $from_addr: expr, $to: expr, $to_addr: expr, $message: expr) => { ($from: expr, $from_addr: expr, $to: expr, $to_addr: expr, $message: expr) => {
let (addr, mut data) = msg4_get(&mut $from); let (addr, mut data) = msg_get(&mut $from);
assert_eq!($to_addr, addr); assert_eq!($to_addr, addr);
{ {
let message = $from.decode_message(&mut data).unwrap(); let message = $from.decode_message(&mut data).unwrap();
assert_eq!($message, message.without_data()); assert_eq!($message, message.without_data());
} }
msg4_put(&mut $to, $from_addr, data); msg_put(&mut $to, $from_addr, data);
}; };
} }

View File

@ -8,9 +8,9 @@ mod nat;
mod payload; mod payload;
mod peers; mod peers;
pub use std::net::SocketAddr;
use std::{ use std::{
io::Write, io::Write,
net::{IpAddr, Ipv6Addr, SocketAddr},
sync::{ sync::{
atomic::{AtomicUsize, Ordering}, atomic::{AtomicUsize, Ordering},
Once Once
@ -73,13 +73,17 @@ thread_local! {
static NEXT_PORT: AtomicUsize = AtomicUsize::new(1); static NEXT_PORT: AtomicUsize = AtomicUsize::new(1);
} }
fn next_sock_addr() -> SocketAddr {
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16)
}
fn create_tap_node(nat: bool) -> TapTestNode { fn create_tap_node(nat: bool) -> TapTestNode {
create_tap_node_with_config(nat, Config::default()) create_tap_node_with_config(nat, Config::default())
} }
fn create_tap_node_with_config(nat: bool, mut config: Config) -> TapTestNode { fn create_tap_node_with_config(nat: bool, mut config: Config) -> TapTestNode {
MockSocket::set_nat(nat); MockSocket::set_nat(nat);
config.port = NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16; config.listen = next_sock_addr();
TestNode::new(&config, MockDevice::new(), SwitchTable::new(1800, 10), true, true, vec![], Crypto::None, None, None) TestNode::new(&config, MockDevice::new(), SwitchTable::new(1800, 10), true, true, vec![], Crypto::None, None, None)
} }
@ -87,7 +91,7 @@ fn create_tap_node_with_config(nat: bool, mut config: Config) -> TapTestNode {
fn create_tun_node(nat: bool, addresses: Vec<Range>) -> TunTestNode { fn create_tun_node(nat: bool, addresses: Vec<Range>) -> TunTestNode {
MockSocket::set_nat(nat); MockSocket::set_nat(nat);
TestNode::new( TestNode::new(
&Config { port: NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16, ..Config::default() }, &Config { listen: next_sock_addr(), ..Config::default() },
MockDevice::new(), MockDevice::new(),
RoutingTable::new(), RoutingTable::new(),
false, false,
@ -100,28 +104,15 @@ fn create_tun_node(nat: bool, addresses: Vec<Range>) -> TunTestNode {
} }
fn msg4_get<P: Protocol, T: Table>(node: &mut TestNode<P, T>) -> (SocketAddr, Vec<u8>) { fn msg_get<P: Protocol, T: Table>(node: &mut TestNode<P, T>) -> (SocketAddr, Vec<u8>) {
let msg = node.socket4().pop_outbound(); let msg = node.socket().pop_outbound();
assert!(msg.is_some()); assert!(msg.is_some());
msg.unwrap() msg.unwrap()
} }
#[allow(dead_code)] fn msg_put<P: Protocol, T: Table>(node: &mut TestNode<P, T>, from: SocketAddr, msg: Vec<u8>) {
fn msg6_get<P: Protocol, T: Table>(node: &mut TestNode<P, T>) -> (SocketAddr, Vec<u8>) { if node.socket().put_inbound(from, msg) {
let msg = node.socket6().pop_outbound(); node.trigger_socket_event();
assert!(msg.is_some());
msg.unwrap()
}
fn msg4_put<P: Protocol, T: Table>(node: &mut TestNode<P, T>, from: SocketAddr, msg: Vec<u8>) {
if node.socket4().put_inbound(from, msg) {
node.trigger_socket_v4_event();
}
}
fn msg6_put<P: Protocol, T: Table>(node: &mut TestNode<P, T>, from: SocketAddr, msg: Vec<u8>) {
if node.socket6().put_inbound(from, msg) {
node.trigger_socket_v6_event();
} }
} }
@ -136,7 +127,7 @@ fn simulate<P: Protocol, T: Table>(nodes: &mut [(&mut TestNode<P, T>, SocketAddr
clean = true; clean = true;
let mut msgs = Vec::new(); let mut msgs = Vec::new();
for (ref mut node, ref from_addr) in nodes.iter_mut() { for (ref mut node, ref from_addr) in nodes.iter_mut() {
while let Some((to_addr, msg)) = node.socket4().pop_outbound() { while let Some((to_addr, msg)) = node.socket().pop_outbound() {
msgs.push((msg, *from_addr, to_addr)); msgs.push((msg, *from_addr, to_addr));
} }
} }
@ -144,22 +135,7 @@ fn simulate<P: Protocol, T: Table>(nodes: &mut [(&mut TestNode<P, T>, SocketAddr
for (msg, from_addr, to_addr) in msgs { for (msg, from_addr, to_addr) in msgs {
for (ref mut node, ref addr) in nodes.iter_mut() { for (ref mut node, ref addr) in nodes.iter_mut() {
if *addr == to_addr { if *addr == to_addr {
msg4_put(node, from_addr, msg); msg_put(node, from_addr, msg);
break
}
}
}
let mut msgs = Vec::new();
for (ref mut node, ref from_addr) in nodes.iter_mut() {
while let Some((to_addr, msg)) = node.socket6().pop_outbound() {
msgs.push((msg, *from_addr, to_addr));
}
}
clean &= msgs.is_empty();
for (msg, from_addr, to_addr) in msgs {
for (ref mut node, ref addr) in nodes.iter_mut() {
if *addr == to_addr {
msg6_put(node, from_addr, msg);
break break
} }
} }

View File

@ -101,7 +101,7 @@ fn connect_via_beacons() {
let beacon_path = "target/.vpncloud_test"; let beacon_path = "target/.vpncloud_test";
let mut node1 = let mut node1 =
create_tap_node_with_config(false, Config { beacon_store: Some(beacon_path.to_string()), ..Config::default() }); create_tap_node_with_config(false, Config { beacon_store: Some(beacon_path.to_string()), ..Config::default() });
let node1_addr = node1.address().unwrap().0; let node1_addr = node1.address().unwrap();
let mut node2 = let mut node2 =
create_tap_node_with_config(false, Config { beacon_load: Some(beacon_path.to_string()), ..Config::default() }); create_tap_node_with_config(false, Config { beacon_load: Some(beacon_path.to_string()), ..Config::default() });
let node2_addr = addr!("2.2.2.2:2222"); let node2_addr = addr!("2.2.2.2:2222");
@ -162,7 +162,7 @@ fn lost_init1() {
assert_clean!(node1); assert_clean!(node1);
// Node 2 -> Node 1: Init 1 | Node 2 -> Node 1: Peers // Node 2 -> Node 1: Init 1 | Node 2 -> Node 1: Peers
assert!(node2.socket4().pop_outbound().is_some()); assert!(node2.socket().pop_outbound().is_some());
assert!(!node1.peers().contains_node(&node2.node_id())); assert!(!node1.peers().contains_node(&node2.node_id()));
simulate!(node1 => node1_addr, node2 => node2_addr); simulate!(node1 => node1_addr, node2 => node2_addr);

View File

@ -38,9 +38,12 @@ vpncloud(1) -- Peer-to-peer VPN
peers and ignore them otherwise. The **normal** mode is switch for tap peers and ignore them otherwise. The **normal** mode is switch for tap
devices and router for tun devices. [default: `normal`] devices and router for tun devices. [default: `normal`]
* `-l <port>`, `--listen <port>`: * `-l <addr>`, `--listen <addr>`:
The port number on which to listen for data. [default: `3210`] The address on which to listen for data. This can be simply a port number
or a full address in form IP:PORT. If the IP is specified as '*' or only
a port number is given, then the socket will listen on all IPs (v4 and v6),
otherwise the socket will only listen on the given IP. [default: `3210`]
* `-c <addr>`, `--connect <addr>`: * `-c <addr>`, `--connect <addr>`:
@ -311,7 +314,8 @@ detailed descriptions of the options.
* `crypto`: The encryption method to use. Same as `--crypto` * `crypto`: The encryption method to use. Same as `--crypto`
* `shared_key`: The shared key to encrypt all traffic. Same as `--shared-key` * `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` * `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` * `port`: A port number to listen on. This option is DEPRECATED.
* `listen`: The address on which to listen for data. Same as `--listen`
* `peers`: A list of addresses to connect to. See `--connect` * `peers`: A list of addresses to connect to. See `--connect`
* `peer_timeout`: Peer timeout in seconds. Same as`--peer-timeout` * `peer_timeout`: Peer timeout in seconds. Same as`--peer-timeout`
* `beacon_store`: Path or command to store beacons. Same as `--beacon-store` * `beacon_store`: Path or command to store beacons. Same as `--beacon-store`
@ -334,7 +338,7 @@ device_name: vpncloud%d
ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up
crypto: aes256 crypto: aes256
shared_key: mysecret shared_key: mysecret
port: 3210 listen: 3210
peers: peers:
- remote.machine.foo:3210 - remote.machine.foo:3210
- remote.machine.bar:3210 - remote.machine.bar:3210