shuffling files around in the probe, before improving it (#6391)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::common::nodes::TestedNodeLpDetails;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_ip_packet_requests::v8::response::{
|
||||
ControlResponse, DataResponse, InfoLevel, IpPacketResponse, IpPacketResponseData,
|
||||
};
|
||||
use nym_lp::peer::LpRemotePeer;
|
||||
use nym_sdk::mixnet::ReconstructedMessage;
|
||||
use tracing::*;
|
||||
|
||||
pub fn to_lp_remote_peer(identity: ed25519::PublicKey, data: TestedNodeLpDetails) -> LpRemotePeer {
|
||||
LpRemotePeer::new(identity, data.x25519).with_key_digests(
|
||||
data.expected_kem_key_hashes,
|
||||
data.expected_signing_key_hashes,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn mixnet_debug_config(
|
||||
min_gateway_performance: Option<u8>,
|
||||
ignore_egress_epoch_role: bool,
|
||||
) -> nym_client_core::config::DebugConfig {
|
||||
let mut debug_config = nym_client_core::config::DebugConfig::default();
|
||||
debug_config
|
||||
.traffic
|
||||
.disable_main_poisson_packet_distribution = true;
|
||||
debug_config.cover_traffic.disable_loop_cover_traffic_stream = true;
|
||||
if let Some(minimum_gateway_performance) = min_gateway_performance {
|
||||
debug_config.topology.minimum_gateway_performance = minimum_gateway_performance;
|
||||
}
|
||||
if ignore_egress_epoch_role {
|
||||
debug_config.topology.ignore_egress_epoch_role = ignore_egress_epoch_role;
|
||||
}
|
||||
|
||||
debug_config
|
||||
}
|
||||
|
||||
pub fn unpack_data_response(reconstructed_message: &ReconstructedMessage) -> Option<DataResponse> {
|
||||
match IpPacketResponse::from_reconstructed_message(reconstructed_message) {
|
||||
Ok(response) => match response.data {
|
||||
IpPacketResponseData::Data(data_response) => Some(data_response),
|
||||
IpPacketResponseData::Control(control) => match *control {
|
||||
ControlResponse::Info(info) => {
|
||||
let msg = format!("Received info response from the mixnet: {}", info.reply);
|
||||
match info.level {
|
||||
InfoLevel::Info => info!("{msg}"),
|
||||
InfoLevel::Warn => warn!("{msg}"),
|
||||
InfoLevel::Error => error!("{msg}"),
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
info!("Ignoring: {:?}", control);
|
||||
None
|
||||
}
|
||||
},
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Failed to parse mixnet message: {err}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,11 @@
|
||||
//! This module contains shared functionality used by multiple test modes:
|
||||
//! - WireGuard tunnel testing via netstack
|
||||
|
||||
pub mod wireguard;
|
||||
|
||||
pub use wireguard::{
|
||||
TwoHopWgTunnelConfig, WgTunnelConfig, run_tunnel_tests, run_two_hop_tunnel_tests,
|
||||
};
|
||||
pub(crate) mod bandwidth_helpers;
|
||||
pub(crate) mod helpers;
|
||||
pub(crate) mod icmp;
|
||||
pub(crate) mod netstack;
|
||||
pub(crate) mod nodes;
|
||||
pub(crate) mod probe_tests;
|
||||
pub(crate) mod types;
|
||||
pub(crate) mod wireguard;
|
||||
|
||||
@@ -15,7 +15,7 @@ mod sys {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::NetstackArgs;
|
||||
use crate::config::NetstackArgs;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct NetstackRequest {
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{TestedNodeDetails, TestedNodeLpDetails};
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use nym_api_requests::models::{
|
||||
AuthenticatorDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2, HostInformationV2,
|
||||
@@ -10,16 +9,20 @@ use nym_api_requests::models::{
|
||||
};
|
||||
use nym_authenticator_requests::AuthenticatorVersion;
|
||||
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_http_api_client::UserAgent;
|
||||
use nym_kkt_ciphersuite::SignatureScheme;
|
||||
use nym_kkt_ciphersuite::{KEM, KEMKeyDigests};
|
||||
use nym_network_defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
use nym_node_requests::api::client::NymNodeApiClientExt;
|
||||
use nym_node_requests::api::v1::node::models::AuxiliaryDetails as NodeAuxiliaryDetails;
|
||||
use nym_sdk::mixnet::NodeIdentity;
|
||||
use nym_sdk::mixnet::Recipient;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::models::NymNodeDescriptionV2;
|
||||
use rand::prelude::IteratorRandom;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{debug, info, warn};
|
||||
@@ -437,3 +440,69 @@ impl NymApiDirectory {
|
||||
Ok(maybe_entry)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub enum TestedNode {
|
||||
#[default]
|
||||
SameAsEntry,
|
||||
Custom {
|
||||
identity: NodeIdentity,
|
||||
shares_entry: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl TestedNode {
|
||||
pub fn is_same_as_entry(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
TestedNode::SameAsEntry
|
||||
| TestedNode::Custom {
|
||||
shares_entry: true,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestedNodeDetails {
|
||||
pub identity: NodeIdentity,
|
||||
pub exit_router_address: Option<Recipient>,
|
||||
pub authenticator_address: Option<Recipient>,
|
||||
pub authenticator_version: AuthenticatorVersion,
|
||||
pub ip_address: Option<IpAddr>,
|
||||
pub lp_data: Option<TestedNodeLpDetails>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestedNodeLpDetails {
|
||||
pub address: SocketAddr,
|
||||
pub expected_kem_key_hashes: HashMap<KEM, KEMKeyDigests>,
|
||||
pub expected_signing_key_hashes: HashMap<SignatureScheme, KEMKeyDigests>,
|
||||
pub x25519: x25519::PublicKey,
|
||||
}
|
||||
|
||||
impl TestedNodeDetails {
|
||||
/// Create from CLI args (localnet mode - no HTTP query needed)
|
||||
pub fn from_cli(identity: NodeIdentity, lp_data: TestedNodeLpDetails) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
ip_address: Some(lp_data.address.ip()),
|
||||
lp_data: Some(lp_data),
|
||||
// These are None in localnet mode - only needed for mixnet/authenticator
|
||||
exit_router_address: None,
|
||||
authenticator_address: None,
|
||||
authenticator_version: AuthenticatorVersion::UNKNOWN,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this node has sufficient info for LP testing
|
||||
pub fn can_test_lp(&self) -> bool {
|
||||
self.lp_data.is_some()
|
||||
}
|
||||
|
||||
/// Check if this node has sufficient info for mixnet testing
|
||||
pub fn can_test_mixnet(&self) -> bool {
|
||||
self.exit_router_address.is_some() || self.authenticator_address.is_some()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,697 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::common::nodes::{TestedNodeDetails, TestedNodeLpDetails};
|
||||
use crate::common::types::{
|
||||
Entry, Exit, IpPingReplies, LpProbeResults, ProbeOutcome, WgProbeResults,
|
||||
};
|
||||
use crate::common::wireguard::{
|
||||
TwoHopWgTunnelConfig, WgTunnelConfig, run_tunnel_tests, run_two_hop_tunnel_tests,
|
||||
};
|
||||
use crate::common::{bandwidth_helpers, helpers, icmp};
|
||||
use crate::config::NetstackArgs;
|
||||
use anyhow::bail;
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use bytes::BytesMut;
|
||||
use futures::StreamExt;
|
||||
use nym_authenticator_client::AuthenticatorClient;
|
||||
use nym_authenticator_requests::{
|
||||
AuthenticatorVersion, client_message::ClientMessage, response::AuthenticatorResponse, v2, v3,
|
||||
v4, v5, v6,
|
||||
};
|
||||
use nym_config::defaults::mixnet_vpn::{NYM_TUN_DEVICE_ADDRESS_V4, NYM_TUN_DEVICE_ADDRESS_V6};
|
||||
use nym_connection_monitor::self_ping_and_wait;
|
||||
use nym_credentials_interface::{CredentialSpendingData, TicketType};
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_ip_packet_client::IprClientConnect;
|
||||
use nym_ip_packet_requests::{IpPair, codec::MultiIpPacketCodec};
|
||||
use nym_registration_client::{LpRegistrationClient, NestedLpSession};
|
||||
use nym_sdk::mixnet::{MixnetClient, NodeIdentity, Recipient};
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::{codec::Decoder, sync::CancellationToken};
|
||||
use tracing::*;
|
||||
|
||||
pub async fn wg_probe(
|
||||
mut auth_client: AuthenticatorClient,
|
||||
gateway_ip: IpAddr,
|
||||
auth_version: AuthenticatorVersion,
|
||||
awg_args: String,
|
||||
netstack_args: NetstackArgs,
|
||||
// TODO: update type
|
||||
credential: CredentialSpendingData,
|
||||
) -> anyhow::Result<WgProbeResults> {
|
||||
info!("attempting to use authenticator version {auth_version:?}");
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// that's a long conversion chain
|
||||
// (it should be simplified later...)
|
||||
// nym x25519 -> dalek x25519 -> wireguard wrapper x25519
|
||||
let private_key = nym_crypto::asymmetric::encryption::PrivateKey::new(&mut rng);
|
||||
let public_key = private_key.public_key();
|
||||
|
||||
let authenticator_pub_key = public_key.inner().into();
|
||||
let init_message = match auth_version {
|
||||
AuthenticatorVersion::V2 => ClientMessage::Initial(Box::new(
|
||||
v2::registration::InitMessage::new(authenticator_pub_key),
|
||||
)),
|
||||
AuthenticatorVersion::V3 => ClientMessage::Initial(Box::new(
|
||||
v3::registration::InitMessage::new(authenticator_pub_key),
|
||||
)),
|
||||
AuthenticatorVersion::V4 => ClientMessage::Initial(Box::new(
|
||||
v4::registration::InitMessage::new(authenticator_pub_key),
|
||||
)),
|
||||
AuthenticatorVersion::V5 => ClientMessage::Initial(Box::new(
|
||||
v5::registration::InitMessage::new(authenticator_pub_key),
|
||||
)),
|
||||
AuthenticatorVersion::V6 => ClientMessage::Initial(Box::new(
|
||||
v6::registration::InitMessage::new(authenticator_pub_key),
|
||||
)),
|
||||
AuthenticatorVersion::V1 | AuthenticatorVersion::UNKNOWN => bail!("unknown version number"),
|
||||
};
|
||||
|
||||
let mut wg_outcome = WgProbeResults::default();
|
||||
|
||||
info!(
|
||||
"connecting to authenticator: {}...",
|
||||
auth_client.auth_recipient
|
||||
);
|
||||
let response = auth_client
|
||||
.send_and_wait_for_response(&init_message)
|
||||
.await?;
|
||||
|
||||
let registered_data = match response {
|
||||
AuthenticatorResponse::PendingRegistration(pending_registration_response) => {
|
||||
// Unwrap since we have already checked that we have the keypair.
|
||||
debug!("Verifying data");
|
||||
pending_registration_response.verify(&private_key)?;
|
||||
|
||||
let credential = credential
|
||||
.try_into()
|
||||
.inspect_err(|err| error!("invalid zk-nym data: {err}"))
|
||||
.ok();
|
||||
|
||||
let finalized_message =
|
||||
pending_registration_response.finalise_registration(&private_key, credential);
|
||||
let client_message = ClientMessage::Final(finalized_message);
|
||||
|
||||
let response = auth_client
|
||||
.send_and_wait_for_response(&client_message)
|
||||
.await?;
|
||||
let AuthenticatorResponse::Registered(registered_response) = response else {
|
||||
bail!("Unexpected response");
|
||||
};
|
||||
registered_response
|
||||
}
|
||||
AuthenticatorResponse::Registered(registered_response) => registered_response,
|
||||
_ => bail!("Unexpected response"),
|
||||
};
|
||||
|
||||
let peer_public = registered_data.pub_key().inner();
|
||||
let static_private = x25519_dalek::StaticSecret::from(private_key.to_bytes());
|
||||
let public_key_bs64 = general_purpose::STANDARD.encode(peer_public.as_bytes());
|
||||
let private_key_hex = hex::encode(static_private.to_bytes());
|
||||
let public_key_hex = hex::encode(peer_public.as_bytes());
|
||||
|
||||
info!("WG connection details");
|
||||
info!("Peer public key: {}", public_key_bs64);
|
||||
info!(
|
||||
"ips {}(v4) {}(v6), port {}",
|
||||
registered_data.private_ips().ipv4,
|
||||
registered_data.private_ips().ipv6,
|
||||
registered_data.wg_port(),
|
||||
);
|
||||
|
||||
let wg_endpoint = format!("{gateway_ip}:{}", registered_data.wg_port());
|
||||
|
||||
info!("Successfully registered with the gateway");
|
||||
|
||||
wg_outcome.can_register = true;
|
||||
|
||||
// Run tunnel connectivity tests using shared helper
|
||||
let tunnel_config = WgTunnelConfig::new(
|
||||
registered_data.private_ips().ipv4.to_string(),
|
||||
registered_data.private_ips().ipv6.to_string(),
|
||||
private_key_hex,
|
||||
public_key_hex,
|
||||
wg_endpoint,
|
||||
);
|
||||
|
||||
run_tunnel_tests(&tunnel_config, &netstack_args, &awg_args, &mut wg_outcome);
|
||||
|
||||
Ok(wg_outcome)
|
||||
}
|
||||
|
||||
pub async fn lp_registration_probe<St>(
|
||||
gateway_identity: NodeIdentity,
|
||||
gateway_lp_data: TestedNodeLpDetails,
|
||||
bandwidth_controller: &nym_bandwidth_controller::BandwidthController<
|
||||
nym_validator_client::nyxd::NyxdClient<nym_validator_client::HttpRpcClient>,
|
||||
St,
|
||||
>,
|
||||
use_mock_ecash: bool,
|
||||
) -> anyhow::Result<LpProbeResults>
|
||||
where
|
||||
St: nym_sdk::mixnet::CredentialStorage + Clone + Send + Sync + 'static,
|
||||
<St as nym_sdk::mixnet::CredentialStorage>::StorageError: Send + Sync,
|
||||
{
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_registration_client::LpRegistrationClient;
|
||||
|
||||
let lp_address = gateway_lp_data.address;
|
||||
let peer = helpers::to_lp_remote_peer(gateway_identity, gateway_lp_data);
|
||||
|
||||
info!("Starting LP registration probe for gateway at {lp_address}",);
|
||||
|
||||
let mut lp_outcome = LpProbeResults::default();
|
||||
|
||||
// Generate Ed25519 keypair for this connection (X25519 will be derived internally by LP)
|
||||
let mut rng = rand::thread_rng();
|
||||
let client_ed25519_keypair = std::sync::Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
|
||||
// Step 0: Derive X25519 keys from Ed25519 for the gateways
|
||||
|
||||
// Create LP registration client (uses Ed25519 keys directly, derives X25519 internally)
|
||||
let mut client = LpRegistrationClient::<TcpStream>::new_with_default_config(
|
||||
client_ed25519_keypair,
|
||||
peer,
|
||||
lp_address,
|
||||
);
|
||||
|
||||
// Step 1: Perform handshake (connection is implicit in packet-per-connection model)
|
||||
// LpRegistrationClient uses packet-per-connection model - connect() is gone,
|
||||
// connection is established during handshake and registration automatically.
|
||||
info!("Performing LP handshake at {lp_address}...");
|
||||
match client.perform_handshake().await {
|
||||
Ok(_) => {
|
||||
info!("LP handshake completed successfully");
|
||||
lp_outcome.can_connect = true; // Connection succeeded if handshake succeeded
|
||||
lp_outcome.can_handshake = true;
|
||||
}
|
||||
Err(e) => {
|
||||
let error_msg = format!("LP handshake failed: {}", e);
|
||||
error!("{}", error_msg);
|
||||
lp_outcome.error = Some(error_msg);
|
||||
return Ok(lp_outcome);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Register with gateway (send request + receive response in one call)
|
||||
info!("Sending LP registration request...");
|
||||
|
||||
// Generate WireGuard keypair for dVPN registration
|
||||
let mut rng = rand::thread_rng();
|
||||
let wg_keypair = nym_crypto::asymmetric::x25519::KeyPair::new(&mut rng);
|
||||
|
||||
// Convert gateway identity to ed25519 public key
|
||||
let gateway_ed25519_pubkey = match nym_crypto::asymmetric::ed25519::PublicKey::from_bytes(
|
||||
&gateway_identity.to_bytes(),
|
||||
) {
|
||||
Ok(key) => key,
|
||||
Err(e) => {
|
||||
let error_msg = format!("Failed to convert gateway identity: {}", e);
|
||||
error!("{}", error_msg);
|
||||
lp_outcome.error = Some(error_msg);
|
||||
return Ok(lp_outcome);
|
||||
}
|
||||
};
|
||||
|
||||
// Register using the new packet-per-connection API (returns GatewayData directly)
|
||||
let ticket_type = TicketType::V1WireguardEntry;
|
||||
let gateway_data = if use_mock_ecash {
|
||||
info!("Using mock ecash credential for LP registration");
|
||||
let credential = bandwidth_helpers::create_dummy_credential(
|
||||
&gateway_ed25519_pubkey.to_bytes(),
|
||||
ticket_type,
|
||||
);
|
||||
|
||||
match client
|
||||
.register_with_credential(&mut rng, &wg_keypair, credential, ticket_type)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
let error_msg = format!("LP registration failed (mock ecash): {}", e);
|
||||
error!("{}", error_msg);
|
||||
lp_outcome.error = Some(error_msg);
|
||||
return Ok(lp_outcome);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("Using real bandwidth controller for LP registration");
|
||||
match client
|
||||
.register(
|
||||
&mut rng,
|
||||
&wg_keypair,
|
||||
&gateway_ed25519_pubkey,
|
||||
bandwidth_controller,
|
||||
ticket_type,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
let error_msg = format!("LP registration failed: {}", e);
|
||||
error!("{}", error_msg);
|
||||
lp_outcome.error = Some(error_msg);
|
||||
return Ok(lp_outcome);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
info!("LP registration successful! Received gateway data:");
|
||||
info!(" - Gateway public key: {:?}", gateway_data.public_key);
|
||||
info!(" - Private IPv4: {}", gateway_data.private_ipv4);
|
||||
info!(" - Private IPv6: {}", gateway_data.private_ipv6);
|
||||
info!(" - Endpoint: {}", gateway_data.endpoint);
|
||||
lp_outcome.can_register = true;
|
||||
|
||||
Ok(lp_outcome)
|
||||
}
|
||||
|
||||
/// LP-based WireGuard probe: Tests LP nested session registration + WireGuard tunnel connectivity
|
||||
///
|
||||
/// This function tests the full VPN flow using LP registration instead of mixnet+authenticator:
|
||||
/// 1. Connects to entry gateway (outer LP session)
|
||||
/// 2. Registers with exit gateway via entry forwarding (nested LP session)
|
||||
/// 3. Receives WireGuard configuration from both gateways
|
||||
/// 4. Tests WireGuard tunnel connectivity (IPv4/IPv6)
|
||||
///
|
||||
/// This validates that IP hiding works (exit sees entry IP, not client IP) and that the
|
||||
/// full VPN tunnel operates correctly after LP registration.
|
||||
///
|
||||
// Known issue in localnet mode - After this probe runs, container networking
|
||||
// to the external internet becomes unstable while internal container-to-container traffic
|
||||
// continues to work. The two-hop WireGuard tunnel itself succeeds (handshake completes),
|
||||
// but subsequent DNS/ping tests may timeout. This appears to be related to Apple Container
|
||||
// Runtime networking quirks combined with our NAT/iptables configuration. Tracked in
|
||||
// beads issue nym-vbdo. Workaround: restart the localnet containers between probe runs.
|
||||
pub async fn wg_probe_lp<St>(
|
||||
entry_gateway: &TestedNodeDetails,
|
||||
exit_gateway: &TestedNodeDetails,
|
||||
bandwidth_controller: &nym_bandwidth_controller::BandwidthController<
|
||||
nym_validator_client::nyxd::NyxdClient<nym_validator_client::HttpRpcClient>,
|
||||
St,
|
||||
>,
|
||||
use_mock_ecash: bool,
|
||||
awg_args: String,
|
||||
netstack_args: NetstackArgs,
|
||||
) -> anyhow::Result<WgProbeResults>
|
||||
where
|
||||
St: nym_sdk::mixnet::CredentialStorage + Clone + Send + Sync + 'static,
|
||||
<St as nym_sdk::mixnet::CredentialStorage>::StorageError: Send + Sync,
|
||||
{
|
||||
// Validate that both gateways have required information
|
||||
let entry_lp_data = entry_gateway
|
||||
.lp_data
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("Entry gateway missing LP data"))?;
|
||||
|
||||
let exit_lp_data = exit_gateway
|
||||
.lp_data
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("Exit gateway missing LP data"))?;
|
||||
|
||||
let entry_address = entry_lp_data.address;
|
||||
let exit_address = exit_lp_data.address;
|
||||
|
||||
let entry_ip = entry_address.ip();
|
||||
let exit_ip = exit_address.ip();
|
||||
|
||||
info!("Starting LP-based WireGuard probe (entry→exit via forwarding)");
|
||||
|
||||
let mut wg_outcome = WgProbeResults::default();
|
||||
|
||||
// Generate Ed25519 keypairs for LP protocol
|
||||
let mut rng = rand::thread_rng();
|
||||
let entry_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
let exit_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
|
||||
// Generate WireGuard keypairs for VPN registration
|
||||
let entry_wg_keypair = x25519::KeyPair::new(&mut rng);
|
||||
let exit_wg_keypair = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let entry_peer = helpers::to_lp_remote_peer(entry_gateway.identity, entry_lp_data);
|
||||
let exit_peer = helpers::to_lp_remote_peer(exit_gateway.identity, exit_lp_data);
|
||||
|
||||
// STEP 1: Establish outer LP session with entry gateway
|
||||
// LpRegistrationClient uses packet-per-connection model - connect() is gone,
|
||||
// connection is established automatically during handshake.
|
||||
info!("Establishing outer LP session with entry gateway...");
|
||||
let mut entry_client = LpRegistrationClient::<TcpStream>::new_with_default_config(
|
||||
entry_lp_keypair,
|
||||
entry_peer,
|
||||
entry_address,
|
||||
);
|
||||
|
||||
// Perform handshake with entry gateway (connection is implicit)
|
||||
if let Err(e) = entry_client.perform_handshake().await {
|
||||
error!("Failed to handshake with entry gateway: {}", e);
|
||||
return Ok(wg_outcome);
|
||||
}
|
||||
info!("Outer LP session with entry gateway established");
|
||||
|
||||
// STEP 2: Use nested session to register with exit gateway via forwarding
|
||||
info!("Registering with exit gateway via entry forwarding...");
|
||||
let mut nested_session =
|
||||
NestedLpSession::new(exit_address.to_string(), exit_lp_keypair, exit_peer);
|
||||
|
||||
// Convert exit gateway identity to ed25519 public key for registration
|
||||
let exit_gateway_pubkey = ed25519::PublicKey::from_bytes(&exit_gateway.identity.to_bytes())
|
||||
.map_err(|e| anyhow::anyhow!("Invalid exit gateway identity: {}", e))?;
|
||||
|
||||
// Perform handshake and registration with exit gateway via forwarding
|
||||
let exit_gateway_data = if use_mock_ecash {
|
||||
info!("Using mock ecash credential for exit gateway registration");
|
||||
let credential = bandwidth_helpers::create_dummy_credential(
|
||||
&exit_gateway_pubkey.to_bytes(),
|
||||
TicketType::V1WireguardExit,
|
||||
);
|
||||
match nested_session
|
||||
.handshake_and_register_with_credential(
|
||||
&mut entry_client,
|
||||
&mut rng,
|
||||
&exit_wg_keypair,
|
||||
credential,
|
||||
TicketType::V1WireguardExit,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("Failed to register with exit gateway (mock ecash): {}", e);
|
||||
return Ok(wg_outcome);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match nested_session
|
||||
.handshake_and_register(
|
||||
&mut entry_client,
|
||||
&mut rng,
|
||||
&exit_wg_keypair,
|
||||
&exit_gateway_pubkey,
|
||||
bandwidth_controller,
|
||||
TicketType::V1WireguardExit,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("Failed to register with exit gateway: {}", e);
|
||||
return Ok(wg_outcome);
|
||||
}
|
||||
}
|
||||
};
|
||||
info!("Exit gateway registration successful via forwarding");
|
||||
|
||||
// STEP 3: Register with entry gateway
|
||||
info!("Registering with entry gateway...");
|
||||
let entry_gateway_pubkey =
|
||||
ed25519::PublicKey::from_bytes(&entry_gateway.identity.to_bytes())
|
||||
.map_err(|e| anyhow::anyhow!("Invalid entry gateway identity: {}", e))?;
|
||||
|
||||
// Use packet-per-connection register() which returns GatewayData directly
|
||||
let entry_gateway_data = if use_mock_ecash {
|
||||
info!("Using mock ecash credential for entry gateway registration");
|
||||
let credential = bandwidth_helpers::create_dummy_credential(
|
||||
&entry_gateway_pubkey.to_bytes(),
|
||||
TicketType::V1WireguardEntry,
|
||||
);
|
||||
match entry_client
|
||||
.register_with_credential(
|
||||
&mut rng,
|
||||
&entry_wg_keypair,
|
||||
credential,
|
||||
TicketType::V1WireguardEntry,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("Failed to register with entry gateway (mock ecash): {}", e);
|
||||
return Ok(wg_outcome);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match entry_client
|
||||
.register(
|
||||
&mut rng,
|
||||
&entry_wg_keypair,
|
||||
&entry_gateway_pubkey,
|
||||
bandwidth_controller,
|
||||
TicketType::V1WireguardEntry,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("Failed to register with entry gateway: {}", e);
|
||||
return Ok(wg_outcome);
|
||||
}
|
||||
}
|
||||
};
|
||||
info!("Entry gateway registration successful");
|
||||
|
||||
info!("LP registration successful for both gateways!");
|
||||
wg_outcome.can_register = true;
|
||||
|
||||
// STEP 4: Test WireGuard tunnels using two-hop configuration
|
||||
// Traffic flows: Exit tunnel -> UDP Forwarder -> Entry tunnel -> Exit Gateway -> Internet
|
||||
// The exit gateway endpoint is not directly reachable from the host in localnet.
|
||||
// We must tunnel through the entry gateway using the UDP forwarder pattern.
|
||||
|
||||
// Convert keys to hex for netstack
|
||||
let entry_private_key_hex = hex::encode(entry_wg_keypair.private_key().to_bytes());
|
||||
let entry_public_key_hex = hex::encode(entry_gateway_data.public_key.to_bytes());
|
||||
let exit_private_key_hex = hex::encode(exit_wg_keypair.private_key().to_bytes());
|
||||
let exit_public_key_hex = hex::encode(exit_gateway_data.public_key.to_bytes());
|
||||
|
||||
// Build WireGuard endpoint addresses
|
||||
// Entry endpoint uses entry_ip (host-reachable) + port from registration
|
||||
let entry_wg_endpoint = format!("{}:{}", entry_ip, entry_gateway_data.endpoint.port());
|
||||
// Exit endpoint uses exit_ip + port from registration (forwarded via entry)
|
||||
let exit_wg_endpoint = format!("{}:{}", exit_ip, exit_gateway_data.endpoint.port());
|
||||
|
||||
info!("Two-hop WireGuard configuration:");
|
||||
info!(" Entry gateway:");
|
||||
info!(" Private IPv4: {}", entry_gateway_data.private_ipv4);
|
||||
info!(" Endpoint: {}", entry_wg_endpoint);
|
||||
info!(" Exit gateway:");
|
||||
info!(" Private IPv4: {}", exit_gateway_data.private_ipv4);
|
||||
info!(" Endpoint (via forwarder): {}", exit_wg_endpoint);
|
||||
|
||||
// Build two-hop tunnel configuration
|
||||
let two_hop_config = TwoHopWgTunnelConfig::new(
|
||||
entry_gateway_data.private_ipv4.to_string(),
|
||||
entry_private_key_hex,
|
||||
entry_public_key_hex,
|
||||
entry_wg_endpoint,
|
||||
awg_args.clone(), // Entry AWG args
|
||||
exit_gateway_data.private_ipv4.to_string(),
|
||||
exit_private_key_hex,
|
||||
exit_public_key_hex,
|
||||
exit_wg_endpoint,
|
||||
awg_args, // Exit AWG args
|
||||
);
|
||||
|
||||
// Run two-hop tunnel connectivity tests
|
||||
run_two_hop_tunnel_tests(&two_hop_config, &netstack_args, &mut wg_outcome);
|
||||
|
||||
info!("LP-based two-hop WireGuard probe completed");
|
||||
Ok(wg_outcome)
|
||||
}
|
||||
|
||||
pub async fn do_ping(
|
||||
mut mixnet_client: MixnetClient,
|
||||
our_address: Recipient,
|
||||
exit_router_address: Option<Recipient>,
|
||||
tested_entry: bool,
|
||||
) -> (anyhow::Result<ProbeOutcome>, MixnetClient) {
|
||||
let entry = do_ping_entry(&mut mixnet_client, our_address, tested_entry).await;
|
||||
|
||||
let (exit_result, mixnet_client) = if let Some(exit_router_address) = exit_router_address {
|
||||
let (maybe_ip_pair, mut mixnet_client) =
|
||||
connect_exit(mixnet_client, exit_router_address).await;
|
||||
match maybe_ip_pair {
|
||||
Some(ip_pair) => (
|
||||
do_ping_exit(&mut mixnet_client, ip_pair, exit_router_address).await,
|
||||
mixnet_client,
|
||||
),
|
||||
None => (Ok(Some(Exit::fail_to_connect())), mixnet_client),
|
||||
}
|
||||
} else {
|
||||
(Ok(None), mixnet_client)
|
||||
};
|
||||
|
||||
(
|
||||
exit_result.map(|exit| ProbeOutcome {
|
||||
as_entry: entry,
|
||||
as_exit: exit,
|
||||
wg: None,
|
||||
lp: None,
|
||||
}),
|
||||
mixnet_client,
|
||||
)
|
||||
}
|
||||
|
||||
async fn do_ping_entry(
|
||||
mixnet_client: &mut MixnetClient,
|
||||
our_address: Recipient,
|
||||
tested_entry: bool,
|
||||
) -> Entry {
|
||||
// Step 1: confirm that the entry gateway is routing our mixnet traffic
|
||||
info!("Sending mixnet ping to ourselves to verify mixnet connection");
|
||||
|
||||
if self_ping_and_wait(our_address, mixnet_client)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
return if tested_entry {
|
||||
Entry::fail_to_connect()
|
||||
} else {
|
||||
Entry::EntryFailure
|
||||
};
|
||||
}
|
||||
info!("Successfully mixnet pinged ourselves");
|
||||
|
||||
Entry::success()
|
||||
}
|
||||
|
||||
async fn connect_exit(
|
||||
mixnet_client: MixnetClient,
|
||||
exit_router_address: Recipient,
|
||||
) -> (Option<IpPair>, MixnetClient) {
|
||||
// Step 2: connect to the exit gateway
|
||||
info!(
|
||||
"Connecting to exit gateway: {}",
|
||||
exit_router_address.gateway().to_base58_string()
|
||||
);
|
||||
// The IPR supports cancellation, but it's unused in the gateway probe
|
||||
let cancel_token = CancellationToken::new();
|
||||
let mut ipr_client = IprClientConnect::new(mixnet_client, cancel_token);
|
||||
|
||||
let maybe_ip_pair = ipr_client.connect(exit_router_address).await;
|
||||
let mixnet_client = ipr_client.into_mixnet_client();
|
||||
|
||||
if let Ok(our_ips) = maybe_ip_pair {
|
||||
info!("Successfully connected to exit gateway");
|
||||
info!("Using mixnet VPN IP addresses: {our_ips}");
|
||||
(Some(our_ips), mixnet_client)
|
||||
} else {
|
||||
(None, mixnet_client)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn do_ping_exit(
|
||||
mixnet_client: &mut MixnetClient,
|
||||
our_ips: IpPair,
|
||||
exit_router_address: Recipient,
|
||||
) -> anyhow::Result<Option<Exit>> {
|
||||
// Step 3: perform ICMP connectivity checks for the exit gateway
|
||||
send_icmp_pings(mixnet_client, our_ips, exit_router_address).await?;
|
||||
listen_for_icmp_ping_replies(mixnet_client, our_ips).await
|
||||
}
|
||||
|
||||
async fn send_icmp_pings(
|
||||
mixnet_client: &MixnetClient,
|
||||
our_ips: IpPair,
|
||||
exit_router_address: Recipient,
|
||||
) -> anyhow::Result<()> {
|
||||
// ipv4 addresses for testing
|
||||
let ipr_tun_ip_v4 = NYM_TUN_DEVICE_ADDRESS_V4;
|
||||
let external_ip_v4 = Ipv4Addr::new(8, 8, 8, 8);
|
||||
|
||||
// ipv6 addresses for testing
|
||||
let ipr_tun_ip_v6 = NYM_TUN_DEVICE_ADDRESS_V6;
|
||||
let external_ip_v6 = Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888);
|
||||
|
||||
info!(
|
||||
"Sending ICMP echo requests to: {ipr_tun_ip_v4}, {ipr_tun_ip_v6}, {external_ip_v4}, {external_ip_v6}"
|
||||
);
|
||||
|
||||
// send ipv4 pings
|
||||
for ii in 0..10 {
|
||||
icmp::send_ping_v4(
|
||||
mixnet_client,
|
||||
our_ips,
|
||||
ii,
|
||||
ipr_tun_ip_v4,
|
||||
exit_router_address,
|
||||
)
|
||||
.await?;
|
||||
icmp::send_ping_v4(
|
||||
mixnet_client,
|
||||
our_ips,
|
||||
ii,
|
||||
external_ip_v4,
|
||||
exit_router_address,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// send ipv6 pings
|
||||
for ii in 0..10 {
|
||||
icmp::send_ping_v6(
|
||||
mixnet_client,
|
||||
our_ips,
|
||||
ii,
|
||||
ipr_tun_ip_v6,
|
||||
exit_router_address,
|
||||
)
|
||||
.await?;
|
||||
icmp::send_ping_v6(
|
||||
mixnet_client,
|
||||
our_ips,
|
||||
ii,
|
||||
external_ip_v6,
|
||||
exit_router_address,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn listen_for_icmp_ping_replies(
|
||||
mixnet_client: &mut MixnetClient,
|
||||
our_ips: IpPair,
|
||||
) -> anyhow::Result<Option<Exit>> {
|
||||
let mut multi_ip_packet_decoder = MultiIpPacketCodec::new();
|
||||
let mut registered_replies = IpPingReplies::new();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(Duration::from_secs(2)) => {
|
||||
info!("Finished waiting for ICMP echo reply from exit gateway");
|
||||
break;
|
||||
}
|
||||
Some(reconstructed_message) = mixnet_client.next() => {
|
||||
let Some(data_response) = helpers::unpack_data_response(&reconstructed_message) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// IP packets are bundled together in a mixnet message
|
||||
let mut bytes = BytesMut::from(&*data_response.ip_packet);
|
||||
while let Ok(Some(packet)) = multi_ip_packet_decoder.decode(&mut bytes) {
|
||||
if let Some(event) = icmp::check_for_icmp_beacon_reply(&packet.into_bytes(), icmp::icmp_identifier(), our_ips) {
|
||||
info!("Received ICMP echo reply from exit gateway");
|
||||
info!("Connection event: {event:?}");
|
||||
registered_replies.register_event(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(Exit {
|
||||
can_connect: true,
|
||||
can_route_ip_v4: registered_replies.ipr_tun_ip_v4,
|
||||
can_route_ip_external_v4: registered_replies.external_ip_v4,
|
||||
can_route_ip_v6: registered_replies.ipr_tun_ip_v6,
|
||||
can_route_ip_external_v6: registered_replies.external_ip_v6,
|
||||
}))
|
||||
}
|
||||
@@ -9,11 +9,11 @@
|
||||
use nym_config::defaults::{WG_METADATA_PORT, WG_TUN_DEVICE_IP_ADDRESS_V4};
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::NetstackArgs;
|
||||
use crate::netstack::{
|
||||
use crate::common::netstack::{
|
||||
NetstackRequest, NetstackRequestGo, NetstackResult, TwoHopNetstackRequestGo,
|
||||
};
|
||||
use crate::types::WgProbeResults;
|
||||
use crate::common::types::WgProbeResults;
|
||||
use crate::config::NetstackArgs;
|
||||
|
||||
/// Safe division that returns 0.0 when divisor is 0 (instead of NaN/Inf)
|
||||
fn safe_ratio(received: u16, sent: u16) -> f32 {
|
||||
@@ -99,7 +99,7 @@ pub fn run_tunnel_tests(
|
||||
info!("Testing IPv4 tunnel connectivity...");
|
||||
let ipv4_request = NetstackRequestGo::from_rust_v4(&netstack_request);
|
||||
|
||||
match crate::netstack::ping(&ipv4_request) {
|
||||
match crate::common::netstack::ping(&ipv4_request) {
|
||||
Ok(NetstackResult::Response(netstack_response_v4)) => {
|
||||
info!(
|
||||
"WireGuard probe response for IPv4: {:#?}",
|
||||
@@ -137,7 +137,7 @@ pub fn run_tunnel_tests(
|
||||
info!("Testing IPv6 tunnel connectivity...");
|
||||
let ipv6_request = NetstackRequestGo::from_rust_v6(&netstack_request);
|
||||
|
||||
match crate::netstack::ping(&ipv6_request) {
|
||||
match crate::common::netstack::ping(&ipv6_request) {
|
||||
Ok(NetstackResult::Response(netstack_response_v6)) => {
|
||||
info!(
|
||||
"WireGuard probe response for IPv6: {:#?}",
|
||||
@@ -281,7 +281,7 @@ pub fn run_two_hop_tunnel_tests(
|
||||
info!(" Entry endpoint: {}", config.entry_endpoint);
|
||||
info!(" Exit endpoint (via forwarder): {}", config.exit_endpoint);
|
||||
|
||||
match crate::netstack::ping_two_hop(&request) {
|
||||
match crate::common::netstack::ping_two_hop(&request) {
|
||||
Ok(NetstackResult::Response(response)) => {
|
||||
info!("Two-hop WireGuard probe response (IPv4): {:#?}", response);
|
||||
wg_outcome.can_handshake_v4 = response.can_handshake;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Args;
|
||||
use nym_node_status_client::models::AttachedTicketMaterials;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct CredentialArgs {
|
||||
#[arg(long)]
|
||||
ticket_materials: Option<String>,
|
||||
|
||||
#[arg(long, default_value_t = 1)]
|
||||
ticket_materials_revision: u8,
|
||||
}
|
||||
|
||||
impl CredentialArgs {
|
||||
pub fn decode_attached_ticket_materials(&self) -> anyhow::Result<AttachedTicketMaterials> {
|
||||
let ticket_materials = self
|
||||
.ticket_materials
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("ticket_materials is required"))?
|
||||
.clone();
|
||||
|
||||
Ok(AttachedTicketMaterials::from_serialised_string(
|
||||
ticket_materials,
|
||||
self.ticket_materials_revision,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod credentials;
|
||||
mod netstack;
|
||||
mod test_mode;
|
||||
|
||||
pub use credentials::CredentialArgs;
|
||||
pub use netstack::NetstackArgs;
|
||||
pub use test_mode::TestMode;
|
||||
@@ -0,0 +1,209 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub struct NetstackArgs {
|
||||
#[arg(long, default_value_t = 180)]
|
||||
pub netstack_download_timeout_sec: u64,
|
||||
|
||||
#[arg(long, default_value_t = 30)]
|
||||
pub metadata_timeout_sec: u64,
|
||||
|
||||
#[arg(long, default_value = "1.1.1.1")]
|
||||
pub netstack_v4_dns: String,
|
||||
|
||||
#[arg(long, default_value = "2606:4700:4700::1111")]
|
||||
pub netstack_v6_dns: String,
|
||||
|
||||
#[arg(long, default_value_t = 5)]
|
||||
pub netstack_num_ping: u8,
|
||||
|
||||
#[arg(long, default_value_t = 3)]
|
||||
pub netstack_send_timeout_sec: u64,
|
||||
|
||||
#[arg(long, default_value_t = 3)]
|
||||
pub netstack_recv_timeout_sec: u64,
|
||||
|
||||
#[arg(long, default_values_t = vec!["nym.com".to_string()])]
|
||||
pub netstack_ping_hosts_v4: Vec<String>,
|
||||
|
||||
#[arg(long, default_values_t = vec!["1.1.1.1".to_string()])]
|
||||
pub netstack_ping_ips_v4: Vec<String>,
|
||||
|
||||
#[arg(long, default_values_t = vec!["cloudflare.com".to_string()])]
|
||||
pub netstack_ping_hosts_v6: Vec<String>,
|
||||
|
||||
#[arg(long, default_values_t = vec!["2001:4860:4860::8888".to_string(), "2606:4700:4700::1111".to_string(), "2620:fe::fe".to_string()])]
|
||||
pub netstack_ping_ips_v6: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_netstack_args_default_values() {
|
||||
// Test that the default values are correctly set in the struct definition
|
||||
// This validates that our changes to the default values are correct
|
||||
|
||||
// Create a default instance to test the values
|
||||
let args = NetstackArgs {
|
||||
netstack_download_timeout_sec: 180,
|
||||
metadata_timeout_sec: 30,
|
||||
netstack_v4_dns: "1.1.1.1".to_string(),
|
||||
netstack_v6_dns: "2606:4700:4700::1111".to_string(),
|
||||
netstack_num_ping: 5,
|
||||
netstack_send_timeout_sec: 3,
|
||||
netstack_recv_timeout_sec: 3,
|
||||
netstack_ping_hosts_v4: vec!["nym.com".to_string()],
|
||||
netstack_ping_ips_v4: vec!["1.1.1.1".to_string()],
|
||||
netstack_ping_hosts_v6: vec!["cloudflare.com".to_string()],
|
||||
netstack_ping_ips_v6: vec![
|
||||
"2001:4860:4860::8888".to_string(),
|
||||
"2606:4700:4700::1111".to_string(),
|
||||
"2620:fe::fe".to_string(),
|
||||
],
|
||||
};
|
||||
|
||||
// Test IPv4 defaults
|
||||
assert_eq!(args.netstack_ping_hosts_v4, vec!["nym.com"]);
|
||||
assert_eq!(args.netstack_ping_ips_v4, vec!["1.1.1.1"]);
|
||||
assert_eq!(args.netstack_v4_dns, "1.1.1.1");
|
||||
|
||||
// Test IPv6 defaults
|
||||
assert_eq!(args.netstack_ping_hosts_v6, vec!["cloudflare.com"]);
|
||||
assert_eq!(
|
||||
args.netstack_ping_ips_v6,
|
||||
vec![
|
||||
"2001:4860:4860::8888",
|
||||
"2606:4700:4700::1111",
|
||||
"2620:fe::fe"
|
||||
]
|
||||
);
|
||||
assert_eq!(args.netstack_v6_dns, "2606:4700:4700::1111");
|
||||
|
||||
// Test other defaults
|
||||
assert_eq!(args.netstack_download_timeout_sec, 180);
|
||||
assert_eq!(args.netstack_num_ping, 5);
|
||||
assert_eq!(args.netstack_send_timeout_sec, 3);
|
||||
assert_eq!(args.netstack_recv_timeout_sec, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_netstack_args_custom_construction() {
|
||||
// Test that we can create instances with custom values
|
||||
let args = NetstackArgs {
|
||||
netstack_download_timeout_sec: 300,
|
||||
metadata_timeout_sec: 30,
|
||||
netstack_v4_dns: "8.8.8.8".to_string(),
|
||||
netstack_v6_dns: "2001:4860:4860::8888".to_string(),
|
||||
netstack_num_ping: 10,
|
||||
netstack_send_timeout_sec: 5,
|
||||
netstack_recv_timeout_sec: 5,
|
||||
netstack_ping_hosts_v4: vec!["example.com".to_string()],
|
||||
netstack_ping_ips_v4: vec!["8.8.8.8".to_string()],
|
||||
netstack_ping_hosts_v6: vec!["ipv6.example.com".to_string()],
|
||||
netstack_ping_ips_v6: vec!["2001:4860:4860::8888".to_string()],
|
||||
};
|
||||
|
||||
assert_eq!(args.netstack_ping_hosts_v4, vec!["example.com"]);
|
||||
assert_eq!(args.netstack_ping_hosts_v6, vec!["ipv6.example.com"]);
|
||||
assert_eq!(args.netstack_ping_ips_v4, vec!["8.8.8.8"]);
|
||||
assert_eq!(args.netstack_ping_ips_v6, vec!["2001:4860:4860::8888"]);
|
||||
assert_eq!(args.netstack_v4_dns, "8.8.8.8");
|
||||
assert_eq!(args.netstack_v6_dns, "2001:4860:4860::8888");
|
||||
assert_eq!(args.netstack_download_timeout_sec, 300);
|
||||
assert_eq!(args.netstack_num_ping, 10);
|
||||
assert_eq!(args.netstack_send_timeout_sec, 5);
|
||||
assert_eq!(args.netstack_recv_timeout_sec, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_netstack_args_multiple_values() {
|
||||
// Test that multiple hosts and IPs can be stored
|
||||
let args = NetstackArgs {
|
||||
netstack_download_timeout_sec: 180,
|
||||
metadata_timeout_sec: 30,
|
||||
netstack_v4_dns: "1.1.1.1".to_string(),
|
||||
netstack_v6_dns: "2606:4700:4700::1111".to_string(),
|
||||
netstack_num_ping: 5,
|
||||
netstack_send_timeout_sec: 3,
|
||||
netstack_recv_timeout_sec: 3,
|
||||
netstack_ping_hosts_v4: vec!["nym.com".to_string(), "example.com".to_string()],
|
||||
netstack_ping_ips_v4: vec!["1.1.1.1".to_string(), "8.8.8.8".to_string()],
|
||||
netstack_ping_hosts_v6: vec![
|
||||
"cloudflare.com".to_string(),
|
||||
"ipv6.example.com".to_string(),
|
||||
],
|
||||
netstack_ping_ips_v6: vec![
|
||||
"2001:4860:4860::8888".to_string(),
|
||||
"2606:4700:4700::1111".to_string(),
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(args.netstack_ping_hosts_v4, vec!["nym.com", "example.com"]);
|
||||
assert_eq!(
|
||||
args.netstack_ping_hosts_v6,
|
||||
vec!["cloudflare.com", "ipv6.example.com"]
|
||||
);
|
||||
assert_eq!(args.netstack_ping_ips_v4, vec!["1.1.1.1", "8.8.8.8"]);
|
||||
assert_eq!(
|
||||
args.netstack_ping_ips_v6,
|
||||
vec!["2001:4860:4860::8888", "2606:4700:4700::1111"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_netstack_args_edge_cases() {
|
||||
// Test edge cases like zero values and empty vectors
|
||||
let args = NetstackArgs {
|
||||
netstack_download_timeout_sec: 0,
|
||||
metadata_timeout_sec: 30,
|
||||
netstack_v4_dns: "1.1.1.1".to_string(),
|
||||
netstack_v6_dns: "2606:4700:4700::1111".to_string(),
|
||||
netstack_num_ping: 0,
|
||||
netstack_send_timeout_sec: 0,
|
||||
netstack_recv_timeout_sec: 0,
|
||||
netstack_ping_hosts_v4: vec![],
|
||||
netstack_ping_ips_v4: vec![],
|
||||
netstack_ping_hosts_v6: vec![],
|
||||
netstack_ping_ips_v6: vec![],
|
||||
};
|
||||
|
||||
assert_eq!(args.netstack_num_ping, 0);
|
||||
assert_eq!(args.netstack_send_timeout_sec, 0);
|
||||
assert_eq!(args.netstack_recv_timeout_sec, 0);
|
||||
assert_eq!(args.netstack_download_timeout_sec, 0);
|
||||
assert!(args.netstack_ping_hosts_v4.is_empty());
|
||||
assert!(args.netstack_ping_ips_v4.is_empty());
|
||||
assert!(args.netstack_ping_hosts_v6.is_empty());
|
||||
assert!(args.netstack_ping_ips_v6.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_netstack_args_domain_validation() {
|
||||
// Test that our domain choices are reasonable
|
||||
let args = NetstackArgs {
|
||||
netstack_download_timeout_sec: 180,
|
||||
metadata_timeout_sec: 30,
|
||||
netstack_v4_dns: "1.1.1.1".to_string(),
|
||||
netstack_v6_dns: "2606:4700:4700::1111".to_string(),
|
||||
netstack_num_ping: 5,
|
||||
netstack_send_timeout_sec: 3,
|
||||
netstack_recv_timeout_sec: 3,
|
||||
netstack_ping_hosts_v4: vec!["nym.com".to_string()],
|
||||
netstack_ping_ips_v4: vec!["1.1.1.1".to_string()],
|
||||
netstack_ping_hosts_v6: vec!["cloudflare.com".to_string()],
|
||||
netstack_ping_ips_v6: vec!["2001:4860:4860::8888".to_string()],
|
||||
};
|
||||
|
||||
assert!(args.netstack_ping_hosts_v4[0].contains("nym"));
|
||||
|
||||
assert!(args.netstack_ping_hosts_v6[0].contains("cloudflare"));
|
||||
|
||||
assert_eq!(args.netstack_v4_dns, "1.1.1.1");
|
||||
assert_eq!(args.netstack_v6_dns, "2606:4700:4700::1111");
|
||||
}
|
||||
}
|
||||
+21
-1067
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,9 @@ use clap::{Parser, Subcommand};
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_config::defaults::setup_env;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_gateway_probe::nodes::{NymApiDirectory, query_gateway_by_ip};
|
||||
use nym_gateway_probe::{
|
||||
CredentialArgs, NetstackArgs, ProbeResult, TestMode, TestedNode, TestedNodeDetails,
|
||||
TestedNodeLpDetails,
|
||||
CredentialArgs, NetstackArgs, NymApiDirectory, ProbeResult, TestMode, TestedNode,
|
||||
TestedNodeDetails, TestedNodeLpDetails, query_gateway_by_ip,
|
||||
};
|
||||
use nym_kkt_ciphersuite::{HashFunction, KEM};
|
||||
use nym_sdk::mixnet::NodeIdentity;
|
||||
|
||||
Reference in New Issue
Block a user