summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2024-01-31 11:03:52 +0100
committerDavid Lönnhager <david.l@mullvad.net>2024-01-31 11:03:52 +0100
commit962a55443b91c401f3ccbe197fd06aac7d3640aa (patch)
tree1efbe615ca7d6c0ea336ff940dca971ca6003351
parent21d1883687ed32f5abe60de062eb83ae31f97163 (diff)
parent8e78ab6fb04125b6424c825bee9c5c6e5f155991 (diff)
downloadmullvadvpn-962a55443b91c401f3ccbe197fd06aac7d3640aa.tar.xz
mullvadvpn-962a55443b91c401f3ccbe197fd06aac7d3640aa.zip
Merge branch 'detect-driver-conflict' into main
-rw-r--r--gui/src/main/daemon-rpc.ts6
-rw-r--r--gui/src/shared/daemon-rpc-types.ts6
-rw-r--r--gui/src/shared/notifications/error.ts9
-rw-r--r--mullvad-management-interface/proto/management_interface.proto11
-rw-r--r--mullvad-management-interface/src/types/conversions/states.rs23
-rw-r--r--talpid-core/src/tunnel/mod.rs27
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs71
-rw-r--r--talpid-openvpn/src/lib.rs22
-rw-r--r--talpid-routing/src/unix/mod.rs19
-rw-r--r--talpid-routing/src/windows/mod.rs7
-rw-r--r--talpid-types/src/tunnel.rs11
-rw-r--r--talpid-wireguard/src/lib.rs39
-rw-r--r--talpid-wireguard/src/wireguard_go.rs2
-rw-r--r--talpid-wireguard/src/wireguard_nt.rs2
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,
}
})