diff options
| author | Joakim Hulthe <joakim.hulthe@mullvad.net> | 2025-09-10 18:38:04 +0200 |
|---|---|---|
| committer | Joakim Hulthe <joakim.hulthe@mullvad.net> | 2025-09-15 11:08:27 +0200 |
| commit | 568c64d4e7c5407a383fb1a5d4995a9afbee2541 (patch) | |
| tree | 60ba53f2212603259f99ab611d6790716d4cd55d | |
| parent | 3a9d97641a8999abd5b4721fbb03642b8c0868be (diff) | |
| download | mullvadvpn-568c64d4e7c5407a383fb1a5d4995a9afbee2541.tar.xz mullvadvpn-568c64d4e7c5407a383fb1a5d4995a9afbee2541.zip | |
Fix IPv6 in e2e connection-checker
| -rw-r--r-- | test/connection-checker/src/net.rs | 32 | ||||
| -rw-r--r-- | test/test-manager/src/tests/helpers.rs | 81 |
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, + }), }) } |
