summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-04-27 11:04:25 +0200
committerDavid Lönnhager <david.l@mullvad.net>2022-04-27 11:04:25 +0200
commit9a5580ecbd7fed165138e08eb4ff3a270e8bfa31 (patch)
treea2f8e6c54d4c4321a159d2166a606edef1e71f40
parentb2fec06992fdac12bc0976490ca2e9f0aff0e37a (diff)
parentaf8d1c3fe75f94636fe96940ef0be8c0ec4c0428 (diff)
downloadmullvadvpn-9a5580ecbd7fed165138e08eb4ff3a270e8bfa31.tar.xz
mullvadvpn-9a5580ecbd7fed165138e08eb4ff3a270e8bfa31.zip
Merge branch 'fix-multihop-any-tunnel-constraint'
-rw-r--r--CHANGELOG.md3
-rw-r--r--mullvad-relay-selector/src/lib.rs144
-rw-r--r--mullvad-relay-selector/src/matcher.rs6
3 files changed, 99 insertions, 54 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7179694398..a2027d8ed1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -60,6 +60,9 @@ Line wrap the file at 100 chars. Th
- Parse old account history formats correctly when they are empty.
- Use suspend-aware timers for relay list updates and version checks on all platforms.
- Don't attempt to use bridges when using OpenVPN over UDP and bridge mode is set to auto.
+- Use the entry endpoint when the relay selector fails to find a relay using the preferred
+ constraints and the tunnel protocol is "any". Previously, the entry endpoint was ignored in this
+ case.
#### Windows
- Fix "Open Mullvad VPN" tray context menu item not working after toggling unpinned window setting.
diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs
index 48464f5616..e011d747dc 100644
--- a/mullvad-relay-selector/src/lib.rs
+++ b/mullvad-relay-selector/src/lib.rs
@@ -3,6 +3,7 @@
use chrono::{DateTime, Local};
use ipnetwork::IpNetwork;
+use matcher::AnyTunnelMatcher;
use mullvad_types::{
endpoint::{MullvadEndpoint, MullvadWireguardEndpoint},
location::{Coordinates, Location},
@@ -508,64 +509,111 @@ impl RelaySelector {
self.get_wireguard_multi_hop_endpoint(entry_relay_matcher, location.clone())
}
- /// Returns a tunnel endpoint of any type, should only be used when the user hasn't specified a
- /// tunnel protocol.
- fn get_any_tunnel_endpoint(
+ /// Like [Self::get_tunnel_endpoint_internal] but also selects an entry endpoint if applicable.
+ fn get_multihop_tunnel_endpoint_internal(
&self,
relay_constraints: &RelayConstraints,
- bridge_state: BridgeState,
- retry_attempt: u32,
) -> Result<NormalSelectedRelay, Error> {
- let preferred_constraints =
- self.preferred_constraints(&relay_constraints, bridge_state, retry_attempt);
- let original_matcher: RelayMatcher<_> = relay_constraints.clone().into();
+ let mut matcher: RelayMatcher<AnyTunnelMatcher> = relay_constraints.clone().into();
- let preferred_tunnel_protocol = preferred_constraints.tunnel_protocol;
- let preferred_matcher: RelayMatcher<_> = preferred_constraints.into();
+ let mut selected_entry_relay = None;
+ let mut selected_entry_endpoint = None;
+ let mut entry_matcher = RelayMatcher {
+ location: relay_constraints
+ .wireguard_constraints
+ .entry_location
+ .clone(),
+ ..matcher.clone()
+ }
+ .to_wireguard_matcher();
- match preferred_tunnel_protocol {
- Constraint::Only(TunnelType::Wireguard)
- if relay_constraints.wireguard_constraints.use_multihop =>
+ // Pick the entry relay first if its location constraint is a subset of the exit location.
+ if relay_constraints.wireguard_constraints.use_multihop {
+ matcher.tunnel.wireguard = WIREGUARD_EXIT_CONSTRAINTS.clone().into();
+ if relay_constraints
+ .wireguard_constraints
+ .entry_location
+ .is_subset(&matcher.location)
{
- let exit_location = relay_constraints.location.clone();
- let mut preferred_entry_matcher = preferred_matcher.to_wireguard_matcher();
- preferred_entry_matcher.location = relay_constraints
- .wireguard_constraints
- .entry_location
- .clone();
- let mut original_entry_matcher = original_matcher.to_wireguard_matcher();
- original_entry_matcher.location = relay_constraints
- .wireguard_constraints
- .entry_location
- .clone();
- self.get_wireguard_multi_hop_endpoint(
- preferred_entry_matcher,
- exit_location.clone(),
- )
- .or_else(|_| {
- self.get_wireguard_multi_hop_endpoint(original_entry_matcher, exit_location)
- })
+ if let Ok((entry_relay, entry_endpoint)) = self.get_entry_endpoint(&entry_matcher) {
+ matcher.tunnel.wireguard.peer = Some(entry_relay.clone());
+ selected_entry_relay = Some(entry_relay);
+ selected_entry_endpoint = Some(entry_endpoint);
+ }
}
+ }
- _ => {
- if let Ok(result) = self.get_tunnel_endpoint_internal(&preferred_matcher) {
- log::debug!(
- "Relay matched on highest preference for retry attempt {}",
- retry_attempt
+ let mut selected_relay = self.get_tunnel_endpoint_internal(&matcher)?;
+
+ // Pick the entry relay last if its location constraint is NOT a subset of the exit
+ // location.
+ if matches!(selected_relay.endpoint, MullvadEndpoint::Wireguard(..))
+ && relay_constraints.wireguard_constraints.use_multihop
+ {
+ if !relay_constraints
+ .wireguard_constraints
+ .entry_location
+ .is_subset(&matcher.location)
+ {
+ entry_matcher.tunnel.peer = Some(selected_relay.exit_relay.clone());
+ if let Ok((entry_relay, entry_endpoint)) = self.get_entry_endpoint(&entry_matcher) {
+ selected_entry_relay = Some(entry_relay);
+ selected_entry_endpoint = Some(entry_endpoint);
+ }
+ }
+
+ match (selected_entry_endpoint, selected_entry_relay) {
+ (Some(mut entry_endpoint), Some(entry_relay)) => {
+ Self::set_entry_peers(
+ &selected_relay.endpoint.unwrap_wireguard().peer,
+ &mut entry_endpoint,
);
- Ok(result)
- } else if let Ok(result) = self.get_tunnel_endpoint_internal(&original_matcher) {
- log::debug!(
- "Relay matched on second preference for retry attempt {}",
- retry_attempt
+
+ log::info!(
+ "Selected entry relay {} at {} going through {} at {}",
+ entry_relay.hostname,
+ entry_endpoint.peer.endpoint.ip(),
+ selected_relay.exit_relay.hostname,
+ selected_relay.endpoint.to_endpoint().address.ip(),
);
- Ok(result)
- } else {
- log::warn!("No relays matching {}", &relay_constraints);
- Err(Error::NoRelay)
+
+ selected_relay.endpoint = MullvadEndpoint::Wireguard(entry_endpoint);
+ selected_relay.entry_relay = Some(entry_relay);
}
+ _ => return Err(Error::NoRelay),
}
}
+
+ Ok(selected_relay)
+ }
+
+ /// Returns a tunnel endpoint of any type, should only be used when the user hasn't specified a
+ /// tunnel protocol.
+ fn get_any_tunnel_endpoint(
+ &self,
+ relay_constraints: &RelayConstraints,
+ bridge_state: BridgeState,
+ retry_attempt: u32,
+ ) -> Result<NormalSelectedRelay, Error> {
+ let preferred_constraints =
+ self.preferred_constraints(&relay_constraints, bridge_state, retry_attempt);
+
+ if let Ok(result) = self.get_multihop_tunnel_endpoint_internal(&preferred_constraints) {
+ log::debug!(
+ "Relay matched on highest preference for retry attempt {}",
+ retry_attempt
+ );
+ Ok(result)
+ } else if let Ok(result) = self.get_multihop_tunnel_endpoint_internal(&relay_constraints) {
+ log::debug!(
+ "Relay matched on second preference for retry attempt {}",
+ retry_attempt
+ );
+ Ok(result)
+ } else {
+ log::warn!("No relays matching {}", &relay_constraints);
+ Err(Error::NoRelay)
+ }
}
// This function ignores the tunnel type constraint on purpose.
@@ -642,12 +690,6 @@ impl RelaySelector {
}
};
- if relay_constraints.wireguard_constraints.port.is_any() {
- relay_constraints.wireguard_constraints.port = preferred_port;
- }
-
- relay_constraints.tunnel_protocol = Constraint::Only(preferred_tunnel);
-
relay_constraints
}
diff --git a/mullvad-relay-selector/src/matcher.rs b/mullvad-relay-selector/src/matcher.rs
index 7d75141b16..ffd07b9121 100644
--- a/mullvad-relay-selector/src/matcher.rs
+++ b/mullvad-relay-selector/src/matcher.rs
@@ -109,13 +109,13 @@ pub type OpenVpnMatcher = OpenVpnConstraints;
#[derive(Clone)]
pub struct AnyTunnelMatcher {
- wireguard: WireguardMatcher,
- openvpn: OpenVpnMatcher,
+ pub wireguard: WireguardMatcher,
+ pub openvpn: OpenVpnMatcher,
/// in the case that a user hasn't specified a tunnel protocol, the relay
/// selector might still construct preferred constraints that do select a
/// specific tunnel protocol, which is why the tunnel type may be specified
/// in the `AnyTunnelMatcher`.
- tunnel_type: Constraint<TunnelType>,
+ pub tunnel_type: Constraint<TunnelType>,
}
impl TunnelMatcher for AnyTunnelMatcher {