From ab9aaec34d3a9833d8fef45eb3e07e6bb40fa610 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 28 Aug 2024 14:48:44 +0200 Subject: [PATCH] Add DAITA use_anywhere e2e tests --- test/test-manager/src/tests/daita.rs | 153 ++++++++++++++++++++++++++ test/test-manager/src/tests/mod.rs | 3 +- test/test-manager/src/tests/tunnel.rs | 37 +------ 3 files changed, 156 insertions(+), 37 deletions(-) create mode 100644 test/test-manager/src/tests/daita.rs diff --git a/test/test-manager/src/tests/daita.rs b/test/test-manager/src/tests/daita.rs new file mode 100644 index 000000000000..5bf02ba44f8b --- /dev/null +++ b/test/test-manager/src/tests/daita.rs @@ -0,0 +1,153 @@ +use anyhow::{anyhow, bail, ensure, Context}; +use mullvad_management_interface::MullvadProxyClient; +use mullvad_relay_selector::query::builder::RelayQueryBuilder; +use mullvad_types::relay_list::RelayEndpointData; +use talpid_types::{net::TunnelEndpoint, tunnel::ErrorStateCause}; +use test_macro::test_function; +use test_rpc::ServiceClient; + +use super::{helpers, Error, TestContext}; + +/// Test that daita and daita_smart_routing works by connecting +/// - to a non-DAITA relay with singlehop (should block) +/// - to a DAITA relay with singlehop +/// - to a DAITA relay with auto-multihop using smart_routing +/// - to a DAITA relay with explicit multihop +/// - to a non-DAITA relay with multihop (should block) +/// +/// # Limitations +/// +/// The test does not analyze any traffic, nor verify that DAITA is in use in any way except +/// by looking at [TunnelEndpoint::daita]. +#[test_function] +pub async fn test_daita( + _ctx: TestContext, + _rpc: ServiceClient, + mut mullvad_client: MullvadProxyClient, +) -> anyhow::Result<()> { + let relay_list = mullvad_client.get_relay_locations().await?; + let wg_relays = relay_list + .countries + .iter() + .flat_map(|countries| &countries.cities) + .flat_map(|cities| &cities.relays) + .flat_map(|relays| match &relays.endpoint_data { + RelayEndpointData::Wireguard(wireguard) => Some((relays, wireguard)), + _ => None, + }); + + // Select two relays to use for hte test, one with DAITA and one without. + let daita_relay = wg_relays + .clone() + .find(|(_relay, wireguard_data)| wireguard_data.daita) + .map(|(relay, _)| relay) + .context("Failed to find a daita wireguard relay")?; + log::info!("Selected daita relay: {}", daita_relay.hostname); + + let non_daita_relay = wg_relays + .clone() + .find(|(_relay, wireguard_data)| !wireguard_data.daita) + .map(|(relay, _)| relay) + .context("Failed to find a non-daita wireguard relay")?; + log::info!("Selected non-daita relay: {}", non_daita_relay.hostname); + + let mut non_daita_location_query = RelayQueryBuilder::new().wireguard().build(); + non_daita_location_query.location = helpers::into_constraint(non_daita_relay); + + let mut daita_location_query = RelayQueryBuilder::new().wireguard().build(); + daita_location_query.location = helpers::into_constraint(daita_relay); + + let mut daita_to_non_daita_multihop_query = + RelayQueryBuilder::new().wireguard().multihop().build(); + daita_to_non_daita_multihop_query + .wireguard_constraints + .entry_location = helpers::into_constraint(daita_relay); + daita_to_non_daita_multihop_query.location = helpers::into_constraint(non_daita_relay); + + let mut non_daita_multihop_query = RelayQueryBuilder::new().wireguard().multihop().build(); + non_daita_multihop_query + .wireguard_constraints + .entry_location = helpers::into_constraint(non_daita_relay); + + log::info!("Enabling DAITA and trying to connect without smart_routing"); + { + mullvad_client.set_enable_daita(true).await?; + mullvad_client.set_daita_smart_routing(false).await?; + helpers::set_relay_settings(&mut mullvad_client, non_daita_location_query).await?; + + let result = helpers::connect_and_wait(&mut mullvad_client).await; + let Err(Error::UnexpectedErrorState(state)) = result else { + bail!("Connection failed unsuccessfully, reason: {:?}", result); + }; + let ErrorStateCause::TunnelParameterError(_) = state.cause() else { + bail!("Connection failed unsuccessfully, cause: {}", state.cause()); + }; + + log::info!("Failed to connect, this is expected!"); + } + + log::info!("Connecting to non-daita relay with use smart_routing"); + { + helpers::disconnect_and_wait(&mut mullvad_client).await?; + mullvad_client.set_daita_smart_routing(true).await?; + let state = helpers::connect_and_wait(&mut mullvad_client) + .await + .context("Failed to connect with smart_routing enabled")?; + + let endpoint: &TunnelEndpoint = state.endpoint().ok_or(anyhow!("No endpoint"))?; + ensure!(endpoint.daita, "DAITA must be used"); + ensure!(endpoint.entry_endpoint.is_some(), "multihop must be used"); + + log::info!("Successfully multihopped with use smart_routing"); + } + + log::info!("Connecting to daita relay with smart_routing"); + { + helpers::set_relay_settings(&mut mullvad_client, daita_location_query).await?; + + // TODO: connect_and_wait has a timing issue where if we're already connected, it might + // pick up on the previous connect state before the daemon can do its thing + let state = helpers::connect_and_wait(&mut mullvad_client) + .await + .context("Failed to connect to daita location with smart_routing enabled")?; + + let endpoint = state.endpoint().context("No endpoint")?; + ensure!(endpoint.daita, "DAITA must be used"); + ensure!( + endpoint.entry_endpoint.is_none(), + "multihop must not be used" + ); + + log::info!("Successfully singlehopped with smart_routing"); + } + + log::info!("Connecting to daita relay with multihop"); + { + helpers::set_relay_settings(&mut mullvad_client, daita_to_non_daita_multihop_query).await?; + let state = helpers::connect_and_wait(&mut mullvad_client) + .await + .context("Failed to connect via daita location with multihop enabled")?; + + let endpoint = state.endpoint().context("No endpoint")?; + ensure!(endpoint.daita, "DAITA must be used"); + ensure!(endpoint.entry_endpoint.is_some(), "multihop must be used"); + + log::info!("Successfully connected with multihop"); + } + + log::info!("Connecting to non_daita relay with multihop"); + { + helpers::set_relay_settings(&mut mullvad_client, non_daita_multihop_query).await?; + let result = helpers::connect_and_wait(&mut mullvad_client).await; + let Err(Error::UnexpectedErrorState(state)) = result else { + bail!("Connection failed unsuccessfully, reason: {:?}", result); + }; + let ErrorStateCause::TunnelParameterError(_) = state.cause() else { + bail!("Connection failed unsuccessfully, cause: {}", state.cause()); + }; + + log::info!("Failed to connect, this is expected!"); + } + + Ok(()) +} diff --git a/test/test-manager/src/tests/mod.rs b/test/test-manager/src/tests/mod.rs index 7e4cbc9eb6a5..f03e363f07ee 100644 --- a/test/test-manager/src/tests/mod.rs +++ b/test/test-manager/src/tests/mod.rs @@ -2,6 +2,7 @@ mod access_methods; mod account; pub mod config; mod cve_2019_14899; +mod daita; mod dns; mod helpers; mod install; @@ -57,7 +58,7 @@ pub enum Error { #[error("The daemon returned an error: {0}")] Daemon(String), - #[error("The daemon ended up in the error state")] + #[error("The daemon ended up in the error state: {0:?}")] UnexpectedErrorState(talpid_types::tunnel::ErrorState), #[error("The gRPC client ran into an error: {0}")] diff --git a/test/test-manager/src/tests/tunnel.rs b/test/test-manager/src/tests/tunnel.rs index f5d8f6c95960..cfedba372c51 100644 --- a/test/test-manager/src/tests/tunnel.rs +++ b/test/test-manager/src/tests/tunnel.rs @@ -11,7 +11,7 @@ use crate::{ tests::helpers::{login_with_retries, ConnChecker}, }; -use anyhow::{bail, ensure, Context}; +use anyhow::{bail, ensure}; use mullvad_management_interface::MullvadProxyClient; use mullvad_relay_selector::query::builder::RelayQueryBuilder; use mullvad_types::{ @@ -390,41 +390,6 @@ pub async fn test_wireguard_autoconnect( Ok(()) } -/// Test connecting to a WireGuard relay using DAITA. -/// -/// # Limitations -/// -/// The test does not analyze any traffic, nor verify that DAITA is in use. -#[test_function] -pub async fn test_daita( - _: TestContext, - rpc: ServiceClient, - mut mullvad_client: MullvadProxyClient, -) -> anyhow::Result<()> { - log::info!("Connecting to relay with DAITA"); - - apply_settings_from_relay_query( - &mut mullvad_client, - RelayQueryBuilder::new().wireguard().build(), - ) - .await?; - - mullvad_client - .set_daita_settings(wireguard::DaitaSettings { - enabled: true, - use_anywhere: false, - }) - .await - .context("Failed to enable daita")?; - - connect_and_wait(&mut mullvad_client).await?; - - log::info!("Check that the connection works"); - let _ = helpers::geoip_lookup_with_retries(&rpc).await?; - - Ok(()) -} - /// Test whether the daemon automatically connects on reboot when using /// OpenVPN. ///