mirror of https://github.com/dswd/vpncloud.git
Implemented beacon support
This commit is contained in:
parent
2c818d7079
commit
537cb55c41
|
@ -5,5 +5,4 @@ vpncloud-oldnodes
|
|||
deb/vpncloud/vpncloud
|
||||
deb/vpncloud/vpncloud.1*
|
||||
Stats.ods
|
||||
.sodium-build
|
||||
wiki
|
||||
dist
|
||||
|
|
|
@ -4,7 +4,9 @@ This project follows [semantic versioning](http://semver.org).
|
|||
|
||||
### UNRELEASED
|
||||
|
||||
- [added] Added ability to publish small beacons for rendezvous
|
||||
- [changed] Allow to build binary without manpage
|
||||
- [fixed] Fixed bug that could cause repeated initialization messages
|
||||
|
||||
### v0.9.1 (2019-02-16)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
VpnCloud - Peer-to-Peer VPN
|
||||
---------------------------
|
||||
|
||||
[![Build Status](https://travis-ci.org/dswd/vpncloud.rs.svg?branch=master)](https://travis-ci.org/dswd/vpncloud.rs)
|
||||
[![Coverage Status](https://coveralls.io/repos/dswd/vpncloud.rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/dswd/vpncloud.rs?branch=master)
|
||||
[![Build Status](https://travis-ci.org/dswd/vpncloud.svg?branch=master)](https://travis-ci.org/dswd/vpncloud)
|
||||
[![Coverage Status](https://coveralls.io/repos/dswd/vpncloud/badge.svg?branch=master&service=github)](https://coveralls.io/github/dswd/vpncloud?branch=master)
|
||||
|
||||
**VpnCloud** is a simple VPN over UDP. It creates a virtual network interface on
|
||||
the host and forwards all received data via UDP to the destination. VpnCloud
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2019-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use base_62;
|
||||
use ring::digest;
|
||||
|
||||
use std::num::Wrapping;
|
||||
use std::path::Path;
|
||||
use std::io::{self, Write, Read};
|
||||
use std::fs::{self, Permissions, File};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::mem;
|
||||
use std::thread;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use super::util::{now, Encoder};
|
||||
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
|
||||
|
@ -19,16 +32,27 @@ fn now_hour_16() -> u16 {
|
|||
((now() / 3600) & 0xffff) as u16
|
||||
}
|
||||
|
||||
struct FutureResult<T> {
|
||||
has_result: AtomicBool,
|
||||
result: Mutex<T>
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BeaconSerializer {
|
||||
magic: Vec<u8>,
|
||||
shared_key: Vec<u8>
|
||||
shared_key: Vec<u8>,
|
||||
future_peers: Arc<FutureResult<Vec<SocketAddr>>>,
|
||||
}
|
||||
|
||||
impl BeaconSerializer {
|
||||
pub fn new(magic: &[u8], shared_key: &[u8]) -> Self {
|
||||
BeaconSerializer {
|
||||
magic: magic.to_owned(),
|
||||
shared_key: shared_key.to_owned()
|
||||
shared_key: shared_key.to_owned(),
|
||||
future_peers: Arc::new(FutureResult {
|
||||
has_result: AtomicBool::new(false),
|
||||
result: Mutex::new(Vec::new())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,6 +186,35 @@ impl BeaconSerializer {
|
|||
self.encode_internal(peers, now_hour_16())
|
||||
}
|
||||
|
||||
pub fn write_to_file<P: AsRef<Path>>(&self, peers: &[SocketAddr], path: P) -> Result<(), io::Error> {
|
||||
let beacon = self.encode(peers);
|
||||
debug!("Beacon: {}", beacon);
|
||||
let mut f = try!(File::create(&path));
|
||||
try!(writeln!(&mut f, "{}", beacon));
|
||||
try!(fs::set_permissions(&path, Permissions::from_mode(0o644)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_to_cmd(&self, peers: &[SocketAddr], cmd: &str) -> Result<(), io::Error> {
|
||||
let begin = self.begin();
|
||||
let data = self.peerlist_encode(peers, now_hour_16());
|
||||
let end = self.end();
|
||||
let beacon = format!("{}{}{}", begin, data, end);
|
||||
debug!("Calling beacon command: {}", cmd);
|
||||
let process = try!(Command::new("sh").args(&["-c", cmd])
|
||||
.env("begin", begin).env("data", data).env("end", end).env("beacon", beacon)
|
||||
.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());
|
||||
thread::spawn(move || {
|
||||
let output = process.wait_with_output().expect("Failed to wait on child");
|
||||
if !output.status.success() {
|
||||
error!("Beacon command failed: {}", String::from_utf8_lossy(&output.stderr));
|
||||
} else {
|
||||
debug!("Beacon command succeeded");
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_internal(&self, data: &str, ttl_hours: Option<u16>, now_hour: u16) -> Vec<SocketAddr> {
|
||||
let data = base_62_sanitize(data);
|
||||
let mut peers = Vec::new();
|
||||
|
@ -185,6 +238,48 @@ impl BeaconSerializer {
|
|||
pub fn decode(&self, data: &str, ttl_hours: Option<u16>) -> Vec<SocketAddr> {
|
||||
self.decode_internal(data, ttl_hours, now_hour_16())
|
||||
}
|
||||
|
||||
pub fn read_from_file<P: AsRef<Path>>(&self, path: P, ttl_hours: Option<u16>) -> Result<Vec<SocketAddr>, io::Error> {
|
||||
let mut f = try!(File::open(&path));
|
||||
let mut contents = String::new();
|
||||
try!(f.read_to_string(&mut contents));
|
||||
Ok(self.decode(&contents, ttl_hours))
|
||||
}
|
||||
|
||||
pub fn read_from_cmd(&self, cmd: &str, ttl_hours: Option<u16>) -> Result<(), io::Error> {
|
||||
let begin = self.begin();
|
||||
let end = self.end();
|
||||
debug!("Calling beacon command: {}", cmd);
|
||||
let process = try!(Command::new("sh").args(&["-c", cmd])
|
||||
.env("begin", begin).env("end", end)
|
||||
.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());
|
||||
let this = self.clone();
|
||||
thread::spawn(move || {
|
||||
let output = process.wait_with_output().expect("Failed to wait on child");
|
||||
if output.status.success() {
|
||||
let data = String::from_utf8_lossy(&output.stdout);
|
||||
let mut peers = this.decode(&data, ttl_hours);
|
||||
debug!("Beacon command succeeded with {} peers", peers.len());
|
||||
mem::swap(&mut peers, &mut this.future_peers.result.lock().expect("Lock poisoned"));
|
||||
this.future_peers.has_result.store(true, Ordering::Relaxed);
|
||||
} else {
|
||||
error!("Beacon command failed: {}", String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
});
|
||||
//TODO: implement
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_cmd_results(&self) -> Option<Vec<SocketAddr>> {
|
||||
if self.future_peers.has_result.load(Ordering::Relaxed) {
|
||||
let mut peers = Vec::new();
|
||||
mem::swap(&mut peers, &mut self.future_peers.result.lock().expect("Lock poisoned"));
|
||||
self.future_peers.has_result.store(false, Ordering::Relaxed);
|
||||
Some(peers)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use test::Bencher;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
#include <stdint.h>
|
||||
|
|
111
src/cloud.rs
111
src/cloud.rs
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
|
@ -140,6 +140,7 @@ impl PeerList {
|
|||
peer.alt_addrs.retain(|i| i != &addr);
|
||||
peer.alt_addrs.push(old_addr);
|
||||
self.peers.insert(addr, peer);
|
||||
self.addresses.insert(addr, node_id);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -202,6 +203,7 @@ pub struct ReconnectEntry {
|
|||
|
||||
|
||||
pub struct GenericCloud<P: Protocol, T: Table> {
|
||||
config: Config,
|
||||
magic: HeaderMagic,
|
||||
node_id: NodeId,
|
||||
peers: PeerList,
|
||||
|
@ -209,7 +211,7 @@ pub struct GenericCloud<P: Protocol, T: Table> {
|
|||
learning: bool,
|
||||
broadcast: bool,
|
||||
reconnect_peers: Vec<ReconnectEntry>,
|
||||
blacklist_peers: Vec<SocketAddr>,
|
||||
own_addresses: Vec<SocketAddr>,
|
||||
table: T,
|
||||
socket4: UdpSocket,
|
||||
socket6: UdpSocket,
|
||||
|
@ -220,9 +222,9 @@ pub struct GenericCloud<P: Protocol, T: Table> {
|
|||
buffer_out: [u8; 64*1024],
|
||||
next_housekeep: Time,
|
||||
next_stats_out: Time,
|
||||
next_beacon: Time,
|
||||
port_forwarding: Option<PortForwarding>,
|
||||
traffic: TrafficStats,
|
||||
stats_file: Option<String>,
|
||||
beacon_serializer: BeaconSerializer,
|
||||
_dummy_p: PhantomData<P>,
|
||||
}
|
||||
|
@ -251,7 +253,7 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
learning,
|
||||
broadcast,
|
||||
reconnect_peers: Vec::new(),
|
||||
blacklist_peers: Vec::new(),
|
||||
own_addresses: Vec::new(),
|
||||
table,
|
||||
socket4,
|
||||
socket6,
|
||||
|
@ -261,11 +263,12 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
buffer_out: [0; 64*1024],
|
||||
next_housekeep: now(),
|
||||
next_stats_out: now() + STATS_INTERVAL,
|
||||
next_beacon: now(),
|
||||
port_forwarding,
|
||||
traffic: TrafficStats::default(),
|
||||
stats_file: config.stats_file.clone(),
|
||||
beacon_serializer: BeaconSerializer::new(&config.get_magic(), crypto.get_key()),
|
||||
crypto,
|
||||
config: config.clone(),
|
||||
_dummy_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -356,14 +359,14 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns whether the address is blacklisted
|
||||
/// Returns whether the address is of this node
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an `Error::SocketError` if the given address is a name that failed to resolve to
|
||||
/// actual addresses.
|
||||
fn is_blacklisted<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
|
||||
fn is_own_address<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
|
||||
for addr in try!(resolve(&addr)) {
|
||||
if self.blacklist_peers.contains(&addr) {
|
||||
if self.own_addresses.contains(&addr) {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +382,7 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
/// # Errors
|
||||
/// This method returns `Error::NameError` if the address is a name that fails to resolve.
|
||||
pub fn connect<Addr: ToSocketAddrs+fmt::Debug+Clone>(&mut self, addr: Addr) -> Result<(), Error> {
|
||||
if try!(self.peers.is_connected(addr.clone())) || try!(self.is_blacklisted(addr.clone())) {
|
||||
if try!(self.peers.is_connected(addr.clone())) || try!(self.is_own_address(addr.clone())) {
|
||||
return Ok(())
|
||||
}
|
||||
debug!("Connecting to {:?}", addr);
|
||||
|
@ -394,6 +397,25 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Connects to a node given by its address
|
||||
///
|
||||
/// This method connects to node by sending a `Message::Init` to it. If `addr` is a name that
|
||||
/// resolves to multiple addresses, one message is sent to each of them.
|
||||
/// If the node is already a connected peer or the address is blacklisted, no message is sent.
|
||||
///
|
||||
/// # Errors
|
||||
/// This method returns `Error::NameError` if the address is a name that fails to resolve.
|
||||
fn connect_sock(&mut self, addr: SocketAddr) -> Result<(), Error> {
|
||||
if self.peers.contains_addr(&addr) || self.own_addresses.contains(&addr) {
|
||||
return Ok(())
|
||||
}
|
||||
debug!("Connecting to {:?}", addr);
|
||||
let subnets = self.addresses.clone();
|
||||
let node_id = self.node_id;
|
||||
let mut msg = Message::Init(0, node_id, subnets.clone());
|
||||
self.send_msg(addr, &mut msg)
|
||||
}
|
||||
|
||||
/// Run all periodic housekeeping tasks
|
||||
///
|
||||
/// This method executes several tasks:
|
||||
|
@ -426,10 +448,6 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
// ...and send them to all peers
|
||||
let mut msg = Message::Peers(peers);
|
||||
try!(self.broadcast_msg(&mut msg));
|
||||
// Output beacon
|
||||
let beacon = self.beacon_serializer.encode(&self.peers.subset(3));
|
||||
//TODO: publish beacon
|
||||
debug!("Beacon: {}", beacon);
|
||||
// Reschedule for next update
|
||||
self.next_peerlist = now + Time::from(self.update_freq);
|
||||
}
|
||||
|
@ -478,21 +496,65 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
self.next_stats_out = now + STATS_INTERVAL;
|
||||
self.traffic.period(Some(60));
|
||||
}
|
||||
if let Some(peers) = self.beacon_serializer.get_cmd_results() {
|
||||
debug!("Loaded beacon with peers: {:?}", peers);
|
||||
for peer in peers {
|
||||
try!(self.connect_sock(peer));
|
||||
}
|
||||
}
|
||||
if self.next_beacon < now {
|
||||
try!(self.store_beacon());
|
||||
try!(self.load_beacon());
|
||||
self.next_beacon = now + Time::from(self.config.beacon_interval);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stores the beacon
|
||||
fn store_beacon(&mut self) -> Result<(), Error> {
|
||||
if let Some(ref path) = self.config.beacon_store {
|
||||
let peers: Vec<_> = self.own_addresses.choose_multiple(&mut thread_rng(),3).cloned().collect();
|
||||
if path.starts_with('|') {
|
||||
try!(self.beacon_serializer.write_to_cmd(&peers, &path[1..]).map_err(|e| Error::Beacon("Failed to call beacon command", e)));
|
||||
} else {
|
||||
try!(self.beacon_serializer.write_to_file(&peers, &path).map_err(|e| Error::Beacon("Failed to write beacon to file", e)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loads the beacon
|
||||
fn load_beacon(&mut self) -> Result<(), Error> {
|
||||
let peers;
|
||||
if let Some(ref path) = self.config.beacon_load {
|
||||
if path.starts_with('|') {
|
||||
try!(self.beacon_serializer.read_from_cmd(&path[1..], Some(50)).map_err(|e| Error::Beacon("Failed to call beacon command", e)));
|
||||
return Ok(())
|
||||
} else {
|
||||
peers = try!(self.beacon_serializer.read_from_file(&path, Some(50)).map_err(|e| Error::Beacon("Failed to read beacon from file", e)));
|
||||
}
|
||||
} else {
|
||||
return Ok(())
|
||||
}
|
||||
debug!("Loaded beacon with peers: {:?}", peers);
|
||||
for peer in peers {
|
||||
try!(self.connect_sock(peer));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Calculates, resets and writes out the statistics to a file
|
||||
fn write_out_stats(&mut self) -> Result<(), io::Error> {
|
||||
if self.stats_file.is_none() { return Ok(()) }
|
||||
if self.config.stats_file.is_none() { return Ok(()) }
|
||||
debug!("Writing out stats");
|
||||
let mut f = try!(File::create(self.stats_file.as_ref().unwrap()));
|
||||
let mut f = try!(File::create(self.config.stats_file.as_ref().unwrap()));
|
||||
try!(self.peers.write_out(&mut f));
|
||||
try!(writeln!(&mut f));
|
||||
try!(self.table.write_out(&mut f));
|
||||
try!(writeln!(&mut f));
|
||||
try!(self.traffic.write_out(&mut f));
|
||||
try!(writeln!(&mut f));
|
||||
try!(fs::set_permissions(self.stats_file.as_ref().unwrap(), Permissions::from_mode(0o644)));
|
||||
try!(fs::set_permissions(self.config.stats_file.as_ref().unwrap(), Permissions::from_mode(0o644)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -525,7 +587,7 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
// to reconnect.
|
||||
warn!("Destination for {} not found in peers: {}", dst, addr);
|
||||
self.table.remove(&dst);
|
||||
try!(self.connect(&addr));
|
||||
try!(self.connect_sock(addr));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
|
@ -594,16 +656,14 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
Message::Peers(peers) => {
|
||||
// Connect to sender if not connected
|
||||
if !self.peers.contains_addr(&peer) {
|
||||
try!(self.connect(&peer));
|
||||
try!(self.connect_sock(peer));
|
||||
}
|
||||
if let Some(node_id) = self.peers.get_node_id(&peer) {
|
||||
self.peers.make_primary(node_id, peer);
|
||||
}
|
||||
// Connect to all peers in the message
|
||||
for p in &peers {
|
||||
if ! self.peers.contains_addr(p) && ! self.blacklist_peers.contains(p) {
|
||||
try!(self.connect(p));
|
||||
}
|
||||
try!(self.connect_sock(*p));
|
||||
}
|
||||
// Refresh peer
|
||||
self.peers.refresh(&peer);
|
||||
|
@ -611,7 +671,7 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
Message::Init(stage, node_id, ranges) => {
|
||||
// Avoid connecting to self
|
||||
if node_id == self.node_id {
|
||||
self.blacklist_peers.push(peer);
|
||||
self.own_addresses.push(peer);
|
||||
return Ok(())
|
||||
}
|
||||
// Add sender as peer or as alternative address to existing peer
|
||||
|
@ -649,6 +709,13 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
|
|||
/// Also, this method will call `housekeep` every second.
|
||||
#[allow(unknown_lints, clippy::cyclomatic_complexity)]
|
||||
pub fn run(&mut self) {
|
||||
match self.address() {
|
||||
Err(err) => error!("Failed to obtain local addresses: {}", err),
|
||||
Ok((v4, v6)) => {
|
||||
self.own_addresses.push(v4);
|
||||
self.own_addresses.push(v6);
|
||||
}
|
||||
}
|
||||
let dummy_time = Instant::now();
|
||||
let trap = Trap::trap(&[Signal::SIGINT, Signal::SIGTERM, Signal::SIGQUIT]);
|
||||
let mut poll_handle = try_fail!(Poll::new(3), "Failed to create poll handle: {}");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use super::{MAGIC, Args};
|
||||
|
@ -30,6 +30,9 @@ pub struct Config {
|
|||
pub peers: Vec<String>,
|
||||
pub peer_timeout: Duration,
|
||||
pub keepalive: Option<Duration>,
|
||||
pub beacon_store: Option<String>,
|
||||
pub beacon_load: Option<String>,
|
||||
pub beacon_interval: Duration,
|
||||
pub mode: Mode,
|
||||
pub dst_timeout: Duration,
|
||||
pub subnets: Vec<String>,
|
||||
|
@ -49,6 +52,7 @@ impl Default for Config {
|
|||
crypto: CryptoMethod::ChaCha20, shared_key: None,
|
||||
magic: None,
|
||||
port: 3210, peers: vec![], peer_timeout: 1800, keepalive: None,
|
||||
beacon_store: None, beacon_load: None, beacon_interval: 3600,
|
||||
mode: Mode::Normal, dst_timeout: 300,
|
||||
subnets: vec![],
|
||||
port_forwarding: true,
|
||||
|
@ -99,6 +103,15 @@ impl Config {
|
|||
if let Some(val) = file.keepalive {
|
||||
self.keepalive = Some(val);
|
||||
}
|
||||
if let Some(val) = file.beacon_store {
|
||||
self.beacon_store = Some(val);
|
||||
}
|
||||
if let Some(val) = file.beacon_load {
|
||||
self.beacon_load = Some(val);
|
||||
}
|
||||
if let Some(val) = file.beacon_interval {
|
||||
self.beacon_interval = val;
|
||||
}
|
||||
if let Some(val) = file.mode {
|
||||
self.mode = val;
|
||||
}
|
||||
|
@ -164,6 +177,15 @@ impl Config {
|
|||
if let Some(val) = args.flag_keepalive {
|
||||
self.keepalive = Some(val);
|
||||
}
|
||||
if let Some(val) = args.flag_beacon_store {
|
||||
self.beacon_store = Some(val);
|
||||
}
|
||||
if let Some(val) = args.flag_beacon_load {
|
||||
self.beacon_load = Some(val);
|
||||
}
|
||||
if let Some(val) = args.flag_beacon_interval {
|
||||
self.beacon_interval = val;
|
||||
}
|
||||
if let Some(val) = args.flag_mode {
|
||||
self.mode = val;
|
||||
}
|
||||
|
@ -233,6 +255,9 @@ pub struct ConfigFile {
|
|||
pub peers: Option<Vec<String>>,
|
||||
pub peer_timeout: Option<Duration>,
|
||||
pub keepalive: Option<Duration>,
|
||||
pub beacon_store: Option<String>,
|
||||
pub beacon_load: Option<String>,
|
||||
pub beacon_interval: Option<Duration>,
|
||||
pub mode: Option<Mode>,
|
||||
pub dst_timeout: Option<Duration>,
|
||||
pub subnets: Option<Vec<String>>,
|
||||
|
@ -262,6 +287,9 @@ peers:
|
|||
peer_timeout: 1800
|
||||
keepalive: 840
|
||||
dst_timeout: 300
|
||||
beacon_store: /run/vpncloud.beacon.out
|
||||
beacon_load: /run/vpncloud.beacon.in
|
||||
beacon_interval: 3600
|
||||
mode: normal
|
||||
subnets:
|
||||
- 10.0.1.0/24
|
||||
|
@ -284,6 +312,9 @@ stats_file: /var/log/vpncloud.stats
|
|||
peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]),
|
||||
peer_timeout: Some(1800),
|
||||
keepalive: Some(840),
|
||||
beacon_store: Some("/run/vpncloud.beacon.out".to_string()),
|
||||
beacon_load: Some("/run/vpncloud.beacon.in".to_string()),
|
||||
beacon_interval: Some(3600),
|
||||
mode: Some(Mode::Normal),
|
||||
dst_timeout: Some(300),
|
||||
subnets: Some(vec!["10.0.1.0/24".to_string()]),
|
||||
|
@ -311,6 +342,9 @@ fn config_merge() {
|
|||
peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]),
|
||||
peer_timeout: Some(1800),
|
||||
keepalive: Some(840),
|
||||
beacon_store: Some("/run/vpncloud.beacon.out".to_string()),
|
||||
beacon_load: Some("/run/vpncloud.beacon.in".to_string()),
|
||||
beacon_interval: Some(7200),
|
||||
mode: Some(Mode::Normal),
|
||||
dst_timeout: Some(300),
|
||||
subnets: Some(vec!["10.0.1.0/24".to_string()]),
|
||||
|
@ -334,6 +368,9 @@ fn config_merge() {
|
|||
peer_timeout: 1800,
|
||||
keepalive: Some(840),
|
||||
dst_timeout: 300,
|
||||
beacon_store: Some("/run/vpncloud.beacon.out".to_string()),
|
||||
beacon_load: Some("/run/vpncloud.beacon.in".to_string()),
|
||||
beacon_interval: 7200,
|
||||
mode: Mode::Normal,
|
||||
port_forwarding: true,
|
||||
subnets: vec!["10.0.1.0/24".to_string()],
|
||||
|
@ -356,6 +393,9 @@ fn config_merge() {
|
|||
flag_peer_timeout: Some(1801),
|
||||
flag_keepalive: Some(850),
|
||||
flag_dst_timeout: Some(301),
|
||||
flag_beacon_store: Some("/run/vpncloud.beacon.out2".to_string()),
|
||||
flag_beacon_load: Some("/run/vpncloud.beacon.in2".to_string()),
|
||||
flag_beacon_interval: Some(3600),
|
||||
flag_mode: Some(Mode::Switch),
|
||||
flag_subnet: vec![],
|
||||
flag_connect: vec!["another:3210".to_string()],
|
||||
|
@ -381,6 +421,9 @@ fn config_merge() {
|
|||
peer_timeout: 1801,
|
||||
keepalive: Some(850),
|
||||
dst_timeout: 301,
|
||||
beacon_store: Some("/run/vpncloud.beacon.out2".to_string()),
|
||||
beacon_load: Some("/run/vpncloud.beacon.in2".to_string()),
|
||||
beacon_interval: 3600,
|
||||
mode: Mode::Switch,
|
||||
port_forwarding: false,
|
||||
subnets: vec!["10.0.1.0/24".to_string()],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
#![cfg_attr(feature = "bench", feature(test))]
|
||||
|
@ -82,6 +82,9 @@ pub struct Args {
|
|||
flag_peer_timeout: Option<Duration>,
|
||||
flag_keepalive: Option<Duration>,
|
||||
flag_dst_timeout: Option<Duration>,
|
||||
flag_beacon_store: Option<String>,
|
||||
flag_beacon_load: Option<String>,
|
||||
flag_beacon_interval: Option<Duration>,
|
||||
flag_verbose: bool,
|
||||
flag_quiet: bool,
|
||||
flag_ifup: Option<String>,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use libc;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::net::{SocketAddrV4, UdpSocket, SocketAddr};
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2018-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, Write};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::net::{SocketAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
@ -227,7 +227,8 @@ pub enum Error {
|
|||
Name(String),
|
||||
TunTapDev(&'static str, io::Error),
|
||||
Crypto(&'static str),
|
||||
File(&'static str, io::Error)
|
||||
File(&'static str, io::Error),
|
||||
Beacon(&'static str, io::Error)
|
||||
}
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
|
@ -238,7 +239,8 @@ impl fmt::Display for Error {
|
|||
Error::Crypto(msg) => write!(formatter, "{}", msg),
|
||||
Error::Name(ref name) => write!(formatter, "failed to resolve name '{}'", name),
|
||||
Error::WrongHeaderMagic(net) => write!(formatter, "wrong header magic: {}", bytes_to_hex(&net)),
|
||||
Error::File(msg, ref err) => write!(formatter, "{}: {:?}", msg, err)
|
||||
Error::File(msg, ref err) => write!(formatter, "{}: {:?}", msg, err),
|
||||
Error::Beacon(msg, ref err) => write!(formatter, "{}: {:?}", msg, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::fmt;
|
||||
|
|
|
@ -23,6 +23,9 @@ Options:
|
|||
--keepalive <secs> Periodically send message to keep
|
||||
connections alive.
|
||||
--dst-timeout <secs> Switch table entry timeout in seconds.
|
||||
--beacon-store <path|command> The file or command to store the beacon.
|
||||
--beacon-load <path|command> The file or command to load the beacon.
|
||||
--beacon-interval <secs> Beacon store/load interval in seconds.
|
||||
--ifup <command> A command to setup the network interface.
|
||||
--ifdown <command> A command to bring down the network
|
||||
interface.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// VpnCloud - Peer-to-Peer VPN
|
||||
// Copyright (C) 2015-2017 Dennis Schwerdel
|
||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
|
|
61
vpncloud.md
61
vpncloud.md
|
@ -86,6 +86,32 @@ vpncloud(1) -- Peer-to-peer VPN
|
|||
mode. Addresses that have not been seen for the given period of time will
|
||||
be forgotten. [default: `300`]
|
||||
|
||||
* `--beacon-store <path|command>`:
|
||||
|
||||
Periodically store beacons containing the address of this node in the given
|
||||
file or via the given command. If the parameter value starts with a pipe
|
||||
character (`|`), the rest of the value is interpreted as a shell command.
|
||||
Otherwise the value is interpreted as a file to write the beacon to.
|
||||
If this parameter is not given, beacon storage is disabled.
|
||||
Please see the section **BEACONS** for more information.
|
||||
|
||||
* `--beacon-load <path|command>`:
|
||||
|
||||
Periodically load beacons containing the addresses of other nodes from the
|
||||
given file or via the given command. If the parameter value starts with a
|
||||
pipe character (`|`), the rest of the value is interpreted as a shell
|
||||
command. Otherwise the value is interpreted as a file to read the beacon
|
||||
from.
|
||||
If this parameter is not given, beacon loading is disabled.
|
||||
Please see the section **BEACONS** for more information.
|
||||
|
||||
* `--beacon-interval <secs>`:
|
||||
|
||||
Beacon storage/loading interval in seconds. If configured to do so via
|
||||
`--beacon-store` and `--beacon-load`, the node will periodically store its
|
||||
beacon and load beacons of other nodes. This parameter defines the interval
|
||||
in seconds. [default: `3600`]
|
||||
|
||||
* `--ifup <command>`:
|
||||
|
||||
A command to setup the network interface. The command will be run (as
|
||||
|
@ -283,6 +309,9 @@ detailed descriptions of the options.
|
|||
* `port`: The port number on which to listen for data. Same as `--listen`
|
||||
* `peers`: A list of addresses to connect to. See `--connect`
|
||||
* `peer_timeout`: Peer timeout in seconds. Same as`--peer-timeout`
|
||||
* `beacon_store`: Path or command to store beacons. Same as `--beacon-store`
|
||||
* `beacon_load`: Path or command to load beacons. Same as `--beacon-load`
|
||||
* `beacon_interval`: Interval for loading and storing beacons in seconds. Same as `--beacon-interval`
|
||||
* `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`
|
||||
|
@ -314,6 +343,38 @@ group: nogroup
|
|||
pid_file: /run/vpncloud.pid
|
||||
|
||||
|
||||
## BEACONS
|
||||
|
||||
Beacons are short character sequences that contain a timestamp and a list of
|
||||
addresses. They can be published and retrieved by other nodes to find peers
|
||||
without the need for static addresses.
|
||||
|
||||
The beacons are short (less than 100 characters), encrypted and encoded with
|
||||
printable characters to allow publishing them in various places on the
|
||||
internet, e.g.:
|
||||
- On shared drives or synchronized folders (e.g. on Dropbox)
|
||||
- Via a dedicated database
|
||||
- Via a general purpose message board of message service (e.g. Twitter)
|
||||
|
||||
The beacons are very robust. They only consist of alphanumeric characters
|
||||
and can be interleaved with non-alphanumeric characters (e.g. whitespace).
|
||||
Also the beacons contain a prefix and suffix that depends on the configured
|
||||
network magic and secret key (if set) so that all nodes can find beacons in
|
||||
a long text.
|
||||
|
||||
When beacons are stored or loaded via a command (using the pipe character `|`),
|
||||
the command is interpreted using the configured shell `sh`. This command has
|
||||
access to the following environment variables:
|
||||
* `$begin`: The prefix of the beacon.
|
||||
* `$end`: The suffix of the beacon.
|
||||
* `$data` (only on store): The middle part of the beacon. Do not use this
|
||||
without prefix and suffix!
|
||||
* `$beacon` (only on store): The full beacon consisting of prefix, data and
|
||||
suffix.
|
||||
The commands are called in separate threads, so even longer running commands
|
||||
will not block the node.
|
||||
|
||||
|
||||
## NETWORK PROTOCOL
|
||||
|
||||
The protocol of VpnCloud is kept as simple as possible to allow other
|
||||
|
|
Loading…
Reference in New Issue