Merge remote-tracking branch 'origin/feature/probe-ports-test' into feature/probe-ports-test

This commit is contained in:
benedettadavico
2026-04-14 17:55:24 +02:00
+108 -7
View File
@@ -1,12 +1,13 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use clap::{Args, Parser, Subcommand};
use clap::{ArgGroup, Args, Parser, Subcommand, ValueEnum};
use nym_bin_common::bin_info;
use nym_config::defaults::setup_env;
use nym_gateway_probe::config::{CredentialArgs, CredentialMode, NetstackArgs, ProbeConfig};
use nym_gateway_probe::{
NymApiDirectory, PortCheckResult, ProbeResult, RunPortsConfig, query_gateway_by_ip,
DirectPortCheckProtocol, NymApiDirectory, PortCheckResult, ProbeResult, RunPortsConfig,
query_gateway_by_ip,
};
use nym_sdk::mixnet::NodeIdentity;
use serde::Serialize;
@@ -93,11 +94,19 @@ enum Commands {
config_dir: Option<PathBuf>,
/// Bonded gateway identity.
#[arg(long, short = 'g', alias = "gateway")]
entry_gateway: NodeIdentity,
/// Cannot be used with --gateway-ip.
#[arg(long, short = 'g', alias = "gateway", conflicts_with = "gateway_ip")]
entry_gateway: Option<NodeIdentity>,
/// Gateway queried directly by IP address (unannounced/local gateways)
/// Cannot be used with --gateway/--entry-gateway.
/// Cannot be used with --mnemonic or --use-mock-ecash.
#[arg(long, conflicts_with = "entry_gateway", conflicts_with = "gateway")]
gateway_ip: Option<String>,
/// Separate exit gateway to test (entry_gateway is used for mixnet entry)
#[arg(long)]
/// Cannot be used with --gateway-ip.
#[arg(long, conflicts_with = "gateway_ip")]
exit_gateway: Option<NodeIdentity>,
/// Test every port in the canonical exit policy (network-tunnel-manager.sh PORT_MAPPINGS).
@@ -105,9 +114,14 @@ enum Commands {
#[arg(long)]
check_all_ports: bool,
/// Credential arguments (required).
/// Specify the protocol used for tests with --gateway-ip.
#[arg(long, value_enum, default_value_t = PortCheckProtocol::Auto)]
check_protocol: PortCheckProtocol,
/// Optional credential arguments.
/// Required only in bonded mode (when using --gateway/--entry-gateway).
#[command(flatten)]
credential_mode: CredentialMode,
credential_mode: OptionalCredentialMode,
#[command(flatten)]
probe_config: RunPortsProbeConfig,
@@ -128,6 +142,45 @@ enum Commands {
},
}
#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
enum PortCheckProtocol {
Auto,
Tcp,
Udp,
}
#[derive(Debug, Args, Clone)]
#[command(group(
ArgGroup::new("run_ports_credential_mode")
.args(["use_mock_ecash","mnemonic"])
.required(false)
.multiple(false)
))]
struct OptionalCredentialMode {
/// Use mock ecash credentials for testing
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with = "gateway_ip")]
use_mock_ecash: bool,
/// Mnemonic to get credentials from the blockchain
#[arg(long, conflicts_with = "gateway_ip")]
mnemonic: Option<String>,
}
impl OptionalCredentialMode {
fn into_required(self) -> anyhow::Result<CredentialMode> {
if self.use_mock_ecash || self.mnemonic.is_some() {
Ok(CredentialMode {
use_mock_ecash: self.use_mock_ecash,
mnemonic: self.mnemonic,
})
} else {
anyhow::bail!(
"missing credentials for bonded run-ports mode: provide --mnemonic <MNEMONIC> or --use-mock-ecash"
)
}
}
}
#[derive(Debug, Args, Clone)]
struct RunPortsProbeConfig {
/// Only choose gateway with that minimum performance
@@ -272,9 +325,11 @@ pub(crate) async fn run() -> anyhow::Result<ProbeOutput> {
}
Commands::RunPorts {
entry_gateway,
gateway_ip,
exit_gateway,
config_dir,
check_all_ports,
check_protocol,
credential_mode,
probe_config,
} => {
@@ -294,6 +349,47 @@ pub(crate) async fn run() -> anyhow::Result<ProbeOutput> {
);
}
if let Some(gateway_ip) = gateway_ip {
info!("Using direct IP-only port check mode for gateway: {gateway_ip}");
if entry_gateway.is_some() {
anyhow::bail!("--gateway/--entry-gateway cannot be used with --gateway-ip");
}
let target_ip: std::net::IpAddr = gateway_ip.parse().map_err(|_| {
anyhow::anyhow!(
"invalid --gateway-ip value '{gateway_ip}': expected plain IP address"
)
})?;
let direct_protocol = match check_protocol {
PortCheckProtocol::Auto => DirectPortCheckProtocol::Auto,
PortCheckProtocol::Tcp => DirectPortCheckProtocol::Tcp,
PortCheckProtocol::Udp => DirectPortCheckProtocol::Udp,
};
return Box::pin(nym_gateway_probe::Probe::probe_run_ports_direct_ip(
&gateway_ip,
target_ip,
&run_ports_config,
direct_protocol,
))
.await
.map(ProbeOutput::PortCheck);
}
if check_protocol == PortCheckProtocol::Udp {
anyhow::bail!(
"--check-protocol udp is only supported with --gateway-ip direct mode"
);
}
if check_protocol == PortCheckProtocol::Auto {
info!(
"Bonded run-ports mode uses TCP checks; treating --check-protocol auto as tcp"
);
}
let credential_mode = credential_mode.into_required()?;
let api_url = network
.endpoints
.first()
@@ -301,6 +397,11 @@ pub(crate) async fn run() -> anyhow::Result<ProbeOutput> {
.ok_or(anyhow::anyhow!("missing api url"))?;
let directory = NymApiDirectory::new(api_url).await?;
let entry_gateway = entry_gateway.ok_or_else(|| {
anyhow::anyhow!(
"missing gateway selection: provide --gateway <ID> or --gateway-ip <IP[:PORT]>"
)
})?;
let entry_details = directory
.entry_gateway(&entry_gateway)?