diff options
| author | David Lönnhager <david.l@mullvad.net> | 2024-01-31 11:03:52 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2024-01-31 11:03:52 +0100 |
| commit | 962a55443b91c401f3ccbe197fd06aac7d3640aa (patch) | |
| tree | 1efbe615ca7d6c0ea336ff940dca971ca6003351 | |
| parent | 21d1883687ed32f5abe60de062eb83ae31f97163 (diff) | |
| parent | 8e78ab6fb04125b6424c825bee9c5c6e5f155991 (diff) | |
| download | mullvadvpn-962a55443b91c401f3ccbe197fd06aac7d3640aa.tar.xz mullvadvpn-962a55443b91c401f3ccbe197fd06aac7d3640aa.zip | |
Merge branch 'detect-driver-conflict' into main
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 6 | ||||
| -rw-r--r-- | gui/src/shared/daemon-rpc-types.ts | 6 | ||||
| -rw-r--r-- | gui/src/shared/notifications/error.ts | 9 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 11 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/states.rs | 23 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 27 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 71 | ||||
| -rw-r--r-- | talpid-openvpn/src/lib.rs | 22 | ||||
| -rw-r--r-- | talpid-routing/src/unix/mod.rs | 19 | ||||
| -rw-r--r-- | talpid-routing/src/windows/mod.rs | 7 | ||||
| -rw-r--r-- | talpid-types/src/tunnel.rs | 11 | ||||
| -rw-r--r-- | talpid-wireguard/src/lib.rs | 39 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_go.rs | 2 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_nt.rs | 2 |
14 files changed, 187 insertions, 68 deletions
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index f93258a589..a14db54b52 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -1029,6 +1029,12 @@ function convertFromTunnelStateError(state: grpcTypes.ErrorState.AsObject): Erro ...baseError, cause: ErrorStateCause.startTunnelError, }; + case grpcTypes.ErrorState.Cause.CREATE_TUNNEL_DEVICE: + return { + ...baseError, + cause: ErrorStateCause.createTunnelDeviceError, + osError: state.createTunnelError, + }; case grpcTypes.ErrorState.Cause.SPLIT_TUNNEL_ERROR: return { ...baseError, diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index b409a6e835..71f6936804 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -44,6 +44,7 @@ export enum ErrorStateCause { setFirewallPolicyError, setDnsError, startTunnelError, + createTunnelDeviceError, tunnelParameterError, isOffline, splitTunnelError, @@ -79,6 +80,11 @@ export type ErrorState = authFailedError: AuthFailedError; } | { + cause: ErrorStateCause.createTunnelDeviceError; + blockingError?: FirewallPolicyError; + osError?: number; + } + | { cause: ErrorStateCause.tunnelParameterError; blockingError?: FirewallPolicyError; parameterError: TunnelParameterError; diff --git a/gui/src/shared/notifications/error.ts b/gui/src/shared/notifications/error.ts index 8b65a1e86d..44c28a092c 100644 --- a/gui/src/shared/notifications/error.ts +++ b/gui/src/shared/notifications/error.ts @@ -163,6 +163,15 @@ function getMessage(errorState: ErrorState): string { 'notifications', 'Unable to start tunnel connection. Please send a problem report.', ); + case ErrorStateCause.createTunnelDeviceError: + if (errorState.osError === 4319) { + // TODO: add improved error for network device conflicts + } + // TODO: 'Failed to create tunnel device. Please send a problem report.', + return messages.pgettext( + 'notifications', + 'Unable to start tunnel connection. Please send a problem report.', + ); case ErrorStateCause.tunnelParameterError: return getTunnelParameterMessage(errorState.parameterError); case ErrorStateCause.isOffline: diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 7f2ad07b43..21de18df8e 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -134,10 +134,11 @@ message ErrorState { SET_FIREWALL_POLICY_ERROR = 2; SET_DNS_ERROR = 3; START_TUNNEL_ERROR = 4; - TUNNEL_PARAMETER_ERROR = 5; - IS_OFFLINE = 6; - VPN_PERMISSION_DENIED = 7; - SPLIT_TUNNEL_ERROR = 8; + CREATE_TUNNEL_DEVICE = 5; + TUNNEL_PARAMETER_ERROR = 6; + IS_OFFLINE = 7; + VPN_PERMISSION_DENIED = 8; + SPLIT_TUNNEL_ERROR = 9; } enum AuthFailedError { @@ -175,6 +176,8 @@ message ErrorState { GenerationError parameter_error = 4; // SET_FIREWALL_POLICY_ERROR FirewallPolicyError policy_error = 5; + // CREATE_TUNNEL_DEVICE + optional int32 create_tunnel_error = 6; } message TunnelState { diff --git a/mullvad-management-interface/src/types/conversions/states.rs b/mullvad-management-interface/src/types/conversions/states.rs index 17329193c9..f6e41f4d87 100644 --- a/mullvad-management-interface/src/types/conversions/states.rs +++ b/mullvad-management-interface/src/types/conversions/states.rs @@ -89,6 +89,10 @@ impl From<mullvad_types::states::TunnelState> for proto::TunnelState { talpid_tunnel::ErrorStateCause::StartTunnelError => { i32::from(Cause::StartTunnelError) } + #[cfg(target_os = "windows")] + talpid_tunnel::ErrorStateCause::CreateTunnelDevice { os_error: _ } => { + i32::from(Cause::CreateTunnelDevice) + } talpid_tunnel::ErrorStateCause::TunnelParameterError(_) => { i32::from(Cause::TunnelParameterError) } @@ -142,6 +146,15 @@ impl From<mullvad_types::states::TunnelState> for proto::TunnelState { } else { None }, + #[cfg(not(target_os = "windows"))] + create_tunnel_error: None, + #[cfg(target_os = "windows")] + create_tunnel_error: match error_state.cause() { + talpid_tunnel::ErrorStateCause::CreateTunnelDevice { os_error } => { + *os_error + } + _ => None, + }, }), }) } @@ -254,8 +267,12 @@ impl TryFrom<proto::TunnelState> for mullvad_types::states::TunnelState { auth_failed_error, parameter_error, policy_error, + create_tunnel_error, }), })) => { + #[cfg(not(target_os = "windows"))] + let _ = create_tunnel_error; + let cause = match proto::error_state::Cause::try_from(cause) { Ok(proto::error_state::Cause::AuthFailed) => { let auth_failed = try_auth_failed_from_i32(auth_failed_error)?; @@ -286,6 +303,12 @@ impl TryFrom<proto::TunnelState> for mullvad_types::states::TunnelState { Ok(proto::error_state::Cause::StartTunnelError) => { talpid_tunnel::ErrorStateCause::StartTunnelError } + #[cfg(target_os = "windows")] + Ok(proto::error_state::Cause::CreateTunnelDevice) => { + talpid_tunnel::ErrorStateCause::CreateTunnelDevice { + os_error: create_tunnel_error, + } + } Ok(proto::error_state::Cause::TunnelParameterError) => { let parameter_error = match proto::error_state::GenerationError::try_from(parameter_error) { Ok(proto::error_state::GenerationError::CustomTunnelHostResolutionError) => talpid_tunnel::ParameterGenerationError::CustomTunnelHostResultionError, diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 3d953e965a..3e6df61f76 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -27,11 +27,6 @@ pub enum Error { #[error(display = "Can't enable IPv6 on tunnel interface because IPv6 is disabled")] EnableIpv6Error, - /// Failure in Windows syscall. - #[cfg(windows)] - #[error(display = "Failure in Windows syscall")] - WinnetError(#[error(source)] talpid_routing::Error), - /// Running on an operating system which is not supported yet. #[error(display = "Tunnel type not supported on this operating system")] UnsupportedPlatform, @@ -58,6 +53,28 @@ pub enum Error { AssignMtuError, } +impl Error { + /// Return whether retrying the operation that caused this error is likely to succeed. + pub fn is_recoverable(&self) -> bool { + match self { + Error::WireguardTunnelMonitoringError(error) => error.is_recoverable(), + #[cfg(not(target_os = "android"))] + Error::OpenVpnTunnelMonitoringError(error) => error.is_recoverable(), + _ => false, + } + } + + /// Get the inner tunnel device error, if there is one + #[cfg(target_os = "windows")] + pub fn get_tunnel_device_error(&self) -> Option<&std::io::Error> { + match self { + Error::WireguardTunnelMonitoringError(error) => error.get_tunnel_device_error(), + Error::OpenVpnTunnelMonitoringError(error) => error.get_tunnel_device_error(), + _ => None, + } + } +} + /// Abstraction for monitoring a generic VPN tunnel. pub struct TunnelMonitor { monitor: InternalTunnelMonitor, diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 75f3d8eebf..3b0afd8abf 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -37,7 +37,7 @@ pub(crate) type TunnelCloseEvent = Fuse<oneshot::Receiver<Option<ErrorStateCause const MAX_ATTEMPTS_WITH_SAME_TUN: u32 = 5; const MIN_TUNNEL_ALIVE_TIME: Duration = Duration::from_millis(1000); #[cfg(target_os = "windows")] -const MAX_RECOVERABLE_FAIL_RETRIES: u32 = 4; +const MAX_ATTEMPT_CREATE_TUN: u32 = 4; const INITIAL_ALLOWED_TUNNEL_TRAFFIC: AllowedTunnelTraffic = AllowedTunnelTraffic::None; @@ -265,7 +265,7 @@ impl ConnectingState { #[cfg(target_os = "android")] tunnel::Error::WireguardTunnelMonitoringError( talpid_wireguard::Error::TunnelError( - talpid_wireguard::TunnelError::SetupTunnelDeviceError( + talpid_wireguard::TunnelError::SetupTunnelDevice( tun_provider::Error::PermissionDenied, ), ), @@ -273,11 +273,19 @@ impl ConnectingState { #[cfg(target_os = "android")] tunnel::Error::WireguardTunnelMonitoringError( talpid_wireguard::Error::TunnelError( - talpid_wireguard::TunnelError::SetupTunnelDeviceError( + talpid_wireguard::TunnelError::SetupTunnelDevice( tun_provider::Error::InvalidDnsServers(addresses), ), ), ) => ErrorStateCause::InvalidDnsServers(addresses), + #[cfg(target_os = "windows")] + error => match error.get_tunnel_device_error() { + Some(error) => ErrorStateCause::CreateTunnelDevice { + os_error: error.raw_os_error(), + }, + None => ErrorStateCause::StartTunnelError, + }, + #[cfg(not(target_os = "windows"))] _ => ErrorStateCause::StartTunnelError, }; Some(block_reason) @@ -580,60 +588,11 @@ impl ConnectingState { #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] fn should_retry(error: &tunnel::Error, retry_attempt: u32) -> bool { - use talpid_wireguard::{Error, TunnelError}; - - match error { - tunnel::Error::WireguardTunnelMonitoringError(Error::CreateObfuscatorError(_)) => true, - - tunnel::Error::WireguardTunnelMonitoringError(Error::ObfuscatorError(_)) => true, - - tunnel::Error::WireguardTunnelMonitoringError(Error::PskNegotiationError(_)) => true, - - #[cfg(not(windows))] - tunnel::Error::WireguardTunnelMonitoringError(Error::TunnelError( - TunnelError::RecoverableStartWireguardError, - )) => true, - - #[cfg(target_os = "android")] - tunnel::Error::WireguardTunnelMonitoringError(Error::TunnelError( - TunnelError::BypassError(_), - )) => true, - - #[cfg(any(target_os = "windows", target_os = "macos"))] - tunnel::Error::WireguardTunnelMonitoringError(Error::SetupRoutingError(error)) => { - is_recoverable_routing_error(error) - } - - #[cfg(windows)] - tunnel::Error::WireguardTunnelMonitoringError(Error::TunnelError( - // This usually occurs when the tunnel interface cannot be created. - TunnelError::RecoverableStartWireguardError, - )) if retry_attempt < MAX_RECOVERABLE_FAIL_RETRIES => true, - - #[cfg(windows)] - tunnel::Error::OpenVpnTunnelMonitoringError( - talpid_openvpn::Error::WintunCreateAdapterError(_), - ) if retry_attempt < MAX_RECOVERABLE_FAIL_RETRIES => true, - - _ => false, + #[cfg(target_os = "windows")] + if error.get_tunnel_device_error().is_some() { + return retry_attempt < MAX_ATTEMPT_CREATE_TUN; } -} - -#[cfg(windows)] -fn is_recoverable_routing_error(error: &talpid_routing::Error) -> bool { - matches!(error, talpid_routing::Error::AddRoutesFailed(_)) -} - -#[cfg(target_os = "macos")] -fn is_recoverable_routing_error(error: &talpid_routing::Error) -> bool { - // If the default route disappears while connecting but before it is caught by the offline - // monitor, then the gateway will be unreachable. In this case, just retry. - matches!( - error, - talpid_routing::Error::PlatformError(talpid_routing::PlatformError::AddRoute( - talpid_routing::RouteError::Unreachable, - )) - ) + error.is_recoverable() } impl TunnelState for ConnectingState { diff --git a/talpid-openvpn/src/lib.rs b/talpid-openvpn/src/lib.rs index f47072a9f6..1cd58bdf11 100644 --- a/talpid-openvpn/src/lib.rs +++ b/talpid-openvpn/src/lib.rs @@ -125,6 +125,28 @@ pub enum Error { ParseRemoteHost(#[error(source)] std::net::AddrParseError), } +impl Error { + /// Return whether retrying the operation that caused this error is likely to succeed. + pub fn is_recoverable(&self) -> bool { + match self { + #[cfg(windows)] + _ => self.get_tunnel_device_error().is_some(), + + #[cfg(not(windows))] + _ => false, + } + } + + /// Get the inner tunnel device error, if there is one + #[cfg(target_os = "windows")] + pub fn get_tunnel_device_error(&self) -> Option<&io::Error> { + match self { + Error::WintunCreateAdapterError(ref error) => Some(error), + _ => None, + } + } +} + #[cfg(unix)] static OPENVPN_DIE_TIMEOUT: Duration = Duration::from_secs(4); #[cfg(windows)] diff --git a/talpid-routing/src/unix/mod.rs b/talpid-routing/src/unix/mod.rs index 863e3a3aac..a0952e43be 100644 --- a/talpid-routing/src/unix/mod.rs +++ b/talpid-routing/src/unix/mod.rs @@ -52,6 +52,25 @@ pub enum Error { RouteManagerDown, } +impl Error { + /// Return whether retrying the operation that caused this error is likely to succeed. + #[cfg(target_os = "macos")] + pub fn is_recoverable(&self) -> bool { + // If the default route disappears while connecting but before it is caught by the offline + // monitor, then the gateway will be unreachable. In this case, just retry. + matches!( + self, + Error::PlatformError(PlatformError::AddRoute(imp::RouteError::Unreachable,)) + ) + } + + /// Return whether retrying the operation that caused this error is likely to succeed. + #[cfg(not(target_os = "macos"))] + pub fn is_recoverable(&self) -> bool { + false + } +} + /// Handle to a route manager. #[derive(Clone)] pub struct RouteManagerHandle { diff --git a/talpid-routing/src/windows/mod.rs b/talpid-routing/src/windows/mod.rs index 845366753b..7f30327cb0 100644 --- a/talpid-routing/src/windows/mod.rs +++ b/talpid-routing/src/windows/mod.rs @@ -91,6 +91,13 @@ pub enum Error { GetDeviceByGateway, } +impl Error { + /// Return whether retrying the operation that caused this error is likely to succeed. + pub fn is_recoverable(&self) -> bool { + matches!(self, Error::AddRoutesFailed(_)) + } +} + pub type Result<T> = std::result::Result<T, Error>; /// Manages routes by calling into WinNet diff --git a/talpid-types/src/tunnel.rs b/talpid-types/src/tunnel.rs index c035206a1d..ebd3121a86 100644 --- a/talpid-types/src/tunnel.rs +++ b/talpid-types/src/tunnel.rs @@ -95,6 +95,9 @@ pub enum ErrorStateCause { /// Android has rejected one or more DNS server addresses. #[cfg(target_os = "android")] InvalidDnsServers(Vec<IpAddr>), + /// Failed to create tunnel device. + #[cfg(target_os = "windows")] + CreateTunnelDevice { os_error: Option<i32> }, /// Failed to start connection to remote server. StartTunnelError, /// Tunnel parameter generation failure @@ -198,6 +201,14 @@ impl fmt::Display for ErrorStateCause { ); } StartTunnelError => "Failed to start connection to remote server", + #[cfg(target_os = "windows")] + CreateTunnelDevice { + os_error: Some(error), + } => return write!(f, "Failed to create tunnel device: {error}"), + #[cfg(target_os = "windows")] + CreateTunnelDevice { os_error: None } => { + return write!(f, "Failed to create tunnel device") + } TunnelParameterError(ref err) => { return write!(f, "Failure to generate tunnel parameters: {err}"); } diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs index f1ddc827e5..e8a63d0b1b 100644 --- a/talpid-wireguard/src/lib.rs +++ b/talpid-wireguard/src/lib.rs @@ -104,6 +104,38 @@ pub enum Error { SetIpAddressesError(#[error(source)] talpid_windows::net::Error), } +impl Error { + /// Return whether retrying the operation that caused this error is likely to succeed. + pub fn is_recoverable(&self) -> bool { + match self { + Error::CreateObfuscatorError(_) => true, + Error::ObfuscatorError(_) => true, + Error::PskNegotiationError(_) => true, + Error::TunnelError(TunnelError::RecoverableStartWireguardError) => true, + + Error::SetupRoutingError(error) => error.is_recoverable(), + + #[cfg(target_os = "android")] + Error::TunnelError(TunnelError::BypassError(_)) => true, + + #[cfg(windows)] + _ => self.get_tunnel_device_error().is_some(), + + #[cfg(not(windows))] + _ => false, + } + } + + /// Get the inner tunnel device error, if there is one + #[cfg(windows)] + pub fn get_tunnel_device_error(&self) -> Option<&io::Error> { + match self { + Error::TunnelError(TunnelError::SetupTunnelDevice(error)) => Some(error), + _ => None, + } + } +} + /// Spawns and monitors a wireguard tunnel pub struct WireguardMonitor { runtime: tokio::runtime::Handle, @@ -983,7 +1015,12 @@ pub enum TunnelError { /// Failed to setup a tunnel device. #[cfg(not(windows))] #[error(display = "Failed to create tunnel device")] - SetupTunnelDeviceError(#[error(source)] tun_provider::Error), + SetupTunnelDevice(#[error(source)] tun_provider::Error), + + /// Failed to set up a tunnel device + #[cfg(windows)] + #[error(display = "Failed to create tunnel device")] + SetupTunnelDevice(#[error(source)] io::Error), /// Failed to setup a tunnel device. #[cfg(windows)] diff --git a/talpid-wireguard/src/wireguard_go.rs b/talpid-wireguard/src/wireguard_go.rs index 0e3ea3e9ce..28c05676de 100644 --- a/talpid-wireguard/src/wireguard_go.rs +++ b/talpid-wireguard/src/wireguard_go.rs @@ -159,7 +159,7 @@ impl WgGoTunnel { for _ in 1..=MAX_PREPARE_TUN_ATTEMPTS { let tunnel_device = tun_provider .get_tun(tunnel_config.clone()) - .map_err(TunnelError::SetupTunnelDeviceError)?; + .map_err(TunnelError::SetupTunnelDevice)?; match nix::unistd::dup(tunnel_device.as_raw_fd()) { Ok(fd) => return Ok((tunnel_device, fd)), diff --git a/talpid-wireguard/src/wireguard_nt.rs b/talpid-wireguard/src/wireguard_nt.rs index 0a9cc15219..1c7c9d0c71 100644 --- a/talpid-wireguard/src/wireguard_nt.rs +++ b/talpid-wireguard/src/wireguard_nt.rs @@ -417,7 +417,7 @@ impl WgNtTunnel { ); match error { - Error::CreateTunnelDevice(_) => super::TunnelError::RecoverableStartWireguardError, + Error::CreateTunnelDevice(error) => super::TunnelError::SetupTunnelDevice(error), _ => super::TunnelError::FatalStartWireguardError, } }) |
