summaryrefslogtreecommitdiffhomepage
path: root/talpid-core
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2024-02-02 16:14:53 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2024-02-09 13:04:06 +0100
commita6a26bf92ddd690c85e7cefbca1c8e5bb7fe7683 (patch)
treeea5b784ceca66b772e8065ce6686a9cefdc3faeb /talpid-core
parent7fe5f5fe63621a448623ee53c651ed2dd50b9d4a (diff)
downloadmullvadvpn-a6a26bf92ddd690c85e7cefbca1c8e5bb7fe7683.tar.xz
mullvadvpn-a6a26bf92ddd690c85e7cefbca1c8e5bb7fe7683.zip
Detect available IP versions
Try to detect available IP versions by looking at the available routes on the host. On Linux, we check if there exists IPv4 and/or IPv6 routes to some public IP addresses. On macOS and Windows, we check if there exists default routes for IPv4 and/or IPv6. On Android, we check if there is any connectivity at all. The intention is to be able to generate better default constraints for tunnel endpoints. If we can detect "working" IPv4 and/or IPv6 and forward this information to a `TunnelParametersGenerator`, we may choose to connect to a Wireguard relay using IPv6 as part of our retry-strategy. This has not been possible before.
Diffstat (limited to 'talpid-core')
-rw-r--r--talpid-core/src/offline/android.rs47
-rw-r--r--talpid-core/src/offline/linux.rs48
-rw-r--r--talpid-core/src/offline/macos.rs83
-rw-r--r--talpid-core/src/offline/mod.rs34
-rw-r--r--talpid-core/src/offline/windows.rs83
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs6
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs8
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnected_state.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnecting_state.rs16
-rw-r--r--talpid-core/src/tunnel_state_machine/error_state.rs8
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs22
11 files changed, 212 insertions, 147 deletions
diff --git a/talpid-core/src/offline/android.rs b/talpid-core/src/offline/android.rs
index de9708f096..936da896bd 100644
--- a/talpid-core/src/offline/android.rs
+++ b/talpid-core/src/offline/android.rs
@@ -4,13 +4,13 @@ use jnix::{
self,
objects::{GlobalRef, JObject, JValue},
signature::{JavaType, Primitive},
- sys::{jboolean, jlong, JNI_FALSE},
+ sys::{jboolean, jlong, JNI_TRUE},
JNIEnv, JavaVM,
},
JnixEnv,
};
use std::sync::{Arc, Weak};
-use talpid_types::{android::AndroidContext, ErrorExt};
+use talpid_types::{android::AndroidContext, net::Connectivity, ErrorExt};
#[derive(err_derive::Error, Debug)]
#[error(no_from)]
@@ -43,13 +43,13 @@ pub struct MonitorHandle {
jvm: Arc<JavaVM>,
class: GlobalRef,
object: GlobalRef,
- _sender: Arc<UnboundedSender<bool>>,
+ _sender: Arc<UnboundedSender<Connectivity>>,
}
impl MonitorHandle {
pub fn new(
android_context: AndroidContext,
- sender: Arc<UnboundedSender<bool>>,
+ sender: Arc<UnboundedSender<Connectivity>>,
) -> Result<Self, Error> {
let env = JnixEnv::from(
android_context
@@ -101,30 +101,29 @@ impl MonitorHandle {
}
#[allow(clippy::unused_async)]
- pub async fn host_is_offline(&self) -> bool {
- match self.get_is_connected() {
- Ok(is_connected) => !is_connected,
- Err(error) => {
+ pub async fn connectivity(&self) -> Connectivity {
+ self.get_is_connected()
+ .map(|connected| Connectivity::Status { connected })
+ .unwrap_or_else(|error| {
log::error!(
"{}",
error.display_chain_with_msg("Failed to check connectivity status")
);
- false
- }
- }
+ Connectivity::PresumeOnline
+ })
}
fn get_is_connected(&self) -> Result<bool, Error> {
- let result = self.call_method(
+ let is_connected = self.call_method(
"isConnected",
"()Z",
&[],
JavaType::Primitive(Primitive::Boolean),
)?;
- match result {
- JValue::Bool(JNI_FALSE) => Ok(false),
- JValue::Bool(_) => Ok(true),
+ match is_connected {
+ JValue::Bool(JNI_TRUE) => Ok(true),
+ JValue::Bool(_) => Ok(false),
value => Err(Error::InvalidMethodResult(
"ConnectivityListener",
"isConnected",
@@ -133,7 +132,7 @@ impl MonitorHandle {
}
}
- fn set_sender(&self, sender: Weak<UnboundedSender<bool>>) -> Result<(), Error> {
+ fn set_sender(&self, sender: Weak<UnboundedSender<Connectivity>>) -> Result<(), Error> {
let sender_ptr = Box::new(sender);
let sender_address = Box::into_raw(sender_ptr) as jlong;
@@ -182,14 +181,16 @@ impl MonitorHandle {
pub extern "system" fn Java_net_mullvad_talpid_ConnectivityListener_notifyConnectivityChange(
_: JNIEnv<'_>,
_: JObject<'_>,
- is_connected: jboolean,
+ connected: jboolean,
sender_address: jlong,
) {
+ let connected = JNI_TRUE == connected;
let sender_ref = Box::leak(unsafe { get_sender_from_address(sender_address) });
- let is_offline = is_connected == JNI_FALSE;
-
if let Some(sender) = sender_ref.upgrade() {
- if sender.unbounded_send(is_offline).is_err() {
+ if sender
+ .unbounded_send(Connectivity::Status { connected })
+ .is_err()
+ {
log::warn!("Failed to send offline change event");
}
}
@@ -206,13 +207,13 @@ pub extern "system" fn Java_net_mullvad_talpid_ConnectivityListener_destroySende
let _ = unsafe { get_sender_from_address(sender_address) };
}
-unsafe fn get_sender_from_address(address: jlong) -> Box<Weak<UnboundedSender<bool>>> {
- Box::from_raw(address as *mut Weak<UnboundedSender<bool>>)
+unsafe fn get_sender_from_address(address: jlong) -> Box<Weak<UnboundedSender<Connectivity>>> {
+ Box::from_raw(address as *mut Weak<UnboundedSender<Connectivity>>)
}
#[allow(clippy::unused_async)]
pub async fn spawn_monitor(
- sender: UnboundedSender<bool>,
+ sender: UnboundedSender<Connectivity>,
android_context: AndroidContext,
) -> Result<MonitorHandle, Error> {
let sender = Arc::new(sender);
diff --git a/talpid-core/src/offline/linux.rs b/talpid-core/src/offline/linux.rs
index 09db30e359..9f76a19591 100644
--- a/talpid-core/src/offline/linux.rs
+++ b/talpid-core/src/offline/linux.rs
@@ -4,7 +4,7 @@ use std::{
sync::Arc,
};
use talpid_routing::{self, RouteManagerHandle};
-use talpid_types::ErrorExt;
+use talpid_types::{net::Connectivity, ErrorExt};
pub type Result<T> = std::result::Result<T, Error>;
@@ -18,30 +18,31 @@ pub enum Error {
pub struct MonitorHandle {
route_manager: RouteManagerHandle,
fwmark: Option<u32>,
- _notify_tx: Arc<UnboundedSender<bool>>,
+ _notify_tx: Arc<UnboundedSender<Connectivity>>,
}
+/// A non-local IPv4 address.
const PUBLIC_INTERNET_ADDRESS_V4: IpAddr = IpAddr::V4(Ipv4Addr::new(193, 138, 218, 78));
+/// A non-local IPv6 address.
const PUBLIC_INTERNET_ADDRESS_V6: IpAddr =
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6));
impl MonitorHandle {
- pub async fn host_is_offline(&self) -> bool {
- match public_ip_unreachable(&self.route_manager, self.fwmark).await {
- Ok(is_offline) => is_offline,
- Err(err) => {
+ pub async fn connectivity(&self) -> Connectivity {
+ public_ip_unreachable(&self.route_manager, self.fwmark)
+ .await
+ .unwrap_or_else(|err| {
log::error!(
"Failed to verify offline state: {}. Presuming connectivity",
err
);
- false
- }
- }
+ Connectivity::PresumeOnline
+ })
}
}
pub async fn spawn_monitor(
- notify_tx: UnboundedSender<bool>,
+ notify_tx: UnboundedSender<Connectivity>,
route_manager: RouteManagerHandle,
fwmark: Option<u32>,
) -> Result<MonitorHandle> {
@@ -71,7 +72,7 @@ pub async fn spawn_monitor(
"{}",
err.display_chain_with_msg("Failed to infer offline state")
);
- false
+ Connectivity::PresumeOnline
});
if new_offline_state != is_offline {
is_offline = new_offline_state;
@@ -86,15 +87,20 @@ pub async fn spawn_monitor(
Ok(monitor_handle)
}
-async fn public_ip_unreachable(handle: &RouteManagerHandle, fwmark: Option<u32>) -> Result<bool> {
- Ok(handle
- .get_destination_route(PUBLIC_INTERNET_ADDRESS_V4, fwmark)
- .await
- .map_err(Error::RouteManagerError)?
- .is_none()
- && handle
- .get_destination_route(PUBLIC_INTERNET_ADDRESS_V6, fwmark)
+async fn public_ip_unreachable(
+ handle: &RouteManagerHandle,
+ fwmark: Option<u32>,
+) -> Result<Connectivity> {
+ let route_exists = |destination| async move {
+ handle
+ .get_destination_route(destination, fwmark)
.await
- .unwrap_or(None)
- .is_none())
+ .map_err(Error::RouteManagerError)
+ .map(|route| route.is_some())
+ };
+ let connectivity = Connectivity::Status {
+ ipv4: route_exists(PUBLIC_INTERNET_ADDRESS_V4).await?,
+ ipv6: route_exists(PUBLIC_INTERNET_ADDRESS_V6).await?,
+ };
+ Ok(connectivity)
}
diff --git a/talpid-core/src/offline/macos.rs b/talpid-core/src/offline/macos.rs
index 926d68918c..dff62a975d 100644
--- a/talpid-core/src/offline/macos.rs
+++ b/talpid-core/src/offline/macos.rs
@@ -18,6 +18,7 @@ use std::{
time::Duration,
};
use talpid_routing::{DefaultRouteEvent, RouteManagerHandle};
+use talpid_types::net::Connectivity;
const SYNTHETIC_OFFLINE_DURATION: Duration = Duration::from_secs(1);
@@ -28,33 +29,42 @@ pub enum Error {
}
pub struct MonitorHandle {
- state: Arc<Mutex<ConnectivityState>>,
- _notify_tx: Arc<UnboundedSender<bool>>,
-}
-
-#[derive(Clone)]
-struct ConnectivityState {
- v4_connectivity: bool,
- v6_connectivity: bool,
-}
-
-impl ConnectivityState {
- fn get_connectivity(&self) -> bool {
- self.v4_connectivity || self.v6_connectivity
- }
+ state: Arc<Mutex<ConnectivityInner>>,
+ _notify_tx: Arc<UnboundedSender<Connectivity>>,
}
impl MonitorHandle {
/// Return whether the host is offline
#[allow(clippy::unused_async)]
- pub async fn host_is_offline(&self) -> bool {
+ pub async fn connectivity(&self) -> Connectivity {
let state = self.state.lock().unwrap();
- !state.get_connectivity()
+ state.into_connectivity()
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+struct ConnectivityInner {
+ /// Whether IPv4 connectivity seems to be available on the host.
+ ipv4: bool,
+ /// Whether IPv6 connectivity seems to be available on the host.
+ ipv6: bool,
+}
+
+impl ConnectivityInner {
+ fn into_connectivity(self) -> Connectivity {
+ Connectivity::Status {
+ ipv4: self.ipv4,
+ ipv6: self.ipv6,
+ }
+ }
+
+ fn is_online(&self) -> bool {
+ self.into_connectivity().is_online()
}
}
pub async fn spawn_monitor(
- notify_tx: UnboundedSender<bool>,
+ notify_tx: UnboundedSender<Connectivity>,
route_manager_handle: RouteManagerHandle,
) -> Result<MonitorHandle, Error> {
let notify_tx = Arc::new(notify_tx);
@@ -62,7 +72,7 @@ pub async fn spawn_monitor(
// note: begin observing before initializing the state
let route_listener = route_manager_handle.default_route_listener().await?;
- let (v4_connectivity, v6_connectivity) = match route_manager_handle.get_default_routes().await {
+ let (ipv4, ipv6) = match route_manager_handle.get_default_routes().await {
Ok((v4_route, v6_route)) => (v4_route.is_some(), v6_route.is_some()),
Err(error) => {
log::warn!("Failed to initialize offline monitor: {error}");
@@ -72,11 +82,8 @@ pub async fn spawn_monitor(
}
};
- let state = ConnectivityState {
- v4_connectivity,
- v6_connectivity,
- };
- let mut real_state = state.clone();
+ let state = ConnectivityInner { ipv4, ipv6 };
+ let mut real_state = state;
let state = Arc::new(Mutex::new(state));
@@ -95,16 +102,17 @@ pub async fn spawn_monitor(
let Some(state) = weak_state.upgrade() else {
break;
};
- let mut state = state.lock().unwrap();
- *state = real_state.clone();
- if state.get_connectivity() {
+ let mut state = state.lock().unwrap();
+ if real_state.is_online() {
log::info!("Connectivity changed: Connected");
let Some(tx) = weak_notify_tx.upgrade() else {
break;
};
- let _ = tx.unbounded_send(false);
+ let _ = tx.unbounded_send(real_state.into_connectivity());
}
+
+ *state = real_state;
}
route_event = route_listener.next() => {
@@ -115,16 +123,16 @@ pub async fn spawn_monitor(
// Update real state
match event {
DefaultRouteEvent::AddedOrChangedV4 => {
- real_state.v4_connectivity = true;
+ real_state.ipv4 = true;
}
DefaultRouteEvent::AddedOrChangedV6 => {
- real_state.v6_connectivity = true;
+ real_state.ipv6 = true;
}
DefaultRouteEvent::RemovedV4 => {
- real_state.v4_connectivity = false;
+ real_state.ipv4 = false;
}
DefaultRouteEvent::RemovedV6 => {
- real_state.v6_connectivity = false;
+ real_state.ipv6 = false;
}
}
@@ -134,18 +142,19 @@ pub async fn spawn_monitor(
break;
};
let mut state = state.lock().unwrap();
- let previous_connectivity = state.get_connectivity();
- state.v4_connectivity = false;
- state.v6_connectivity = false;
+ let previous_connectivity = *state;
+ state.ipv4 = false;
+ state.ipv6 = false;
- if previous_connectivity {
+ if previous_connectivity.is_online() {
let Some(tx) = weak_notify_tx.upgrade() else {
break;
};
- let _ = tx.unbounded_send(true);
+ let _ = tx.unbounded_send(state.into_connectivity());
log::info!("Connectivity changed: Offline");
}
- if real_state.get_connectivity() {
+
+ if real_state.is_online() {
timeout = Box::pin(tokio::time::sleep(SYNTHETIC_OFFLINE_DURATION)).fuse();
}
}
diff --git a/talpid-core/src/offline/mod.rs b/talpid-core/src/offline/mod.rs
index ca99c61774..1a9a17e788 100644
--- a/talpid-core/src/offline/mod.rs
+++ b/talpid-core/src/offline/mod.rs
@@ -4,6 +4,7 @@ use once_cell::sync::Lazy;
use talpid_routing::RouteManagerHandle;
#[cfg(target_os = "android")]
use talpid_types::android::AndroidContext;
+use talpid_types::net::Connectivity;
#[cfg(target_os = "macos")]
#[path = "macos.rs"]
@@ -33,35 +34,46 @@ pub use self::imp::Error;
pub struct MonitorHandle(Option<imp::MonitorHandle>);
impl MonitorHandle {
- pub async fn host_is_offline(&self) -> bool {
+ pub async fn connectivity(&self) -> Connectivity {
match self.0.as_ref() {
- Some(monitor) => monitor.host_is_offline().await,
- None => false,
+ Some(monitor) => monitor.connectivity().await,
+ None => Connectivity::PresumeOnline,
}
}
}
+#[cfg(not(target_os = "android"))]
pub async fn spawn_monitor(
- sender: UnboundedSender<bool>,
- #[cfg(not(target_os = "android"))] route_manager: RouteManagerHandle,
+ sender: UnboundedSender<Connectivity>,
+ route_manager: RouteManagerHandle,
#[cfg(target_os = "linux")] fwmark: Option<u32>,
- #[cfg(target_os = "android")] android_context: AndroidContext,
) -> Result<MonitorHandle, Error> {
- let monitor = if !*FORCE_DISABLE_OFFLINE_MONITOR {
+ let monitor = if *FORCE_DISABLE_OFFLINE_MONITOR {
+ None
+ } else {
Some(
imp::spawn_monitor(
sender,
- #[cfg(not(target_os = "android"))]
route_manager,
#[cfg(target_os = "linux")]
fwmark,
- #[cfg(target_os = "android")]
- android_context,
)
.await?,
)
- } else {
+ };
+
+ Ok(MonitorHandle(monitor))
+}
+
+#[cfg(target_os = "android")]
+pub async fn spawn_monitor(
+ sender: UnboundedSender<Connectivity>,
+ android_context: AndroidContext,
+) -> Result<MonitorHandle, Error> {
+ let monitor = if *FORCE_DISABLE_OFFLINE_MONITOR {
None
+ } else {
+ Some(imp::spawn_monitor(sender, android_context).await?)
};
Ok(MonitorHandle(monitor))
diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs
index 6539e6e256..caa3ac8f5e 100644
--- a/talpid-core/src/offline/windows.rs
+++ b/talpid-core/src/offline/windows.rs
@@ -1,5 +1,3 @@
-use talpid_routing::{get_best_default_route, CallbackHandle, EventType, RouteManagerHandle};
-
use crate::window::{PowerManagementEvent, PowerManagementListener};
use futures::channel::mpsc::UnboundedSender;
use parking_lot::Mutex;
@@ -8,7 +6,8 @@ use std::{
sync::{Arc, Weak},
time::Duration,
};
-use talpid_types::ErrorExt;
+use talpid_routing::{get_best_default_route, CallbackHandle, EventType, RouteManagerHandle};
+use talpid_types::{net::Connectivity, ErrorExt};
use talpid_windows::net::AddressFamily;
#[derive(err_derive::Error, Debug)]
@@ -22,23 +21,25 @@ pub enum Error {
pub struct BroadcastListener {
system_state: Arc<Mutex<SystemState>>,
_callback_handle: CallbackHandle,
- _notify_tx: Arc<UnboundedSender<bool>>,
+ _notify_tx: Arc<UnboundedSender<Connectivity>>,
}
unsafe impl Send for BroadcastListener {}
impl BroadcastListener {
pub async fn start(
- notify_tx: UnboundedSender<bool>,
+ notify_tx: UnboundedSender<Connectivity>,
route_manager_handle: RouteManagerHandle,
mut power_mgmt_rx: PowerManagementListener,
) -> Result<Self, Error> {
let notify_tx = Arc::new(notify_tx);
- let (v4_connectivity, v6_connectivity) = Self::check_initial_connectivity();
+ let (ipv4, ipv6) = Self::check_initial_connectivity();
let system_state = Arc::new(Mutex::new(SystemState {
- v4_connectivity,
- v6_connectivity,
- suspended: false,
+ connectivity: ConnectivityInner {
+ ipv4,
+ ipv6,
+ suspended: false,
+ },
notify_tx: Arc::downgrade(&notify_tx),
}));
@@ -139,9 +140,9 @@ impl BroadcastListener {
}
#[allow(clippy::unused_async)]
- pub async fn host_is_offline(&self) -> bool {
+ pub async fn connectivity(&self) -> Connectivity {
let state = self.system_state.lock();
- state.is_offline_currently()
+ state.connectivity.into_connectivity()
}
}
@@ -153,10 +154,8 @@ enum StateChange {
}
struct SystemState {
- v4_connectivity: bool,
- v6_connectivity: bool,
- suspended: bool,
- notify_tx: Weak<UnboundedSender<bool>>,
+ connectivity: ConnectivityInner,
+ notify_tx: Weak<UnboundedSender<Connectivity>>,
}
impl SystemState {
@@ -164,23 +163,21 @@ impl SystemState {
let old_state = self.is_offline_currently();
match change {
StateChange::NetworkV4Connectivity(connectivity) => {
- self.v4_connectivity = connectivity;
+ self.connectivity.ipv4 = connectivity;
}
-
StateChange::NetworkV6Connectivity(connectivity) => {
- self.v6_connectivity = connectivity;
+ self.connectivity.ipv6 = connectivity;
}
-
StateChange::Suspended(suspended) => {
- self.suspended = suspended;
+ self.connectivity.suspended = suspended;
}
};
- let new_state = self.is_offline_currently();
+ let new_state = self.connectivity.is_offline();
if old_state != new_state {
log::info!("Connectivity changed: {}", is_offline_str(new_state));
if let Some(notify_tx) = self.notify_tx.upgrade() {
- if let Err(e) = notify_tx.unbounded_send(new_state) {
+ if let Err(e) = notify_tx.unbounded_send(self.connectivity.into_connectivity()) {
log::error!("Failed to send new offline state to daemon: {}", e);
}
}
@@ -188,7 +185,7 @@ impl SystemState {
}
fn is_offline_currently(&self) -> bool {
- (!self.v4_connectivity && !self.v6_connectivity) || self.suspended
+ self.connectivity.is_offline()
}
}
@@ -204,7 +201,7 @@ fn is_offline_str(offline: bool) -> &'static str {
pub type MonitorHandle = BroadcastListener;
pub async fn spawn_monitor(
- sender: UnboundedSender<bool>,
+ sender: UnboundedSender<Connectivity>,
route_manager_handle: RouteManagerHandle,
) -> Result<MonitorHandle, Error> {
let power_mgmt_rx = crate::window::PowerManagementListener::new();
@@ -215,3 +212,41 @@ fn apply_system_state_change(state: Arc<Mutex<SystemState>>, change: StateChange
let mut state = state.lock();
state.apply_change(change);
}
+
+#[derive(Clone, Copy, Debug)]
+struct ConnectivityInner {
+ /// Whether IPv4 connectivity seems to be available on the host.
+ ipv4: bool,
+ /// Whether IPv6 connectivity seems to be available on the host.
+ ipv6: bool,
+ /// The host is suspended.
+ suspended: bool,
+}
+
+impl ConnectivityInner {
+ /// Map [`ConnectivityInner`] to the public [`Connectivity`].
+ ///
+ /// # Note
+ ///
+ /// If the host is suspended, there is a great likelihood that we should
+ /// consider the host to be offline. We synthesize this by setting both
+ /// `ipv4` and `ipv6` availability to `false`.
+ fn into_connectivity(self) -> Connectivity {
+ if self.suspended {
+ Connectivity::Status {
+ ipv4: false,
+ ipv6: false,
+ }
+ } else {
+ Connectivity::Status {
+ ipv4: self.ipv4,
+ ipv6: self.ipv6,
+ }
+ }
+ }
+
+ /// See [`Connectivity::is_offline`] for details.
+ fn is_offline(&self) -> bool {
+ self.into_connectivity().is_offline()
+ }
+}
diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs
index 6f32b796c3..c73232a895 100644
--- a/talpid-core/src/tunnel_state_machine/connected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connected_state.rs
@@ -298,9 +298,9 @@ impl ConnectedState {
let _ = complete_tx.send(());
SameState(self)
}
- Some(TunnelCommand::IsOffline(is_offline)) => {
- shared_values.is_offline = is_offline;
- if is_offline {
+ Some(TunnelCommand::Connectivity(connectivity)) => {
+ shared_values.connectivity = connectivity;
+ if connectivity.is_offline() {
self.disconnect(
shared_values,
AfterDisconnect::Block(ErrorStateCause::IsOffline),
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index 3b0afd8abf..927de207bf 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -57,7 +57,7 @@ impl ConnectingState {
shared_values: &mut SharedTunnelStateValues,
retry_attempt: u32,
) -> (Box<dyn TunnelState>, TunnelStateTransition) {
- if shared_values.is_offline {
+ if shared_values.connectivity.is_offline() {
// FIXME: Temporary: Nudge route manager to update the default interface
#[cfg(target_os = "macos")]
if let Ok(handle) = shared_values.route_manager.handle() {
@@ -457,9 +457,9 @@ impl ConnectingState {
let _ = complete_tx.send(());
SameState(self)
}
- Some(TunnelCommand::IsOffline(is_offline)) => {
- shared_values.is_offline = is_offline;
- if is_offline {
+ Some(TunnelCommand::Connectivity(connectivity)) => {
+ shared_values.connectivity = connectivity;
+ if connectivity.is_offline() {
self.disconnect(
shared_values,
AfterDisconnect::Block(ErrorStateCause::IsOffline),
diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
index 49ed2575a3..00af35abd5 100644
--- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
@@ -200,8 +200,8 @@ impl TunnelState for DisconnectedState {
SameState(self)
}
}
- Some(TunnelCommand::IsOffline(is_offline)) => {
- shared_values.is_offline = is_offline;
+ Some(TunnelCommand::Connectivity(connectivity)) => {
+ shared_values.connectivity = connectivity;
SameState(self)
}
Some(TunnelCommand::Connect) => NewState(ConnectingState::enter(shared_values, 0)),
diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
index 185d2f7d0a..22fbcd04a3 100644
--- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
@@ -63,8 +63,8 @@ impl DisconnectingState {
let _ = complete_tx.send(());
AfterDisconnect::Nothing
}
- Some(TunnelCommand::IsOffline(is_offline)) => {
- shared_values.is_offline = is_offline;
+ Some(TunnelCommand::Connectivity(connectivity)) => {
+ shared_values.connectivity = connectivity;
AfterDisconnect::Nothing
}
Some(TunnelCommand::Connect) => AfterDisconnect::Reconnect(0),
@@ -105,9 +105,9 @@ impl DisconnectingState {
let _ = complete_tx.send(());
AfterDisconnect::Block(reason)
}
- Some(TunnelCommand::IsOffline(is_offline)) => {
- shared_values.is_offline = is_offline;
- if !is_offline && matches!(reason, ErrorStateCause::IsOffline) {
+ Some(TunnelCommand::Connectivity(connectivity)) => {
+ shared_values.connectivity = connectivity;
+ if !connectivity.is_offline() && matches!(reason, ErrorStateCause::IsOffline) {
AfterDisconnect::Reconnect(0)
} else {
AfterDisconnect::Block(reason)
@@ -152,9 +152,9 @@ impl DisconnectingState {
let _ = complete_tx.send(());
AfterDisconnect::Reconnect(retry_attempt)
}
- Some(TunnelCommand::IsOffline(is_offline)) => {
- shared_values.is_offline = is_offline;
- if is_offline {
+ Some(TunnelCommand::Connectivity(connectivity)) => {
+ shared_values.connectivity = connectivity;
+ if connectivity.is_offline() {
AfterDisconnect::Block(ErrorStateCause::IsOffline)
} else {
AfterDisconnect::Reconnect(retry_attempt)
diff --git a/talpid-core/src/tunnel_state_machine/error_state.rs b/talpid-core/src/tunnel_state_machine/error_state.rs
index 2f82cb4cf5..538ee5de1a 100644
--- a/talpid-core/src/tunnel_state_machine/error_state.rs
+++ b/talpid-core/src/tunnel_state_machine/error_state.rs
@@ -181,9 +181,11 @@ impl TunnelState for ErrorState {
let _ = complete_tx.send(());
SameState(self)
}
- Some(TunnelCommand::IsOffline(is_offline)) => {
- shared_values.is_offline = is_offline;
- if !is_offline && matches!(self.block_reason, ErrorStateCause::IsOffline) {
+ Some(TunnelCommand::Connectivity(connectivity)) => {
+ shared_values.connectivity = connectivity;
+ if !connectivity.is_offline()
+ && matches!(self.block_reason, ErrorStateCause::IsOffline)
+ {
Self::reset_dns(shared_values);
NewState(ConnectingState::enter(shared_values, 0))
} else {
diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs
index 5957b2f731..6be9b8a1c3 100644
--- a/talpid-core/src/tunnel_state_machine/mod.rs
+++ b/talpid-core/src/tunnel_state_machine/mod.rs
@@ -42,7 +42,7 @@ use std::{
#[cfg(target_os = "android")]
use talpid_types::{android::AndroidContext, ErrorExt};
use talpid_types::{
- net::{AllowedEndpoint, TunnelParameters},
+ net::{AllowedEndpoint, Connectivity, TunnelParameters},
tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition},
};
@@ -122,7 +122,7 @@ pub async fn spawn(
log_dir: Option<PathBuf>,
resource_dir: PathBuf,
state_change_listener: impl Sender<TunnelStateTransition> + Send + 'static,
- offline_state_listener: mpsc::UnboundedSender<bool>,
+ offline_state_listener: mpsc::UnboundedSender<Connectivity>,
#[cfg(target_os = "windows")] volume_update_rx: mpsc::UnboundedReceiver<()>,
#[cfg(target_os = "android")] android_context: AndroidContext,
#[cfg(target_os = "linux")] linux_ids: LinuxNetworkingIdentifiers,
@@ -199,7 +199,7 @@ pub enum TunnelCommand {
/// Enable or disable the block_when_disconnected feature.
BlockWhenDisconnected(bool, oneshot::Sender<()>),
/// Notify the state machine of the connectivity of the device.
- IsOffline(bool),
+ Connectivity(Connectivity),
/// Open tunnel connection.
Connect,
/// Close tunnel connection.
@@ -241,7 +241,7 @@ struct TunnelStateMachine {
struct TunnelStateMachineInitArgs<G: TunnelParametersGenerator> {
settings: InitialTunnelState,
command_tx: std::sync::Weak<mpsc::UnboundedSender<TunnelCommand>>,
- offline_state_tx: mpsc::UnboundedSender<bool>,
+ offline_state_tx: mpsc::UnboundedSender<Connectivity>,
tunnel_parameters_generator: G,
tun_provider: TunProvider,
log_dir: Option<PathBuf>,
@@ -319,13 +319,13 @@ impl TunnelStateMachine {
let (offline_tx, mut offline_rx) = mpsc::unbounded();
let initial_offline_state_tx = args.offline_state_tx.clone();
tokio::spawn(async move {
- while let Some(offline) = offline_rx.next().await {
+ while let Some(connectivity) = offline_rx.next().await {
if let Some(tx) = args.command_tx.upgrade() {
- let _ = tx.unbounded_send(TunnelCommand::IsOffline(offline));
+ let _ = tx.unbounded_send(TunnelCommand::Connectivity(connectivity));
} else {
break;
}
- let _ = args.offline_state_tx.unbounded_send(offline);
+ let _ = args.offline_state_tx.unbounded_send(connectivity);
}
});
let offline_monitor = offline::spawn_monitor(
@@ -339,8 +339,8 @@ impl TunnelStateMachine {
)
.await
.map_err(Error::OfflineMonitorError)?;
- let is_offline = offline_monitor.host_is_offline().await;
- let _ = initial_offline_state_tx.unbounded_send(is_offline);
+ let connectivity = offline_monitor.connectivity().await;
+ let _ = initial_offline_state_tx.unbounded_send(connectivity);
#[cfg(windows)]
split_tunnel
@@ -357,7 +357,7 @@ impl TunnelStateMachine {
_offline_monitor: offline_monitor,
allow_lan: args.settings.allow_lan,
block_when_disconnected: args.settings.block_when_disconnected,
- is_offline,
+ connectivity,
dns_servers: args.settings.dns_servers,
allowed_endpoint: args.settings.allowed_endpoint,
tunnel_parameters_generator: Box::new(args.tunnel_parameters_generator),
@@ -441,7 +441,7 @@ struct SharedTunnelStateValues {
/// Should network access be allowed when in the disconnected state.
block_when_disconnected: bool,
/// True when the computer is known to be offline.
- is_offline: bool,
+ connectivity: Connectivity,
/// DNS servers to use (overriding default).
dns_servers: Option<Vec<IpAddr>>,
/// Endpoint that should not be blocked by the firewall.