Skip to content

Commit 623df1a

Browse files
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.
1 parent 7fe5f5f commit 623df1a

File tree

14 files changed

+280
-157
lines changed

14 files changed

+280
-157
lines changed

mullvad-daemon/src/api.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use mullvad_types::access_method::{
2121
};
2222
use std::{net::SocketAddr, path::PathBuf};
2323
use talpid_core::mpsc::Sender;
24-
use talpid_types::net::{AllowedClients, AllowedEndpoint, Endpoint, TransportProtocol};
24+
use talpid_types::net::{
25+
AllowedClients, AllowedEndpoint, Connectivity, Endpoint, TransportProtocol,
26+
};
2527

2628
pub enum Message {
2729
Get(ResponseTx<ResolvedConnectionMode>),
@@ -527,18 +529,26 @@ pub fn allowed_clients(connection_mode: &ApiConnectionMode) -> AllowedClients {
527529
}
528530
}
529531

532+
/// Forwards the received values from `offline_state_rx` to the [`ApiAvailabilityHandle`].
530533
pub(crate) fn forward_offline_state(
531534
api_availability: ApiAvailabilityHandle,
532-
mut offline_state_rx: mpsc::UnboundedReceiver<bool>,
535+
mut offline_state_rx: mpsc::UnboundedReceiver<Connectivity>,
533536
) {
534537
tokio::spawn(async move {
535-
let initial_state = offline_state_rx
538+
let is_offline = offline_state_rx
536539
.next()
537540
.await
538-
.expect("missing initial offline state");
539-
api_availability.set_offline(initial_state);
540-
while let Some(is_offline) = offline_state_rx.next().await {
541-
api_availability.set_offline(is_offline);
541+
.expect("missing initial offline state")
542+
.is_offline();
543+
log::info!(
544+
"Initial offline state - {state}",
545+
state = if is_offline { "offline" } else { "online" },
546+
);
547+
api_availability.set_offline(is_offline);
548+
549+
while let Some(state) = offline_state_rx.next().await {
550+
log::info!("Detecting changes to offline state - {state:?}");
551+
api_availability.set_offline(state.is_offline());
542552
}
543553
});
544554
}

talpid-core/src/offline/android.rs

+24-23
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ use jnix::{
44
self,
55
objects::{GlobalRef, JObject, JValue},
66
signature::{JavaType, Primitive},
7-
sys::{jboolean, jlong, JNI_FALSE},
7+
sys::{jboolean, jlong, JNI_TRUE},
88
JNIEnv, JavaVM,
99
},
1010
JnixEnv,
1111
};
1212
use std::sync::{Arc, Weak};
13-
use talpid_types::{android::AndroidContext, ErrorExt};
13+
use talpid_types::{android::AndroidContext, net::Connectivity, ErrorExt};
1414

1515
#[derive(err_derive::Error, Debug)]
1616
#[error(no_from)]
@@ -43,13 +43,13 @@ pub struct MonitorHandle {
4343
jvm: Arc<JavaVM>,
4444
class: GlobalRef,
4545
object: GlobalRef,
46-
_sender: Arc<UnboundedSender<bool>>,
46+
_sender: Arc<UnboundedSender<Connectivity>>,
4747
}
4848

4949
impl MonitorHandle {
5050
pub fn new(
5151
android_context: AndroidContext,
52-
sender: Arc<UnboundedSender<bool>>,
52+
sender: Arc<UnboundedSender<Connectivity>>,
5353
) -> Result<Self, Error> {
5454
let env = JnixEnv::from(
5555
android_context
@@ -101,30 +101,29 @@ impl MonitorHandle {
101101
}
102102

103103
#[allow(clippy::unused_async)]
104-
pub async fn host_is_offline(&self) -> bool {
105-
match self.get_is_connected() {
106-
Ok(is_connected) => !is_connected,
107-
Err(error) => {
104+
pub async fn connectivity(&self) -> Connectivity {
105+
self.get_is_connected()
106+
.map(|connected| Connectivity::Status { connected })
107+
.unwrap_or_else(|error| {
108108
log::error!(
109109
"{}",
110110
error.display_chain_with_msg("Failed to check connectivity status")
111111
);
112-
false
113-
}
114-
}
112+
Connectivity::PresumeOnline
113+
})
115114
}
116115

117116
fn get_is_connected(&self) -> Result<bool, Error> {
118-
let result = self.call_method(
117+
let is_connected = self.call_method(
119118
"isConnected",
120119
"()Z",
121120
&[],
122121
JavaType::Primitive(Primitive::Boolean),
123122
)?;
124123

125-
match result {
126-
JValue::Bool(JNI_FALSE) => Ok(false),
127-
JValue::Bool(_) => Ok(true),
124+
match is_connected {
125+
JValue::Bool(JNI_TRUE) => Ok(true),
126+
JValue::Bool(_) => Ok(false),
128127
value => Err(Error::InvalidMethodResult(
129128
"ConnectivityListener",
130129
"isConnected",
@@ -133,7 +132,7 @@ impl MonitorHandle {
133132
}
134133
}
135134

136-
fn set_sender(&self, sender: Weak<UnboundedSender<bool>>) -> Result<(), Error> {
135+
fn set_sender(&self, sender: Weak<UnboundedSender<Connectivity>>) -> Result<(), Error> {
137136
let sender_ptr = Box::new(sender);
138137
let sender_address = Box::into_raw(sender_ptr) as jlong;
139138

@@ -182,14 +181,16 @@ impl MonitorHandle {
182181
pub extern "system" fn Java_net_mullvad_talpid_ConnectivityListener_notifyConnectivityChange(
183182
_: JNIEnv<'_>,
184183
_: JObject<'_>,
185-
is_connected: jboolean,
184+
connected: jboolean,
186185
sender_address: jlong,
187186
) {
187+
let connected = JNI_TRUE == connected;
188188
let sender_ref = Box::leak(unsafe { get_sender_from_address(sender_address) });
189-
let is_offline = is_connected == JNI_FALSE;
190-
191189
if let Some(sender) = sender_ref.upgrade() {
192-
if sender.unbounded_send(is_offline).is_err() {
190+
if sender
191+
.unbounded_send(Connectivity::Status { connected })
192+
.is_err()
193+
{
193194
log::warn!("Failed to send offline change event");
194195
}
195196
}
@@ -206,13 +207,13 @@ pub extern "system" fn Java_net_mullvad_talpid_ConnectivityListener_destroySende
206207
let _ = unsafe { get_sender_from_address(sender_address) };
207208
}
208209

209-
unsafe fn get_sender_from_address(address: jlong) -> Box<Weak<UnboundedSender<bool>>> {
210-
Box::from_raw(address as *mut Weak<UnboundedSender<bool>>)
210+
unsafe fn get_sender_from_address(address: jlong) -> Box<Weak<UnboundedSender<Connectivity>>> {
211+
Box::from_raw(address as *mut Weak<UnboundedSender<Connectivity>>)
211212
}
212213

213214
#[allow(clippy::unused_async)]
214215
pub async fn spawn_monitor(
215-
sender: UnboundedSender<bool>,
216+
sender: UnboundedSender<Connectivity>,
216217
android_context: AndroidContext,
217218
) -> Result<MonitorHandle, Error> {
218219
let sender = Arc::new(sender);

talpid-core/src/offline/linux.rs

+27-21
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
sync::Arc,
55
};
66
use talpid_routing::{self, RouteManagerHandle};
7-
use talpid_types::ErrorExt;
7+
use talpid_types::{net::Connectivity, ErrorExt};
88

99
pub type Result<T> = std::result::Result<T, Error>;
1010

@@ -18,30 +18,31 @@ pub enum Error {
1818
pub struct MonitorHandle {
1919
route_manager: RouteManagerHandle,
2020
fwmark: Option<u32>,
21-
_notify_tx: Arc<UnboundedSender<bool>>,
21+
_notify_tx: Arc<UnboundedSender<Connectivity>>,
2222
}
2323

24+
/// A non-local IPv4 address.
2425
const PUBLIC_INTERNET_ADDRESS_V4: IpAddr = IpAddr::V4(Ipv4Addr::new(193, 138, 218, 78));
26+
/// A non-local IPv6 address.
2527
const PUBLIC_INTERNET_ADDRESS_V6: IpAddr =
2628
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6));
2729

2830
impl MonitorHandle {
29-
pub async fn host_is_offline(&self) -> bool {
30-
match public_ip_unreachable(&self.route_manager, self.fwmark).await {
31-
Ok(is_offline) => is_offline,
32-
Err(err) => {
31+
pub async fn connectivity(&self) -> Connectivity {
32+
public_ip_unreachable(&self.route_manager, self.fwmark)
33+
.await
34+
.unwrap_or_else(|err| {
3335
log::error!(
3436
"Failed to verify offline state: {}. Presuming connectivity",
3537
err
3638
);
37-
false
38-
}
39-
}
39+
Connectivity::PresumeOnline
40+
})
4041
}
4142
}
4243

4344
pub async fn spawn_monitor(
44-
notify_tx: UnboundedSender<bool>,
45+
notify_tx: UnboundedSender<Connectivity>,
4546
route_manager: RouteManagerHandle,
4647
fwmark: Option<u32>,
4748
) -> Result<MonitorHandle> {
@@ -71,7 +72,7 @@ pub async fn spawn_monitor(
7172
"{}",
7273
err.display_chain_with_msg("Failed to infer offline state")
7374
);
74-
false
75+
Connectivity::PresumeOnline
7576
});
7677
if new_offline_state != is_offline {
7778
is_offline = new_offline_state;
@@ -86,15 +87,20 @@ pub async fn spawn_monitor(
8687
Ok(monitor_handle)
8788
}
8889

89-
async fn public_ip_unreachable(handle: &RouteManagerHandle, fwmark: Option<u32>) -> Result<bool> {
90-
Ok(handle
91-
.get_destination_route(PUBLIC_INTERNET_ADDRESS_V4, fwmark)
92-
.await
93-
.map_err(Error::RouteManagerError)?
94-
.is_none()
95-
&& handle
96-
.get_destination_route(PUBLIC_INTERNET_ADDRESS_V6, fwmark)
90+
async fn public_ip_unreachable(
91+
handle: &RouteManagerHandle,
92+
fwmark: Option<u32>,
93+
) -> Result<Connectivity> {
94+
let route_exists = |destination| async move {
95+
handle
96+
.get_destination_route(destination, fwmark)
9797
.await
98-
.unwrap_or(None)
99-
.is_none())
98+
.map_err(Error::RouteManagerError)
99+
.map(|x| x.is_some())
100+
};
101+
let connectivity = Connectivity::Status {
102+
ipv4: route_exists(PUBLIC_INTERNET_ADDRESS_V4).await?,
103+
ipv6: route_exists(PUBLIC_INTERNET_ADDRESS_V6).await?,
104+
};
105+
Ok(connectivity)
100106
}

0 commit comments

Comments
 (0)