From 2f2f7b725fe7c0b7f927a472130b3acfd437b684 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Mon, 2 May 2016 08:35:11 +0200 Subject: [PATCH] Listening on ipv4 and ipv6 --- CHANGELOG.md | 2 ++ Cargo.lock | 91 +++++++++++++++++++++++++++++++++++--------------- Cargo.toml | 3 +- performance.md | 6 ++-- src/cloud.rs | 60 ++++++++++++++++++++++++--------- src/main.rs | 5 +-- src/usage.txt | 4 +-- vpncloud.md | 4 +-- 8 files changed, 122 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f05db..a3b24b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ This project follows [semantic versioning](http://semver.org). ### Unreleased +- [changed] Using SO_REUSEADDR to allow frequent rebinding - [changed] Building and using local libsodium library automatically +- [changed] Updated dependencies ### v0.5.0 (2016-04-05) diff --git a/Cargo.lock b/Cargo.lock index 0a72e56..67a2b6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,23 +3,24 @@ name = "vpncloud" version = "0.5.0" dependencies = [ "aligned_alloc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", "epoll 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (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.23 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "signal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "signal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aho-corasick" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -30,8 +31,8 @@ name = "aligned_alloc" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -45,12 +46,17 @@ name = "bitflags" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cfg-if" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "docopt" -version = "0.6.78" +version = "0.6.80" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "regex 0.1.62 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.69 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -61,7 +67,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "errno 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -69,8 +75,8 @@ name = "errno" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -81,12 +87,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "gcc" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "kernel32-sys" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -100,7 +106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -113,13 +119,20 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "mempool" -version = "0.3.0" +name = "net2" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "nix" @@ -136,7 +149,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -149,18 +162,18 @@ name = "rand" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "0.1.62" +version = "0.1.69" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mempool 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -176,7 +189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "signal" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -189,13 +202,30 @@ name = "strsim" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "thread-id" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "time" version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -214,3 +244,12 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + diff --git a/Cargo.toml b/Cargo.toml index e8d4470..71c5faf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,12 +16,13 @@ docopt = "0.6" rustc-serialize = "0.3" log = "0.3" epoll = "0.3" -signal = "0.1" +signal = ">=0.1.4" nix = "0.5" libc = "0.2" aligned_alloc = "0.1" rand = "0.3" fnv = "1" +net2 = "*" [build-dependencies] gcc = "0.3" diff --git a/performance.md b/performance.md index 0837aa5..7749d2b 100644 --- a/performance.md +++ b/performance.md @@ -20,15 +20,13 @@ VpnCloud version: `VpnCloud v0.5.0, protocol version 1, libsodium 1.0.10 (AES256 The sender runs the following command: ``` -$> ./vpncloud -t tap -l SENDER:3210 -c RECEIVER:3210 \ - --ifup 'ifconfig $IFNAME 10.2.1.1/24 mtu 1400 up' & +$> ./vpncloud -t tap -l 3210 -c RECEIVER:3210 --ifup 'ifconfig $IFNAME 10.0.0.1/24 mtu 1400 up' & ``` and the receiver runs: ``` -$> ./vpncloud -t tap -l RECEIVER:3210 -c SENDER:3210 \ - --ifup 'ifconfig $IFNAME 10.2.1.2/24 mtu 1400 up' & +$> ./vpncloud -t tap -l 3210 -c SENDER:3210 --ifup 'ifconfig $IFNAME 10.0.0.2/24 mtu 1400 up' & $> iperf -s & $> top ``` diff --git a/src/cloud.rs b/src/cloud.rs index 313652d..8b31013 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -2,8 +2,8 @@ // Copyright (C) 2015-2016 Dennis Schwerdel // This software is licensed under GPL-3 or newer (see LICENSE.md) -use std::net::{SocketAddr, ToSocketAddrs}; -use std::collections::HashMap; +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +use std::collections::{HashSet, HashMap}; use std::hash::Hasher; use std::net::UdpSocket; use std::io::Read; @@ -19,6 +19,7 @@ use nix::sys::signal::{SIGTERM, SIGQUIT, SIGINT}; use signal::trap::Trap; use time::SteadyTime; use rand::{random, sample, thread_rng}; +use net2::UdpBuilder; use super::types::{Table, Protocol, Range, Error, NetworkId, NodeId}; use super::device::Device; @@ -98,7 +99,8 @@ pub struct GenericCloud { reconnect_peers: Vec, blacklist_peers: Vec, table: Box, - socket: UdpSocket, + socket4: UdpSocket, + socket6: UdpSocket, device: Device, options: Options, crypto: Crypto, @@ -110,12 +112,19 @@ pub struct GenericCloud { } impl GenericCloud

{ - pub fn new(device: Device, listen: &str, network_id: Option, table: Box

, + pub fn new(device: Device, listen: u16, network_id: Option, table: Box
, peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec, crypto: Crypto) -> Self { - let socket = match UdpSocket::bind(listen) { + 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, - _ => fail!("Failed to open socket {}", listen) + Err(err) => fail!("Failed to open ipv4 address 0.0.0.0:{}: {}", listen, err) + }; + let socket6 = match UdpBuilder::new_v6().expect("Failed to obtain ipv6 socket builder") + .only_v6(true).expect("Failed to set only_v6") + .reuse_address(true).expect("Failed to set so_reuseaddr").bind(("::", listen)) { + Ok(socket) => socket, + Err(err) => fail!("Failed to open ipv6 address ::{}: {}", listen, err) }; let mut options = Options::default(); options.network_id = network_id; @@ -128,7 +137,8 @@ impl GenericCloud

{ reconnect_peers: Vec::new(), blacklist_peers: Vec::new(), table: table, - socket: socket, + socket4: socket4, + socket6: socket6, device: device, options: options, crypto: crypto, @@ -150,7 +160,11 @@ impl GenericCloud

{ debug!("Broadcasting {:?}", msg); let msg_data = encode(&mut self.options, msg, &mut self.buffer_out, &mut self.crypto); for addr in &self.peers.as_vec() { - try!(match self.socket.send_to(msg_data, addr) { + let socket = match addr { + &SocketAddr::V4(_) => &self.socket4, + &SocketAddr::V6(_) => &self.socket6 + }; + try!(match socket.send_to(msg_data, addr) { Ok(written) if written == msg_data.len() => Ok(()), Ok(_) => Err(Error::SocketError("Sent out truncated packet")), Err(e) => { @@ -166,7 +180,11 @@ impl GenericCloud

{ fn send_msg(&mut self, addr: SocketAddr, msg: &mut Message) -> Result<(), Error> { debug!("Sending {:?} to {}", msg, addr); let msg_data = encode(&mut self.options, msg, &mut self.buffer_out, &mut self.crypto); - match self.socket.send_to(msg_data, addr) { + let socket = match &addr { + &SocketAddr::V4(_) => &self.socket4, + &SocketAddr::V6(_) => &self.socket6 + }; + match socket.send_to(msg_data, addr) { Ok(written) if written == msg_data.len() => Ok(()), Ok(_) => Err(Error::SocketError("Sent out truncated packet")), Err(e) => { @@ -177,8 +195,8 @@ impl GenericCloud

{ } #[allow(dead_code)] - pub fn address(&self) -> IoResult { - self.socket.local_addr() + pub fn address(&self) -> IoResult<(SocketAddr, SocketAddr)> { + Ok((try!(self.socket4.local_addr()), try!(self.socket6.local_addr()))) } #[allow(dead_code)] @@ -325,11 +343,14 @@ impl GenericCloud

{ let dummy_time = SteadyTime::now(); let trap = Trap::trap(&[SIGINT, SIGTERM, SIGQUIT]); let epoll_handle = try_fail!(epoll::create1(0), "Failed to create epoll handle: {}"); - let socket_fd = self.socket.as_raw_fd(); + let socket4_fd = self.socket4.as_raw_fd(); + let socket6_fd = self.socket6.as_raw_fd(); let device_fd = self.device.as_raw_fd(); - let mut socket_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 0}; - let mut device_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 1}; - try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket_fd, &mut socket_event), "Failed to add socket to epoll handle: {}"); + let mut socket4_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 0}; + let mut socket6_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 1}; + let mut device_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 2}; + try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket4_fd, &mut socket4_event), "Failed to add ipv4 socket to epoll handle: {}"); + try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket6_fd, &mut socket6_event), "Failed to add ipv6 socket to epoll handle: {}"); try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, device_fd, &mut device_event), "Failed to add device to epoll handle: {}"); let mut events = [epoll::EpollEvent{events: 0, data: 0}; 2]; let mut buffer = [0; 64*1024]; @@ -339,13 +360,20 @@ impl GenericCloud

{ for i in 0..count { match &events[i].data { &0 => { - let (size, src) = try_fail!(self.socket.recv_from(&mut buffer), "Failed to read from network socket: {}"); + let (size, src) = try_fail!(self.socket4.recv_from(&mut buffer), "Failed to read from ipv4 network socket: {}"); match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) { Ok(_) => (), Err(e) => error!("Error: {}, from: {}", e, src) } }, &1 => { + let (size, src) = try_fail!(self.socket6.recv_from(&mut buffer), "Failed to read from ipv6 network socket: {}"); + match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) { + Ok(_) => (), + Err(e) => error!("Error: {}, from: {}", e, src) + } + }, + &2 => { let start = 64; let size = try_fail!(self.device.read(&mut buffer[start..]), "Failed to read from tap device: {}"); match self.handle_interface_data(&mut buffer, start, start+size) { diff --git a/src/main.rs b/src/main.rs index 0610e70..5203528 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ extern crate libc; extern crate aligned_alloc; extern crate rand; extern crate fnv; +extern crate net2; #[cfg(feature = "bench")] extern crate test; #[macro_use] mod util; @@ -69,7 +70,7 @@ struct Args { flag_crypto: CryptoMethod, flag_subnet: Vec, flag_device: String, - flag_listen: String, + flag_listen: u16, flag_network_id: Option, flag_connect: Vec, flag_peer_timeout: Duration, @@ -123,7 +124,7 @@ fn run (args: Args) { Some(key) => Crypto::from_shared_key(args.flag_crypto, &key), None => Crypto::None }; - let mut cloud = GenericCloud::::new(device, &args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto); + let mut cloud = GenericCloud::::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto); if let Some(script) = args.flag_ifup { run_script(script, cloud.ifname()); } diff --git a/src/usage.txt b/src/usage.txt index c1216ef..10287d9 100644 --- a/src/usage.txt +++ b/src/usage.txt @@ -8,8 +8,8 @@ Options: [default: vpncloud%d] -m , --mode The mode of the VPN ("hub", "switch", "router", or "normal"). [default: normal] - -l , --listen The address to listen for data. - [default: 0.0.0.0:3210] + -l , --listen The port number on which to listen for data. + [default: 3210] -c , --connect Address of a peer to connect to. --subnet The local subnets to use. --network-id Optional token that identifies the network. diff --git a/vpncloud.md b/vpncloud.md index a58c974..d6dfbbd 100644 --- a/vpncloud.md +++ b/vpncloud.md @@ -27,9 +27,9 @@ vpncloud(1) -- Peer-to-peer VPN peers and ignore them otherwise. The **normal** mode is switch for tap devices and router for tun devices. [default: `normal`] - * `-l `, `--listen `: + * `-l `, `--listen `: - The address to listen for data. [default: `0.0.0.0:3210`] + The port number on which to listen for data. [default: `3210`] * `-c `, `--connect `: