diff options
| author | David Lönnhager <david.l@mullvad.net> | 2025-03-04 14:28:52 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2025-03-05 09:39:30 +0100 |
| commit | be0877a65e0f5e53a06ca7ee7a1303b5716b41dd (patch) | |
| tree | 544ab5d544ba148c7656d51d1ab508e01f218629 | |
| parent | a2be04b4792ec942bd20d5fb69bcfa8cf7d854d7 (diff) | |
| download | mullvadvpn-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.rs | 31 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 14 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/relay_constraints.rs | 22 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/lib.rs | 3 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/relay_selector/matcher.rs | 11 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/relay_selector/mod.rs | 100 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/relay_selector/query.rs | 66 | ||||
| -rw-r--r-- | mullvad-relay-selector/tests/relay_selector.rs | 206 | ||||
| -rw-r--r-- | mullvad-types/src/relay_constraints.rs | 2 | ||||
| -rw-r--r-- | talpid-types/src/net/mod.rs | 3 |
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, } |
