multiprobe-0.1.0 has been yanked.
multiprobe

Enterprise-grade multi-protocol network probing library for Rust with Paris Traceroute, path analytics, MTU discovery, bufferbloat detection, and TLS analysis.
Why multiprobe?
- Paris Traceroute - First pure-Rust implementation of ECMP-aware traceroute
- Comprehensive Analytics - Jitter, MTU, bufferbloat, reordering in one crate
- Protocol Differential Score - Novel metric for cross-protocol path analysis
- Production Ready - 100+ tests, zero unsafe in public API
Features
Protocol Probes
| Probe |
Description |
Privileges |
| TCP |
Connect probes with timing breakdown |
None |
| UDP |
Packet probes with response detection |
None |
| ICMP |
Echo requests with RTT measurement |
CAP_NET_RAW |
| TLS |
Handshake timing, cipher, version analysis |
None |
Path Discovery
| Feature |
Description |
| Traceroute |
Standard hop-by-hop path discovery |
| Paris Traceroute |
ECMP-aware traceroute (maintains flow consistency) |
| Multi-path Discovery |
Find all paths through load balancers |
| Load Balancing Detection |
Identify per-flow vs per-packet ECMP |
Path Analytics
| Metric |
Description |
| Latency Stats |
min/max/mean/median/p95/p99/jitter |
| Path MTU |
Binary search MTU discovery |
| Bufferbloat |
Latency degradation under load (A-F grading) |
| Packet Reordering |
Out-of-order delivery detection |
Classification
| Feature |
Description |
| Path Fingerprint |
Stable hashes for path comparison |
| Protocol Differential Score |
Cross-protocol behavior analysis |
| Network Behavior |
ICMP filtering, asymmetric routing detection |
Installation
[dependencies]
multiprobe = "0.1"
Quick Start
use multiprobe::Probe;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), multiprobe::Error> {
let tcp = Probe::tcp("example.com", 443)
.timeout(Duration::from_secs(5))
.send().await?;
println!("Success: {}", tcp.success);
println!("Latency: {:.2}ms", tcp.timing.total_ms());
Ok(())
}
Multi-Protocol Analysis
use multiprobe::{Probe, Classifier};
let result = Probe::multi("example.com")
.tcp(80)
.tcp(443)
.udp(53)
.timeout(Duration::from_secs(5))
.send().await?;
for probe in &result.results {
println!("{}: {} ({:.2}ms)",
probe.protocol,
if probe.success { "OK" } else { "FAIL" },
probe.timing.total_ms());
}
println!("Classification: {}", result.classify());
let pds = Classifier::differential_score(&result.results);
println!("Consistency: {:.2}", pds.consistency);
println!("Behavior: {}", pds.interpret());
println!("Fingerprint: {}", Classifier::fingerprint(&result.results));
println!("Hash: {:016x}", Classifier::fingerprint_hash(&result.results));
Paris Traceroute
Standard traceroute fails behind ECMP load balancers because it varies flow identifiers between probes. Paris Traceroute (Augustin et al., 2006) maintains constant flow IDs for consistent path discovery.
use multiprobe::{Probe, ParisMode, FlowId};
let trace = Probe::paris("example.com")
.max_hops(30)
.timeout_per_hop(Duration::from_secs(2))
.send().await?;
println!("Target: {} ({})", trace.target, trace.target_ip);
println!("Hops: {}", trace.hops.len());
println!("Reached: {}", trace.reached_destination);
println!("Load Balancing: {}", trace.load_balancing);
for hop in &trace.hops {
let addr = hop.addr.map(|ip| ip.to_string())
.unwrap_or_else(|| "*".to_string());
println!("{:2}. {:15} {:.2}ms",
hop.ttl, addr, hop.rtt.as_secs_f64() * 1000.0);
}
let trace = Probe::paris("example.com")
.flow_id(FlowId::udp(33434, 33434))
.mode(ParisMode::Udp)
.detect_load_balancing(true)
.send().await?;
let paths = multiprobe::discover_paths("example.com", 6, &Default::default()).await?;
println!("Found {} distinct paths", paths.len());
TLS Handshake Analysis
use multiprobe::Probe;
let tls = Probe::tls("example.com")
.port(443)
.timeout(Duration::from_secs(10))
.send().await?;
if tls.success {
println!("TLS Version: {}", tls.tls_version);
println!("Cipher: {:?}", tls.cipher_suite);
println!("HTTP/2: {}", tls.supports_http2());
println!("Modern TLS: {}", tls.is_modern_tls());
println!("\nTiming Breakdown:");
println!(" DNS: {:.2}ms", tls.timing.dns_ms());
println!(" TCP: {:.2}ms", tls.timing.tcp_ms());
println!(" TLS: {:.2}ms", tls.timing.tls_ms());
println!(" Total: {:.2}ms", tls.timing.total_ms());
} else {
println!("TLS failed: {:?}", tls.error);
}
Latency Statistics
use multiprobe::Probe;
let stats = Probe::latency("example.com", 443)
.samples(100)
.interval(Duration::from_millis(100))
.send().await?;
println!("Samples: {}/{}", stats.success_count, stats.sample_count);
println!("Loss: {:.1}%", stats.loss_rate * 100.0);
println!();
println!("Min: {:.2}ms", stats.min_rtt.as_secs_f64() * 1000.0);
println!("Max: {:.2}ms", stats.max_rtt.as_secs_f64() * 1000.0);
println!("Mean: {:.2}ms", stats.mean_rtt.as_secs_f64() * 1000.0);
println!("Median: {:.2}ms", stats.median_rtt.as_secs_f64() * 1000.0);
println!("P95: {:.2}ms", stats.p95_rtt.as_secs_f64() * 1000.0);
println!("P99: {:.2}ms", stats.p99_rtt.as_secs_f64() * 1000.0);
println!("Jitter: {:.2}ms", stats.jitter.as_secs_f64() * 1000.0);
println!("StdDev: {:.2}ms", stats.std_dev.as_secs_f64() * 1000.0);
if stats.has_high_jitter() {
println!("WARNING: High jitter detected!");
}
if stats.has_packet_loss() {
println!("WARNING: Packet loss detected!");
}
Path MTU Discovery
use multiprobe::Probe;
let mtu = Probe::mtu("example.com")
.min_mtu(68)
.max_mtu(1500)
.timeout(Duration::from_secs(2))
.send().await?;
println!("Path MTU: {} bytes", mtu.path_mtu);
println!("DF Honored: {}", mtu.df_honored);
println!("Frag Needed msgs: {}", mtu.frag_needed_count);
Bufferbloat Detection
use multiprobe::Probe;
let bloat = Probe::bufferbloat("example.com")
.port(443)
.baseline_samples(10)
.loaded_samples(10)
.send().await?;
println!("Baseline: {:.2}ms", bloat.baseline_latency.as_secs_f64() * 1000.0);
println!("Under Load: {:.2}ms", bloat.loaded_latency.as_secs_f64() * 1000.0);
println!("Bloat Factor: {:.2}x", bloat.bloat_factor);
println!("Grade: {}", bloat.grade); println!("Detected: {}", bloat.detected);
Path Classification
| Classification |
Description |
Open |
All protocols succeed |
IcmpFiltered |
ICMP blocked, TCP/UDP pass |
TcpFiltered |
ICMP passes, TCP blocked |
SelectiveFirewall |
Some ports open, some closed |
Blocked |
All protocols fail |
NatDetected |
TTL variance indicates NAT |
Protocol Differential Score
Quantifies behavioral differences across protocols:
use multiprobe::Classifier;
let pds = Classifier::differential_score(&results);
println!("ICMP vs TCP: {:.2}", pds.icmp_tcp_diff);
println!("ICMP vs UDP: {:.2}", pds.icmp_udp_diff);
println!("TCP vs UDP: {:.2}", pds.tcp_udp_diff);
println!("Consistency: {:.2}", pds.consistency);
println!("Latency Variance: {:.2}ms", pds.latency_variance_ms);
use multiprobe::NetworkBehavior;
match pds.interpret() {
NetworkBehavior::Direct => println!("Direct path"),
NetworkBehavior::IcmpFiltering => println!("ICMP filtering"),
NetworkBehavior::ProtocolSpecificFiltering => println!("Protocol filtering"),
NetworkBehavior::AsymmetricRouting => println!("Asymmetric routing"),
NetworkBehavior::Unknown => println!("Unknown"),
}
Load Balancing Detection
use multiprobe::LoadBalancingType;
let trace = Probe::paris("example.com")
.detect_load_balancing(true)
.send().await?;
match trace.load_balancing {
LoadBalancingType::None => println!("No load balancing"),
LoadBalancingType::PerFlow => println!("Per-flow ECMP"),
LoadBalancingType::PerPacket => println!("Per-packet ECMP"),
LoadBalancingType::Unknown => println!("Unknown"),
}
Permissions
| Feature |
Linux |
macOS |
Windows |
| TCP/UDP probes |
None |
None |
None |
| TLS probes |
None |
None |
None |
| Latency stats |
None |
None |
None |
| ICMP ping |
CAP_NET_RAW |
root |
Admin |
| Traceroute |
CAP_NET_RAW |
root |
Admin |
| Paris Traceroute |
CAP_NET_RAW |
root |
Admin |
| MTU Discovery |
CAP_NET_RAW |
root |
Admin |
sudo setcap cap_net_raw+ep ./your-binary
sudo ./your-binary
API Reference
Probe Builders
Probe::tcp(target, port) Probe::udp(target, port) Probe::icmp(target) Probe::multi(target) Probe::traceroute(target) Probe::paris(target) Probe::tls(target) Probe::latency(target, port) Probe::mtu(target) Probe::bufferbloat(target)
Standalone Functions
multiprobe::paris_traceroute(target, &options) multiprobe::discover_paths(target, flows, &options) multiprobe::measure_latency(target, port, samples, interval)
multiprobe::discover_path_mtu(target, &options)
multiprobe::detect_bufferbloat(target, &options)
multiprobe::analyze_reordering(target, port, packets)
multiprobe::probe_tls(target, &options)
multiprobe::compare_tls(target1, target2, &options)
References
- Paris Traceroute: Augustin et al., "Avoiding traceroute anomalies with Paris traceroute" (IMC 2006)
- RFC 3550: RTP jitter calculation
- RFC 1191: Path MTU Discovery
- Bufferbloat: Gettys & Nichols, "Bufferbloat: Dark Buffers in the Internet" (2012)
License
MIT OR Apache-2.0