#![cfg(target_os = "linux")]
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
use std::time::Instant;
use gossan_core::{Config, DiscoverySource, HostTarget, PortMode, ScanInput, Scanner, Target};
use tokio::sync::{mpsc, Mutex};
fn make_scan_input(target: Target, seed: String) -> ScanInput {
let (target_tx_loaded, target_rx) = mpsc::channel(1024);
target_tx_loaded.try_send(target).expect("send target");
drop(target_tx_loaded);
let (live_tx, _live_rx) = mpsc::channel(1024);
let (downstream_tx, _downstream_rx) = mpsc::channel(1024);
ScanInput {
seed,
target_rx: Mutex::new(target_rx),
live_tx,
target_tx: downstream_tx,
resolver: Arc::new(
hickory_resolver::TokioResolver::builder_tokio()
.expect("tokio resolver builder")
.build(),
),
}
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires root + loopback; run manually with sudo"]
async fn loopback_engine_throughput() {
let _ = tracing_subscriber::fmt::try_init();
let target = Target::Host(gossan_core::HostTarget {
ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
domain: None,
});
let _ = HostTarget {
ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
domain: None,
};
let _ = DiscoverySource::Seed;
const REPEATS: usize = 50;
let mut config = Config::default();
config.port_mode = PortMode::Top100;
config.rate_limit = 0;
let scanner = gossan_engine::EngineScanner::new();
let start = Instant::now();
for _ in 0..REPEATS {
let input = make_scan_input(target.clone(), "127.0.0.1".to_string());
scanner.run(input, &config).await.expect("scan");
}
let elapsed = start.elapsed();
let total_probes = (REPEATS * 100) as f64;
let pps = (total_probes / elapsed.as_secs_f64()) as u64;
eprintln!(
"loopback_engine_throughput: {REPEATS} runs × 100 ports = {} probes in {:?}; pps = {}",
REPEATS * 100,
elapsed,
pps
);
assert!(
pps > 100_000,
"loopback engine pps far below ceiling: {pps}"
);
}