Skip to content

Commit 36fe9b8

Browse files
committed
Avoid using an unavailable ip version to connect to a relay
1 parent a14eca4 commit 36fe9b8

File tree

6 files changed

+72
-13
lines changed

6 files changed

+72
-13
lines changed

mullvad-daemon/src/tunnel.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,13 @@ impl InnerParametersGenerator {
160160
async fn generate(
161161
&mut self,
162162
retry_attempt: u32,
163+
ipv4: bool,
163164
ipv6: bool,
164165
) -> Result<TunnelParameters, Error> {
165166
let data = self.device().await?;
166167
let selected_relay = self
167168
.relay_selector
168-
.get_relay(retry_attempt as usize, RuntimeParameters { ipv6 })?;
169+
.get_relay(retry_attempt as usize, RuntimeParameters { ipv4, ipv6 })?;
169170

170171
match selected_relay {
171172
#[cfg(not(target_os = "android"))]
@@ -299,13 +300,14 @@ impl TunnelParametersGenerator for ParametersGenerator {
299300
fn generate(
300301
&mut self,
301302
retry_attempt: u32,
303+
ipv4: bool,
302304
ipv6: bool,
303305
) -> Pin<Box<dyn Future<Output = Result<TunnelParameters, ParameterGenerationError>>>> {
304306
let generator = self.0.clone();
305307
Box::pin(async move {
306308
let mut inner = generator.lock().await;
307309
inner
308-
.generate(retry_attempt, ipv6)
310+
.generate(retry_attempt, ipv4, ipv6)
309311
.await
310312
.inspect_err(|error| {
311313
log::error!(

mullvad-relay-selector/src/relay_selector/mod.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ use std::{
2828

2929
use chrono::{DateTime, Local};
3030
use itertools::Itertools;
31-
3231
use mullvad_types::{
3332
constraints::Constraint,
3433
custom_list::CustomListsSettings,
@@ -186,13 +185,27 @@ pub struct AdditionalWireguardConstraints {
186185
/// Values which affect the choice of relay but are only known at runtime.
187186
#[derive(Clone, Debug)]
188187
pub struct RuntimeParameters {
188+
/// Whether IPv6 is available
189+
pub ipv4: bool,
189190
/// Whether IPv6 is available
190191
pub ipv6: bool,
191192
}
192193

193194
impl RuntimeParameters {
194195
/// Return whether a given [query][`RelayQuery`] is valid given the current runtime parameters
195196
pub fn compatible(&self, query: &RelayQuery) -> bool {
197+
if !self.ipv4 {
198+
let must_use_ipv4 = matches!(
199+
query.wireguard_constraints().ip_version,
200+
Constraint::Only(talpid_types::net::IpVersion::V4)
201+
);
202+
if must_use_ipv4 {
203+
log::trace!(
204+
"{query:?} is incompatible with {self:?} due to IPv4 not being available"
205+
);
206+
return false;
207+
}
208+
}
196209
if !self.ipv6 {
197210
let must_use_ipv6 = matches!(
198211
query.wireguard_constraints().ip_version,
@@ -214,7 +227,10 @@ impl RuntimeParameters {
214227
#[allow(clippy::derivable_impls)]
215228
impl Default for RuntimeParameters {
216229
fn default() -> Self {
217-
RuntimeParameters { ipv6: false }
230+
RuntimeParameters {
231+
ipv4: true,
232+
ipv6: false,
233+
}
218234
}
219235
}
220236

@@ -655,7 +671,9 @@ impl RelaySelector {
655671
// Remove candidate queries based on runtime parameters before trying to merge user
656672
// settings
657673
.filter(|query| runtime_params.compatible(query))
674+
.filter(|query| runtime_params.compatible(&user_query))
658675
.filter_map(|query| query.clone().intersection(user_query.clone()))
676+
.map(|query| force_valid_ip_version(&query, runtime_params.clone()))
659677
.filter(|query| Self::get_relay_inner(query, parsed_relays, user_config.custom_lists).is_ok())
660678
.cycle() // If the above filters remove all relays, cycle will also return an empty iterator
661679
.nth(retry_attempt)
@@ -1187,6 +1205,23 @@ impl RelaySelector {
11871205
}
11881206
}
11891207

1208+
fn force_valid_ip_version(query: &RelayQuery, runtime_params: RuntimeParameters) -> RelayQuery {
1209+
let mut wireguard_constraints = query.wireguard_constraints().clone();
1210+
if wireguard_constraints.ip_version.is_any() {
1211+
if runtime_params.ipv4 && !runtime_params.ipv6 {
1212+
wireguard_constraints.ip_version = Constraint::Only(talpid_types::net::IpVersion::V4)
1213+
}
1214+
if runtime_params.ipv6 && !runtime_params.ipv4 {
1215+
wireguard_constraints.ip_version = Constraint::Only(talpid_types::net::IpVersion::V6)
1216+
}
1217+
}
1218+
let mut ret = query.clone();
1219+
match (ret.set_wireguard_constraints(wireguard_constraints)) {
1220+
Ok(()) => ret,
1221+
_Error => query.clone(),
1222+
}
1223+
}
1224+
11901225
#[derive(Clone)]
11911226
struct RelayWithDistance {
11921227
distance: f64,

mullvad-relay-selector/tests/relay_selector.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,13 @@ fn test_wireguard_retry_order() {
394394
let relay_selector = default_relay_selector();
395395
for (retry_attempt, query) in WIREGUARD_RETRY_ORDER.iter().enumerate() {
396396
let relay = relay_selector
397-
.get_relay(retry_attempt, RuntimeParameters { ipv6: true })
397+
.get_relay(
398+
retry_attempt,
399+
RuntimeParameters {
400+
ipv4: true,
401+
ipv6: true,
402+
},
403+
)
398404
.unwrap_or_else(|_| panic!("Retry attempt {retry_attempt} did not yield any relay"));
399405
// For each relay, cross-check that the it has the expected tunnel protocol
400406
let tunnel_type = tunnel_type(&unwrap_relay(relay.clone()));
@@ -452,7 +458,13 @@ fn test_openvpn_retry_order() {
452458

453459
for (retry_attempt, query) in OPENVPN_RETRY_ORDER.iter().enumerate() {
454460
let relay = relay_selector
455-
.get_relay(retry_attempt, RuntimeParameters { ipv6: true })
461+
.get_relay(
462+
retry_attempt,
463+
RuntimeParameters {
464+
ipv4: true,
465+
ipv6: true,
466+
},
467+
)
456468
.unwrap_or_else(|_| panic!("Retry attempt {retry_attempt} did not yield any relay"));
457469
// For each relay, cross-check that the it has the expected tunnel protocol
458470
let tunnel_type = tunnel_type(&unwrap_relay(relay.clone()));

talpid-core/src/tunnel_state_machine/connecting_state.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ impl ConnectingState {
8282
}
8383
return ErrorState::enter(shared_values, ErrorStateCause::IsOffline);
8484
}
85-
match shared_values.runtime.block_on(
86-
shared_values
87-
.tunnel_parameters_generator
88-
.generate(retry_attempt, shared_values.connectivity.has_ipv6()),
89-
) {
85+
match shared_values
86+
.runtime
87+
.block_on(shared_values.tunnel_parameters_generator.generate(
88+
retry_attempt,
89+
shared_values.connectivity.has_ipv4(),
90+
shared_values.connectivity.has_ipv6(),
91+
)) {
9092
Err(err) => {
9193
ErrorState::enter(shared_values, ErrorStateCause::TunnelParameterError(err))
9294
}

talpid-core/src/tunnel_state_machine/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ pub trait TunnelParametersGenerator: Send + 'static {
452452
fn generate(
453453
&mut self,
454454
retry_attempt: u32,
455+
ipv4: bool,
455456
ipv6: bool,
456457
) -> Pin<Box<dyn Future<Output = Result<TunnelParameters, ParameterGenerationError>>>>;
457458
}

talpid-types/src/net/mod.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use self::proxy::{CustomProxy, Socks5Local};
12
use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
23
#[cfg(target_os = "android")]
34
use jnix::FromJava;
45
use obfuscation::ObfuscatorConfig;
56
use serde::{Deserialize, Serialize};
7+
use std::ops::Not;
68
#[cfg(windows)]
79
use std::path::PathBuf;
810
use std::{
@@ -12,8 +14,6 @@ use std::{
1214
sync::LazyLock,
1315
};
1416

15-
use self::proxy::{CustomProxy, Socks5Local};
16-
1717
pub mod obfuscation;
1818
pub mod openvpn;
1919
pub mod proxy;
@@ -600,6 +600,13 @@ impl Connectivity {
600600
)
601601
}
602602

603+
/// Whether IPv4 connectivity seems to be available on the host.
604+
///
605+
/// If IPv4 status is unknown, `true` is returned.
606+
pub fn has_ipv4(&self) -> bool {
607+
matches!(self, Connectivity::Status { ipv4: false, .. }).not()
608+
}
609+
603610
/// Whether IPv6 connectivity seems to be available on the host.
604611
///
605612
/// If IPv6 status is unknown, `false` is returned.

0 commit comments

Comments
 (0)