summaryrefslogtreecommitdiffhomepage
path: root/mullvad-daemon/src
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2024-02-16 16:24:33 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2024-03-27 11:43:44 +0100
commit707ecf44bd2b21642e51c8b9f5440bc287bcc511 (patch)
tree1c4e914a879cc6d1c126db1e47019cc2f5f2cea4 /mullvad-daemon/src
parent66f2127aed8bea1e1434c7e8efc50293ebdd9223 (diff)
downloadmullvadvpn-707ecf44bd2b21642e51c8b9f5440bc287bcc511.tar.xz
mullvadvpn-707ecf44bd2b21642e51c8b9f5440bc287bcc511.zip
Refactor `mullvad-relay-selector`
Implement a system built on 'queries' for selecting appropriate relays. A query is a set of constraints which dictates which relay(s) that *can* be chosen by the relay selector. The user's settings can naturally be expressed as a query. The semantics of merging two queries in a way that always prefer user settings is defined by the new `Intersection` trait. Split `mullvad-relay-selector` into several modules: - `query.rs`: Definition of a query on different types of relays. This module is integral to the new API of `mullvad-relay-selector` - `matcher.rs`: Logic for filtering out candidate relays based on a query. - `detailer.rs`: Logic for deriving connection details for the selected relay. - `tests/`: Integration tests for the new relay selector. These tests only use the public APIs of `RelaySelector` and make sure that the output matches the expected output in different scenarios.
Diffstat (limited to 'mullvad-daemon/src')
-rw-r--r--mullvad-daemon/src/custom_list.rs7
-rw-r--r--mullvad-daemon/src/lib.rs8
-rw-r--r--mullvad-daemon/src/migrations/v1.rs2
-rw-r--r--mullvad-daemon/src/migrations/v4.rs2
-rw-r--r--mullvad-daemon/src/migrations/v5.rs2
-rw-r--r--mullvad-daemon/src/migrations/v6.rs2
-rw-r--r--mullvad-daemon/src/relay_list/mod.rs214
-rw-r--r--mullvad-daemon/src/relay_list/updater.rs201
-rw-r--r--mullvad-daemon/src/settings/mod.rs13
-rw-r--r--mullvad-daemon/src/tunnel.rs225
10 files changed, 343 insertions, 333 deletions
diff --git a/mullvad-daemon/src/custom_list.rs b/mullvad-daemon/src/custom_list.rs
index 8a9573fcb0..c15fd14a70 100644
--- a/mullvad-daemon/src/custom_list.rs
+++ b/mullvad-daemon/src/custom_list.rs
@@ -1,9 +1,8 @@
use crate::{new_selector_config, Daemon, Error, EventListener};
use mullvad_types::{
+ constraints::Constraint,
custom_list::{CustomList, Id},
- relay_constraints::{
- BridgeState, Constraint, LocationConstraint, RelaySettings, ResolvedBridgeSettings,
- },
+ relay_constraints::{BridgeState, LocationConstraint, RelaySettings, ResolvedBridgeSettings},
};
use talpid_types::net::TunnelType;
@@ -133,7 +132,7 @@ where
{
match endpoint.tunnel_type {
TunnelType::Wireguard => {
- if relay_settings.wireguard_constraints.use_multihop {
+ if relay_settings.wireguard_constraints.multihop() {
if let Constraint::Only(LocationConstraint::CustomList { list_id }) =
&relay_settings.wireguard_constraints.entry_location
{
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 6ffd696890..c3c64ebacf 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -56,7 +56,7 @@ use mullvad_types::{
version::{AppVersion, AppVersionInfo},
wireguard::{PublicKey, QuantumResistantState, RotationInterval},
};
-use relay_list::updater::{self, RelayListUpdater, RelayListUpdaterHandle};
+use relay_list::{RelayListUpdater, RelayListUpdaterHandle, RELAYS_FILENAME};
use settings::SettingsPersister;
#[cfg(target_os = "android")]
use std::os::unix::io::RawFd;
@@ -698,8 +698,8 @@ where
let initial_selector_config = new_selector_config(&settings);
let relay_selector = RelaySelector::new(
initial_selector_config,
- resource_dir.join(updater::RELAYS_FILENAME),
- cache_dir.join(updater::RELAYS_FILENAME),
+ resource_dir.join(RELAYS_FILENAME),
+ cache_dir.join(RELAYS_FILENAME),
);
let settings_relay_selector = relay_selector.clone();
@@ -1105,7 +1105,7 @@ where
// Note that `Constraint::Any` corresponds to just IPv4
matches!(
relay_constraints.wireguard_constraints.ip_version,
- mullvad_types::relay_constraints::Constraint::Only(IpVersion::V6)
+ mullvad_types::constraints::Constraint::Only(IpVersion::V6)
)
} else {
false
diff --git a/mullvad-daemon/src/migrations/v1.rs b/mullvad-daemon/src/migrations/v1.rs
index c3013c7b28..4205f23ebf 100644
--- a/mullvad-daemon/src/migrations/v1.rs
+++ b/mullvad-daemon/src/migrations/v1.rs
@@ -1,5 +1,5 @@
use super::Result;
-use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion};
+use mullvad_types::{constraints::Constraint, settings::SettingsVersion};
use serde::{Deserialize, Serialize};
// ======================================================
diff --git a/mullvad-daemon/src/migrations/v4.rs b/mullvad-daemon/src/migrations/v4.rs
index cd11056d4a..8e03daa51b 100644
--- a/mullvad-daemon/src/migrations/v4.rs
+++ b/mullvad-daemon/src/migrations/v4.rs
@@ -1,5 +1,5 @@
use super::{Error, Result};
-use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion};
+use mullvad_types::{constraints::Constraint, settings::SettingsVersion};
use serde::{Deserialize, Serialize};
// ======================================================
diff --git a/mullvad-daemon/src/migrations/v5.rs b/mullvad-daemon/src/migrations/v5.rs
index 351b427a85..e458bb1a1d 100644
--- a/mullvad-daemon/src/migrations/v5.rs
+++ b/mullvad-daemon/src/migrations/v5.rs
@@ -1,5 +1,5 @@
use super::{Error, Result};
-use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion};
+use mullvad_types::{constraints::Constraint, settings::SettingsVersion};
use serde::{Deserialize, Serialize};
// ======================================================
diff --git a/mullvad-daemon/src/migrations/v6.rs b/mullvad-daemon/src/migrations/v6.rs
index 076dc1b71f..c481be8d92 100644
--- a/mullvad-daemon/src/migrations/v6.rs
+++ b/mullvad-daemon/src/migrations/v6.rs
@@ -1,5 +1,5 @@
use super::{Error, Result};
-use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion};
+use mullvad_types::{constraints::Constraint, settings::SettingsVersion};
use serde::{Deserialize, Serialize};
// ======================================================
diff --git a/mullvad-daemon/src/relay_list/mod.rs b/mullvad-daemon/src/relay_list/mod.rs
index 8936941035..2b4be3db54 100644
--- a/mullvad-daemon/src/relay_list/mod.rs
+++ b/mullvad-daemon/src/relay_list/mod.rs
@@ -1,3 +1,213 @@
-//! Relay list
+//! Relay list updater
-pub mod updater;
+use futures::{
+ channel::mpsc,
+ future::{Fuse, FusedFuture},
+ Future, FutureExt, SinkExt, StreamExt,
+};
+use std::{
+ path::{Path, PathBuf},
+ time::{Duration, SystemTime, UNIX_EPOCH},
+};
+use tokio::fs::File;
+
+use mullvad_api::{availability::ApiAvailabilityHandle, rest::MullvadRestHandle, RelayListProxy};
+use mullvad_relay_selector::RelaySelector;
+use mullvad_types::relay_list::RelayList;
+use talpid_future::retry::{retry_future, ExponentialBackoff, Jittered};
+use talpid_types::ErrorExt;
+
+/// How often the updater should wake up to check the cache of the in-memory cache of relays.
+/// This check is very cheap. The only reason to not have it very often is because if downloading
+/// constantly fails it will try very often and fill the logs etc.
+const UPDATE_CHECK_INTERVAL: Duration = Duration::from_secs(60 * 15);
+/// How old the cached relays need to be to trigger an update
+const UPDATE_INTERVAL: Duration = Duration::from_secs(60 * 60);
+
+const DOWNLOAD_RETRY_STRATEGY: Jittered<ExponentialBackoff> = Jittered::jitter(
+ ExponentialBackoff::new(Duration::from_secs(16), 8)
+ .max_delay(Some(Duration::from_secs(2 * 60 * 60))),
+);
+
+/// Where the relay list is cached on disk.
+pub(crate) const RELAYS_FILENAME: &str = "relays.json";
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error("Downloader already shut down")]
+ DownloaderShutdown,
+
+ #[error("Mullvad relay selector error")]
+ RelaySelector(#[from] mullvad_relay_selector::Error),
+}
+
+#[derive(Clone)]
+pub struct RelayListUpdaterHandle {
+ tx: mpsc::Sender<()>,
+}
+
+impl RelayListUpdaterHandle {
+ pub async fn update(&mut self) {
+ if let Err(error) = self
+ .tx
+ .send(())
+ .await
+ .map_err(|_| Error::DownloaderShutdown)
+ {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Unable to send update command to relay list updater")
+ );
+ }
+ }
+}
+
+pub struct RelayListUpdater {
+ api_client: RelayListProxy,
+ cache_path: PathBuf,
+ relay_selector: RelaySelector,
+ on_update: Box<dyn Fn(&RelayList) + Send + 'static>,
+ last_check: SystemTime,
+ api_availability: ApiAvailabilityHandle,
+}
+
+impl RelayListUpdater {
+ pub fn spawn(
+ selector: RelaySelector,
+ api_handle: MullvadRestHandle,
+ cache_dir: &Path,
+ on_update: impl Fn(&RelayList) + Send + 'static,
+ ) -> RelayListUpdaterHandle {
+ let (tx, cmd_rx) = mpsc::channel(1);
+ let api_availability = api_handle.availability.clone();
+ let api_client = RelayListProxy::new(api_handle);
+ let updater = RelayListUpdater {
+ api_client,
+ cache_path: cache_dir.join(RELAYS_FILENAME),
+ relay_selector: selector,
+ on_update: Box::new(on_update),
+ last_check: UNIX_EPOCH,
+ api_availability,
+ };
+
+ tokio::spawn(updater.run(cmd_rx));
+
+ RelayListUpdaterHandle { tx }
+ }
+
+ async fn run(mut self, mut cmd_rx: mpsc::Receiver<()>) {
+ let mut download_future = Box::pin(Fuse::terminated());
+ loop {
+ let next_check = tokio::time::sleep(UPDATE_CHECK_INTERVAL).fuse();
+ tokio::pin!(next_check);
+
+ futures::select! {
+ _check_update = next_check => {
+ if download_future.is_terminated() && self.should_update() {
+ let tag = self.relay_selector.etag();
+ download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse());
+ self.last_check = SystemTime::now();
+ }
+ },
+
+ new_relay_list = download_future => {
+ self.consume_new_relay_list(new_relay_list).await;
+ },
+
+ cmd = cmd_rx.next() => {
+ match cmd {
+ Some(()) => {
+ let tag = self.relay_selector.etag();
+ download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse());
+ self.last_check = SystemTime::now();
+ },
+ None => {
+ log::trace!("Relay list updater shutting down");
+ return;
+ }
+ }
+ }
+
+ };
+ }
+ }
+
+ async fn consume_new_relay_list(
+ &mut self,
+ result: Result<Option<RelayList>, mullvad_api::Error>,
+ ) {
+ match result {
+ Ok(Some(relay_list)) => {
+ if let Err(err) = self.update_cache(relay_list).await {
+ log::error!("Failed to update relay list cache: {}", err);
+ }
+ }
+ Ok(None) => log::debug!("Relay list is up-to-date"),
+ Err(error) => log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to fetch new relay list")
+ ),
+ }
+ }
+
+ /// Returns true if the current parsed_relays is older than UPDATE_INTERVAL
+ fn should_update(&mut self) -> bool {
+ let last_check = std::cmp::max(self.relay_selector.last_updated(), self.last_check);
+ match SystemTime::now().duration_since(last_check) {
+ Ok(duration) => duration >= UPDATE_INTERVAL,
+ // If the clock is skewed we have no idea by how much or when the last update
+ // actually was, better download again to get in sync and get a `last_updated`
+ // timestamp corresponding to the new time.
+ Err(_) => true,
+ }
+ }
+
+ fn download_relay_list(
+ api_handle: ApiAvailabilityHandle,
+ proxy: RelayListProxy,
+ tag: Option<String>,
+ ) -> impl Future<Output = Result<Option<RelayList>, mullvad_api::Error>> + 'static {
+ let download_futures = move || {
+ let available = api_handle.wait_background();
+ let req = proxy.relay_list(tag.clone());
+ async move {
+ available.await?;
+ req.await.map_err(mullvad_api::Error::from)
+ }
+ };
+
+ retry_future(
+ download_futures,
+ |result| result.is_err(),
+ DOWNLOAD_RETRY_STRATEGY,
+ )
+ }
+
+ async fn update_cache(&mut self, new_relay_list: RelayList) -> Result<(), Error> {
+ if let Err(error) = Self::cache_relays(&self.cache_path, &new_relay_list).await {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to update relay cache on disk")
+ );
+ }
+
+ self.relay_selector.set_relays(new_relay_list.clone());
+ (self.on_update)(&new_relay_list);
+ Ok(())
+ }
+
+ /// Write a `RelayList` to the cache file.
+ async fn cache_relays(cache_path: &Path, relays: &RelayList) -> Result<(), Error> {
+ log::debug!("Writing relays cache to {}", cache_path.display());
+ let mut file = File::create(cache_path)
+ .await
+ .map_err(mullvad_relay_selector::Error::OpenRelayCache)?;
+ let bytes =
+ serde_json::to_vec_pretty(relays).map_err(mullvad_relay_selector::Error::Serialize)?;
+ let mut slice: &[u8] = bytes.as_slice();
+ let _ = tokio::io::copy(&mut slice, &mut file)
+ .await
+ .map_err(mullvad_relay_selector::Error::WriteRelayCache)?;
+ Ok(())
+ }
+}
diff --git a/mullvad-daemon/src/relay_list/updater.rs b/mullvad-daemon/src/relay_list/updater.rs
deleted file mode 100644
index 317dbd45cd..0000000000
--- a/mullvad-daemon/src/relay_list/updater.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-use futures::{
- channel::mpsc,
- future::{Fuse, FusedFuture},
- Future, FutureExt, SinkExt, StreamExt,
-};
-use std::{
- path::{Path, PathBuf},
- time::{Duration, SystemTime, UNIX_EPOCH},
-};
-use tokio::fs::File;
-
-use mullvad_api::{availability::ApiAvailabilityHandle, rest::MullvadRestHandle, RelayListProxy};
-use mullvad_relay_selector::{Error, RelaySelector};
-use mullvad_types::relay_list::RelayList;
-use talpid_future::retry::{retry_future, ExponentialBackoff, Jittered};
-use talpid_types::ErrorExt;
-
-/// How often the updater should wake up to check the cache of the in-memory cache of relays.
-/// This check is very cheap. The only reason to not have it very often is because if downloading
-/// constantly fails it will try very often and fill the logs etc.
-const UPDATE_CHECK_INTERVAL: Duration = Duration::from_secs(60 * 15);
-/// How old the cached relays need to be to trigger an update
-const UPDATE_INTERVAL: Duration = Duration::from_secs(60 * 60);
-
-const DOWNLOAD_RETRY_STRATEGY: Jittered<ExponentialBackoff> = Jittered::jitter(
- ExponentialBackoff::new(Duration::from_secs(16), 8)
- .max_delay(Some(Duration::from_secs(2 * 60 * 60))),
-);
-
-/// Where the relay list is cached on disk.
-pub(crate) const RELAYS_FILENAME: &str = "relays.json";
-
-#[derive(Clone)]
-pub struct RelayListUpdaterHandle {
- tx: mpsc::Sender<()>,
-}
-
-impl RelayListUpdaterHandle {
- pub async fn update(&mut self) {
- if let Err(error) = self
- .tx
- .send(())
- .await
- .map_err(|_| Error::DownloaderShutDown)
- {
- log::error!(
- "{}",
- error.display_chain_with_msg("Unable to send update command to relay list updater")
- );
- }
- }
-}
-
-pub struct RelayListUpdater {
- api_client: RelayListProxy,
- cache_path: PathBuf,
- relay_selector: RelaySelector,
- on_update: Box<dyn Fn(&RelayList) + Send + 'static>,
- last_check: SystemTime,
- api_availability: ApiAvailabilityHandle,
-}
-
-impl RelayListUpdater {
- pub fn spawn(
- selector: RelaySelector,
- api_handle: MullvadRestHandle,
- cache_dir: &Path,
- on_update: impl Fn(&RelayList) + Send + 'static,
- ) -> RelayListUpdaterHandle {
- let (tx, cmd_rx) = mpsc::channel(1);
- let api_availability = api_handle.availability.clone();
- let api_client = RelayListProxy::new(api_handle);
- let updater = RelayListUpdater {
- api_client,
- cache_path: cache_dir.join(RELAYS_FILENAME),
- relay_selector: selector,
- on_update: Box::new(on_update),
- last_check: UNIX_EPOCH,
- api_availability,
- };
-
- tokio::spawn(updater.run(cmd_rx));
-
- RelayListUpdaterHandle { tx }
- }
-
- async fn run(mut self, mut cmd_rx: mpsc::Receiver<()>) {
- let mut download_future = Box::pin(Fuse::terminated());
- loop {
- let next_check = tokio::time::sleep(UPDATE_CHECK_INTERVAL).fuse();
- tokio::pin!(next_check);
-
- futures::select! {
- _check_update = next_check => {
- if download_future.is_terminated() && self.should_update() {
- let tag = self.relay_selector.etag();
- download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse());
- self.last_check = SystemTime::now();
- }
- },
-
- new_relay_list = download_future => {
- self.consume_new_relay_list(new_relay_list).await;
- },
-
- cmd = cmd_rx.next() => {
- match cmd {
- Some(()) => {
- let tag = self.relay_selector.etag();
- download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse());
- self.last_check = SystemTime::now();
- },
- None => {
- log::trace!("Relay list updater shutting down");
- return;
- }
- }
- }
-
- };
- }
- }
-
- async fn consume_new_relay_list(
- &mut self,
- result: Result<Option<RelayList>, mullvad_api::Error>,
- ) {
- match result {
- Ok(Some(relay_list)) => {
- if let Err(err) = self.update_cache(relay_list).await {
- log::error!("Failed to update relay list cache: {}", err);
- }
- }
- Ok(None) => log::debug!("Relay list is up-to-date"),
- Err(error) => log::error!(
- "{}",
- error.display_chain_with_msg("Failed to fetch new relay list")
- ),
- }
- }
-
- /// Returns true if the current parsed_relays is older than UPDATE_INTERVAL
- fn should_update(&mut self) -> bool {
- let last_check = std::cmp::max(self.relay_selector.last_updated(), self.last_check);
- match SystemTime::now().duration_since(last_check) {
- Ok(duration) => duration >= UPDATE_INTERVAL,
- // If the clock is skewed we have no idea by how much or when the last update
- // actually was, better download again to get in sync and get a `last_updated`
- // timestamp corresponding to the new time.
- Err(_) => true,
- }
- }
-
- fn download_relay_list(
- api_handle: ApiAvailabilityHandle,
- proxy: RelayListProxy,
- tag: Option<String>,
- ) -> impl Future<Output = Result<Option<RelayList>, mullvad_api::Error>> + 'static {
- let download_futures = move || {
- let available = api_handle.wait_background();
- let req = proxy.relay_list(tag.clone());
- async move {
- available.await?;
- req.await.map_err(mullvad_api::Error::from)
- }
- };
-
- retry_future(
- download_futures,
- |result| result.is_err(),
- DOWNLOAD_RETRY_STRATEGY,
- )
- }
-
- async fn update_cache(&mut self, new_relay_list: RelayList) -> Result<(), Error> {
- if let Err(error) = Self::cache_relays(&self.cache_path, &new_relay_list).await {
- log::error!(
- "{}",
- error.display_chain_with_msg("Failed to update relay cache on disk")
- );
- }
-
- self.relay_selector.set_relays(new_relay_list.clone());
- (self.on_update)(&new_relay_list);
- Ok(())
- }
-
- /// Write a `RelayList` to the cache file.
- async fn cache_relays(cache_path: &Path, relays: &RelayList) -> Result<(), Error> {
- log::debug!("Writing relays cache to {}", cache_path.display());
- let mut file = File::create(cache_path)
- .await
- .map_err(Error::OpenRelayCache)?;
- let bytes = serde_json::to_vec_pretty(relays).map_err(Error::Serialize)?;
- let mut slice: &[u8] = bytes.as_slice();
- let _ = tokio::io::copy(&mut slice, &mut file)
- .await
- .map_err(Error::WriteRelayCache)?;
- Ok(())
- }
-}
diff --git a/mullvad-daemon/src/settings/mod.rs b/mullvad-daemon/src/settings/mod.rs
index abd6ea2e0a..b1d9c2b8e6 100644
--- a/mullvad-daemon/src/settings/mod.rs
+++ b/mullvad-daemon/src/settings/mod.rs
@@ -376,16 +376,13 @@ impl<'a> Display for SettingsSummary<'a> {
write!(f, ", wg ip version: {ip_version}")?;
}
- let multihop = matches!(
- relay_settings,
+ let multihop = match relay_settings {
RelaySettings::Normal(RelayConstraints {
- wireguard_constraints: WireguardConstraints {
- use_multihop: true,
- ..
- },
+ wireguard_constraints,
..
- })
- );
+ }) => wireguard_constraints.multihop(),
+ _ => false,
+ };
write!(
f,
diff --git a/mullvad-daemon/src/tunnel.rs b/mullvad-daemon/src/tunnel.rs
index 2094838173..f230a3c4b2 100644
--- a/mullvad-daemon/src/tunnel.rs
+++ b/mullvad-daemon/src/tunnel.rs
@@ -8,20 +8,22 @@ use std::{
use tokio::sync::Mutex;
-use mullvad_relay_selector::{RelaySelector, SelectedBridge, SelectedObfuscator, SelectedRelay};
+use mullvad_relay_selector::{GetRelay, RelaySelector, RuntimeParameters, WireguardConfig};
use mullvad_types::{
- endpoint::MullvadEndpoint, location::GeoIpLocation, relay_list::Relay, settings::TunnelOptions,
+ endpoint::MullvadWireguardEndpoint, location::GeoIpLocation, relay_list::Relay,
+ settings::TunnelOptions,
};
use once_cell::sync::Lazy;
use talpid_core::tunnel_state_machine::TunnelParametersGenerator;
-use talpid_types::{
- net::{wireguard, TunnelParameters},
- tunnel::ParameterGenerationError,
- ErrorExt,
+#[cfg(not(target_os = "android"))]
+use talpid_types::net::{
+ obfuscation::ObfuscatorConfig, openvpn, proxy::CustomProxy, wireguard, Endpoint,
+ TunnelParameters,
};
+#[cfg(target_os = "android")]
+use talpid_types::net::{obfuscation::ObfuscatorConfig, wireguard, TunnelParameters};
-#[cfg(not(target_os = "android"))]
-use talpid_types::net::openvpn;
+use talpid_types::{tunnel::ParameterGenerationError, ErrorExt};
use crate::device::{AccountManagerHandle, PrivateAccountAndDevice};
@@ -138,127 +140,129 @@ impl ParametersGenerator {
}
impl InnerParametersGenerator {
- async fn generate(&mut self, retry_attempt: u32) -> Result<TunnelParameters, Error> {
- let _data = self.device().await?;
- match self.relay_selector.get_relay(retry_attempt) {
- Ok((SelectedRelay::Custom(custom_relay), _bridge, _obfsucator)) => {
- self.last_generated_relays = None;
- custom_relay
- // TODO: generate proxy settings for custom tunnels
- .to_tunnel_parameters(self.tunnel_options.clone(), None)
- .map_err(|e| {
- log::error!("Failed to resolve hostname for custom tunnel config: {}", e);
- Error::ResolveCustomHostname
- })
- }
- Ok((SelectedRelay::Normal(constraints), bridge, obfuscator)) => {
- self.create_tunnel_parameters(
- &constraints.exit_relay,
- &constraints.entry_relay,
- constraints.endpoint,
- bridge,
- obfuscator,
- )
- .await
- }
- Err(mullvad_relay_selector::Error::NoBridge) => Err(Error::NoBridgeAvailable),
- Err(_error) => Err(Error::NoRelayAvailable),
- }
- }
-
- #[cfg_attr(target_os = "android", allow(unused_variables))]
- async fn create_tunnel_parameters(
+ async fn generate(
&mut self,
- relay: &Relay,
- entry_relay: &Option<Relay>,
- endpoint: MullvadEndpoint,
- bridge: Option<SelectedBridge>,
- obfuscator: Option<SelectedObfuscator>,
+ retry_attempt: u32,
+ ipv6: bool,
) -> Result<TunnelParameters, Error> {
let data = self.device().await?;
- match endpoint {
- #[cfg(not(target_os = "android"))]
- MullvadEndpoint::OpenVpn(endpoint) => {
- let (bridge_settings, bridge_relay) = match bridge {
- Some(SelectedBridge::Normal(bridge)) => {
- (Some(bridge.settings), Some(bridge.relay))
- }
- Some(SelectedBridge::Custom(settings)) => (Some(settings), None),
- None => (None, None),
- };
+ let selected_relay = self
+ .relay_selector
+ .get_relay(retry_attempt as usize, RuntimeParameters { ipv6 })
+ .map_err(|err| match err {
+ mullvad_relay_selector::Error::NoBridge => Error::NoBridgeAvailable,
+ _ => Error::NoRelayAvailable,
+ })?;
+ match selected_relay {
+ #[cfg(not(target_os = "android"))]
+ GetRelay::OpenVpn {
+ endpoint,
+ exit,
+ bridge,
+ } => {
+ let bridge_relay = bridge.as_ref().and_then(|bridge| bridge.relay());
self.last_generated_relays = Some(LastSelectedRelays::OpenVpn {
- relay: relay.clone(),
- bridge: bridge_relay,
+ relay: exit.clone(),
+ bridge: bridge_relay.cloned(),
});
-
- Ok(openvpn::TunnelParameters {
- config: openvpn::ConnectionConfig::new(
- endpoint,
- data.account_token,
- "-".to_string(),
- ),
- options: self.tunnel_options.openvpn.clone(),
- generic_options: self.tunnel_options.generic.clone(),
- proxy: bridge_settings,
- #[cfg(target_os = "linux")]
- fwmark: mullvad_types::TUNNEL_FWMARK,
- }
- .into())
+ let bridge_settings = bridge.as_ref().map(|bridge| bridge.settings());
+ Ok(self.create_openvpn_tunnel_parameters(endpoint, data, bridge_settings.cloned()))
}
- #[cfg(target_os = "android")]
- MullvadEndpoint::OpenVpn(endpoint) => {
- unreachable!("OpenVPN is not supported on Android");
- }
- MullvadEndpoint::Wireguard(endpoint) => {
- let tunnel_ipv4 = data.device.wg_data.addresses.ipv4_address.ip();
- let tunnel_ipv6 = data.device.wg_data.addresses.ipv6_address.ip();
- let tunnel = wireguard::TunnelConfig {
- private_key: data.device.wg_data.private_key,
- addresses: vec![IpAddr::from(tunnel_ipv4), IpAddr::from(tunnel_ipv6)],
- };
- // FIXME: Used for debugging purposes during the migration to same IP. Remove when
- // the migration is over.
- if tunnel_ipv4 == *SAME_IP_V4 || tunnel_ipv6 == *SAME_IP_V6 {
- log::debug!("Same IP is being used");
- } else {
- log::debug!("Same IP is NOT being used");
- }
-
+ GetRelay::Wireguard {
+ endpoint,
+ obfuscator,
+ inner,
+ } => {
let (obfuscator_relay, obfuscator_config) = match obfuscator {
Some(obfuscator) => (Some(obfuscator.relay), Some(obfuscator.config)),
None => (None, None),
};
+ let (wg_entry, wg_exit) = match inner {
+ WireguardConfig::Singlehop { exit } => (None, exit),
+ WireguardConfig::Multihop { exit, entry } => (Some(entry), exit),
+ };
self.last_generated_relays = Some(LastSelectedRelays::WireGuard {
- wg_entry: entry_relay.clone(),
- wg_exit: relay.clone(),
+ wg_entry,
+ wg_exit,
obfuscator: obfuscator_relay,
});
- Ok(wireguard::TunnelParameters {
- connection: wireguard::ConnectionConfig {
- tunnel,
- peer: endpoint.peer,
- exit_peer: endpoint.exit_peer,
- ipv4_gateway: endpoint.ipv4_gateway,
- ipv6_gateway: Some(endpoint.ipv6_gateway),
- #[cfg(target_os = "linux")]
- fwmark: Some(mullvad_types::TUNNEL_FWMARK),
- },
- options: self
- .tunnel_options
- .wireguard
- .clone()
- .into_talpid_tunnel_options(),
- generic_options: self.tunnel_options.generic.clone(),
- obfuscation: obfuscator_config,
- }
- .into())
+ Ok(self.create_wireguard_tunnel_parameters(endpoint, data, obfuscator_config))
+ }
+ GetRelay::Custom(custom_relay) => {
+ self.last_generated_relays = None;
+ custom_relay
+ // TODO: generate proxy settings for custom tunnels
+ .to_tunnel_parameters(self.tunnel_options.clone(), None)
+ .map_err(|e| {
+ log::error!("Failed to resolve hostname for custom tunnel config: {}", e);
+ Error::ResolveCustomHostname
+ })
}
}
}
+ #[cfg(not(target_os = "android"))]
+ fn create_openvpn_tunnel_parameters(
+ &self,
+ endpoint: Endpoint,
+ data: PrivateAccountAndDevice,
+ bridge_settings: Option<CustomProxy>,
+ ) -> TunnelParameters {
+ openvpn::TunnelParameters {
+ config: openvpn::ConnectionConfig::new(endpoint, data.account_token, "-".to_string()),
+ options: self.tunnel_options.openvpn.clone(),
+ generic_options: self.tunnel_options.generic.clone(),
+ proxy: bridge_settings,
+ #[cfg(target_os = "linux")]
+ fwmark: mullvad_types::TUNNEL_FWMARK,
+ }
+ .into()
+ }
+
+ fn create_wireguard_tunnel_parameters(
+ &self,
+ endpoint: MullvadWireguardEndpoint,
+ data: PrivateAccountAndDevice,
+ obfuscator_config: Option<ObfuscatorConfig>,
+ ) -> TunnelParameters {
+ let tunnel_ipv4 = data.device.wg_data.addresses.ipv4_address.ip();
+ let tunnel_ipv6 = data.device.wg_data.addresses.ipv6_address.ip();
+ let tunnel = wireguard::TunnelConfig {
+ private_key: data.device.wg_data.private_key,
+ addresses: vec![IpAddr::from(tunnel_ipv4), IpAddr::from(tunnel_ipv6)],
+ };
+ // FIXME: Used for debugging purposes during the migration to same IP. Remove when
+ // the migration is over.
+ if tunnel_ipv4 == *SAME_IP_V4 || tunnel_ipv6 == *SAME_IP_V6 {
+ log::debug!("Same IP is being used");
+ } else {
+ log::debug!("Same IP is NOT being used");
+ }
+
+ wireguard::TunnelParameters {
+ connection: wireguard::ConnectionConfig {
+ tunnel,
+ peer: endpoint.peer,
+ exit_peer: endpoint.exit_peer,
+ ipv4_gateway: endpoint.ipv4_gateway,
+ ipv6_gateway: Some(endpoint.ipv6_gateway),
+ #[cfg(target_os = "linux")]
+ fwmark: Some(mullvad_types::TUNNEL_FWMARK),
+ },
+ options: self
+ .tunnel_options
+ .wireguard
+ .clone()
+ .into_talpid_tunnel_options(),
+ generic_options: self.tunnel_options.generic.clone(),
+ obfuscation: obfuscator_config,
+ }
+ .into()
+ }
+
async fn device(&self) -> Result<PrivateAccountAndDevice, Error> {
self.account_manager
.data()
@@ -274,12 +278,13 @@ impl TunnelParametersGenerator for ParametersGenerator {
fn generate(
&mut self,
retry_attempt: u32,
+ ipv6: bool,
) -> Pin<Box<dyn Future<Output = Result<TunnelParameters, ParameterGenerationError>>>> {
let generator = self.0.clone();
Box::pin(async move {
let mut inner = generator.lock().await;
inner
- .generate(retry_attempt)
+ .generate(retry_attempt, ipv6)
.await
.map_err(|error| match error {
Error::NoBridgeAvailable => ParameterGenerationError::NoMatchingBridgeRelay,