2016-02-05 15:58:32 +00:00
// VpnCloud - Peer-to-Peer VPN
2020-05-28 07:03:48 +00:00
// Copyright (C) 2015-2020 Dennis Schwerdel
2016-02-05 15:58:32 +00:00
// This software is licensed under GPL-3 or newer (see LICENSE.md)
2015-11-25 20:05:11 +00:00
#![ cfg_attr(feature = " bench " , feature(test)) ]
2016-06-26 17:18:38 +00:00
2015-11-19 15:34:20 +00:00
#[ macro_use ] extern crate log ;
2017-07-22 14:34:20 +00:00
#[ macro_use ] extern crate serde_derive ;
2019-03-01 22:25:42 +00:00
2019-02-21 15:57:25 +00:00
#[ cfg(test) ] extern crate tempfile ;
2015-11-25 20:05:11 +00:00
#[ cfg(feature = " bench " ) ] extern crate test ;
2015-11-19 15:34:20 +00:00
2019-12-04 08:32:35 +00:00
#[ macro_use ]
pub mod util ;
#[ cfg(test) ]
#[ macro_use ]
mod tests ;
pub mod beacon ;
#[ cfg(feature = " bench " ) ] mod benches ;
pub mod cloud ;
pub mod config ;
2016-06-27 13:43:30 +00:00
pub mod crypto ;
2019-12-04 08:32:35 +00:00
pub mod device ;
2020-09-24 17:48:13 +00:00
pub mod error ;
pub mod messages ;
2019-12-04 08:32:35 +00:00
pub mod net ;
2020-09-24 17:48:13 +00:00
pub mod payload ;
2016-06-30 08:05:37 +00:00
pub mod poll ;
2016-08-10 09:34:13 +00:00
pub mod port_forwarding ;
2020-09-24 17:48:13 +00:00
pub mod table ;
2019-01-09 16:45:12 +00:00
pub mod traffic ;
2019-12-04 08:32:35 +00:00
pub mod types ;
2015-11-19 15:34:20 +00:00
2020-05-29 06:37:29 +00:00
use structopt ::StructOpt ;
2015-11-19 15:34:20 +00:00
2019-12-04 08:32:35 +00:00
use std ::{
2019-12-06 08:55:24 +00:00
fs ::{ self , File , Permissions } ,
2019-12-04 08:32:35 +00:00
io ::{ self , Write } ,
2020-09-24 17:48:13 +00:00
net ::{ Ipv4Addr , UdpSocket } ,
2019-12-06 08:55:24 +00:00
os ::unix ::fs ::PermissionsExt ,
2019-12-04 08:32:35 +00:00
path ::Path ,
process ::Command ,
str ::FromStr ,
2020-06-03 13:49:06 +00:00
sync ::Mutex ,
thread
2019-12-04 08:32:35 +00:00
} ;
2015-11-20 17:40:23 +00:00
2019-12-04 08:32:35 +00:00
use crate ::{
cloud ::GenericCloud ,
2020-09-24 17:48:13 +00:00
config ::{ Args , Config } ,
crypto ::Crypto ,
2019-12-04 08:32:35 +00:00
device ::{ Device , TunTapDevice , Type } ,
port_forwarding ::PortForwarding ,
2020-09-24 17:48:13 +00:00
payload ::Protocol ,
util ::SystemTimeSource
2019-12-04 08:32:35 +00:00
} ;
2016-08-08 07:34:13 +00:00
2016-11-23 14:21:22 +00:00
struct DualLogger {
file : Mutex < Option < File > >
}
impl DualLogger {
pub fn new < P : AsRef < Path > > ( path : Option < P > ) -> Result < Self , io ::Error > {
if let Some ( path ) = path {
2019-12-19 15:08:51 +00:00
let path = path . as_ref ( ) ;
if path . exists ( ) {
fs ::remove_file ( path ) ?
}
2019-03-01 22:12:19 +00:00
let file = File ::create ( path ) ? ;
2019-12-04 08:32:35 +00:00
Ok ( DualLogger { file : Mutex ::new ( Some ( file ) ) } )
2016-11-23 14:21:22 +00:00
} else {
2019-12-04 08:32:35 +00:00
Ok ( DualLogger { file : Mutex ::new ( None ) } )
2016-11-23 14:21:22 +00:00
}
}
}
2015-11-19 15:34:20 +00:00
2016-11-23 14:21:22 +00:00
impl log ::Log for DualLogger {
2016-06-11 14:08:57 +00:00
#[ inline ]
2019-01-01 23:35:14 +00:00
fn enabled ( & self , _metadata : & log ::Metadata ) -> bool {
2015-11-19 15:34:20 +00:00
true
}
2016-06-11 14:08:57 +00:00
#[ inline ]
2019-01-01 23:35:14 +00:00
fn log ( & self , record : & log ::Record ) {
2015-11-19 15:34:20 +00:00
if self . enabled ( record . metadata ( ) ) {
2016-11-23 14:21:22 +00:00
println! ( " {} - {} " , record . level ( ) , record . args ( ) ) ;
let mut file = self . file . lock ( ) . expect ( " Lock poisoned " ) ;
2016-11-25 06:15:19 +00:00
if let Some ( ref mut file ) = * file {
2020-04-17 20:35:24 +00:00
let time = time ::OffsetDateTime ::now_local ( ) . format ( " %F %T " ) ;
2019-12-04 08:32:35 +00:00
writeln! ( file , " {} - {} - {} " , time , record . level ( ) , record . args ( ) )
. expect ( " Failed to write to logfile " ) ;
2016-11-23 14:21:22 +00:00
}
2015-11-19 15:34:20 +00:00
}
}
2019-01-01 23:35:14 +00:00
#[ inline ]
fn flush ( & self ) {
let mut file = self . file . lock ( ) . expect ( " Lock poisoned " ) ;
if let Some ( ref mut file ) = * file {
try_fail! ( file . flush ( ) , " Logging error: {} " ) ;
}
}
2015-11-19 15:34:20 +00:00
}
2017-05-04 05:26:21 +00:00
fn run_script ( script : & str , ifname : & str ) {
2015-11-23 18:06:25 +00:00
let mut cmd = Command ::new ( " sh " ) ;
cmd . arg ( " -c " ) . arg ( & script ) . env ( " IFNAME " , ifname ) ;
debug! ( " Running script: {:?} " , cmd ) ;
match cmd . status ( ) {
2019-12-04 08:32:35 +00:00
Ok ( status ) = > {
if ! status . success ( ) {
error! ( " Script returned with error: {:?} " , status . code ( ) )
}
}
2015-11-23 18:06:25 +00:00
Err ( e ) = > error! ( " Failed to execute script {:?}: {} " , script , e )
}
2015-11-19 15:34:20 +00:00
}
2020-09-24 17:48:13 +00:00
fn parse_ip_netmask ( addr : & str ) -> Result < ( Ipv4Addr , Ipv4Addr ) , String > {
let ( ip_str , len_str ) = match addr . find ( '/' ) {
Some ( pos ) = > ( & addr [ .. pos ] , & addr [ pos + 1 .. ] ) ,
None = > ( addr , " 24 " )
} ;
let prefix_len = u8 ::from_str ( len_str ) . map_err ( | _ | format! ( " Invalid prefix length: {} " , len_str ) ) ? ;
if prefix_len > 32 {
return Err ( format! ( " Invalid prefix length: {} " , prefix_len ) )
}
let ip = Ipv4Addr ::from_str ( ip_str ) . map_err ( | _ | format! ( " Invalid ip address: {} " , ip_str ) ) ? ;
let netmask = Ipv4Addr ::from ( u32 ::max_value ( ) . checked_shl ( 32 - prefix_len as u32 ) . unwrap ( ) ) ;
Ok ( ( ip , netmask ) )
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
fn setup_device ( config : & Config ) -> TunTapDevice {
let device = try_fail! (
TunTapDevice ::new ( & config . device_name , config . device_type , config . device_path . as_ref ( ) . map ( | s | s as & str ) ) ,
" Failed to open virtual {} interface {}: {} " ,
config . device_type ,
config . device_name
) ;
info! ( " Opened device {} " , device . ifname ( ) ) ;
if let Err ( err ) = device . set_mtu ( None ) {
error! ( " Error setting optimal MTU on {}: {} " , device . ifname ( ) , err ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if let Some ( ip ) = & config . ip {
let ( ip , netmask ) = try_fail! ( parse_ip_netmask ( ip ) , " Invalid ip address given: {} " ) ;
info! ( " Configuring device with ip {}, netmask {} " , ip , netmask ) ;
try_fail! ( device . configure ( ip , netmask ) , " Failed to configure device: {} " ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if let Some ( script ) = & config . ifup {
run_script ( script , device . ifname ( ) ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if config . fix_rp_filter {
try_fail! ( device . fix_rp_filter ( ) , " Failed to change rp_filter settings: {} " ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if let Ok ( val ) = device . get_rp_filter ( ) {
if val ! = 1 {
warn! ( " Your networking configuration might be affected by a vulnerability (https://seclists.org/oss-sec/2019/q4/122), please change your rp_filter setting to 1 (currently {}). " , val ) ;
2017-05-04 06:22:24 +00:00
}
}
2020-09-24 17:48:13 +00:00
device
2017-05-04 06:22:24 +00:00
}
2019-12-20 12:54:58 +00:00
#[ allow(clippy::cognitive_complexity) ]
2019-12-04 08:32:35 +00:00
fn run < P : Protocol > ( config : Config ) {
2020-09-24 17:48:13 +00:00
let device = setup_device ( & config ) ;
2020-02-20 15:25:35 +00:00
let port_forwarding = if config . port_forwarding { PortForwarding ::new ( config . listen . port ( ) ) } else { None } ;
2019-12-06 08:55:24 +00:00
let stats_file = match config . stats_file {
None = > None ,
Some ( ref name ) = > {
2019-12-19 15:08:51 +00:00
let path = Path ::new ( name ) ;
if path . exists ( ) {
try_fail! ( fs ::remove_file ( path ) , " Failed to remove file {}: {} " , name ) ;
}
2019-12-06 08:55:24 +00:00
let file = try_fail! ( File ::create ( name ) , " Failed to create stats file: {} " ) ;
try_fail! (
fs ::set_permissions ( name , Permissions ::from_mode ( 0o644 ) ) ,
" Failed to set permissions on stats file: {} "
) ;
Some ( file )
}
} ;
let mut cloud =
2020-09-24 17:48:13 +00:00
GenericCloud ::< TunTapDevice , P , UdpSocket , SystemTimeSource > ::new ( & config , device , port_forwarding , stats_file ) ;
2016-08-08 14:30:22 +00:00
for addr in config . peers {
2016-05-11 08:54:00 +00:00
try_fail! ( cloud . connect ( & addr as & str ) , " Failed to send message to {}: {} " , & addr ) ;
cloud . add_reconnect_peer ( addr ) ;
2015-11-25 11:29:12 +00:00
}
2016-11-23 14:21:22 +00:00
if config . daemonize {
info! ( " Running process as daemon " ) ;
let mut daemonize = daemonize ::Daemonize ::new ( ) ;
if let Some ( user ) = config . user {
daemonize = daemonize . user ( & user as & str ) ;
}
if let Some ( group ) = config . group {
daemonize = daemonize . group ( & group as & str ) ;
}
if let Some ( pid_file ) = config . pid_file {
daemonize = daemonize . pid_file ( pid_file ) . chown_pid_file ( true ) ;
2020-06-03 13:49:06 +00:00
// Give child process some time to write PID file
daemonize = daemonize . exit_action ( | | thread ::sleep ( std ::time ::Duration ::from_millis ( 10 ) ) ) ;
2016-11-23 14:21:22 +00:00
}
try_fail! ( daemonize . start ( ) , " Failed to daemonize: {} " ) ;
2019-12-06 07:54:27 +00:00
} else if config . user . is_some ( ) | | config . group . is_some ( ) {
info! ( " Dropping privileges " ) ;
let mut pd = privdrop ::PrivDrop ::default ( ) ;
if let Some ( user ) = config . user {
pd = pd . user ( user ) ;
}
if let Some ( group ) = config . group {
pd = pd . group ( group ) ;
}
try_fail! ( pd . apply ( ) , " Failed to drop privileges: {} " ) ;
2016-11-23 14:21:22 +00:00
}
2015-11-25 11:29:12 +00:00
cloud . run ( ) ;
2016-08-08 14:30:22 +00:00
if let Some ( script ) = config . ifdown {
2017-05-04 05:26:21 +00:00
run_script ( & script , cloud . ifname ( ) ) ;
2015-11-25 11:29:12 +00:00
}
}
fn main ( ) {
2020-05-29 06:37:29 +00:00
let args : Args = Args ::from_args ( ) ;
if args . version {
2020-09-24 17:48:13 +00:00
println! ( " VpnCloud v {} " , env! ( " CARGO_PKG_VERSION " ) ) ;
return
}
if args . genkey {
2020-09-28 10:50:08 +00:00
let ( privkey , pubkey ) = Crypto ::generate_keypair ( args . password . as_deref ( ) ) ;
2020-09-24 17:48:13 +00:00
println! ( " Private key: {} \n Public key: {} \n " , privkey , pubkey ) ;
println! (
" Attention: Keep the private key secret and use only the public key on other nodes to establish trust. "
) ;
2019-12-04 08:32:35 +00:00
return
2015-11-25 11:29:12 +00:00
}
2020-05-29 06:37:29 +00:00
let logger = try_fail! ( DualLogger ::new ( args . log_file . as_ref ( ) ) , " Failed to open logfile: {} " ) ;
2019-01-01 23:35:14 +00:00
log ::set_boxed_logger ( Box ::new ( logger ) ) . unwrap ( ) ;
2020-05-29 06:37:29 +00:00
assert! ( ! args . verbose | | ! args . quiet ) ;
log ::set_max_level ( if args . verbose {
2019-12-04 08:32:35 +00:00
log ::LevelFilter ::Debug
2020-05-29 06:37:29 +00:00
} else if args . quiet {
2019-12-04 08:32:35 +00:00
log ::LevelFilter ::Error
} else {
log ::LevelFilter ::Info
} ) ;
2016-08-08 14:30:22 +00:00
let mut config = Config ::default ( ) ;
2020-05-29 06:37:29 +00:00
if let Some ( ref file ) = args . config {
2016-08-08 14:30:22 +00:00
info! ( " Reading config file '{}' " , file ) ;
2017-07-22 14:34:20 +00:00
let f = try_fail! ( File ::open ( file ) , " Failed to open config file: {:?} " ) ;
let config_file = try_fail! ( serde_yaml ::from_reader ( f ) , " Failed to load config file: {:?} " ) ;
2016-08-08 14:30:22 +00:00
config . merge_file ( config_file )
}
config . merge_args ( args ) ;
debug! ( " Config: {:?} " , config ) ;
match config . device_type {
2020-09-24 17:48:13 +00:00
Type ::Tap = > run ::< payload ::Frame > ( config ) ,
Type ::Tun = > run ::< payload ::Packet > ( config ) ,
Type ::Dummy = > run ::< payload ::Frame > ( config )
2015-11-25 11:29:12 +00:00
}
2015-11-22 19:02:02 +00:00
}