summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJoakim Hulthe <joakim.hulthe@mullvad.net>2025-09-10 18:38:04 +0200
committerJoakim Hulthe <joakim.hulthe@mullvad.net>2025-09-15 11:08:27 +0200
commit568c64d4e7c5407a383fb1a5d4995a9afbee2541 (patch)
tree60ba53f2212603259f99ab611d6790716d4cd55d
parent3a9d97641a8999abd5b4721fbb03642b8c0868be (diff)
downloadmullvadvpn-568c64d4e7c5407a383fb1a5d4995a9afbee2541.tar.xz
mullvadvpn-568c64d4e7c5407a383fb1a5d4995a9afbee2541.zip
Fix IPv6 in e2e connection-checker
-rw-r--r--test/connection-checker/src/net.rs32
-rw-r--r--test/test-manager/src/tests/helpers.rs81
2 files changed, 73 insertions, 40 deletions
diff --git a/test/connection-checker/src/net.rs b/test/connection-checker/src/net.rs
index 287c33bdfe..e087ad50b5 100644
--- a/test/connection-checker/src/net.rs
+++ b/test/connection-checker/src/net.rs
@@ -1,26 +1,26 @@
use anyhow::{Context, anyhow};
use std::{
io::Write,
- net::{IpAddr, Ipv4Addr, SocketAddr},
+ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
time::Duration,
};
use crate::cli::Opt;
pub fn send_tcp(opt: &Opt, destination: SocketAddr) -> anyhow::Result<()> {
- let bind_addr: SocketAddr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0);
+ eprintln!("Leaking TCP packets to {destination}");
- let family = match &destination {
- SocketAddr::V4(_) => socket2::Domain::IPV4,
- SocketAddr::V6(_) => socket2::Domain::IPV6,
+ let (family, bind_address) = match &destination {
+ SocketAddr::V4(_) => (socket2::Domain::IPV4, IpAddr::from(Ipv4Addr::UNSPECIFIED)),
+ SocketAddr::V6(_) => (socket2::Domain::IPV6, IpAddr::from(Ipv6Addr::UNSPECIFIED)),
};
+ let bind_address: SocketAddr = SocketAddr::new(bind_address, 0);
+
let sock = socket2::Socket::new(family, socket2::Type::STREAM, Some(socket2::Protocol::TCP))
.context(anyhow!("Failed to create TCP socket"))?;
- eprintln!("Leaking TCP packets to {destination}");
-
- sock.bind(&socket2::SockAddr::from(bind_addr))
- .context(anyhow!("Failed to bind TCP socket to {bind_addr}"))?;
+ sock.bind(&socket2::SockAddr::from(bind_address))
+ .context(anyhow!("Failed to bind TCP socket to {bind_address}"))?;
let timeout = Duration::from_secs(opt.leak_timeout);
sock.set_write_timeout(Some(timeout))?;
@@ -38,19 +38,19 @@ pub fn send_tcp(opt: &Opt, destination: SocketAddr) -> anyhow::Result<()> {
}
pub fn send_udp(opt: &Opt, destination: SocketAddr) -> Result<(), anyhow::Error> {
- let bind_addr: SocketAddr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0);
-
eprintln!("Leaking UDP packets to {destination}");
- let family = match &destination {
- SocketAddr::V4(_) => socket2::Domain::IPV4,
- SocketAddr::V6(_) => socket2::Domain::IPV6,
+ let (family, bind_address) = match &destination {
+ SocketAddr::V4(_) => (socket2::Domain::IPV4, IpAddr::from(Ipv4Addr::UNSPECIFIED)),
+ SocketAddr::V6(_) => (socket2::Domain::IPV6, IpAddr::from(Ipv6Addr::UNSPECIFIED)),
};
+ let bind_address: SocketAddr = SocketAddr::new(bind_address, 0);
+
let sock = socket2::Socket::new(family, socket2::Type::DGRAM, Some(socket2::Protocol::UDP))
.context("Failed to create UDP socket")?;
- sock.bind(&socket2::SockAddr::from(bind_addr))
- .context(anyhow!("Failed to bind UDP socket to {bind_addr}"))?;
+ sock.bind(&socket2::SockAddr::from(bind_address))
+ .context(anyhow!("Failed to bind UDP socket to {bind_address}"))?;
let std_socket = std::net::UdpSocket::from(sock);
std_socket
diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs
index 6426c6ace2..ac3a16fc9a 100644
--- a/test/test-manager/src/tests/helpers.rs
+++ b/test/test-manager/src/tests/helpers.rs
@@ -1072,6 +1072,11 @@ impl ConnChecker {
log::debug!("spawning connection checker");
let opts = {
+ let ipvx = match self.leak_destination {
+ SocketAddr::V4(..) => "ipv4",
+ SocketAddr::V6(..) => "ipv6",
+ };
+
let mut args = [
"--interactive",
"--timeout",
@@ -1085,7 +1090,7 @@ impl ConnChecker {
"--leak-udp",
"--leak-icmp",
"--url",
- &format!("https://am.i.{}/json", TEST_CONFIG.mullvad_host),
+ &format!("https://{ipvx}.am.i.{}/json", TEST_CONFIG.mullvad_host),
]
.map(String::from)
.to_vec();
@@ -1181,37 +1186,61 @@ impl ConnCheckerHandle<'_> {
/// Assert that traffic is blocked and that no packets are leaked.
pub async fn assert_blocked(&mut self) -> anyhow::Result<()> {
log::info!("checking that connection is blocked");
- let status = self.check_connection().await?;
- ensure!(status.am_i_mullvad.is_err());
- ensure!(!status.leaked_tcp);
- ensure!(!status.leaked_udp);
- ensure!(!status.leaked_icmp);
-
- Ok(())
+ async {
+ let status = self.check_connection().await?;
+ ensure!(status.am_i_mullvad.is_err());
+ ensure!(!status.leaked_tcp);
+ ensure!(!status.leaked_udp);
+ ensure!(!status.leaked_icmp);
+ Ok(())
+ }
+ .await
+ .with_context(|| {
+ anyhow!(
+ "assert_secure failed (leak_destination={})",
+ self.checker.leak_destination,
+ )
+ })
}
/// Assert that traffic is flowing through the Mullvad tunnel and that no packets are leaked.
pub async fn assert_secure(&mut self) -> anyhow::Result<()> {
log::info!("checking that connection is secure");
- let status = self.check_connection().await?;
- ensure!(status.am_i_mullvad?);
- ensure!(!status.leaked_tcp);
- ensure!(!status.leaked_udp);
- ensure!(!status.leaked_icmp);
-
- Ok(())
+ async {
+ let status = self.check_connection().await?;
+ ensure!(status.am_i_mullvad?);
+ ensure!(!status.leaked_tcp);
+ ensure!(!status.leaked_udp);
+ ensure!(!status.leaked_icmp);
+ Ok(())
+ }
+ .await
+ .with_context(|| {
+ anyhow!(
+ "assert_secure failed (leak_destination={})",
+ self.checker.leak_destination,
+ )
+ })
}
/// Assert that traffic is NOT flowing through the Mullvad tunnel and that packets ARE leaked.
pub async fn assert_insecure(&mut self) -> anyhow::Result<()> {
log::info!("checking that connection is not secure");
- let status = self.check_connection().await?;
- ensure!(!status.am_i_mullvad?);
- ensure!(status.leaked_tcp);
- ensure!(status.leaked_udp);
- ensure!(status.leaked_icmp);
-
- Ok(())
+ async {
+ let status = self.check_connection().await?;
+ ensure!(!status.am_i_mullvad?);
+ ensure!(status.leaked_tcp);
+ ensure!(status.leaked_udp);
+ ensure!(status.leaked_icmp);
+ Ok(())
+ }
+ .await
+ .with_context(|| {
+ anyhow!(
+ "assert_secure failed (leak_destination={})",
+ self.checker.leak_destination,
+ )
+ })
}
pub async fn check_connection(&mut self) -> anyhow::Result<ConnectionStatus> {
@@ -1240,6 +1269,8 @@ impl ConnCheckerHandle<'_> {
.await
.map_err(|_e| anyhow!("Packet monitor unexpectedly stopped"))?;
+ let leak_destination = self.checker.leak_destination;
+
Ok(ConnectionStatus {
am_i_mullvad: parse_am_i_mullvad(line),
@@ -1249,8 +1280,10 @@ impl ConnCheckerHandle<'_> {
leaked_udp: (monitor_result.packets.iter())
.any(|pkt| pkt.protocol == IpNextHeaderProtocols::Udp),
- leaked_icmp: (monitor_result.packets.iter())
- .any(|pkt| pkt.protocol == IpNextHeaderProtocols::Icmp),
+ leaked_icmp: (monitor_result.packets.iter()).any(|pkt| match leak_destination {
+ SocketAddr::V4(..) => pkt.protocol == IpNextHeaderProtocols::Icmp,
+ SocketAddr::V6(..) => pkt.protocol == IpNextHeaderProtocols::Icmpv6,
+ }),
})
}