summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-03-04 14:28:52 +0100
committerDavid Lönnhager <david.l@mullvad.net>2025-03-05 09:39:30 +0100
commitbe0877a65e0f5e53a06ca7ee7a1303b5716b41dd (patch)
tree544ab5d544ba148c7656d51d1ab508e01f218629
parenta2be04b4792ec942bd20d5fb69bcfa8cf7d854d7 (diff)
downloadmullvadvpn-be0877a65e0f5e53a06ca7ee7a1303b5716b41dd.tar.xz
mullvadvpn-be0877a65e0f5e53a06ca7ee7a1303b5716b41dd.zip
Remove automatic tunnel type
Co-authored-by: Markus Pettersson <markus.pettersson@mullvad.net>
-rw-r--r--mullvad-cli/src/cmds/relay.rs31
-rw-r--r--mullvad-daemon/src/lib.rs14
-rw-r--r--mullvad-management-interface/src/types/conversions/relay_constraints.rs22
-rw-r--r--mullvad-relay-selector/src/lib.rs3
-rw-r--r--mullvad-relay-selector/src/relay_selector/matcher.rs11
-rw-r--r--mullvad-relay-selector/src/relay_selector/mod.rs100
-rw-r--r--mullvad-relay-selector/src/relay_selector/query.rs66
-rw-r--r--mullvad-relay-selector/tests/relay_selector.rs206
-rw-r--r--mullvad-types/src/relay_constraints.rs2
-rw-r--r--talpid-types/src/net/mod.rs3
10 files changed, 196 insertions, 262 deletions
diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs
index 1f2d1f9192..b3fa3812ef 100644
--- a/mullvad-cli/src/cmds/relay.rs
+++ b/mullvad-cli/src/cmds/relay.rs
@@ -96,8 +96,8 @@ pub enum SetCommands {
#[clap(subcommand)]
Tunnel(SetTunnelCommands),
- /// Set tunnel protocol to use: 'any', 'wireguard', or 'openvpn'.
- TunnelProtocol { protocol: Constraint<TunnelType> },
+ /// Set tunnel protocol to use: 'wireguard', or 'openvpn'.
+ TunnelProtocol { protocol: TunnelType },
/// Set a custom VPN relay to use
#[clap(subcommand)]
@@ -565,27 +565,18 @@ impl Relay {
// Depending on the current configured tunnel protocol, we filter only the relevant hosts
let location_constraint = match constraints.tunnel_protocol {
- Constraint::Any => {
+ TunnelType::OpenVpn => {
resolve_location_constraint(&mut rpc, location_constraint_args, |relay| {
- relay.active && relay.endpoint_data != RelayEndpointData::Bridge
+ relay.active && relay.endpoint_data == RelayEndpointData::Openvpn
+ })
+ .await
+ }
+ TunnelType::Wireguard => {
+ resolve_location_constraint(&mut rpc, location_constraint_args, |relay| {
+ relay.active && matches!(relay.endpoint_data, RelayEndpointData::Wireguard(_))
})
.await
}
- Constraint::Only(tunnel) => match tunnel {
- TunnelType::OpenVpn => {
- resolve_location_constraint(&mut rpc, location_constraint_args, |relay| {
- relay.active && relay.endpoint_data == RelayEndpointData::Openvpn
- })
- .await
- }
- TunnelType::Wireguard => {
- resolve_location_constraint(&mut rpc, location_constraint_args, |relay| {
- relay.active
- && matches!(relay.endpoint_data, RelayEndpointData::Wireguard(_))
- })
- .await
- }
- },
}?;
Self::update_constraints(|constraints| {
@@ -721,7 +712,7 @@ impl Relay {
}
}
- async fn set_tunnel_protocol(protocol: Constraint<TunnelType>) -> Result<()> {
+ async fn set_tunnel_protocol(protocol: TunnelType) -> Result<()> {
Self::update_constraints(|constraints| {
constraints.tunnel_protocol = protocol;
})
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 1da90693fc..217823f6cd 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -2465,8 +2465,6 @@ impl Daemon {
#[cfg(daita)]
async fn on_set_daita_enabled(&mut self, tx: ResponseTx<(), settings::Error>, value: bool) {
- use mullvad_types::{constraints::Constraint, Intersection};
-
let result = self
.settings
.update(|settings| {
@@ -2481,10 +2479,7 @@ impl Daemon {
return; // DAITA is not supported for custom relays
};
- let wireguard_enabled = constraints
- .tunnel_protocol
- .intersection(Constraint::Only(TunnelType::Wireguard))
- .is_some();
+ let wireguard_enabled = constraints.tunnel_protocol == TunnelType::Wireguard;
if settings_changed && wireguard_enabled {
log::info!("Reconnecting because DAITA settings changed");
@@ -2504,8 +2499,6 @@ impl Daemon {
tx: ResponseTx<(), settings::Error>,
value: bool,
) {
- use mullvad_types::{constraints::Constraint, Intersection};
-
match self
.settings
.update(|settings| {
@@ -2524,10 +2517,7 @@ impl Daemon {
return; // DAITA is not supported for custom relays
};
- let wireguard_enabled = constraints
- .tunnel_protocol
- .intersection(Constraint::Only(TunnelType::Wireguard))
- .is_some();
+ let wireguard_enabled = constraints.tunnel_protocol == TunnelType::Wireguard;
let daita_enabled = self.settings.tunnel_options.wireguard.daita.enabled;
diff --git a/mullvad-management-interface/src/types/conversions/relay_constraints.rs b/mullvad-management-interface/src/types/conversions/relay_constraints.rs
index ef0afd02b4..94cd5b6f64 100644
--- a/mullvad-management-interface/src/types/conversions/relay_constraints.rs
+++ b/mullvad-management-interface/src/types/conversions/relay_constraints.rs
@@ -97,12 +97,13 @@ impl TryFrom<proto::RelaySettings> for mullvad_types::relay_constraints::RelaySe
.unwrap_or(Constraint::Any);
let providers = try_providers_constraint_from_proto(&settings.providers)?;
let ownership = try_ownership_constraint_from_i32(settings.ownership)?;
- let tunnel_protocol = Constraint::from(
- settings
- .tunnel_type
- .map(try_tunnel_type_from_i32)
- .transpose()?,
- );
+ let tunnel_protocol = settings
+ .tunnel_type
+ .map(try_tunnel_type_from_i32)
+ .transpose()?
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "missing tunnel protocol",
+ ))?;
let openvpn_constraints =
mullvad_constraints::OpenVpnConstraints::try_from(
&settings.openvpn_constraints.ok_or(
@@ -244,13 +245,8 @@ impl From<mullvad_types::relay_constraints::RelaySettings> for proto::RelaySetti
providers: convert_providers_constraint(&constraints.providers),
ownership: convert_ownership_constraint(&constraints.ownership) as i32,
tunnel_type: match constraints.tunnel_protocol {
- Constraint::Any => None,
- Constraint::Only(talpid_net::TunnelType::Wireguard) => {
- Some(proto::TunnelType::Wireguard)
- }
- Constraint::Only(talpid_net::TunnelType::OpenVpn) => {
- Some(proto::TunnelType::Openvpn)
- }
+ talpid_net::TunnelType::Wireguard => Some(proto::TunnelType::Wireguard),
+ talpid_net::TunnelType::OpenVpn => Some(proto::TunnelType::Openvpn),
}
.map(i32::from),
diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs
index e6c91f9793..32779f4431 100644
--- a/mullvad-relay-selector/src/lib.rs
+++ b/mullvad-relay-selector/src/lib.rs
@@ -11,5 +11,6 @@ pub use error::Error;
pub use relay_selector::{
detailer, matcher, matcher::filter_matching_relay_list, query, relays::WireguardConfig,
AdditionalRelayConstraints, AdditionalWireguardConstraints, GetRelay, RelaySelector,
- RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, RETRY_ORDER,
+ RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, OPENVPN_RETRY_ORDER,
+ WIREGUARD_RETRY_ORDER,
};
diff --git a/mullvad-relay-selector/src/relay_selector/matcher.rs b/mullvad-relay-selector/src/relay_selector/matcher.rs
index d46b275b0a..88126137b0 100644
--- a/mullvad-relay-selector/src/relay_selector/matcher.rs
+++ b/mullvad-relay-selector/src/relay_selector/matcher.rs
@@ -190,19 +190,16 @@ pub const fn filter_openvpn(relay: &Relay) -> bool {
/// Returns whether the relay matches the tunnel constraint `filter`
#[cfg(not(target_os = "android"))]
-pub const fn filter_tunnel_type(filter: &Constraint<TunnelType>, relay: &Relay) -> bool {
+pub const fn filter_tunnel_type(filter: &TunnelType, relay: &Relay) -> bool {
match filter {
- Constraint::Any => true,
- Constraint::Only(typ) => match typ {
- TunnelType::OpenVpn => filter_openvpn(relay),
- TunnelType::Wireguard => filter_wireguard(relay),
- },
+ TunnelType::OpenVpn => filter_openvpn(relay),
+ TunnelType::Wireguard => filter_wireguard(relay),
}
}
/// Returns whether the relay matches the tunnel constraint `filter`
#[cfg(target_os = "android")]
-pub const fn filter_tunnel_type(_: &Constraint<TunnelType>, relay: &Relay) -> bool {
+pub const fn filter_tunnel_type(_: &TunnelType, relay: &Relay) -> bool {
// Only keep Wireguard relays on Android (i.e. filter out OpenVPN relays)
filter_wireguard(relay)
}
diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs
index 1522491b50..7b11e3a282 100644
--- a/mullvad-relay-selector/src/relay_selector/mod.rs
+++ b/mullvad-relay-selector/src/relay_selector/mod.rs
@@ -53,19 +53,16 @@ use talpid_types::{
ErrorExt,
};
-/// [`RETRY_ORDER`] defines an ordered set of relay parameters which the relay selector should
+/// [`WIREGUARD_RETRY_ORDER`] defines an ordered set of relay parameters which the relay selector should
/// prioritize on successive connection attempts. Note that these will *never* override user
/// preferences. See [the documentation on `RelayQuery`][RelayQuery] for further details.
///
/// This list should be kept in sync with the expected behavior defined in `docs/relay-selector.md`
-pub static RETRY_ORDER: LazyLock<Vec<RelayQuery>> = LazyLock::new(|| {
+pub static WIREGUARD_RETRY_ORDER: LazyLock<Vec<RelayQuery>> = LazyLock::new(|| {
use query::builder::{IpVersion, RelayQueryBuilder};
vec![
- // 1
- // Note: This query can be unified with all possible user preferences.
- // If the user has tunnel protocol set to 'Auto', the relay selector will
- // default to picking a Wireguard relay.
- RelayQueryBuilder::new().build(),
+ // 1 This works with any wireguard relay
+ RelayQueryBuilder::new().wireguard().build(),
// 2
RelayQueryBuilder::new().wireguard().port(443).build(),
// 3
@@ -83,13 +80,26 @@ pub static RETRY_ORDER: LazyLock<Vec<RelayQuery>> = LazyLock::new(|| {
.udp2tcp()
.ip_version(IpVersion::V6)
.build(),
- // 7
+ ]
+});
+
+/// [`OPENVPN_RETRY_ORDER`] defines an ordered set of relay parameters which the relay selector should
+/// prioritize on successive connection attempts. Note that these will *never* override user
+/// preferences. See [the documentation on `RelayQuery`][RelayQuery] for further details.
+///
+/// This list should be kept in sync with the expected behavior defined in `docs/relay-selector.md`
+pub static OPENVPN_RETRY_ORDER: LazyLock<Vec<RelayQuery>> = LazyLock::new(|| {
+ use query::builder::RelayQueryBuilder;
+ vec![
+ // 1 (openvpn) This works with any openvpn relay
+ RelayQueryBuilder::new().openvpn().build(),
+ // 2
RelayQueryBuilder::new()
.openvpn()
.transport_protocol(TransportProtocol::Tcp)
.port(443)
.build(),
- // 8
+ // 3
RelayQueryBuilder::new()
.openvpn()
.transport_protocol(TransportProtocol::Tcp)
@@ -552,15 +562,37 @@ impl RelaySelector {
}
/// Returns a random relay and relay endpoint matching the current constraints corresponding to
- /// `retry_attempt` in [`RETRY_ORDER`] while considering [runtime_params][`RuntimeParameters`].
- ///
- /// [`RETRY_ORDER`]: crate::RETRY_ORDER
+ /// `retry_attempt` in one of the retry orders while considering
+ /// [runtime_params][`RuntimeParameters`].
pub fn get_relay(
&self,
retry_attempt: usize,
runtime_params: RuntimeParameters,
) -> Result<GetRelay, Error> {
- self.get_relay_with_custom_params(retry_attempt, &RETRY_ORDER, runtime_params)
+ let config_guard = self.config.lock().unwrap();
+ let config = SpecializedSelectorConfig::from(&*config_guard);
+ match config {
+ SpecializedSelectorConfig::Custom(custom_config) => {
+ Ok(GetRelay::Custom(custom_config.clone()))
+ }
+ SpecializedSelectorConfig::Normal(normal_config) => {
+ let tunnel_protocol = normal_config.user_preferences.tunnel_protocol;
+ drop(config_guard);
+
+ match tunnel_protocol {
+ TunnelType::Wireguard => self.get_relay_with_custom_params(
+ retry_attempt,
+ &WIREGUARD_RETRY_ORDER,
+ runtime_params,
+ ),
+ TunnelType::OpenVpn => self.get_relay_with_custom_params(
+ retry_attempt,
+ &OPENVPN_RETRY_ORDER,
+ runtime_params,
+ ),
+ }
+ }
+ }
}
/// Returns a random relay and relay endpoint matching the current constraints defined by
@@ -652,28 +684,10 @@ impl RelaySelector {
custom_lists: &CustomListsSettings,
) -> Result<GetRelay, Error> {
match query.tunnel_protocol() {
- Constraint::Only(TunnelType::Wireguard) => {
- Self::get_wireguard_relay(query, custom_lists, parsed_relays)
- }
- Constraint::Only(TunnelType::OpenVpn) => {
- Self::get_openvpn_relay(query, custom_lists, parsed_relays)
- }
- Constraint::Any => {
- // Try Wireguard, then OpenVPN, then fail
- for tunnel_type in [TunnelType::Wireguard, TunnelType::OpenVpn] {
- let mut new_query = query.clone();
- new_query
- .set_tunnel_protocol(Constraint::Only(tunnel_type))
- .expect("unreachable since tunnel constraint is 'any', not wg");
- // If a suitable relay is found, short-circuit and return it
- if let Ok(relay) =
- Self::get_relay_inner(&new_query, parsed_relays, custom_lists)
- {
- return Ok(relay);
- }
- }
- Err(Error::NoRelay)
+ TunnelType::Wireguard => {
+ Self::get_wireguard_relay_inner(query, custom_lists, parsed_relays)
}
+ TunnelType::OpenVpn => Self::get_openvpn_relay(query, custom_lists, parsed_relays),
}
}
@@ -683,13 +697,13 @@ impl RelaySelector {
parsed_relays: &RelayList,
custom_lists: &CustomListsSettings,
) -> Result<GetRelay, Error> {
- // FIXME: A bit of defensive programming - calling `get_wireguard_relay` with a query that
+ // FIXME: A bit of defensive programming - calling `get_wireguard_relay_inner` with a query that
// doesn't specify Wireguard as the desired tunnel type is not valid and will lead
// to unwanted behavior. This should be seen as a workaround, and it would be nicer
// to lift this invariant to be checked by the type system instead.
let mut query = query.clone();
- query.set_tunnel_protocol(Constraint::Only(TunnelType::Wireguard))?;
- Self::get_wireguard_relay(&query, custom_lists, parsed_relays)
+ query.set_tunnel_protocol(TunnelType::Wireguard)?;
+ Self::get_wireguard_relay_inner(&query, custom_lists, parsed_relays)
}
/// Derive a valid relay configuration from `query`.
@@ -705,15 +719,12 @@ impl RelaySelector {
/// * `Ok(GetRelay::Wireguard)` otherwise
///
/// [`MullvadEndpoint`]: mullvad_types::endpoint::MullvadEndpoint
- fn get_wireguard_relay(
+ fn get_wireguard_relay_inner(
query: &RelayQuery,
custom_lists: &CustomListsSettings,
parsed_relays: &RelayList,
) -> Result<GetRelay, Error> {
- assert_eq!(
- query.tunnel_protocol(),
- Constraint::Only(TunnelType::Wireguard)
- );
+ assert_eq!(query.tunnel_protocol(), TunnelType::Wireguard);
let inner = Self::get_wireguard_relay_config(query, custom_lists, parsed_relays)?;
let endpoint = Self::get_wireguard_endpoint(query, parsed_relays, &inner)?;
let obfuscator =
@@ -966,10 +977,7 @@ impl RelaySelector {
custom_lists: &CustomListsSettings,
parsed_relays: &RelayList,
) -> Result<GetRelay, Error> {
- assert_eq!(
- query.tunnel_protocol(),
- Constraint::Only(TunnelType::OpenVpn)
- );
+ assert_eq!(query.tunnel_protocol(), TunnelType::OpenVpn);
let exit =
Self::choose_openvpn_relay(query, custom_lists, parsed_relays).ok_or(Error::NoRelay)?;
let endpoint = Self::get_openvpn_endpoint(query, &exit, parsed_relays)?;
diff --git a/mullvad-relay-selector/src/relay_selector/query.rs b/mullvad-relay-selector/src/relay_selector/query.rs
index 50bed8aa13..1cf82ded20 100644
--- a/mullvad-relay-selector/src/relay_selector/query.rs
+++ b/mullvad-relay-selector/src/relay_selector/query.rs
@@ -79,7 +79,7 @@ pub struct RelayQuery {
location: Constraint<LocationConstraint>,
providers: Constraint<Providers>,
ownership: Constraint<Ownership>,
- tunnel_protocol: Constraint<TunnelType>,
+ tunnel_protocol: TunnelType,
wireguard_constraints: WireguardRelayQuery,
openvpn_constraints: OpenVpnRelayQuery,
}
@@ -90,7 +90,7 @@ impl RelayQuery {
location: Constraint<LocationConstraint>,
providers: Constraint<Providers>,
ownership: Constraint<Ownership>,
- tunnel_protocol: Constraint<TunnelType>,
+ tunnel_protocol: TunnelType,
wireguard_constraints: WireguardRelayQuery,
openvpn_constraints: OpenVpnRelayQuery,
) -> Result<RelayQuery, Error> {
@@ -108,11 +108,11 @@ impl RelayQuery {
fn validate(&mut self) -> Result<(), Error> {
if self.core_privacy_feature_enabled() {
- if self.tunnel_protocol == Constraint::Only(TunnelType::OpenVpn) {
+ if self.tunnel_protocol == TunnelType::OpenVpn {
log::error!("Cannot use OpenVPN with a core privacy feature enabled (DAITA = {}, PQ = {}, or multihop = {})", self.wireguard_constraints.daita, self.wireguard_constraints.quantum_resistant, self.wireguard_constraints.multihop());
return Err(Error::InvalidConstraints);
}
- self.tunnel_protocol = Constraint::Only(TunnelType::Wireguard);
+ self.tunnel_protocol = TunnelType::Wireguard;
}
Ok(())
}
@@ -139,14 +139,11 @@ impl RelayQuery {
self.ownership
}
- pub fn tunnel_protocol(&self) -> Constraint<TunnelType> {
+ pub fn tunnel_protocol(&self) -> TunnelType {
self.tunnel_protocol
}
- pub fn set_tunnel_protocol(
- &mut self,
- tunnel_protocol: Constraint<TunnelType>,
- ) -> Result<(), Error> {
+ pub fn set_tunnel_protocol(&mut self, tunnel_protocol: TunnelType) -> Result<(), Error> {
self.set_if_valid(|query| query.tunnel_protocol = tunnel_protocol)
}
@@ -245,7 +242,7 @@ impl Default for RelayQuery {
location: Constraint::Any,
providers: Constraint::Any,
ownership: Constraint::Any,
- tunnel_protocol: Constraint::Any,
+ tunnel_protocol: TunnelType::default(),
wireguard_constraints: WireguardRelayQuery::new(),
openvpn_constraints: OpenVpnRelayQuery::new(),
}
@@ -593,7 +590,7 @@ pub mod builder {
daita: Any,
quantum_resistant: Any,
};
- self.query.tunnel_protocol = Constraint::Only(TunnelType::Wireguard);
+ self.query.tunnel_protocol = TunnelType::Wireguard;
// Update the type state
RelayQueryBuilder {
query: self.query,
@@ -607,7 +604,7 @@ pub mod builder {
transport_port: Any,
bridge_settings: Any,
};
- self.query.tunnel_protocol = Constraint::Only(TunnelType::OpenVpn);
+ self.query.tunnel_protocol = TunnelType::OpenVpn;
// Update the type state
RelayQueryBuilder {
query: self.query,
@@ -1066,7 +1063,7 @@ mod test {
fn test_relay_query_daita_openvpn() {
let mut query = RelayQueryBuilder::new().wireguard().daita().build();
query
- .set_tunnel_protocol(Constraint::Only(TunnelType::OpenVpn))
+ .set_tunnel_protocol(TunnelType::OpenVpn)
.expect_err("expected query to be invalid for OpenVPN");
}
@@ -1076,7 +1073,7 @@ mod test {
fn test_relay_query_multihop_openvpn() {
let mut query = RelayQueryBuilder::new().wireguard().multihop().build();
query
- .set_tunnel_protocol(Constraint::Only(TunnelType::OpenVpn))
+ .set_tunnel_protocol(TunnelType::OpenVpn)
.expect_err("expected query to be invalid for OpenVPN");
}
@@ -1089,46 +1086,7 @@ mod test {
.quantum_resistant()
.build();
query
- .set_tunnel_protocol(Constraint::Only(TunnelType::OpenVpn))
+ .set_tunnel_protocol(TunnelType::OpenVpn)
.expect_err("expected query to be invalid for OpenVPN");
}
-
- /// The tunnel protocol should be constrained to Wireguard when enabling DAITA
- /// DAITA is a core privacy feature
- #[test]
- fn test_relay_query_daita_wireguard() {
- let mut query = RelayQueryBuilder::new().wireguard().daita().build();
- query.set_tunnel_protocol(Constraint::Any).unwrap();
- assert_eq!(
- query.tunnel_protocol(),
- Constraint::Only(TunnelType::Wireguard)
- );
- }
-
- /// The tunnel protocol should be constrained to Wireguard when enabling multihop
- /// Multihop is a core privacy feature
- #[test]
- fn test_relay_query_multihop_wireguard() {
- let mut query = RelayQueryBuilder::new().wireguard().multihop().build();
- query.set_tunnel_protocol(Constraint::Any).unwrap();
- assert_eq!(
- query.tunnel_protocol(),
- Constraint::Only(TunnelType::Wireguard)
- );
- }
-
- /// The tunnel protocol should be constrained to Wireguard when enabling PQ
- /// PQ is a core privacy feature
- #[test]
- fn test_relay_query_quantum_resistant_wireguard() {
- let mut query = RelayQueryBuilder::new()
- .wireguard()
- .quantum_resistant()
- .build();
- query.set_tunnel_protocol(Constraint::Any).unwrap();
- assert_eq!(
- query.tunnel_protocol(),
- Constraint::Only(TunnelType::Wireguard)
- );
- }
}
diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs
index 6908d468de..05f6f8adff 100644
--- a/mullvad-relay-selector/tests/relay_selector.rs
+++ b/mullvad-relay-selector/tests/relay_selector.rs
@@ -16,7 +16,7 @@ use talpid_types::net::{
use mullvad_relay_selector::{
query::{builder::RelayQueryBuilder, BridgeQuery, ObfuscationQuery, OpenVpnRelayQuery},
Error, GetRelay, RelaySelector, RuntimeParameters, SelectedObfuscator, SelectorConfig,
- WireguardConfig, RETRY_ORDER,
+ WireguardConfig, OPENVPN_RETRY_ORDER, WIREGUARD_RETRY_ORDER,
};
use mullvad_types::{
constraints::Constraint,
@@ -24,7 +24,7 @@ use mullvad_types::{
location::Location,
relay_constraints::{
BridgeConstraints, BridgeState, GeographicLocationConstraint, Ownership, Providers,
- RelayOverride, TransportPort,
+ RelayConstraints, RelayOverride, RelaySettings, TransportPort,
},
relay_list::{
BridgeEndpointData, OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayEndpointData,
@@ -306,18 +306,18 @@ fn supports_daita(relay: &Relay) -> bool {
}
}
-/// This is not an actual test. Rather, it serves as a reminder that if [`RETRY_ORDER`] is modified,
-/// the programmer should be made aware to update all external documents which rely on the retry
-/// order to be correct.
+/// This is not an actual test. Rather, it serves as a reminder that if [`WIREGUARD_RETRY_ORDER`] is
+/// modified, the programmer should be made aware to update all external documents which rely on the
+/// retry order to be correct.
///
/// When all necessary changes have been made, feel free to update this test to mirror the new
/// [`RETRY_ORDER`].
#[test]
-fn assert_retry_order() {
- use talpid_types::net::{IpVersion, TransportProtocol};
+fn assert_wireguard_retry_order() {
+ use talpid_types::net::IpVersion;
let expected_retry_order = vec![
- // 1
- RelayQueryBuilder::new().build(),
+ // 1 (wireguard)
+ RelayQueryBuilder::new().wireguard().build(),
// 2
RelayQueryBuilder::new().wireguard().port(443).build(),
// 3
@@ -335,13 +335,37 @@ fn assert_retry_order() {
.udp2tcp()
.ip_version(IpVersion::V6)
.build(),
- // 7
+ ];
+
+ assert!(
+ *WIREGUARD_RETRY_ORDER == expected_retry_order,
+ "
+ The relay selector's retry order has been modified!
+ Make sure to update `docs/relay-selector.md` with these changes.
+ Lastly, you may go ahead and fix this test to reflect the new retry order.
+ "
+ );
+}
+
+/// This is not an actual test. Rather, it serves as a reminder that if [`OPENVPN_RETRY_ORDER`] is
+/// modified, the programmer should be made aware to update all external documents which rely on
+/// the retry order to be correct.
+///
+/// When all necessary changes have been made, feel free to update this test to mirror the new
+/// [`RETRY_ORDER`].
+#[test]
+fn assert_openvpn_retry_order() {
+ use talpid_types::net::TransportProtocol;
+ let expected_retry_order = vec![
+ // 1
+ RelayQueryBuilder::new().openvpn().build(),
+ // 2
RelayQueryBuilder::new()
.openvpn()
.transport_protocol(TransportProtocol::Tcp)
.port(443)
.build(),
- // 8
+ // 3
RelayQueryBuilder::new()
.openvpn()
.transport_protocol(TransportProtocol::Tcp)
@@ -350,7 +374,7 @@ fn assert_retry_order() {
];
assert!(
- *RETRY_ORDER == expected_retry_order,
+ *OPENVPN_RETRY_ORDER == expected_retry_order,
"
The relay selector's retry order has been modified!
Make sure to update `docs/relay-selector.md` with these changes.
@@ -359,16 +383,16 @@ fn assert_retry_order() {
);
}
-/// Test whether the relay selector seems to respect the order as defined by [`RETRY_ORDER`].
+/// Test whether the relay selector seems to respect the order as defined by [`WIREGUARD_RETRY_ORDER`].
#[test]
-fn test_retry_order() {
+fn test_wireguard_retry_order() {
// In order to for the relay queries defined by `RETRY_ORDER` to always take precedence,
// the user settings need to be 'neutral' on the type of relay that it wants to connect to.
// A default `SelectorConfig` *should* have this property, but a more robust way to guarantee
// this would be to create a neutral relay query and supply it to the relay selector at every
// call to the `get_relay` function.
let relay_selector = default_relay_selector();
- for (retry_attempt, query) in RETRY_ORDER.iter().enumerate() {
+ for (retry_attempt, query) in WIREGUARD_RETRY_ORDER.iter().enumerate() {
let relay = relay_selector
.get_relay(retry_attempt, RuntimeParameters { ipv6: true })
.unwrap_or_else(|_| panic!("Retry attempt {retry_attempt} did not yield any relay"));
@@ -376,7 +400,7 @@ fn test_retry_order() {
let tunnel_type = tunnel_type(&unwrap_relay(relay.clone()));
assert_eq!(
tunnel_type,
- query.tunnel_protocol().unwrap_or(TunnelType::Wireguard),
+ query.tunnel_protocol(),
"Retry attempt {retry_attempt} yielded an unexpected tunnel type"
);
// Then perform some protocol-specific probing as well.
@@ -404,6 +428,41 @@ fn test_retry_order() {
obfuscator.is_some(),
});
}
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Test whether the relay selector seems to respect the order as defined by [`OPENVPN_RETRY_ORDER`].
+#[test]
+fn test_openvpn_retry_order() {
+ // In order to for the relay queries defined by `RETRY_ORDER` to always take precedence,
+ // the user settings need to be 'neutral' on the type of relay that it wants to connect to.
+ // A default `SelectorConfig` *should* have this property, but a more robust way to guarantee
+ // this would be to create a neutral relay query and supply it to the relay selector at every
+ // call to the `get_relay` function.
+ let mut relay_selector = default_relay_selector();
+ relay_selector.set_config(SelectorConfig {
+ relay_settings: RelaySettings::Normal(RelayConstraints {
+ tunnel_protocol: TunnelType::OpenVpn,
+ ..Default::default()
+ }),
+ ..Default::default()
+ });
+
+ for (retry_attempt, query) in OPENVPN_RETRY_ORDER.iter().enumerate() {
+ let relay = relay_selector
+ .get_relay(retry_attempt, RuntimeParameters { ipv6: true })
+ .unwrap_or_else(|_| panic!("Retry attempt {retry_attempt} did not yield any relay"));
+ // For each relay, cross-check that the it has the expected tunnel protocol
+ let tunnel_type = tunnel_type(&unwrap_relay(relay.clone()));
+ assert_eq!(
+ tunnel_type,
+ query.tunnel_protocol(),
+ "Retry attempt {retry_attempt} yielded an unexpected tunnel type"
+ );
+ // Then perform some protocol-specific probing as well.
+ match relay {
GetRelay::OpenVpn {
endpoint, bridge, ..
} => {
@@ -428,7 +487,7 @@ fn test_retry_order() {
expected = query.openvpn_constraints().port.unwrap().protocol, actual = endpoint.protocol
);
}
- GetRelay::Custom(_) => unreachable!(),
+ _ => unreachable!(),
}
}
}
@@ -454,7 +513,7 @@ fn prefer_wireguard_when_auto() {
/// If a Wireguard relay is only specified by it's hostname (and not tunnel type), the relay
/// selector should still return a relay of the correct tunnel type (Wireguard).
#[test]
-fn test_prefer_wireguard_if_location_supports_it() {
+fn test_fail_wireguard_if_wrong_tunnel_type() {
let relay_selector = default_relay_selector();
let query = RelayQueryBuilder::new()
.location(GeographicLocationConstraint::hostname(
@@ -462,19 +521,19 @@ fn test_prefer_wireguard_if_location_supports_it() {
"got",
"se9-wireguard",
))
+ .openvpn()
.build();
- for _ in 0..RETRY_ORDER.len() {
- let relay = relay_selector.get_relay_by_query(query.clone()).unwrap();
- let tunnel_typ = tunnel_type(&unwrap_relay(relay));
- assert_eq!(tunnel_typ, TunnelType::Wireguard);
+ for _ in 0..WIREGUARD_RETRY_ORDER.len() {
+ relay_selector
+ .get_relay_by_query(query.clone())
+ .expect_err("expected no match (tunnel type is openvpn)");
}
}
-/// If an OpenVPN relay is only specified by it's hostname (and not tunnel type), the relay selector
-/// should still return a relay of the correct tunnel type (OpenVPN).
+/// Fail to select an OpenVPN relay if the tunnel type is WireGuard
#[test]
-fn test_prefer_openvpn_if_location_supports_it() {
+fn test_fail_openvpn_location_wrong_tunnel_type() {
let relay_selector = default_relay_selector();
let query = RelayQueryBuilder::new()
.location(GeographicLocationConstraint::hostname(
@@ -484,10 +543,10 @@ fn test_prefer_openvpn_if_location_supports_it() {
))
.build();
- for _ in 0..RETRY_ORDER.len() {
- let relay = relay_selector.get_relay_by_query(query.clone()).unwrap();
- let tunnel_typ = tunnel_type(&unwrap_relay(relay));
- assert_eq!(tunnel_typ, TunnelType::OpenVpn);
+ for _ in 0..OPENVPN_RETRY_ORDER.len() {
+ relay_selector
+ .get_relay_by_query(query.clone())
+ .expect_err("expected no match (tunnel type is wireguard)");
}
}
@@ -784,22 +843,6 @@ fn test_selecting_wireguard_location_will_consider_multihop() {
}
}
-/// Construct a query for multihop configuration, but the tunnel protocol is forcefully set to Any.
-/// If a Wireguard relay is chosen, the relay selector should also pick an accompanying entry relay.
-#[test]
-fn test_selecting_any_relay_will_consider_multihop() {
- let relay_selector = default_relay_selector();
- let mut query = RelayQueryBuilder::new().wireguard().multihop().build();
- query.set_tunnel_protocol(Constraint::Any).unwrap();
-
- for _ in 0..100 {
- let relay = relay_selector.get_relay_by_query(query.clone()).unwrap();
- assert!(matches!(relay, GetRelay::Wireguard { inner: WireguardConfig::Multihop { .. }, .. }),
- "Relay selector should have picked a Wireguard relay with multihop, instead chose {relay:?}"
- );
- }
-}
-
/// Test whether Shadowsocks is always selected as the obfuscation protocol when Shadowsocks is
/// selected.
#[test]
@@ -1129,11 +1172,17 @@ fn test_providers() {
}
}
-/// Verify that bridges are automatically used when bridge mode is set
-/// to automatic.
+/// Verify that bridges are automatically used when bridge mode is set to automatic.
#[test]
fn test_openvpn_auto_bridge() {
- let relay_selector = default_relay_selector();
+ let mut relay_selector = default_relay_selector();
+ relay_selector.set_config(SelectorConfig {
+ relay_settings: RelaySettings::Normal(RelayConstraints {
+ tunnel_protocol: TunnelType::OpenVpn,
+ ..Default::default()
+ }),
+ ..Default::default()
+ });
let retry_order = [
// This attempt should not use bridge
RelayQueryBuilder::new().openvpn().build(),
@@ -1404,24 +1453,6 @@ fn openvpn_bridge_with_automatic_transport_protocol() {
}
}
-/// Always select a WireGuard relay when DAITA is enabled
-/// DAITA is a core privacy feature
-#[test]
-fn test_daita_any_tunnel_protocol() {
- let relay_selector = RelaySelector::from_list(SelectorConfig::default(), RELAYS.clone());
- let mut query = RelayQueryBuilder::new().wireguard().daita().build();
- query
- .set_tunnel_protocol(Constraint::Any)
- .expect("expected query to be valid for any tunnel protocol");
-
- let relay = relay_selector.get_relay_by_query(query);
-
- assert!(
- matches!(relay, Ok(GetRelay::Wireguard { .. })),
- "expected wg relay, got {relay:?}"
- );
-}
-
/// Always use smart routing to select a DAITA-enabled entry relay if both smart routing and
/// multihop is enabled. This applies even if the entry is set explicitly.
/// DAITA is a core privacy feature
@@ -1475,45 +1506,6 @@ fn test_daita_smart_routing_overrides_multihop() {
);
}
-/// Always select a WireGuard relay when multihop is enabled
-/// Multihop is a core privacy feature
-#[test]
-fn test_multihop_any_tunnel_protocol() {
- let relay_selector = RelaySelector::from_list(SelectorConfig::default(), RELAYS.clone());
- let mut query = RelayQueryBuilder::new().wireguard().multihop().build();
- query
- .set_tunnel_protocol(Constraint::Any)
- .expect("expected query to be valid for any tunnel protocol");
-
- let relay = relay_selector.get_relay_by_query(query);
-
- assert!(
- matches!(relay, Ok(GetRelay::Wireguard { .. })),
- "expected wg relay, got {relay:?}"
- );
-}
-
-/// Always select a WireGuard relay when quantum resistance is enabled
-/// PQ is a core privacy feature
-#[test]
-fn test_quantum_resistant_any_tunnel_protocol() {
- let relay_selector = RelaySelector::from_list(SelectorConfig::default(), RELAYS.clone());
- let mut query = RelayQueryBuilder::new()
- .wireguard()
- .quantum_resistant()
- .build();
- query
- .set_tunnel_protocol(Constraint::Any)
- .expect("expected query to be valid for any tunnel protocol");
-
- let relay = relay_selector.get_relay_by_query(query);
-
- assert!(
- matches!(relay, Ok(GetRelay::Wireguard { .. })),
- "expected wg relay, got {relay:?}"
- );
-}
-
/// Return only entry relays that support DAITA when DAITA filtering is enabled. All relays that
/// support DAITA also support NOT DAITA. Thus, disabling it should not cause any WireGuard relays
/// to be filtered out.
@@ -1652,7 +1644,7 @@ fn test_daita() {
}
}
-/// Check that if the original user query would yield a relay, the result of running the query
+/// Check that if the original user query would yield a relay, the result of running the query
/// which is the intersection between the user query and any of the default queries shall never
/// fail.
#[test]
@@ -1671,7 +1663,7 @@ fn valid_user_setting_should_yield_relay() {
};
let relay_selector = RelaySelector::from_list(config, RELAYS.clone());
let user_result = relay_selector.get_relay_by_query(user_query.clone());
- for retry_attempt in 0..RETRY_ORDER.len() {
+ for retry_attempt in 0..WIREGUARD_RETRY_ORDER.len() {
let post_unification_result =
relay_selector.get_relay(retry_attempt, RuntimeParameters::default());
if user_result.is_ok() {
diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs
index e6eaa7ca55..fb0c932286 100644
--- a/mullvad-types/src/relay_constraints.rs
+++ b/mullvad-types/src/relay_constraints.rs
@@ -123,7 +123,7 @@ pub struct RelayConstraints {
pub location: Constraint<LocationConstraint>,
pub providers: Constraint<Providers>,
pub ownership: Constraint<Ownership>,
- pub tunnel_protocol: Constraint<TunnelType>,
+ pub tunnel_protocol: TunnelType,
pub wireguard_constraints: WireguardConstraints,
pub openvpn_constraints: OpenVpnConstraints,
}
diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs
index 2d67fc5cfe..8b57ba5410 100644
--- a/talpid-types/src/net/mod.rs
+++ b/talpid-types/src/net/mod.rs
@@ -152,12 +152,13 @@ impl From<openvpn::TunnelParameters> for TunnelParameters {
}
/// The tunnel protocol used by a [`TunnelEndpoint`].
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename = "tunnel_type")]
pub enum TunnelType {
#[serde(rename = "openvpn")]
OpenVpn,
#[serde(rename = "wireguard")]
+ #[default]
Wireguard,
}