Skip to content

Commit

Permalink
Obtain tunnel interface from daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
dlon committed Nov 16, 2023
1 parent c2fc533 commit 2916806
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 206 deletions.
4 changes: 4 additions & 0 deletions mullvad-management-interface/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
Ok(self
.0
Expand Down
84 changes: 48 additions & 36 deletions test/test-manager/src/tests/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
//
Expand All @@ -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();
Expand Down Expand Up @@ -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,
),
)
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -661,19 +671,21 @@ async fn connect_local_wg_relay(mullvad_client: &mut ManagementServiceClient) ->

async fn spoof_packets(
rpc: &ServiceClient,
interface: Option<Interface>,
interface: Option<String>,
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);
}
Expand Down
39 changes: 30 additions & 9 deletions test/test-manager/src/tests/helpers.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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]
Expand Down Expand Up @@ -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<String> {
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>,
interface: Option<String>,
destination: SocketAddr,
) -> Result<ProbeResult, Error> {
let pktmon = start_packet_monitor(
Expand Down Expand Up @@ -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>,
interface: Option<String>,
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,
Expand All @@ -145,17 +156,27 @@ 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);
}

pub async fn ping_with_timeout(
rpc: &ServiceClient,
dest: IpAddr,
interface: Option<Interface>,
interface: Option<String>,
) -> Result<(), Error> {
timeout(PING_TIMEOUT, rpc.send_ping(interface, dest))
.await
Expand Down
10 changes: 7 additions & 3 deletions test/test-manager/src/tests/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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");
Expand Down
Loading

0 comments on commit 2916806

Please sign in to comment.