diff options
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | docs/relay-selector.md | 10 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-daemon/src/relays.rs | 121 |
5 files changed, 111 insertions, 24 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 9375d30ccc..049739f95d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ Line wrap the file at 100 chars. Th leaking any data during the reconnection. ### Changed +- Prefer WireGuard when tunnel protocol is set to _auto_ on Linux and MacOS. + #### Android - Allow other apps to request the VPN tunnel to connect or disconnect. diff --git a/Cargo.lock b/Cargo.lock index 1341249e2d..a3ede6d05a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1251,6 +1251,7 @@ name = "mullvad-daemon" version = "2020.1.0" dependencies = [ "android_logger 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/docs/relay-selector.md b/docs/relay-selector.md index 286ce8e0e7..8da8ea69a9 100644 --- a/docs/relay-selector.md +++ b/docs/relay-selector.md @@ -41,11 +41,17 @@ Endpoints may be filtered by: Whilst all user selected constraints are always honored, when the user hasn't selected any specific constraints, following default ones will take effect: -- If no tunnel protocol is specified for tunnel endpoints, then by default only OpenVPN - endpoints will be selected. +- If no tunnel protocol is specified for tunnel endpoints, then the behavior is different on Windows + and other platforms. + - On Windows, OpenVPN is used. + - On MacOS and Linux, first two connection attempts will use WireGuard, over a random port at + first and then port 53. From the third attempt onwards, OpenVPN will be used, alternating + between UDP on any port and TCP on port 443. - If the tunnel protocol is specified as WireGuard without any other protocol constraints, then the transport protocol is not applicable as only UDP endpoints exist and any port will be matched. + The target port alternates between a random one every two attempts, and port 53 for the next 2 + attempts. - If no OpenVPN tunnel constraints are specified, then the first two attempts at selecting a tunnel will try to select UDP endpoints on any port, and the third and fourth attempts will filter for diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index c2f934e716..f9e14b0e4b 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" publish = false [dependencies] +cfg-if = "0.1" chrono = { version = "0.4", features = ["serde"] } clap = "2.25" err-derive = "0.2.1" diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs index 558383b022..f81110badb 100644 --- a/mullvad-daemon/src/relays.rs +++ b/mullvad-daemon/src/relays.rs @@ -212,7 +212,7 @@ impl RelaySelector { retry_attempt: u32, ) -> Result<(Relay, MullvadEndpoint), Error> { let preferred_constraints = - Self::preferred_constraints(relay_constraints, bridge_state, retry_attempt); + self.preferred_constraints(relay_constraints, bridge_state, retry_attempt); if let Some((relay, endpoint)) = self.get_tunnel_endpoint_internal(&preferred_constraints) { debug!( "Relay matched on highest preference for retry attempt {}", @@ -233,25 +233,27 @@ impl RelaySelector { } fn preferred_constraints( + &self, original_constraints: &RelayConstraints, bridge_state: &BridgeState, retry_attempt: u32, ) -> RelayConstraints { - // Prefer UDP by default. But if that has failed a couple of times, then try TCP port 443, - // which works for many with UDP problems. After that, just alternate between protocols. - let (preferred_port, mut preferred_protocol) = match retry_attempt { - 0 | 1 => (Constraint::Any, TransportProtocol::Udp), - 2 | 3 => (Constraint::Only(443), TransportProtocol::Tcp), - attempt if attempt % 2 == 0 => (Constraint::Any, TransportProtocol::Udp), - _ => (Constraint::Any, TransportProtocol::Tcp), - }; - if *bridge_state == BridgeState::On { - preferred_protocol = TransportProtocol::Tcp; - } + let (preferred_port, preferred_protocol, preferred_tunnel) = + if *bridge_state != BridgeState::On { + self.preferred_tunnel_constraints(retry_attempt, &original_constraints.location) + } else { + ( + Constraint::Any, + TransportProtocol::Tcp, + TunnelProtocol::OpenVpn, + ) + }; + let mut relay_constraints = RelayConstraints { location: original_constraints.location.clone(), tunnel_protocol: original_constraints.tunnel_protocol.clone(), + wireguard_constraints: original_constraints.wireguard_constraints, ..Default::default() }; // Highest priority preference. Where we prefer OpenVPN using UDP. But without changing @@ -271,25 +273,32 @@ impl RelaySelector { relay_constraints.openvpn_constraints = original_constraints.openvpn_constraints; } - // TODO: remove this constraint once WireGuard can be enabled in auto-relay - // selection. - relay_constraints.tunnel_protocol = Constraint::Only(TunnelProtocol::OpenVpn); + + if relay_constraints.wireguard_constraints.port.is_any() { + relay_constraints.wireguard_constraints.port = preferred_port; + } + + relay_constraints.tunnel_protocol = Constraint::Only(preferred_tunnel); } Constraint::Only(TunnelProtocol::OpenVpn) => { - relay_constraints.openvpn_constraints = original_constraints.openvpn_constraints; - if *bridge_state == BridgeState::On - && relay_constraints.openvpn_constraints.protocol.is_any() - { + let openvpn_constraints = &mut relay_constraints.openvpn_constraints; + *openvpn_constraints = original_constraints.openvpn_constraints; + if *bridge_state == BridgeState::On && openvpn_constraints.protocol.is_any() { // FIXME: This is temporary while talpid-core only supports TCP proxies - relay_constraints.openvpn_constraints.protocol = - Constraint::Only(TransportProtocol::Tcp); + openvpn_constraints.protocol = Constraint::Only(TransportProtocol::Tcp); + } else if openvpn_constraints.port.is_any() && openvpn_constraints.protocol.is_any() + { + let (preferred_port, preferred_protocol) = + Self::preferred_openvpn_constraints(retry_attempt); + openvpn_constraints.port = preferred_port; + openvpn_constraints.protocol = Constraint::Only(preferred_protocol); } } Constraint::Only(TunnelProtocol::Wireguard) => { relay_constraints.wireguard_constraints = original_constraints.wireguard_constraints; // This ensures that if after the first 2 failed attempts the daemon does not - // conenct, then afterwards 2 of each 4 successive attempts will try to connect on + // connect, then afterwards 2 of each 4 successive attempts will try to connect on // port 53. if retry_attempt % 4 > 1 && relay_constraints.wireguard_constraints.port.is_any() { relay_constraints.wireguard_constraints.port = Constraint::Only(53); @@ -356,6 +365,74 @@ impl RelaySelector { }) } + /// Returns preferred constraints + #[allow(unused_variables)] + fn preferred_tunnel_constraints( + &self, + retry_attempt: u32, + location_constraint: &Constraint<LocationConstraint>, + ) -> (Constraint<u16>, TransportProtocol, TunnelProtocol) { + #[cfg(not(target_os = "windows"))] + { + let location_supports_wireguard = + self.parsed_relays.lock().relays().iter().any(|relay| { + relay.active + && !relay.tunnels.wireguard.is_empty() + && Self::relay_matches_location(relay, &location_constraint) + }); + // If location does not support WireGuard, defer to preferred OpenVPN tunnel + // constraints + if !location_supports_wireguard { + let (preferred_port, preferred_protocol) = + Self::preferred_openvpn_constraints(retry_attempt); + return (preferred_port, preferred_protocol, TunnelProtocol::OpenVpn); + } + + + // Try out WireGuard in the first two connection attempts, first with any port, + // afterwards on port 53. Afterwards, connect through OpenVPN alternating between UDP + // on any port twice and TCP on port 443 once. + match retry_attempt { + 0 if location_supports_wireguard => ( + Constraint::Any, + TransportProtocol::Udp, + TunnelProtocol::Wireguard, + ), + 1 => ( + Constraint::Only(53), + TransportProtocol::Udp, + TunnelProtocol::Wireguard, + ), + _ => { + let (preferred_port, preferred_protocol) = + Self::preferred_openvpn_constraints(retry_attempt - 2); + (preferred_port, preferred_protocol, TunnelProtocol::OpenVpn) + } + } + } + + #[cfg(target_os = "windows")] + { + let (preferred_port, preferred_protocol) = + Self::preferred_openvpn_constraints(retry_attempt); + + + (preferred_port, preferred_protocol, TunnelProtocol::OpenVpn) + } + } + + fn preferred_openvpn_constraints(retry_attempt: u32) -> (Constraint<u16>, TransportProtocol) { + // Prefer UDP by default. But if that has failed a couple of times, then try TCP port + // 443, which works for many with UDP problems. After that, just alternate + // between protocols. + match retry_attempt { + 0 | 1 => (Constraint::Any, TransportProtocol::Udp), + 2 | 3 => (Constraint::Only(443), TransportProtocol::Tcp), + attempt if attempt % 2 == 0 => (Constraint::Any, TransportProtocol::Udp), + _ => (Constraint::Any, TransportProtocol::Tcp), + } + } + /// Returns a random relay endpoint if any is matching the given constraints. fn get_tunnel_endpoint_internal( |
