From dad2d3077394e94ae92e2ee982ef03915ab9b450 Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Tue, 2 Jun 2026 08:03:27 +0200 Subject: [PATCH] address comment --- nym-gateway-probe/src/common/probe_tests.rs | 26 +++++++++---------- nym-gateway-probe/src/common/wireguard.rs | 19 ++++++++------ .../src/cli/run_ports_check.rs | 25 +++++++++++++++--- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/nym-gateway-probe/src/common/probe_tests.rs b/nym-gateway-probe/src/common/probe_tests.rs index 30b14345d0..ee403d6b07 100644 --- a/nym-gateway-probe/src/common/probe_tests.rs +++ b/nym-gateway-probe/src/common/probe_tests.rs @@ -80,8 +80,6 @@ pub async fn wg_probe( AuthenticatorVersion::V1 | AuthenticatorVersion::UNKNOWN => bail!("unknown version number"), }; - let mut wg_outcome = WgProbeResults::default(); - info!( "connecting to authenticator: {}...", auth_client.auth_recipient @@ -135,9 +133,9 @@ pub async fn wg_probe( info!("Successfully registered with the gateway"); - wg_outcome.can_register = true; - - // Run tunnel connectivity tests using shared helper + // Run tunnel connectivity tests using shared helper. + // run_tunnel_tests issues blocking CGo calls into Go, so it must run on + // tokio's dedicated blocking thread pool to avoid stalling the async runtime. let tunnel_config = WgTunnelConfig::new( registered_data.private_ips().ipv4.to_string(), registered_data.private_ips().ipv6.to_string(), @@ -145,16 +143,18 @@ pub async fn wg_probe( public_key_hex, wg_endpoint, ); + let awg_str = awg_args.unwrap_or_default(); - run_tunnel_tests( - &tunnel_config, - &netstack_args, - &awg_args.unwrap_or_default(), - port_check_only, - &mut wg_outcome, - ); + let mut tunnel_result = tokio::task::spawn_blocking(move || { + run_tunnel_tests(&tunnel_config, &netstack_args, &awg_str, port_check_only) + }) + .await + .map_err(|e| anyhow::anyhow!("netstack task panicked: {e}"))?; - Ok(wg_outcome) + // can_register is determined by the auth handshake above, not by netstack + tunnel_result.can_register = true; + + Ok(tunnel_result) } pub async fn lp_registration_probe( diff --git a/nym-gateway-probe/src/common/wireguard.rs b/nym-gateway-probe/src/common/wireguard.rs index a2cb783433..ad20d96693 100644 --- a/nym-gateway-probe/src/common/wireguard.rs +++ b/nym-gateway-probe/src/common/wireguard.rs @@ -65,24 +65,25 @@ impl WgTunnelConfig { /// - Optional download test /// - Optional exit policy port check (TCP connect through tunnel) /// -/// Results are written directly into the provided `wg_outcome` to avoid field-by-field -/// copying at call sites. +/// **Important:** this function issues blocking FFI calls into Go (CGo) and MUST be +/// called via `tokio::task::spawn_blocking` at any async call site. It returns a +/// fresh `WgProbeResults`; the caller sets `can_register` after verifying WG +/// registration succeeded. /// /// # Arguments /// * `config` - WireGuard tunnel configuration /// * `netstack_args` - Netstack test parameters (DNS, hosts to ping, timeouts, etc.) /// * `awg_args` - Amnezia WireGuard arguments (empty string for standard WG) /// * `port_check_only` - If true, skip pings/download and only run TCP port checks -/// * `wg_outcome` - Mutable reference to write test results into -// This function extracts the shared netstack testing logic from -// wg_probe() and wg_probe_lp() to eliminate code duplication. +// This function extracts the shared netstack testing logic from wg_probe() +// to eliminate code duplication across probe modes. pub fn run_tunnel_tests( config: &WgTunnelConfig, netstack_args: &NetstackArgs, awg_args: &str, port_check_only: bool, - wg_outcome: &mut WgProbeResults, -) { +) -> WgProbeResults { + let mut wg_outcome = WgProbeResults::default(); // Build the netstack request let netstack_request = NetstackRequest::new( &config.private_ipv4, @@ -143,7 +144,7 @@ pub fn run_tunnel_tests( // in port-check-only mode, skip IPv6 tests — port checks ran through IPv4 above if port_check_only { info!("Port-check-only mode: skipping IPv6 tunnel tests"); - return; + return wg_outcome; } // Perform IPv6 ping test @@ -182,4 +183,6 @@ pub fn run_tunnel_tests( error!("Internal error (IPv6): {error}") } } + + wg_outcome } diff --git a/nym-node-status-api/nym-node-status-agent/src/cli/run_ports_check.rs b/nym-node-status-api/nym-node-status-agent/src/cli/run_ports_check.rs index c4bc641b6f..aa370ceb15 100644 --- a/nym-node-status-api/nym-node-status-agent/src/cli/run_ports_check.rs +++ b/nym-node-status-api/nym-node-status-agent/src/cli/run_ports_check.rs @@ -3,6 +3,8 @@ use crate::cli::common; use crate::log_capture::LogCapture; use nym_gateway_probe::RunPortsConfig; use tracing::instrument; +// Hard deadline for a single port-scan job. +const PORT_SCAN_HARD_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5400); pub(crate) async fn run_ports_check( servers: &[ServerConfig], @@ -55,15 +57,30 @@ pub(crate) async fn run_ports_check( let credentials_args = common::credential_args_from(testrun.ticket_materials); log_capture.start(); - let port_check_result_res = nym_gateway_probe::Probe::run_ports_for_agent( + let probe_future = nym_gateway_probe::Probe::run_ports_for_agent( gateway_identity_pubkey, network, &run_ports_config, credentials_args, - ) - .await; + ); + let port_check_result_res = tokio::time::timeout(PORT_SCAN_HARD_TIMEOUT, probe_future).await; let probe_log = log_capture.stop_and_drain(); - let port_check_result = port_check_result_res?; + + let port_check_result = match port_check_result_res { + Ok(inner) => inner?, + Err(_elapsed) => { + tracing::error!( + gateway = %gateway_identity_key, + testrun = testrun_id, + timeout_secs = PORT_SCAN_HARD_TIMEOUT.as_secs(), + "Port scan exceeded hard timeout; aborting to free resources" + ); + return Err(anyhow::anyhow!( + "port scan timed out after {}s", + PORT_SCAN_HARD_TIMEOUT.as_secs() + )); + } + }; submit_ports_check_results_to_servers( servers,