summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2025-03-13 09:34:44 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2025-04-04 16:17:49 +0200
commitf2a77bab45ebfdbd1122a2195f9ccd8368babb47 (patch)
tree59fd54f54482541a89e175040c9e5fb868a734e4
parentf8f47dcb7543193a7b03fd0528a3540dcb5eed01 (diff)
downloadmullvadvpn-f2a77bab45ebfdbd1122a2195f9ccd8368babb47.tar.xz
mullvadvpn-f2a77bab45ebfdbd1122a2195f9ccd8368babb47.zip
Implement new debug commands: `relay disable` and `relay enable`
Add two new `mullvad debug` subcommands: - mullvad debug relay enable <country|city|hostname|openvpn|wireguard> - mullvad debug relay disable <country|city|hostname|openvpn|wireguard> These commands are used to update the state of relays the current relay list. This is useful to mock relays going offline or coming online from an offline state. These new debug commands were conceived during the development of the feature for adding warnings for the upcoming OpenVPN deprecation, as there wasn't a convenient way of mocking this.
-rw-r--r--mullvad-cli/src/cmds/debug.rs25
-rw-r--r--mullvad-daemon/src/lib.rs62
-rw-r--r--mullvad-daemon/src/management_interface.rs20
-rw-r--r--mullvad-management-interface/proto/management_interface.proto4
-rw-r--r--mullvad-management-interface/src/client.rs11
-rw-r--r--mullvad-types/src/relay_list.rs8
6 files changed, 130 insertions, 0 deletions
diff --git a/mullvad-cli/src/cmds/debug.rs b/mullvad-cli/src/cmds/debug.rs
index d67194b058..933f1212e8 100644
--- a/mullvad-cli/src/cmds/debug.rs
+++ b/mullvad-cli/src/cmds/debug.rs
@@ -9,6 +9,19 @@ use mullvad_types::{
pub enum DebugCommands {
/// Block all internet connection by setting an invalid relay constraint.
BlockConnection,
+ /// Relay
+ #[clap(subcommand)]
+ Relay(RelayDebugCommands),
+}
+
+#[derive(clap::Subcommand, Debug)]
+pub enum RelayDebugCommands {
+ /// Inactivate this _category of relays_ - a category can be one of the following: a relay, a
+ /// city, a country or a tunnel protocol (`openvpn` or `wireguard`).
+ Disable { relay: String },
+ /// (Re)Activate this _category of relays_ - a category can be one of the following: a relay, a
+ /// city, a country or a tunnel protocol (`openvpn` or `wireguard`).
+ Enable { relay: String },
}
impl DebugCommands {
@@ -41,6 +54,18 @@ impl DebugCommands {
eprintln!("WARNING: ENTERED BLOCKED MODE");
Ok(())
}
+ DebugCommands::Relay(RelayDebugCommands::Disable { relay }) => {
+ let mut rpc = MullvadProxyClient::new().await?;
+ rpc.disable_relay(relay.clone()).await?;
+ println!("{relay} is now marked as inactive");
+ Ok(())
+ }
+ DebugCommands::Relay(RelayDebugCommands::Enable { relay }) => {
+ let mut rpc = MullvadProxyClient::new().await?;
+ rpc.enable_relay(relay.clone()).await?;
+ println!("{relay} is now marked as active");
+ Ok(())
+ }
}
}
}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 68e2e57285..f5ca13f976 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -392,6 +392,16 @@ pub enum DaemonCommand {
ExportJsonSettings(ResponseTx<String, settings::patch::Error>),
/// Request the current feature indicators.
GetFeatureIndicators(oneshot::Sender<FeatureIndicators>),
+
+ // Debug features
+ DisableRelay {
+ relay: String,
+ tx: oneshot::Sender<()>,
+ },
+ EnableRelay {
+ relay: String,
+ tx: oneshot::Sender<()>,
+ },
}
/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
@@ -1454,6 +1464,8 @@ impl Daemon {
ApplyJsonSettings(tx, blob) => self.on_apply_json_settings(tx, blob).await,
ExportJsonSettings(tx) => self.on_export_json_settings(tx),
GetFeatureIndicators(tx) => self.on_get_feature_indicators(tx),
+ DisableRelay { relay, tx } => self.on_toggle_relay(relay, false, tx),
+ EnableRelay { relay, tx } => self.on_toggle_relay(relay, true, tx),
}
}
@@ -3136,6 +3148,56 @@ impl Daemon {
Self::oneshot_send(tx, feature_indicators, "get_feature_indicators response");
}
+ // Debug features
+
+ /// Mark [relay] as active or inactive in the daemon's relay list.
+ fn on_toggle_relay(&mut self, relay: String, active: bool, tx: oneshot::Sender<()>) {
+ use mullvad_types::relay_list::RelayList;
+ let relays = {
+ let relay_list = self.relay_selector.get_relays();
+ let countries = {
+ let mut countries = relay_list.countries;
+ for country in &mut countries {
+ let matching_country = relay == country.name;
+ for city in &mut country.cities {
+ let matching_city = relay == city.name;
+ for settings_relay in &mut city.relays {
+ // `relay` can also be a VPN protocol. This is arbitrary, but useful.
+ let matching_protocol = (relay.to_lowercase().eq("openvpn")
+ && settings_relay.is_openvpn())
+ || (relay.to_lowercase().eq("wireguard")
+ && settings_relay.is_wireguard());
+ let matching_relay = relay == settings_relay.hostname;
+
+ if matching_relay
+ || matching_city
+ || matching_country
+ || matching_protocol
+ {
+ settings_relay.active = active;
+ }
+ }
+ }
+ }
+ countries
+ };
+ RelayList {
+ countries,
+ ..relay_list
+ }
+ };
+
+ self.relay_selector.set_relays(relays.clone());
+
+ self.management_interface
+ .notifier()
+ .notify_relay_list(relays);
+
+ self.reconnect_tunnel();
+
+ Self::oneshot_send(tx, (), "on_toggle_relay response");
+ }
+
/// Set the target state of the client. If it changed trigger the operations needed to
/// progress towards that state.
/// Returns a bool representing whether a state change was initiated.
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 8249aea968..3efe41e288 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -1095,6 +1095,26 @@ impl ManagementService for ManagementServiceImpl {
Ok(Response::new(feature_indicators))
}
+
+ // Debug features
+
+ async fn disable_relay(&self, relay: Request<String>) -> ServiceResult<()> {
+ log::debug!("disable_relay");
+ let (tx, rx) = oneshot::channel();
+ let relay = relay.into_inner();
+ self.send_command_to_daemon(DaemonCommand::DisableRelay { relay, tx })?;
+ self.wait_for_result(rx).await?;
+ Ok(Response::new(()))
+ }
+
+ async fn enable_relay(&self, relay: Request<String>) -> ServiceResult<()> {
+ log::debug!("enable_relay");
+ let (tx, rx) = oneshot::channel();
+ let relay = relay.into_inner();
+ self.send_command_to_daemon(DaemonCommand::EnableRelay { relay, tx })?;
+ self.wait_for_result(rx).await?;
+ Ok(Response::new(()))
+ }
}
impl ManagementServiceImpl {
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 2b2e0bfe59..5acc8c3463 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -127,6 +127,10 @@ service ManagementService {
// Get current feature indicators
rpc GetFeatureIndicators(google.protobuf.Empty) returns (FeatureIndicators) {}
+
+ // Debug features
+ rpc DisableRelay(google.protobuf.StringValue) returns (google.protobuf.Empty) {}
+ rpc EnableRelay(google.protobuf.StringValue) returns (google.protobuf.Empty) {}
}
message UUID { string value = 1; }
diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs
index fd038cc37f..6d17e923f6 100644
--- a/mullvad-management-interface/src/client.rs
+++ b/mullvad-management-interface/src/client.rs
@@ -768,6 +768,17 @@ impl MullvadProxyClient {
.map(|response| response.into_inner())
.map(FeatureIndicators::from)
}
+
+ // Debug features
+ pub async fn disable_relay(&mut self, relay: String) -> Result<()> {
+ self.0.disable_relay(relay).await.map_err(Error::Rpc)?;
+ Ok(())
+ }
+
+ pub async fn enable_relay(&mut self, relay: String) -> Result<()> {
+ self.0.enable_relay(relay).await.map_err(Error::Rpc)?;
+ Ok(())
+ }
}
#[cfg(not(target_os = "android"))]
diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs
index c4d1d61bb7..351c0d0e86 100644
--- a/mullvad-types/src/relay_list.rs
+++ b/mullvad-types/src/relay_list.rs
@@ -100,6 +100,14 @@ impl Relay {
self.ipv6_addr_in = Some(new_ipv6);
self.overridden_ipv6 = true;
}
+
+ pub const fn is_wireguard(&self) -> bool {
+ matches!(self.endpoint_data, RelayEndpointData::Wireguard(_))
+ }
+
+ pub const fn is_openvpn(&self) -> bool {
+ matches!(self.endpoint_data, RelayEndpointData::Openvpn)
+ }
}
impl PartialEq for Relay {