diff options
| author | Emīls <emils@mullvad.net> | 2020-02-20 16:22:13 +0000 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2020-02-20 16:22:13 +0000 |
| commit | 98adb9af255d9c8ee12f5f296bba5a9f973775a3 (patch) | |
| tree | 442a12a69703b8925a1ec92fcb9c08bef63a156a | |
| parent | 59cc56cbc161bf21e1964ebadbcf73334a4bd71a (diff) | |
| parent | aadc0020d5bc239374de86353e6812cb9bdd9441 (diff) | |
| download | mullvadvpn-98adb9af255d9c8ee12f5f296bba5a9f973775a3.tar.xz mullvadvpn-98adb9af255d9c8ee12f5f296bba5a9f973775a3.zip | |
Merge branch 'cleanup-talpid-core-wireguard'
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 1 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/config.rs | 19 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/connectivity_check.rs | 22 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/logging.rs | 13 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/mod.rs | 158 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/stats.rs | 1 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/wireguard_go.rs | 39 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 14 |
8 files changed, 159 insertions, 108 deletions
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 5a0d87452a..22da520ce0 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -15,6 +15,7 @@ use talpid_types::net::{wireguard as wireguard_types, TunnelParameters}; #[cfg(not(target_os = "android"))] pub mod openvpn; +/// A module for all WireGuard related tunnel management. pub mod wireguard; /// A module for low level platform specific tunnel device management. diff --git a/talpid-core/src/tunnel/wireguard/config.rs b/talpid-core/src/tunnel/wireguard/config.rs index e433483a55..62c8d18144 100644 --- a/talpid-core/src/tunnel/wireguard/config.rs +++ b/talpid-core/src/tunnel/wireguard/config.rs @@ -5,11 +5,17 @@ use std::{ }; use talpid_types::net::{wireguard, GenericTunnelOptions}; +/// Config required to set up a single WireGuard tunnel pub struct Config { + /// Contains tunnel endpoint specific config pub tunnel: wireguard::TunnelConfig, + /// List of peer configurations pub peers: Vec<wireguard::PeerConfig>, + /// IPv4 gateway pub ipv4_gateway: Ipv4Addr, + /// IPv6 gateway pub ipv6_gateway: Option<Ipv6Addr>, + /// Maximum transmission unit for the tunnel pub mtu: u16, } @@ -17,19 +23,24 @@ pub struct Config { const SMALLEST_IPV6_MTU: u16 = 1380; const DEFAULT_MTU: u16 = SMALLEST_IPV6_MTU; +/// Configuration errors #[derive(err_derive::Error, Debug)] pub enum Error { + /// Supplied parameters don't contain a valid tunnel IP #[error(display = "No valid tunnel IP")] InvalidTunnelIpError, + /// Peer has no valid IPs #[error(display = "Supplied peer has no valid IPs")] InvalidPeerIpError, + /// Parameters don't contain any peers #[error(display = "No peers supplied")] NoPeersSuppliedError, } impl Config { + /// Constructs a Config from parameters pub fn from_parameters(params: &wireguard::TunnelParameters) -> Result<Config, Error> { let tunnel = params.connection.tunnel.clone(); let peer = vec![params.connection.peer.clone()]; @@ -42,6 +53,7 @@ impl Config { ) } + /// Constructs a new Config struct pub fn new( mut tunnel: wireguard::TunnelConfig, mut peers: Vec<wireguard::PeerConfig>, @@ -90,7 +102,8 @@ impl Config { }) } - // should probably take a flag that alters between additive and overwriting conf + /// Returns a CString with the appropriate config for WireGuard-go + // TODO: Consider outputting both overriding and additive configs pub fn to_userspace_format(&self) -> CString { // the order of insertion matters, public key entry denotes a new peer entry let mut wg_conf = WgConfigBuffer::new(); @@ -115,7 +128,7 @@ impl Config { } } -pub enum ConfValue<'a> { +enum ConfValue<'a> { String(&'a str), Bytes(&'a [u8]), } @@ -142,7 +155,7 @@ impl<'a> ConfValue<'a> { } } -pub struct WgConfigBuffer { +struct WgConfigBuffer { buf: Vec<u8>, } diff --git a/talpid-core/src/tunnel/wireguard/connectivity_check.rs b/talpid-core/src/tunnel/wireguard/connectivity_check.rs index 8b6e03c04a..63f296d2e3 100644 --- a/talpid-core/src/tunnel/wireguard/connectivity_check.rs +++ b/talpid-core/src/tunnel/wireguard/connectivity_check.rs @@ -5,7 +5,7 @@ use std::{ time::{Duration, Instant}, }; -use super::{Error, Tunnel}; +use super::{Tunnel, TunnelError}; /// Sleep time used when initially establishing connectivity const DELAY_ON_INITIAL_SETUP: Duration = Duration::from_millis(50); @@ -26,6 +26,18 @@ const PING_TIMEOUT: Duration = Duration::from_secs(15); /// Number of seconds to wait between sending ICMP packets const SECONDS_PER_PING: Duration = Duration::from_secs(3); +/// Connectivity monitor errors +#[derive(err_derive::Error, Debug)] +pub enum Error { + /// Failed to read tunnel's configuration + #[error(display = "Failed to read tunnel's configuration")] + ConfigReadError(TunnelError), + + /// Failed to send ping + #[error(display = "Ping monitor failed")] + PingError(#[error(source)] crate::ping_monitor::Error), +} + /// Verifies if a connection to a tunnel is working. /// The connectivity monitor is biased to receiving traffic - it is expected that all outgoing @@ -60,7 +72,7 @@ pub struct ConnectivityMonitor { impl ConnectivityMonitor { - pub fn new( + pub(super) fn new( addr: Ipv4Addr, interface: String, tunnel_handle: Weak<Mutex<Option<Box<dyn Tunnel>>>>, @@ -82,7 +94,7 @@ impl ConnectivityMonitor { // checks if the tunnel has ever worked. Intended to check if a connection to a tunnel is // successfull at the start of a connection. - pub fn establish_connectivity(&mut self) -> Result<bool, Error> { + pub(super) fn establish_connectivity(&mut self) -> Result<bool, Error> { if self.conn_state.connected() { return Ok(true); } @@ -99,7 +111,7 @@ impl ConnectivityMonitor { Ok(false) } - pub fn run(&mut self) -> Result<(), Error> { + pub(super) fn run(&mut self) -> Result<(), Error> { self.wait_loop(REGULAR_LOOP_SLEEP) } @@ -144,7 +156,7 @@ impl ConnectivityMonitor { .lock() .ok()? .as_ref() - .map(|tunnel| tunnel.get_config()) + .map(|tunnel| tunnel.get_tunnel_stats().map_err(Error::ConfigReadError)) } fn maybe_send_ping(&mut self) -> Result<(), Error> { diff --git a/talpid-core/src/tunnel/wireguard/logging.rs b/talpid-core/src/tunnel/wireguard/logging.rs index 5d177441e3..1a122ddca8 100644 --- a/talpid-core/src/tunnel/wireguard/logging.rs +++ b/talpid-core/src/tunnel/wireguard/logging.rs @@ -1,4 +1,3 @@ -use super::{Error, Result}; use chrono; use parking_lot::Mutex; use std::{collections::HashMap, fs, io::Write, path::Path}; @@ -9,7 +8,15 @@ lazy_static::lazy_static! { static mut LOG_CONTEXT_NEXT_ORDINAL: u32 = 0; -pub fn initialize_logging(log_path: Option<&Path>) -> Result<u32> { +/// Errors encountered when initializing logging +#[derive(err_derive::Error, Debug)] +pub enum Error { + /// Failed to move or create a log file. + #[error(display = "Failed to setup a logging file")] + PrepareLogFileError(#[error(source)] std::io::Error), +} + +pub fn initialize_logging(log_path: Option<&Path>) -> Result<u32, Error> { let log_file = create_log_file(log_path)?; let log_context_ordinal = unsafe { @@ -29,7 +36,7 @@ static NULL_DEVICE: &str = "NUL"; #[cfg(not(target_os = "windows"))] static NULL_DEVICE: &str = "/dev/null"; -fn create_log_file(log_path: Option<&Path>) -> Result<fs::File> { +fn create_log_file(log_path: Option<&Path>) -> Result<fs::File, Error> { fs::File::create(log_path.unwrap_or(NULL_DEVICE.as_ref())).map_err(Error::PrepareLogFileError) } diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs index efb44ebbc6..7d081bd226 100644 --- a/talpid-core/src/tunnel/wireguard/mod.rs +++ b/talpid-core/src/tunnel/wireguard/mod.rs @@ -1,102 +1,44 @@ -#![allow(missing_docs)] - use self::config::Config; #[cfg(not(windows))] use super::tun_provider; use super::{tun_provider::TunProvider, TunnelEvent, TunnelMetadata}; -use crate::{ping_monitor, routing}; +use crate::routing; use std::{ collections::HashMap, - io, path::Path, sync::{mpsc, Arc, Mutex}, }; use talpid_types::ErrorExt; +/// WireGuard config data-types pub mod config; mod connectivity_check; mod logging; mod stats; -pub mod wireguard_go; +mod wireguard_go; -pub use self::wireguard_go::WgGoTunnel; +use self::wireguard_go::WgGoTunnel; -pub type Result<T> = std::result::Result<T, Error>; +type Result<T> = std::result::Result<T, Error>; /// Errors that can happen in the Wireguard tunnel monitor. #[derive(err_derive::Error, Debug)] -#[error(no_from)] pub enum Error { - /// Failed to setup a tunnel device. - #[cfg(not(windows))] - #[error(display = "Failed to create tunnel device")] - SetupTunnelDeviceError(#[error(source)] tun_provider::Error), - - /// A recoverable error occurred while starting the wireguard tunnel - /// - /// This is an error returned by wireguard-go that indicates that trying to establish the - /// tunnel again should work normally. The error encountered is known to be sporadic. - #[error(display = "Recoverable error while starting wireguard tunnel")] - RecoverableStartWireguardError, - - /// An unrecoverable error occurred while starting the wireguard tunnel - /// - /// This is an error returned by wireguard-go that indicates that trying to establish the - /// tunnel again will likely fail with the same error. An error was encountered during tunnel - /// configuration which can't be dealt with gracefully. - #[error(display = "Failed to start wireguard tunnel")] - FatalStartWireguardError, - - /// Failed to tear down wireguard tunnel. - #[error(display = "Failed to stop wireguard tunnel - {}", status)] - StopWireguardError { status: i32 }, - - /// Failed to get tunnel config - #[error(display = "Failed to obtain tunnel config")] - GetConfigError, - - /// Failed to set ip addresses on tunnel interface. - #[cfg(target_os = "windows")] - #[error(display = "Failed to set IP addresses on WireGuard interface")] - SetIpAddressesError, - /// Failed to set up routing. #[error(display = "Failed to setup routing")] SetupRoutingError(#[error(source)] crate::routing::Error), - /// Failed to move or craete a log file. - #[error(display = "Failed to setup a logging file")] - PrepareLogFileError(#[error(source)] io::Error), - - /// Invalid tunnel interface name. - #[error(display = "Invalid tunnel interface name")] - InterfaceNameError(#[error(source)] std::ffi::NulError), - - /// Failed to configure Wireguard sockets to bypass the tunnel. - #[cfg(target_os = "android")] - #[error(display = "Failed to configure Wireguard sockets to bypass the tunnel")] - BypassError(#[error(source)] tun_provider::Error), - - /// Failed to duplicate tunnel file descriptor for wireguard-go - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] - #[error(display = "Failed to duplicate tunnel file descriptor for wireguard-go")] - FdDuplicationError(#[error(source)] nix::Error), - - /// Error whilst trying to read stats - #[error(display = "Reading tunnel stats failed")] - StatsError(#[error(source)] stats::Error), - - /// Tunnel handle is invalid - #[error(display = "Tunnel handle is invalid")] - InvalidTunnelHandle, - - /// Pinging timed out. - #[error(display = "Ping timed out")] - PingError(#[error(source)] ping_monitor::Error), - /// Tunnel timed out #[error(display = "Tunnel timed out")] TimeoutError, + + /// An interaction with a tunnel failed + #[error(display = "Tunnel failed")] + TunnelError(#[error(source)] TunnelError), + + /// Failed to setup connectivity monitor + #[error(display = "Connectivity monitor failed")] + ConnectivityMonitorError(#[error(source)] connectivity_check::Error), } @@ -114,6 +56,7 @@ pub struct WireguardMonitor { } impl WireguardMonitor { + /// Starts a WireGuard tunnel with the given config pub fn start<F: Fn(TunnelEvent) + Send + Sync + Clone + 'static>( config: &Config, log_path: Option<&Path>, @@ -184,12 +127,14 @@ impl WireguardMonitor { Ok(monitor) } + /// Returns a close handle for the tunnel pub fn close_handle(&self) -> CloseHandle { CloseHandle { chan: self.close_msg_sender.clone(), } } + /// Blocks the current thread until tunnel disconnects pub fn wait(mut self) -> Result<()> { let wait_result = match self.close_msg_receiver.recv() { Ok(CloseMsg::PingErr) => Err(Error::TimeoutError), @@ -274,12 +219,14 @@ enum CloseMsg { PingErr, } +/// Close handle for a WireGuard tunnel. #[derive(Clone, Debug)] pub struct CloseHandle { chan: mpsc::Sender<CloseMsg>, } impl CloseHandle { + /// Closes a WireGuard tunnel pub fn close(&mut self) { if let Err(e) = self.chan.send(CloseMsg::Stop) { log::trace!("Failed to send close message to wireguard tunnel - {}", e); @@ -287,8 +234,71 @@ impl CloseHandle { } } -pub trait Tunnel: Send { +pub(crate) trait Tunnel: Send { fn get_interface_name(&self) -> &str; - fn stop(self: Box<Self>) -> Result<()>; - fn get_config(&self) -> Result<stats::Stats>; + fn stop(self: Box<Self>) -> std::result::Result<(), TunnelError>; + fn get_tunnel_stats(&self) -> std::result::Result<stats::Stats, TunnelError>; +} + +/// Errors to be returned from WireGuard implementations, namely implementers of the Tunnel trait +#[derive(err_derive::Error, Debug)] +#[error(no_from)] +pub enum TunnelError { + /// A recoverable error occurred while starting the wireguard tunnel + /// + /// This is an error returned by wireguard-go that indicates that trying to establish the + /// tunnel again should work normally. The error encountered is known to be sporadic. + #[error(display = "Recoverable error while starting wireguard tunnel")] + RecoverableStartWireguardError, + + /// An unrecoverable error occurred while starting the wireguard tunnel + /// + /// This is an error returned by wireguard-go that indicates that trying to establish the + /// tunnel again will likely fail with the same error. An error was encountered during tunnel + /// configuration which can't be dealt with gracefully. + #[error(display = "Failed to start wireguard tunnel")] + FatalStartWireguardError, + + /// Failed to tear down wireguard tunnel. + #[error(display = "Failed to stop wireguard tunnel - {}", status)] + StopWireguardError { + /// Returned error code + status: i32, + }, + + /// Error whilst trying to parse the WireGuard config to read the stats + #[error(display = "Reading tunnel stats failed")] + StatsError(#[error(source)] stats::Error), + + /// Error whilst trying to retrieve config of a WireGuard tunnel + #[error(display = "Failed to get config of WireGuard tunnel")] + GetConfigError, + + /// Failed to duplicate tunnel file descriptor for wireguard-go + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] + #[error(display = "Failed to duplicate tunnel file descriptor for wireguard-go")] + FdDuplicationError(#[error(source)] nix::Error), + + /// Failed to setup a tunnel device. + #[cfg(not(windows))] + #[error(display = "Failed to create tunnel device")] + SetupTunnelDeviceError(#[error(source)] tun_provider::Error), + + /// Failed to configure Wireguard sockets to bypass the tunnel. + #[cfg(target_os = "android")] + #[error(display = "Failed to configure Wireguard sockets to bypass the tunnel")] + BypassError(#[error(source)] tun_provider::Error), + + /// Invalid tunnel interface name. + #[error(display = "Invalid tunnel interface name")] + InterfaceNameError(#[error(source)] std::ffi::NulError), + + /// Failed to set ip addresses on tunnel interface. + #[cfg(target_os = "windows")] + #[error(display = "Failed to set IP addresses on WireGuard interface")] + SetIpAddressesError, + + /// Failure to set up logging + #[error(display = "Failed to set up logging")] + LoggingError(#[error(source)] logging::Error), } diff --git a/talpid-core/src/tunnel/wireguard/stats.rs b/talpid-core/src/tunnel/wireguard/stats.rs index 545a49d688..474a096ada 100644 --- a/talpid-core/src/tunnel/wireguard/stats.rs +++ b/talpid-core/src/tunnel/wireguard/stats.rs @@ -1,5 +1,4 @@ #[derive(err_derive::Error, Debug, PartialEq)] -#[error(no_from)] pub enum Error { #[error(display = "Failed to parse integer from string \"_0\"")] IntParseError(String, #[error(source)] std::num::ParseIntError), diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-core/src/tunnel/wireguard/wireguard_go.rs index eb657a04aa..058735536a 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs +++ b/talpid-core/src/tunnel/wireguard/wireguard_go.rs @@ -1,4 +1,4 @@ -use super::{stats::Stats, Config, Error, Result, Tunnel}; +use super::{stats::Stats, Config, Tunnel, TunnelError}; use crate::tunnel::{ tun_provider::TunProvider, wireguard::logging::{clean_up_logging, initialize_logging, logging_callback, WgLogLevel}, @@ -26,6 +26,8 @@ use { }, }; +type Result<T> = std::result::Result<T, TunnelError>; + #[cfg(target_os = "windows")] use crate::winnet::{self, add_device_ip_addresses}; @@ -63,7 +65,9 @@ impl WgGoTunnel { let (mut tunnel_device, tunnel_fd) = Self::get_tunnel(tun_provider, config, routes)?; let interface_name: String = tunnel_device.interface_name().to_string(); let wg_config_str = config.to_userspace_format(); - let logging_context = initialize_logging(log_path).map(LoggingContext)?; + let logging_context = initialize_logging(log_path) + .map(LoggingContext) + .map_err(TunnelError::LoggingError)?; #[cfg(not(target_os = "android"))] let handle = unsafe { @@ -89,14 +93,15 @@ impl WgGoTunnel { if handle < 0 { // Error values returned from the wireguard-go library return match handle { - -1 => Err(Error::FatalStartWireguardError), - -2 => Err(Error::RecoverableStartWireguardError), + -1 => Err(TunnelError::FatalStartWireguardError), + -2 => Err(TunnelError::RecoverableStartWireguardError), _ => unreachable!("Unknown status code returned from wireguard-go"), }; } #[cfg(target_os = "android")] - Self::bypass_tunnel_sockets(&mut tunnel_device, handle).map_err(Error::BypassError)?; + Self::bypass_tunnel_sockets(&mut tunnel_device, handle) + .map_err(TunnelError::BypassError)?; Ok(WgGoTunnel { interface_name, @@ -116,8 +121,10 @@ impl WgGoTunnel { let wg_config_str = config.to_userspace_format(); let iface_name: String = "wg-mullvad".to_string(); let cstr_iface_name = - CString::new(iface_name.as_bytes()).map_err(Error::InterfaceNameError)?; - let logging_context = initialize_logging(log_path).map(LoggingContext)?; + CString::new(iface_name.as_bytes()).map_err(TunnelError::InterfaceNameError)?; + let logging_context = initialize_logging(log_path) + .map(LoggingContext) + .map_err(TunnelError::LoggingError)?; let handle = unsafe { wgTurnOn( @@ -130,12 +137,12 @@ impl WgGoTunnel { }; if handle < 0 { - return Err(Error::FatalStartWireguardError); + return Err(TunnelError::FatalStartWireguardError); } if !add_device_ip_addresses(&iface_name, &config.tunnel.addresses) { // Todo: what kind of clean-up is required? - return Err(Error::SetIpAddressesError); + return Err(TunnelError::SetIpAddressesError); } Ok(WgGoTunnel { @@ -224,7 +231,7 @@ impl WgGoTunnel { if let Some(handle) = self.handle.take() { let status = unsafe { wgTurnOff(handle) }; if status < 0 { - return Err(Error::StopWireguardError { status }); + return Err(TunnelError::StopWireguardError { status }); } } Ok(()) @@ -242,18 +249,18 @@ impl WgGoTunnel { for _ in 1..=MAX_PREPARE_TUN_ATTEMPTS { let tunnel_device = tun_provider .get_tun(tunnel_config.clone()) - .map_err(Error::SetupTunnelDeviceError)?; + .map_err(TunnelError::SetupTunnelDeviceError)?; match nix::unistd::dup(tunnel_device.as_raw_fd()) { Ok(fd) => return Ok((tunnel_device, fd)), #[cfg(not(target_os = "macos"))] Err(error @ nix::Error::Sys(nix::errno::Errno::EBADFD)) => last_error = Some(error), Err(error @ nix::Error::Sys(nix::errno::Errno::EBADF)) => last_error = Some(error), - Err(error) => return Err(Error::FdDuplicationError(error)), + Err(error) => return Err(TunnelError::FdDuplicationError(error)), } } - Err(Error::FdDuplicationError( + Err(TunnelError::FdDuplicationError( last_error.expect("Should be collected in loop"), )) } @@ -272,12 +279,12 @@ impl Tunnel for WgGoTunnel { &self.interface_name } - fn get_config(&self) -> Result<Stats> { + fn get_tunnel_stats(&self) -> Result<Stats> { let config_str = unsafe { let ptr = wgGetConfig(self.handle.unwrap()); if ptr.is_null() { log::error!("Failed to get config !"); - return Err(Error::GetConfigError); + return Err(TunnelError::GetConfigError); } CStr::from_ptr(ptr) @@ -285,7 +292,7 @@ impl Tunnel for WgGoTunnel { let result = Stats::parse_config_str(config_str.to_str().expect("Go strings are always UTF-8")) - .map_err(Error::StatsError); + .map_err(TunnelError::StatsError); unsafe { // Zeroing out config string to not leave private key in memory. let slice = std::slice::from_raw_parts_mut( diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 848b2c7485..77608c4c3d 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -295,16 +295,18 @@ fn get_openvpn_proxy_settings( } fn should_retry(error: &tunnel::Error) -> bool { + #[cfg(not(windows))] + use tunnel::wireguard::{Error, TunnelError}; match error { #[cfg(not(windows))] - tunnel::Error::WireguardTunnelMonitoringError( - tunnel::wireguard::Error::RecoverableStartWireguardError, - ) => true, + tunnel::Error::WireguardTunnelMonitoringError(Error::TunnelError( + TunnelError::RecoverableStartWireguardError, + )) => true, #[cfg(target_os = "android")] - tunnel::Error::WireguardTunnelMonitoringError(tunnel::wireguard::Error::BypassError(_)) => { - true - } + tunnel::Error::WireguardTunnelMonitoringError(Error::TunnelError( + TunnelError::BypassError(_), + )) => true, _ => false, } |
