diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 140eddc08a75..417f5e5869a9 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -73,6 +73,10 @@ impl MullvadProxyClient { super::new_rpc_client().await.map(Self) } + pub fn from_rpc_client(client: crate::ManagementServiceClient) -> Self { + Self(client) + } + pub async fn connect_tunnel(&mut self) -> Result { Ok(self .0 diff --git a/test/test-manager/src/tests/dns.rs b/test/test-manager/src/tests/dns.rs index 310d5eb0dc0b..89755c3fd633 100644 --- a/test/test-manager/src/tests/dns.rs +++ b/test/test-manager/src/tests/dns.rs @@ -9,9 +9,12 @@ use mullvad_management_interface::{types, ManagementServiceClient}; use mullvad_types::{relay_constraints::RelaySettings, ConnectionConfig, CustomTunnelEndpoint}; use talpid_types::net::wireguard; use test_macro::test_function; -use test_rpc::{Interface, ServiceClient}; +use test_rpc::ServiceClient; -use super::{helpers::connect_and_wait, Error, TestContext}; +use super::{ + helpers::{self, connect_and_wait, set_relay_settings}, + Error, TestContext, +}; use crate::network_monitor::{ start_packet_monitor_until, start_tunnel_packet_monitor_until, Direction, IpHeaderProtocols, MonitorOptions, @@ -22,8 +25,6 @@ use crate::vm::network::{ CUSTOM_TUN_REMOTE_TUN_ADDR, NON_TUN_GATEWAY, }; -use super::helpers::set_relay_settings; - /// How long to wait for expected "DNS queries" to appear const MONITOR_TIMEOUT: Duration = Duration::from_secs(5); @@ -47,7 +48,7 @@ pub async fn test_dns_leak_default( leak_test_dns( &rpc, &mut mullvad_client, - Interface::Tunnel, + true, IpAddr::V4(CUSTOM_TUN_REMOTE_TUN_ADDR), ) .await @@ -82,7 +83,7 @@ pub async fn test_dns_leak_custom_public_ip( .await .expect("failed to configure DNS server"); - leak_test_dns(&rpc, &mut mullvad_client, Interface::Tunnel, CONFIG_IP).await + leak_test_dns(&rpc, &mut mullvad_client, true, CONFIG_IP).await } /// Test whether DNS leaks can be produced when using a custom private IP. This test succeeds if and @@ -114,7 +115,7 @@ pub async fn test_dns_leak_custom_private_ip( .await .expect("failed to configure DNS server"); - leak_test_dns(&rpc, &mut mullvad_client, Interface::NonTunnel, CONFIG_IP).await + leak_test_dns(&rpc, &mut mullvad_client, false, CONFIG_IP).await } /// See whether it is possible to send "DNS queries" to a particular whitelisted destination on @@ -124,11 +125,9 @@ pub async fn test_dns_leak_custom_private_ip( async fn leak_test_dns( rpc: &ServiceClient, mullvad_client: &mut ManagementServiceClient, - interface: Interface, + use_tun: bool, whitelisted_dest: IpAddr, ) -> Result<(), Error> { - let use_tun = interface == Interface::Tunnel; - // // Connect to local wireguard relay // @@ -137,24 +136,32 @@ async fn leak_test_dns( .await .expect("failed to connect to custom wg relay"); - let guest_ip = rpc - .get_interface_ip(Interface::NonTunnel) + let nontun_iface = rpc + .get_default_interface() + .await + .expect("failed to find non-tun interface"); + let tunnel_iface = helpers::get_tunnel_interface(mullvad_client.clone()) + .await + .expect("failed to find tunnel interface"); + + let nontun_ip = rpc + .get_interface_ip(nontun_iface.clone()) .await .expect("failed to obtain guest IP"); let tunnel_ip = rpc - .get_interface_ip(Interface::Tunnel) + .get_interface_ip(tunnel_iface.clone()) .await .expect("failed to obtain tunnel IP"); log::debug!("Tunnel (guest) IP: {tunnel_ip}"); - log::debug!("Non-tunnel (guest) IP: {guest_ip}"); + log::debug!("Non-tunnel (guest) IP: {nontun_ip}"); // // Spoof DNS packets // let tun_bind_addr = SocketAddr::new(tunnel_ip, 0); - let guest_bind_addr = SocketAddr::new(guest_ip, 0); + let nontun_bind_addr = SocketAddr::new(nontun_ip, 0); let whitelisted_dest = SocketAddr::new(whitelisted_dest, 53); let blocked_dest_local = "10.64.100.100:53".parse().unwrap(); @@ -216,40 +223,35 @@ async fn leak_test_dns( // send to allowed dest spoof_packets( &rpc, - Some(Interface::Tunnel), + Some(tunnel_iface.clone()), tun_bind_addr, whitelisted_dest, ), spoof_packets( &rpc, - Some(Interface::NonTunnel), - guest_bind_addr, + Some(nontun_iface.clone()), + nontun_bind_addr, whitelisted_dest, ), // send to blocked local dest spoof_packets( &rpc, - Some(Interface::Tunnel), + Some(tunnel_iface.clone()), tun_bind_addr, blocked_dest_local, ), spoof_packets( &rpc, - Some(Interface::NonTunnel), - guest_bind_addr, + Some(nontun_iface.clone()), + nontun_bind_addr, blocked_dest_local, ), // send to blocked public dest + spoof_packets(&rpc, Some(tunnel_iface), tun_bind_addr, blocked_dest_public,), spoof_packets( &rpc, - Some(Interface::Tunnel), - tun_bind_addr, - blocked_dest_public, - ), - spoof_packets( - &rpc, - Some(Interface::NonTunnel), - guest_bind_addr, + Some(nontun_iface), + nontun_bind_addr, blocked_dest_public, ), ) @@ -578,17 +580,25 @@ async fn run_dns_config_test< } } - let guest_ip = rpc - .get_interface_ip(Interface::NonTunnel) + let nontun_iface = rpc + .get_default_interface() + .await + .expect("failed to find non-tun interface"); + let tunnel_iface = helpers::get_tunnel_interface(mullvad_client.clone()) + .await + .expect("failed to find tunnel interface"); + + let nontun_ip = rpc + .get_interface_ip(nontun_iface) .await .expect("failed to obtain guest IP"); let tunnel_ip = rpc - .get_interface_ip(Interface::Tunnel) + .get_interface_ip(tunnel_iface) .await .expect("failed to obtain tunnel IP"); log::debug!("Tunnel (guest) IP: {tunnel_ip}"); - log::debug!("Non-tunnel (guest) IP: {guest_ip}"); + log::debug!("Non-tunnel (guest) IP: {nontun_ip}"); let monitor = create_monitor().await; @@ -661,19 +671,21 @@ async fn connect_local_wg_relay(mullvad_client: &mut ManagementServiceClient) -> async fn spoof_packets( rpc: &ServiceClient, - interface: Option, + interface: Option, bind_addr: SocketAddr, dest: SocketAddr, ) { let tcp_rpc = rpc.clone(); + let tcp_interface = interface.clone(); let tcp_send = async move { log::debug!("sending to {}/tcp from {}", dest, bind_addr); - let _ = tcp_rpc.send_tcp(interface, bind_addr, dest).await; + let _ = tcp_rpc.send_tcp(tcp_interface, bind_addr, dest).await; }; let udp_rpc = rpc.clone(); + let udp_interface = interface.clone(); let udp_send = async move { log::debug!("sending to {}/udp from {}", dest, bind_addr); - let _ = udp_rpc.send_udp(interface, bind_addr, dest).await; + let _ = udp_rpc.send_udp(udp_interface, bind_addr, dest).await; }; let _ = tokio::join!(tcp_send, udp_send); } diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index e7c2e12d0f5b..3492b0d4a8e7 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -1,7 +1,7 @@ use super::{config::TEST_CONFIG, Error, PING_TIMEOUT, WAIT_FOR_TUNNEL_STATE_TIMEOUT}; use crate::network_monitor::{start_packet_monitor, MonitorOptions}; use futures::StreamExt; -use mullvad_management_interface::{types, ManagementServiceClient}; +use mullvad_management_interface::{types, ManagementServiceClient, MullvadProxyClient}; use mullvad_types::{ relay_constraints::{ BridgeState, Constraint, GeographicLocationConstraint, LocationConstraint, @@ -17,7 +17,7 @@ use std::{ time::Duration, }; use talpid_types::net::wireguard::{PeerConfig, PrivateKey, TunnelConfig}; -use test_rpc::{package::Package, AmIMullvad, Interface, ServiceClient}; +use test_rpc::{package::Package, AmIMullvad, ServiceClient}; use tokio::time::timeout; #[macro_export] @@ -80,10 +80,21 @@ pub async fn using_mullvad_exit(rpc: &ServiceClient) -> bool { .mullvad_exit_ip } +/// Get VPN tunnel interface name +pub async fn get_tunnel_interface(rpc: ManagementServiceClient) -> Option { + let mut client = MullvadProxyClient::from_rpc_client(rpc); + match client.get_tunnel_state().await.ok()? { + TunnelState::Connecting { endpoint, .. } | TunnelState::Connected { endpoint, .. } => { + endpoint.tunnel_interface + } + _ => None, + } +} + /// Sends a number of probes and returns the number of observed packets (UDP, TCP, or ICMP) pub async fn send_guest_probes( rpc: ServiceClient, - interface: Option, + interface: Option, destination: SocketAddr, ) -> Result { let pktmon = start_packet_monitor( @@ -130,12 +141,12 @@ pub async fn send_guest_probes( /// Send one probe per transport protocol to `destination` without running a packet monitor pub async fn send_guest_probes_without_monitor( rpc: ServiceClient, - interface: Option, + interface: Option, destination: SocketAddr, ) { - let bind_addr = if let Some(interface) = interface { + let bind_addr = if let Some(ref interface) = interface { SocketAddr::new( - rpc.get_interface_ip(interface) + rpc.get_interface_ip(interface.clone()) .await .expect("failed to obtain interface IP"), 0, @@ -145,9 +156,19 @@ pub async fn send_guest_probes_without_monitor( }; let tcp_rpc = rpc.clone(); - let tcp_send = async move { tcp_rpc.send_tcp(interface, bind_addr, destination).await }; + let tcp_interface = interface.clone(); + let tcp_send = async move { + tcp_rpc + .send_tcp(tcp_interface, bind_addr, destination) + .await + }; let udp_rpc = rpc.clone(); - let udp_send = async move { udp_rpc.send_udp(interface, bind_addr, destination).await }; + let udp_interface = interface.clone(); + let udp_send = async move { + udp_rpc + .send_udp(udp_interface, bind_addr, destination) + .await + }; let icmp = async move { ping_with_timeout(&rpc, destination.ip(), interface).await }; let _ = tokio::join!(tcp_send, udp_send, icmp); } @@ -155,7 +176,7 @@ pub async fn send_guest_probes_without_monitor( pub async fn ping_with_timeout( rpc: &ServiceClient, dest: IpAddr, - interface: Option, + interface: Option, ) -> Result<(), Error> { timeout(PING_TIMEOUT, rpc.send_ping(interface, dest)) .await diff --git a/test/test-manager/src/tests/install.rs b/test/test-manager/src/tests/install.rs index 04d401a22d96..37a89f5fc83c 100644 --- a/test/test-manager/src/tests/install.rs +++ b/test/test-manager/src/tests/install.rs @@ -11,7 +11,7 @@ use std::{ }; use test_macro::test_function; use test_rpc::meta::Os; -use test_rpc::{mullvad_daemon::ServiceStatus, Interface, ServiceClient}; +use test_rpc::{mullvad_daemon::ServiceStatus, ServiceClient}; /// Install the last stable version of the app and verify that it is running. #[test_function(priority = -200)] @@ -102,10 +102,14 @@ pub async fn test_upgrade_app(ctx: TestContext, rpc: ServiceClient) -> Result<() // Begin monitoring outgoing traffic and pinging // + let guest_iface = rpc + .get_default_interface() + .await + .expect("failed to obtain default interface"); let guest_ip = rpc - .get_interface_ip(Interface::NonTunnel) + .get_interface_ip(guest_iface) .await - .expect("failed to obtain tunnel IP"); + .expect("failed to obtain non-tun IP"); log::debug!("Guest IP: {guest_ip}"); log::debug!("Monitoring outgoing traffic"); diff --git a/test/test-manager/src/tests/settings.rs b/test/test-manager/src/tests/settings.rs index 4c5808f79042..03e31d216525 100644 --- a/test/test-manager/src/tests/settings.rs +++ b/test/test-manager/src/tests/settings.rs @@ -8,7 +8,7 @@ use mullvad_management_interface::ManagementServiceClient; use mullvad_types::states::TunnelState; use std::net::{IpAddr, SocketAddr}; use test_macro::test_function; -use test_rpc::{Interface, ServiceClient}; +use test_rpc::ServiceClient; /// Verify that traffic to private IPs is blocked when /// "local network sharing" is disabled, but not blocked @@ -46,8 +46,13 @@ pub async fn test_lan( log::info!("Test whether outgoing LAN traffic is blocked"); - let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_destination).await?; + let default_interface = rpc.get_default_interface().await?; + let detected_probes = send_guest_probes( + rpc.clone(), + Some(default_interface.clone()), + lan_destination, + ) + .await?; assert!( detected_probes.none(), "observed unexpected outgoing LAN packets" @@ -71,7 +76,7 @@ pub async fn test_lan( log::info!("Test whether outgoing LAN traffic is blocked"); let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_destination).await?; + send_guest_probes(rpc.clone(), Some(default_interface), lan_destination).await?; assert!( detected_probes.all(), "did not observe all outgoing LAN packets" @@ -133,12 +138,22 @@ pub async fn test_lockdown( // Ensure all destinations are unreachable // - let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_destination).await?; + let default_interface = rpc.get_default_interface().await?; + + let detected_probes = send_guest_probes( + rpc.clone(), + Some(default_interface.clone()), + lan_destination, + ) + .await?; assert!(detected_probes.none(), "observed outgoing packets to LAN"); - let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_destination).await?; + let detected_probes = send_guest_probes( + rpc.clone(), + Some(default_interface.clone()), + inet_destination, + ) + .await?; assert!( detected_probes.none(), "observed outgoing packets to internet" @@ -159,15 +174,23 @@ pub async fn test_lockdown( // Ensure private IPs are reachable, but not others // - let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_destination).await?; + let detected_probes = send_guest_probes( + rpc.clone(), + Some(default_interface.clone()), + lan_destination, + ) + .await?; assert!( detected_probes.all(), "did not observe some outgoing packets" ); - let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_destination).await?; + let detected_probes = send_guest_probes( + rpc.clone(), + Some(default_interface.clone()), + inet_destination, + ) + .await?; assert!( detected_probes.none(), "observed outgoing packets to internet" @@ -191,7 +214,7 @@ pub async fn test_lockdown( // Send traffic outside the tunnel to sanity check that the internet is *not* reachable via non- // tunnel interfaces. let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_destination).await?; + send_guest_probes(rpc.clone(), Some(default_interface), inet_destination).await?; assert!( detected_probes.none(), "observed outgoing packets to internet" diff --git a/test/test-manager/src/tests/tunnel.rs b/test/test-manager/src/tests/tunnel.rs index 1592f7c2a346..872a7bab939a 100644 --- a/test/test-manager/src/tests/tunnel.rs +++ b/test/test-manager/src/tests/tunnel.rs @@ -15,7 +15,7 @@ use talpid_types::net::{TransportProtocol, TunnelType}; use test_macro::test_function; use test_rpc::meta::Os; use test_rpc::mullvad_daemon::ServiceStatus; -use test_rpc::{Interface, ServiceClient}; +use test_rpc::ServiceClient; /// Set up an OpenVPN tunnel, UDP as well as TCP. /// This test fails if a working tunnel cannot be set up. @@ -160,8 +160,12 @@ pub async fn test_udp2tcp_tunnel( // Set up packet monitor // + let non_tunnel_interface = rpc + .get_default_interface() + .await + .expect("failed to obtain non-tun interface"); let guest_ip = rpc - .get_interface_ip(Interface::NonTunnel) + .get_interface_ip(non_tunnel_interface) .await .expect("failed to obtain inet interface IP"); @@ -473,7 +477,7 @@ pub async fn test_quantum_resistant_tunnel( // connect_and_wait(&mut mullvad_client).await?; - check_tunnel_psk(&rpc, false).await; + check_tunnel_psk(&rpc, &mullvad_client, false).await; log::info!("Setting tunnel protocol to WireGuard"); @@ -497,7 +501,7 @@ pub async fn test_quantum_resistant_tunnel( // connect_and_wait(&mut mullvad_client).await?; - check_tunnel_psk(&rpc, true).await; + check_tunnel_psk(&rpc, &mullvad_client, true).await; assert!( helpers::using_mullvad_exit(&rpc).await, @@ -507,11 +511,14 @@ pub async fn test_quantum_resistant_tunnel( Ok(()) } -async fn check_tunnel_psk(rpc: &ServiceClient, should_have_psk: bool) { +async fn check_tunnel_psk( + rpc: &ServiceClient, + mullvad_client: &ManagementServiceClient, + should_have_psk: bool, +) { match rpc.get_os().await.expect("failed to get OS") { Os::Linux => { - let name = rpc - .get_interface_name(Interface::Tunnel) + let name = helpers::get_tunnel_interface(mullvad_client.clone()) .await .expect("failed to get tun name"); let output = rpc diff --git a/test/test-manager/src/tests/tunnel_state.rs b/test/test-manager/src/tests/tunnel_state.rs index cf0b7f4efe0c..17e7d1e4aff7 100644 --- a/test/test-manager/src/tests/tunnel_state.rs +++ b/test/test-manager/src/tests/tunnel_state.rs @@ -16,7 +16,7 @@ use mullvad_types::{ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use talpid_types::net::{Endpoint, TransportProtocol, TunnelEndpoint, TunnelType}; use test_macro::test_function; -use test_rpc::{Interface, ServiceClient}; +use test_rpc::ServiceClient; /// Verify that outgoing TCP, UDP, and ICMP packets can be observed /// in the disconnected state. The purpose is mostly to rule prevent @@ -39,8 +39,13 @@ pub async fn test_disconnected_state( log::info!("Sending packets to {inet_destination}"); + let non_tunnel_interface = rpc + .get_default_interface() + .await + .expect("failed to obtain non-tun interface"); + let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_destination).await?; + send_guest_probes(rpc.clone(), Some(non_tunnel_interface), inet_destination).await?; assert!( detected_probes.all(), "did not see (all) outgoing packets to destination: {detected_probes:?}", @@ -117,26 +122,39 @@ pub async fn test_connecting_state( // Leak test // + let non_tunnel_interface = rpc + .get_default_interface() + .await + .expect("failed to obtain non-tun interface"); + assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_destination) - .await? - .none(), + send_guest_probes( + rpc.clone(), + Some(non_tunnel_interface.clone()), + inet_destination + ) + .await? + .none(), "observed unexpected outgoing packets (inet)" ); assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_destination) - .await? - .none(), + send_guest_probes( + rpc.clone(), + Some(non_tunnel_interface.clone()), + lan_destination + ) + .await? + .none(), "observed unexpected outgoing packets (lan)" ); assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_dns) + send_guest_probes(rpc.clone(), Some(non_tunnel_interface.clone()), inet_dns) .await? .none(), "observed unexpected outgoing packets (DNS, inet)" ); assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_dns) + send_guest_probes(rpc.clone(), Some(non_tunnel_interface), lan_dns) .await? .none(), "observed unexpected outgoing packets (DNS, lan)" @@ -200,26 +218,39 @@ pub async fn test_error_state( // Leak test // + let default_interface = rpc + .get_default_interface() + .await + .expect("failed to obtain non-tun interface"); + assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_destination) - .await? - .none(), + send_guest_probes( + rpc.clone(), + Some(default_interface.clone()), + inet_destination + ) + .await? + .none(), "observed unexpected outgoing packets (inet)" ); assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_destination) - .await? - .none(), + send_guest_probes( + rpc.clone(), + Some(default_interface.clone()), + lan_destination + ) + .await? + .none(), "observed unexpected outgoing packets (lan)" ); assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_dns) + send_guest_probes(rpc.clone(), Some(default_interface.clone()), inet_dns) .await? .none(), "observed unexpected outgoing packets (DNS, inet)" ); assert!( - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), lan_dns) + send_guest_probes(rpc.clone(), Some(default_interface), lan_dns) .await? .none(), "observed unexpected outgoing packets (DNS, lan)" @@ -317,8 +348,13 @@ pub async fn test_connected_state( log::info!("Test whether outgoing non-tunnel traffic is blocked"); + let nontun_iface = rpc + .get_default_interface() + .await + .expect("failed to find non-tun interface"); + let detected_probes = - send_guest_probes(rpc.clone(), Some(Interface::NonTunnel), inet_destination).await?; + send_guest_probes(rpc.clone(), Some(nontun_iface), inet_destination).await?; assert!( detected_probes.none(), "observed unexpected outgoing packets" diff --git a/test/test-rpc/src/client.rs b/test/test-rpc/src/client.rs index 387d0a2435d1..6be77afb4035 100644 --- a/test/test-rpc/src/client.rs +++ b/test/test-rpc/src/client.rs @@ -17,8 +17,6 @@ pub struct ServiceClient { client: service::ServiceClient, } -// TODO: implement wrapper methods using macro on Service trait - impl ServiceClient { pub fn new( connection_handle: transport::ConnectionHandle, @@ -156,7 +154,7 @@ impl ServiceClient { /// Send TCP packet pub async fn send_tcp( &self, - interface: Option, + interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), Error> { @@ -168,7 +166,7 @@ impl ServiceClient { /// Send UDP packet pub async fn send_udp( &self, - interface: Option, + interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), Error> { @@ -180,7 +178,7 @@ impl ServiceClient { /// Send ICMP pub async fn send_ping( &self, - interface: Option, + interface: Option, destination: IpAddr, ) -> Result<(), Error> { self.client @@ -196,16 +194,16 @@ impl ServiceClient { } /// Returns the IP of the given interface. - pub async fn get_interface_name(&self, interface: Interface) -> Result { + pub async fn get_interface_ip(&self, interface: String) -> Result { self.client - .get_interface_name(tarpc::context::current(), interface) + .get_interface_ip(tarpc::context::current(), interface) .await? } - /// Returns the IP of the given interface. - pub async fn get_interface_ip(&self, interface: Interface) -> Result { + /// Returns the name of the default non-tunnel interface + pub async fn get_default_interface(&self) -> Result { self.client - .get_interface_ip(tarpc::context::current(), interface) + .get_default_interface(tarpc::context::current()) .await? } diff --git a/test/test-rpc/src/lib.rs b/test/test-rpc/src/lib.rs index 2fd4411f4940..5b4f3b9a0dac 100644 --- a/test/test-rpc/src/lib.rs +++ b/test/test-rpc/src/lib.rs @@ -55,12 +55,6 @@ pub enum Error { Timeout, } -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)] -pub enum Interface { - Tunnel, - NonTunnel, -} - /// Response from am.i.mullvad.net #[derive(Debug, Serialize, Deserialize)] pub struct AmIMullvad { @@ -128,29 +122,29 @@ mod service { /// Send TCP packet async fn send_tcp( - interface: Option, + interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), Error>; /// Send UDP packet async fn send_udp( - interface: Option, + interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), Error>; /// Send ICMP - async fn send_ping(interface: Option, destination: IpAddr) -> Result<(), Error>; + async fn send_ping(interface: Option, destination: IpAddr) -> Result<(), Error>; /// Fetch the current location. async fn geoip_lookup(mullvad_host: String) -> Result; - /// Returns the name of the given interface. - async fn get_interface_name(interface: Interface) -> Result; + /// Returns the IP of the given interface. + async fn get_interface_ip(interface: String) -> Result; /// Returns the IP of the given interface. - async fn get_interface_ip(interface: Interface) -> Result; + async fn get_default_interface() -> Result; /// Perform DNS resolution. async fn resolve_hostname(hostname: String) -> Result, Error>; diff --git a/test/test-runner/src/main.rs b/test/test-runner/src/main.rs index 5ff1476aa3f7..ebf0d1e47454 100644 --- a/test/test-runner/src/main.rs +++ b/test/test-runner/src/main.rs @@ -13,7 +13,7 @@ use test_rpc::{ mullvad_daemon::{ServiceStatus, SOCKET_PATH}, package::Package, transport::GrpcForwarder, - AppTrace, Interface, Service, + AppTrace, Service, }; use tokio::sync::broadcast::error::TryRecvError; use tokio::{ @@ -117,7 +117,7 @@ impl Service for TestServer { async fn send_tcp( self, _: context::Context, - interface: Option, + interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), test_rpc::Error> { @@ -127,7 +127,7 @@ impl Service for TestServer { async fn send_udp( self, _: context::Context, - interface: Option, + interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), test_rpc::Error> { @@ -137,10 +137,10 @@ impl Service for TestServer { async fn send_ping( self, _: context::Context, - interface: Option, + interface: Option, destination: IpAddr, ) -> Result<(), test_rpc::Error> { - net::send_ping(interface, destination).await + net::send_ping(interface.as_ref().map(String::as_str), destination).await } async fn geoip_lookup( @@ -165,20 +165,16 @@ impl Service for TestServer { .collect()) } - async fn get_interface_name( - self, - _: context::Context, - interface: Interface, - ) -> Result { - net::get_interface_name(interface).await - } - async fn get_interface_ip( self, _: context::Context, - interface: Interface, + interface: String, ) -> Result { - net::get_interface_ip(interface).await + net::get_interface_ip(&interface).await + } + + async fn get_default_interface(self, _: context::Context) -> Result { + Ok(net::get_default_interface().to_owned()) } async fn poll_output( diff --git a/test/test-runner/src/net.rs b/test/test-runner/src/net.rs index f140d72ebdf4..0f28f5b160eb 100644 --- a/test/test-runner/src/net.rs +++ b/test/test-runner/src/net.rs @@ -5,7 +5,6 @@ use std::{ net::{IpAddr, SocketAddr}, process::Output, }; -use test_rpc::Interface; use tokio::{ io::AsyncWriteExt, net::{TcpStream, UdpSocket}, @@ -13,7 +12,7 @@ use tokio::{ }; pub async fn send_tcp( - bind_interface: Option, + bind_interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), test_rpc::Error> { @@ -33,8 +32,6 @@ pub async fn send_tcp( })?; if let Some(iface) = bind_interface { - let iface = get_interface_name(iface).await?; - #[cfg(target_os = "macos")] let interface_index = unsafe { let name = CString::new(iface).unwrap(); @@ -90,7 +87,7 @@ pub async fn send_tcp( } pub async fn send_udp( - bind_interface: Option, + bind_interface: Option, bind_addr: SocketAddr, destination: SocketAddr, ) -> Result<(), test_rpc::Error> { @@ -110,8 +107,6 @@ pub async fn send_udp( })?; if let Some(iface) = bind_interface { - let iface = get_interface_name(iface).await?; - #[cfg(target_os = "macos")] let interface_index = unsafe { let name = CString::new(iface).unwrap(); @@ -164,7 +159,7 @@ pub async fn send_udp( } pub async fn send_ping( - interface: Option, + interface: Option<&str>, destination: IpAddr, ) -> Result<(), test_rpc::Error> { #[cfg(target_os = "windows")] @@ -193,8 +188,8 @@ pub async fn send_ping( cmd.args(["-c", "1"]); match interface { - Some(Interface::Tunnel) => { - log::info!("Pinging {destination} in tunnel"); + Some(interface) => { + log::info!("Pinging {destination} for on interface {interface}"); #[cfg(target_os = "windows")] if let Some(source_ip) = source_ip { @@ -202,24 +197,10 @@ pub async fn send_ping( } #[cfg(target_os = "windows")] - cmd.args(["-I", &tunnel_interface().await?]); + cmd.args(["-I", interface]); #[cfg(target_os = "macos")] - cmd.args(["-b", &tunnel_interface().await?]); - } - Some(Interface::NonTunnel) => { - log::info!("Pinging {destination} outside tunnel"); - - #[cfg(target_os = "windows")] - if let Some(source_ip) = source_ip { - cmd.args(["-S", &source_ip.to_string()]); - } - - #[cfg(target_os = "linux")] - cmd.args(["-I", non_tunnel_interface()]); - - #[cfg(target_os = "macos")] - cmd.args(["-b", non_tunnel_interface()]); + cmd.args(["-b", interface]); } None => log::info!("Pinging {destination}"), } @@ -241,18 +222,16 @@ pub async fn send_ping( } #[cfg(unix)] -pub async fn get_interface_ip(interface: Interface) -> Result { +pub async fn get_interface_ip(interface: &str) -> Result { // TODO: IPv6 use std::net::Ipv4Addr; - let alias = get_interface_name(interface).await?; - let addrs = nix::ifaddrs::getifaddrs().map_err(|error| { log::error!("Failed to obtain interfaces: {}", error); test_rpc::Error::Syscall })?; for addr in addrs { - if addr.interface_name == alias { + if addr.interface_name == interface { if let Some(address) = addr.address { if let Some(sockaddr) = address.as_sockaddr_in() { return Ok(IpAddr::V4(Ipv4Addr::from(sockaddr.ip()))); @@ -265,15 +244,8 @@ pub async fn get_interface_ip(interface: Interface) -> Result Result { - match interface { - Interface::Tunnel => tunnel_interface().await, - Interface::NonTunnel => Ok(non_tunnel_interface().to_owned()), - } -} - #[cfg(target_os = "windows")] -pub async fn get_interface_ip(interface: Interface) -> Result { +pub async fn get_interface_ip(interface: &str) -> Result { // TODO: IPv6 get_interface_ip_for_family(interface, talpid_windows::net::AddressFamily::Ipv4) @@ -283,57 +255,19 @@ pub async fn get_interface_ip(interface: Interface) -> Result Result, ()> { - let interface = match interface { - Interface::NonTunnel => non_tunnel_interface(), - Interface::Tunnel => TUNNEL_INTERFACE, - }; - let interface_alias = talpid_windows::net::luid_from_alias(interface).map_err(|error| { + let luid = talpid_windows::net::luid_from_alias(interface).map_err(|error| { log::error!("Failed to obtain interface LUID: {error}"); })?; - - talpid_windows::net::get_ip_address_for_interface(family, interface_alias).map_err(|error| { + talpid_windows::net::get_ip_address_for_interface(family, luid).map_err(|error| { log::error!("Failed to obtain interface IP: {error}"); }) } -/// Obtain tunnel interface from 'mullvad status -v' -async fn tunnel_interface() -> Result { - let mut cmd = Command::new("mullvad"); - cmd.args(["status", "-v"]); - let output = cmd.output().await.map_err(|_| { - log::error!("Failed to run 'mullvad status -v'"); - test_rpc::Error::InterfaceNotFound - })?; - - let stdout_str = std::str::from_utf8(&output.stdout).map_err(|_| { - log::error!("Failed to parse 'mullvad status -v' as utf8"); - test_rpc::Error::InterfaceNotFound - })?; - - parse_status_output(stdout_str) -} - -fn parse_status_output(stdout_str: &str) -> Result { - let mut tokens = stdout_str.split_whitespace(); - loop { - let token = tokens.next().ok_or_else(|| { - log::error!("Failed to find interface in 'mullvad status -v' output"); - test_rpc::Error::InterfaceNotFound - })?; - if token != "interface:" { - continue; - } - if let Some(interface) = tokens.next() { - return Ok(interface.to_owned()); - } - } -} - #[cfg(target_os = "windows")] -fn non_tunnel_interface() -> &'static str { +pub fn get_default_interface() -> &'static str { use once_cell::sync::OnceCell; use talpid_platform_metadata::WindowsVersion; @@ -350,12 +284,12 @@ fn non_tunnel_interface() -> &'static str { } #[cfg(target_os = "linux")] -fn non_tunnel_interface() -> &'static str { +pub fn get_default_interface() -> &'static str { "ens3" } #[cfg(target_os = "macos")] -fn non_tunnel_interface() -> &'static str { +pub fn get_default_interface() -> &'static str { "en0" }