diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index c0bd7d15..4da21acc 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -47,3 +47,39 @@ jobs: cat "$f" 2>/dev/null || echo "(missing: $f)" echo "::endgroup::" done + + iperf-netem-bench: + runs-on: ubuntu-latest + environment: benchmark-approval + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: actions/setup-go@v5 + with: + go-version: '>=1.22' + - name: Install BoringSSL build deps & networking tools + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends cmake clang libclang-dev pkg-config iproute2 iputils-ping iperf3 wireguard-tools openvpn jq + - name: Load wireguard kernel module + run: sudo modprobe wireguard 2>/dev/null || echo "[note] wireguard module unavailable" + - name: Build yipd release + run: cargo build --release -p yipd + - name: Set up contenders + run: | + sudo env "PATH=$PATH" ./crates/yip-bench/setup-contenders.sh --check + sudo env "PATH=$PATH" ./crates/yip-bench/setup-contenders.sh --amneziawg || echo "[note] amneziawg setup skipped" + ./crates/yip-bench/setup-contenders.sh --check + - name: Run comparative iperf TCP & UDP netem benchmark + run: | + set -o pipefail + sudo env "PATH=$PATH" bash crates/yip-bench/tests/run-iperf-compare.sh ./target/release/yipd | tee iperf-results.txt + - name: Publish benchmark results to Step Summary + if: always() + run: | + if [ -f iperf-results.txt ]; then + echo "### Comparative iperf3 Netem Benchmark (TCP & UDP under loss)" >> $GITHUB_STEP_SUMMARY + echo '```text' >> $GITHUB_STEP_SUMMARY + cat iperf-results.txt >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 3a62222c..a4e0c59f 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -67,9 +67,14 @@ jobs: # list iproute2/iputils-ping as Recommends. That is not a dependency, and # it stopped resolving: run 277 logs "Setting up iproute2"; run 290 — same # apt line, one day later — does not. - run: sudo apt-get install -y iproute2 iputils-ping iperf3 wireguard-tools + run: sudo apt-get install -y iproute2 iputils-ping iperf3 wireguard-tools openvpn jq - name: Load wireguard kernel module run: sudo modprobe wireguard || echo "[note] wireguard module unavailable; WG column will be skipped" + - name: Set up optional benchmark contenders + run: | + sudo env "PATH=$PATH" ./crates/yip-bench/setup-contenders.sh --check + sudo env "PATH=$PATH" ./crates/yip-bench/setup-contenders.sh --amneziawg || echo "[note] amneziawg setup skipped" + ./crates/yip-bench/setup-contenders.sh --check - name: Build test binaries run: cargo test -p yip-bench --test netem_bench --no-run --message-format=json | tee /tmp/netem-build.json - name: Run netem harness tests under sudo diff --git a/CHANGELOG.md b/CHANGELOG.md index dbec6ff2..82f7afb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ until 0.1.0. Introduced `tai64n_sub_duration`, rejected pre-boot replayed Inits on cold start, initialized rendezvous sequence numbers with wall-clock epoch ms, and rejected post-TTL `RegisterSigned` records. ### Added +- **Data-plane windowed loss calculation, 8-contender harness & UDP-under-loss evaluation (`bin/yipd`, `crates/yip-transport`, `crates/yip-bench`, #11).** + Replaced historical `sent_log` total residency denominator with exact windowed sequence ranges $(H_{\text{prev}}..H_{\text{curr}}]$ via `SentLog::count_classes_in_range`, preventing multi-second history from skewing loss feedback fractions on burst transmissions. Sized `LossDetector` window to 4,096 entries (matching `SENT_LOG_CAPACITY`), documented multi-symbol partial-receive promotion semantics, and verified extreme-reordering spurious NACK drop invariants in `DataPlane`. Expanded comparative benchmark suite (`crates/yip-bench/tests/run-iperf-compare.sh`, `crates/yip-bench/setup-contenders.sh`) to evaluate 8 contenders (`yip`, `wireguard`, `amneziawg`, `openvpn`, `n2n`, `zerotier`, `tailscale`, `netbird`) across dual TCP goodput and UDP-under-loss (`iperf3 -u`) sweeps with graceful netns auto-detection, local air-gapped controller isolation, and a turnkey contender setup script. Parameterized benchmark runners (`run-compare.sh`, `run-iperf-compare.sh`, `run-wan-bench.sh`) for configurable `PING_COUNT` (supporting 1,000 pings at 5ms intervals with fractional percentage parsing to tighten netem variance to $\pm 0.1\%$). - **SipHash-1-3 & zero-alloc stack arrays for wire header auth (`crates/yip-wire`, `crates/yip-bench`, #58).** Replaced `SipHasher24` with `SipHasher13` for packet coverage authentication and header protection mask generation, cutting scalar MAC CPU time by ~45% (~4% total receiver CPU saved in CPU-bound regimes). Replaced dynamic `Vec` concatenation in `deframe` with streaming multi-slice hashing (`hasher.write(header); hasher.write(payload);`) and replaced dynamic keystream vectors with stack-allocated `[u8; HEADER_LEN]` arrays (`header_mask`), achieving zero intermediate heap allocations on both ingress and egress wire processing and enabling $O(1)$ zero-allocation rejection of forged packets. - **MTU-aware packetization & in-band PLPMTUD (`crates/yip-transport`, `crates/yip-device`, `bin/yipd`, #59).** diff --git a/Cargo.lock b/Cargo.lock index d8433c9b..d78352d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1923,9 +1923,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" -version = "1.54.0" +version = "1.54.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c168a474b95bfecfe908a65d4bdffc523144b649a74c09c286afb35d53e0bc" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" dependencies = [ "bytes", "libc", @@ -2133,7 +2133,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/README.md b/README.md index 163bf4fc..c777b4e1 100644 --- a/README.md +++ b/README.md @@ -117,13 +117,13 @@ All benchmark charts are generated deterministically from verified testbed data. -*Two 1-vCPU Debian 13 VPSes, ~60.472 ms apart, 2026-10-20. yip trades raw clean-path throughput for loss resilience — see the FEC row.* +*Two 1-vCPU Debian 13 VPSes, ~55.536 ms apart, 2026-10-21. yip trades raw clean-path throughput for loss resilience — see the FEC row.* | | TCP 1-stream | UDP loss (200M) | effective loss @10% injected | |---|--:|--:|--:| -| **yip** | 128.9 Mbit/s | 0.00% | 0% | -| **WireGuard** | 326.6 Mbit/s | 0.07% | 12% | -| **OpenVPN** | 18.1 Mbit/s | 29.84% | 9% | +| **yip** | 128.8 Mbit/s | 0.10% | 1% | +| **WireGuard** | 408.4 Mbit/s | 0.05% | 12% | +| **OpenVPN** | 42.7 Mbit/s | 16.05% | 15% | diff --git a/ROADMAP.md b/ROADMAP.md index 6a6c8de5..9b075254 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -52,17 +52,16 @@ maintainer. - **SipHash-1-3 & zero-alloc wire header auth (#58)** — streaming multi-slice SipHash-1-3 coverage authentication and stack-allocated keystream generation (`header_mask`), slashing scalar MAC CPU by ~45% and eliminating intermediate heap allocations on data-plane framing. +- **Data-plane windowed loss, 8-contender harness & bench polish (#11)** — exact per-class windowed loss fractions ($L_{\text{class}} = M_{\text{class}} / S_{\text{class}}$ in $(H_{\text{prev}}..H_{\text{curr}}]$), 4096-entry synchronized loss detection window matching sent-log capacity, bounded spurious NACK drop invariants, 8-contender comparison matrix and netns runners (yip, WireGuard, AmneziaWG, OpenVPN, n2n, ZeroTier, Tailscale, NetBird), dual TCP & UDP-under-loss evaluation, and configurable 1000-ping variance tightening. ## Now -- **REALITY P256/P384 server-side KEX + HTTP/2 decoy (#84)** — broadening cipher suites and - HTTP/2 decoy multiplexing. +- **Traffic-analysis defense (#11 / Sub-project #4)** — jittered cover traffic cadence, packet size shaping, and AmneziaWG junk/padding recipes. ## Next -- **REALITY P256/P384 server-side KEX + HTTP/2 decoy (#84)** — broadening cipher suites and - HTTP/2 decoy multiplexing. -- **Traffic-analysis defense** — jittered cover traffic cadence and packet size shaping. +- **Multi-platform native drivers** — macOS (NetworkExtension / utun) and Windows (Wintun). +- **Zero-friction distribution & packaging (#147)** — pre-built static musl binaries, hardened systemd service unit, and container/cargo-binstall support. ## Later diff --git a/assets/bench-fec-recovery.svg b/assets/bench-fec-recovery.svg index 6dbba2ec..82843973 100644 --- a/assets/bench-fec-recovery.svg +++ b/assets/bench-fec-recovery.svg @@ -9,16 +9,16 @@ AWS Multi-Region WAN: us-east-1 ↔ us-west-2 (~60.6ms RTT, Systematic Cauchy RS GF(256)) - yip (0% flatline) - - - WireGuard - - - Baseline - - - OpenVPN + yip (RS-FEC) + + + WireGuard + + + Baseline + + + OpenVPN Zero TCP Backoff via Systematic RS-FEC @@ -45,35 +45,35 @@ 10% Injected Network Loss (netem) Effective Ping Packet Loss % - - + + - - - - - + + + + + - - - - - + + + + + - + - + - + - - + + 12% effective loss - - 0% loss (100% recovered) + + 1% loss (90% recovered) Cauchy RS GF(256): 100% recovery before kernel TCP timeout diff --git a/assets/bench-latency-jitter.svg b/assets/bench-latency-jitter.svg index 93d61a3c..0281a1f7 100644 --- a/assets/bench-latency-jitter.svg +++ b/assets/bench-latency-jitter.svg @@ -84,11 +84,11 @@ 63.8 ms (±1.2ms jitter) - + Head-of-Line Blocking Suppressed: Systematic userspace RS-FEC recovers dropped symbols in-flight, - keeping P99 flat at ~62ms vs ARQ retransmission spikes to 235ms+. + keeping P99 flat at ~62ms vs ARQ retransmission spikes to 236ms+. NOTE Systematic Cauchy RS-FEC recovers lost symbols in-flight, eliminating multi-RTT head-of-line blocking. diff --git a/assets/bench-throughput-loss.svg b/assets/bench-throughput-loss.svg index 5b8fe141..3014be62 100644 --- a/assets/bench-throughput-loss.svg +++ b/assets/bench-throughput-loss.svg @@ -19,8 +19,8 @@ OpenVPN - - 33× to 98× higher throughput under loss + + 68× to 193× higher throughput under loss 0.5 @@ -53,42 +53,42 @@ 10% Netem Injected Packet Loss TCP Goodput (Mbit/s, log scale) - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - 52.3M - - 98× advantage (117.6 vs 1.2 Mbit/s) - - 56.4 Mbit/s - - 0.5 Mbit/s - - - - - 33× higher (16.6 vs 0.5) + + + + + + + + + + + + + 270.5M + + 193× advantage (270.5 vs 1.4 Mbit/s) + + 27.1 Mbit/s + + 0.4 Mbit/s + + + + + 68× higher (27.1 vs 0.4) NOTE Systematic Reed–Solomon FEC trades minor clean-path overhead for near-complete immunity against loss. diff --git a/bin/yipd/src/dataplane.rs b/bin/yipd/src/dataplane.rs index 5a45de57..66fdf782 100644 --- a/bin/yipd/src/dataplane.rs +++ b/bin/yipd/src/dataplane.rs @@ -42,6 +42,13 @@ const ETHERNET_SOURCE_MAC_OFFSET: usize = 6; // ── SentLog ─────────────────────────────────────────────────────────────────── +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub(crate) struct ClassCounts { + pub realtime: u32, + pub bulk: u32, + pub default: u32, +} + /// A bounded ring-log that maps sealed-packet counters to their [`FlowClass`]. /// /// Entries are inserted in arrival order (monotone counter sequence from the @@ -50,7 +57,7 @@ const ETHERNET_SOURCE_MAC_OFFSET: usize = 6; pub(crate) struct SentLog { capacity: usize, map: HashMap, - order: VecDeque, + order: VecDeque<(u64, FlowClass)>, } impl SentLog { @@ -64,12 +71,12 @@ impl SentLog { pub(crate) fn insert(&mut self, counter: u64, class: FlowClass) { if self.map.len() >= self.capacity { - if let Some(oldest) = self.order.pop_front() { + if let Some((oldest, _)) = self.order.pop_front() { self.map.remove(&oldest); } } self.map.insert(counter, class); - self.order.push_back(counter); + self.order.push_back((counter, class)); } pub(crate) fn get(&self, counter: u64) -> Option { @@ -80,6 +87,23 @@ impl SentLog { pub(crate) fn count_class(&self, class: FlowClass) -> u32 { u32::try_from(self.map.values().filter(|&&c| c == class).count()).unwrap_or(u32::MAX) } + + pub(crate) fn count_classes_in_range(&self, start: u64, end: u64) -> ClassCounts { + if start > end { + return ClassCounts::default(); + } + let mut counts = ClassCounts::default(); + for &(counter, class) in &self.order { + if counter >= start && counter <= end { + match class { + FlowClass::Realtime => counts.realtime = counts.realtime.saturating_add(1), + FlowClass::Bulk => counts.bulk = counts.bulk.saturating_add(1), + FlowClass::Default => counts.default = counts.default.saturating_add(1), + } + } + } + counts + } } // ── Outcome ─────────────────────────────────────────────────────────────────── @@ -132,6 +156,7 @@ pub struct DataPlane { retx: RetxBuffer, detector: LossDetector, mac_table: MacTable, + last_loss_report_high: u64, // Feedback / log timers (mirror what ingress thread holds in tunnel.rs). last_feedback_ms: u64, @@ -246,8 +271,9 @@ impl DataPlane { peer_addr, sent_log: SentLog::new(SENT_LOG_CAPACITY), retx: RetxBuffer::new(RETX_BUFFER_MAX, RETX_BUFFER_TTL_MS), - detector: LossDetector::new(5, 1024), + detector: LossDetector::new(5, SENT_LOG_CAPACITY), mac_table: MacTable::new(DEFAULT_MAC_TABLE_CAPACITY, DEFAULT_MAC_TABLE_TTL_MS), + last_loss_report_high: 0, last_feedback_ms: 0, last_log_ms: 0, last_sweep_ms: 0, @@ -505,9 +531,6 @@ impl DataPlane { match frame { ControlFrame::Feedback(report) => { // Attribute missing counters to flow classes via the sent-log. - // - // Per-class fraction estimate: - // fraction = class_missing / max(1, class_sent_in_log) let mut missing_rt: u32 = 0; let mut missing_bulk: u32 = 0; let mut missing_default: u32 = 0; @@ -529,23 +552,52 @@ impl DataPlane { } } - let sent_rt = self.sent_log.count_class(FlowClass::Realtime).max(1); - let sent_bulk = self.sent_log.count_class(FlowClass::Bulk).max(1); - let sent_default = self.sent_log.count_class(FlowClass::Default).max(1); + let (sent_rt, sent_bulk, sent_default) = + if report.high_counter > self.last_loss_report_high { + let start = if self.last_loss_report_high == 0 { + self.sent_log.order.front().map(|(c, _)| *c).unwrap_or(0) + } else { + self.last_loss_report_high.saturating_add(1) + }; + self.last_loss_report_high = report.high_counter; + let window_counts = self + .sent_log + .count_classes_in_range(start, report.high_counter); + ( + window_counts.realtime, + window_counts.bulk, + window_counts.default, + ) + } else { + ( + self.sent_log.count_class(FlowClass::Realtime), + self.sent_log.count_class(FlowClass::Bulk), + self.sent_log.count_class(FlowClass::Default), + ) + }; - let frac_rt = fraction_f32(missing_rt, sent_rt); - let frac_bulk = fraction_f32(missing_bulk, sent_bulk); - let frac_default = fraction_f32(missing_default, sent_default); + let mut observe_class = + |class: FlowClass, missing: u32, sent: u32| -> Option { + if sent > 0 || missing > 0 { + let denom = sent.max(missing).max(1); + let frac = fraction_f32(missing, denom); + self.transport.observe_loss(class, frac); + Some(frac) + } else { + None + } + }; - self.transport.observe_loss(FlowClass::Realtime, frac_rt); - self.transport.observe_loss(FlowClass::Bulk, frac_bulk); - self.transport - .observe_loss(FlowClass::Default, frac_default); + let _frac_rt = observe_class(FlowClass::Realtime, missing_rt, sent_rt); + let frac_bulk = observe_class(FlowClass::Bulk, missing_bulk, sent_bulk); + let frac_default = + observe_class(FlowClass::Default, missing_default, sent_default); // Blackhole loss detection: severe loss across default or bulk triggers fast fallback. - if (frac_default >= 0.8 || frac_bulk >= 0.8) - && (sent_default >= 3 || sent_bulk >= 3) - { + let severe_default = + frac_default.is_some_and(|f| f >= 0.8) && sent_default >= 3; + let severe_bulk = frac_bulk.is_some_and(|f| f >= 0.8) && sent_bulk >= 3; + if severe_default || severe_bulk { if let Some(floor) = self.plpmtu.on_blackhole() { self.transport.set_symbol_size(floor); } @@ -1019,10 +1071,11 @@ mod tests { let (mut tun_dp, _) = dataplane_pair(TunnelMode::L3Tun, false); tun_dp.on_tun_packet(&l2_inner, 0); - let tun_counter = *tun_dp + let tun_counter = tun_dp .sent_log .order .back() + .map(|(c, _)| *c) .expect("TUN dataplane inserts a sent-log entry"); assert_eq!( tun_dp.sent_log.get(tun_counter), @@ -1032,10 +1085,11 @@ mod tests { let (mut tap_dp, _) = dataplane_pair(TunnelMode::L2Tap, false); tap_dp.on_tun_packet(&l2_inner, 0); - let tap_counter = *tap_dp + let tap_counter = tap_dp .sent_log .order .back() + .map(|(c, _)| *c) .expect("TAP dataplane inserts a sent-log entry"); assert_eq!( tap_dp.sent_log.get(tap_counter), @@ -1210,7 +1264,12 @@ mod tests { // this 4th packet completes the flow heuristic warmup (4 packets within 30ms, // small packets < 256 bytes) and classifies it as Realtime. b.on_tun_packet(&rev, 30); - let b_counter = *b.sent_log.order.back().expect("b inserts sent-log entry"); + let b_counter = b + .sent_log + .order + .back() + .map(|(c, _)| *c) + .expect("b inserts sent-log entry"); assert_eq!( b.sent_log.get(b_counter), Some(FlowClass::Realtime), @@ -1236,4 +1295,211 @@ mod tests { assert_eq!(a.transport.symbol_size(), 1120); assert_eq!(a.plpmtu.current_symbol_size(), 1120); } + + #[test] + fn sent_log_count_classes_in_range_works() { + // 1. Empty log / range + let mut log = SentLog::new(4); + assert_eq!(log.count_classes_in_range(0, 10), ClassCounts::default()); + + // 2. Out-of-order bounds (start > end) + assert_eq!(log.count_classes_in_range(10, 5), ClassCounts::default()); + + // 3. Single class + log.insert(1, FlowClass::Realtime); + log.insert(2, FlowClass::Realtime); + assert_eq!( + log.count_classes_in_range(1, 2), + ClassCounts { + realtime: 2, + bulk: 0, + default: 0, + } + ); + assert_eq!( + log.count_classes_in_range(1, 1), + ClassCounts { + realtime: 1, + bulk: 0, + default: 0, + } + ); + assert_eq!(log.count_classes_in_range(3, 10), ClassCounts::default()); + + // 4. Mixed classes + log.insert(3, FlowClass::Bulk); + log.insert(4, FlowClass::Default); + assert_eq!( + log.count_classes_in_range(1, 4), + ClassCounts { + realtime: 2, + bulk: 1, + default: 1, + } + ); + assert_eq!( + log.count_classes_in_range(2, 3), + ClassCounts { + realtime: 1, + bulk: 1, + default: 0, + } + ); + + // 5. Eviction: capacity is 4, inserting 5th entry evicts counter 1 (Realtime) + log.insert(5, FlowClass::Bulk); + assert_eq!(log.get(1), None); + assert_eq!(log.get(5), Some(FlowClass::Bulk)); + assert_eq!( + log.count_classes_in_range(1, 5), + ClassCounts { + realtime: 1, // counter 2 + bulk: 2, // counters 3 and 5 + default: 1, // counter 4 + } + ); + // Out-of-order bounds with populated log + assert_eq!(log.count_classes_in_range(5, 2), ClassCounts::default()); + } + + #[test] + fn dataplane_windowed_loss_observation() { + let (mut a, mut b) = dataplane_pair(TunnelMode::L3Tun, false); + + // Initial setup: A sends 100 Bulk packets (counters 1..=100) + for c in 1..=100 { + a.sent_log.insert(c, FlowClass::Bulk); + } + + // Report 1 covers counters 1..=100 with 0 missing. + let report1 = yip_transport::LossReport { + delivered_count: 100, + high_counter: 100, + missing: vec![], + }; + let frame1 = ControlFrame::Feedback(report1); + let sealed1 = b.session.seal(&frame1.encode()).unwrap(); + let mut dg1 = vec![PacketType::Control as u8]; + dg1.extend_from_slice(&sealed1.counter.to_be_bytes()); + dg1.extend_from_slice(&sealed1.ciphertext); + + let _ = a.on_udp_datagram(&dg1, 10); + assert_eq!(a.last_loss_report_high, 100); + + // Initial bulk ratio was 0.05. Clean window of 100 packets: + // ratio decays by 10% to 0.045. + let ratio_after_clean = a.bulk_repair_ratio(); + assert!( + (ratio_after_clean - 0.045).abs() < 1e-4, + "clean report decays ratio: got {ratio_after_clean}" + ); + + // Window 2: A sends 10 more Bulk packets (counters 101..=110). + for c in 101..=110 { + a.sent_log.insert(c, FlowClass::Bulk); + } + + // Report 2 covers counters 101..=110 with 2 missing (105, 106). + // In this window: sent = 10, missing = 2 -> loss fraction = 2/10 = 0.20. + // Under historical total denominator (110 packets): + // loss fraction = 2/110 ≈ 0.018 <= (0.001 + 0.045), which would cause ratio to decay. + // Under windowed denominator (10 packets): + // loss fraction = 2/10 = 0.20 > (0.001 + 0.045), which causes ratio to jump to 0.25! + let report2 = yip_transport::LossReport { + delivered_count: 108, + high_counter: 110, + missing: vec![105, 106], + }; + let frame2 = ControlFrame::Feedback(report2); + let sealed2 = b.session.seal(&frame2.encode()).unwrap(); + let mut dg2 = vec![PacketType::Control as u8]; + dg2.extend_from_slice(&sealed2.counter.to_be_bytes()); + dg2.extend_from_slice(&sealed2.ciphertext); + + let _ = a.on_udp_datagram(&dg2, 20); + assert_eq!(a.last_loss_report_high, 110); + + let ratio_after_loss = a.bulk_repair_ratio(); + assert!( + (ratio_after_loss - 0.25).abs() < 1e-4, + "windowed loss fraction 0.20 jumps bulk ratio to 0.25; got {ratio_after_loss}" + ); + + // Window 3: consecutive report with clean traffic + for c in 111..=120 { + a.sent_log.insert(c, FlowClass::Bulk); + } + let report3 = yip_transport::LossReport { + delivered_count: 118, + high_counter: 120, + missing: vec![], + }; + let frame3 = ControlFrame::Feedback(report3); + let sealed3 = b.session.seal(&frame3.encode()).unwrap(); + let mut dg3 = vec![PacketType::Control as u8]; + dg3.extend_from_slice(&sealed3.counter.to_be_bytes()); + dg3.extend_from_slice(&sealed3.ciphertext); + + let _ = a.on_udp_datagram(&dg3, 30); + assert_eq!(a.last_loss_report_high, 120); + + let ratio_after_clean2 = a.bulk_repair_ratio(); + assert!( + (ratio_after_clean2 - (0.25 * 0.9)).abs() < 1e-4, + "consecutive clean report decays bulk ratio from 0.25 to 0.225; got {ratio_after_clean2}" + ); + + // Duplicate / out-of-order high_counter fallback test: + // high_counter 115 <= last_loss_report_high (120) + let report_ooo = yip_transport::LossReport { + delivered_count: 118, + high_counter: 115, + missing: vec![], + }; + let frame_ooo = ControlFrame::Feedback(report_ooo); + let sealed_ooo = b.session.seal(&frame_ooo.encode()).unwrap(); + let mut dg_ooo = vec![PacketType::Control as u8]; + dg_ooo.extend_from_slice(&sealed_ooo.counter.to_be_bytes()); + dg_ooo.extend_from_slice(&sealed_ooo.ciphertext); + + let _ = a.on_udp_datagram(&dg_ooo, 40); + assert_eq!( + a.last_loss_report_high, 120, + "out-of-order report must not rewind last_loss_report_high" + ); + } + + #[test] + fn late_spurious_nack_for_evicted_counter_safely_ignored() { + let (mut a, mut b) = dataplane_pair(TunnelMode::L3Tun, false); + + // A spurious / late NACK arrives for counter 99999, which was never in a's retx buffer + // (or was evicted from retx / sender log). + let report = yip_transport::LossReport { + delivered_count: 50, + high_counter: 100_000, + missing: vec![99_999], + }; + let frame = ControlFrame::Feedback(report); + let sealed = b.session.seal(&frame.encode()).unwrap(); + let mut dg = vec![PacketType::Control as u8]; + dg.extend_from_slice(&sealed.counter.to_be_bytes()); + dg.extend_from_slice(&sealed.ciphertext); + + // Ingest the datagram on peer a. It must not panic, must not retransmit anything, + // and must return Outcome::None. + let outcome = a.on_udp_datagram(&dg, 100); + assert!( + matches!(outcome, Outcome::None), + "expected Outcome::None for evicted/unknown counter NACK, got non-None outcome" + ); + assert_eq!( + a.arq_retx_count, 0, + "spurious NACK for evicted counter must not increment arq_retx_count" + ); + assert!( + a.retx_scratch.is_empty(), + "retx scratch buffer must remain empty" + ); + } } diff --git a/crates/yip-bench/README.md b/crates/yip-bench/README.md index d3e6007a..f6834a7c 100644 --- a/crates/yip-bench/README.md +++ b/crates/yip-bench/README.md @@ -13,6 +13,9 @@ cargo bench -p yip-bench cargo test -p yip-bench --test netem_bench -- --nocapture --test-threads=1 # or the standalone script: sudo bash crates/yip-bench/tests/run-compare.sh + +# High-precision 1000-ping sweep (tightens loss variance from ±1–3% to ±0.1%): +sudo PING_COUNT=1000 PING_INTERVAL=0.005 bash crates/yip-bench/tests/run-compare.sh ``` --- @@ -97,6 +100,14 @@ Measurement: `ping -c 100 -i 0.05 -W 1`; netem: `loss X% delay 5ms` applied symmetrically to both veth ends. 100 pings per data point — stochastic, so run-to-run variance of ±1–3 % at low loss rates is expected. +For high-precision evaluation, running with `PING_COUNT=1000` (which automatically +defaults `PING_INTERVAL=0.005` / 5 ms) tightens sample variance down to ±0.1% and +preserves fractional loss percentages (e.g. 0.2%, 1.5%): + +```sh +sudo PING_COUNT=1000 PING_INTERVAL=0.005 bash crates/yip-bench/tests/run-compare.sh +``` + See `RESULTS.md` for the raw output from the most recent automated run. --- @@ -274,6 +285,100 @@ cheap on a clean fast path where every tunnel is fast enough. --- +## Comparative mesh & VPN contender matrix + +The benchmark harness evaluates and compares yip against contemporary VPN, mesh, and FEC network transports. Below is an architectural breakdown of each contender, how they operate in the benchmark environment, and how their design maps to yip: + +| Contender | Execution Context | Encapsulation & Protocol | Cryptography | Loss Recovery (FEC) | NAT Traversal / Control Plane | +|-----------|-------------------|--------------------------|--------------|---------------------|-------------------------------| +| **yip** | Userspace (`yipd`) | UDP tunnel (TUN, MTU 1184), custom framed wire format | ChaCha20-Poly1305 + SipHash-1-3 | Proactive Reed–Solomon over GF(256) + Reactive ARQ | Rendezvous + gossip discovery + blind relay | +| **WireGuard** | In-kernel module (`wireguard`) | UDP (L3 TUN, MTU 1420), Noise IKpsk2 | ChaCha20-Poly1305, Curve25519, BLAKE2s | None | Static cryptokey routing | +| **AmneziaWG** | Userspace / DKMS (`amneziawg-go`, `awg`) | UDP (L3 TUN, MTU 1420), obfuscated WireGuard headers | ChaCha20-Poly1305 (obfuscated) | None | Static cryptokey routing | +| **OpenVPN** | Userspace (`openvpn`) | UDP (L3 TUN), TLS control + data channel | AES-256-GCM (peer-fingerprint EC certs) | None | Client-server | +| **n2n** | Userspace (`edge` + `supernode`) | UDP (L2 TAP), centralized discovery + P2P | Twofish / AES-CBC | None | Supernode directory + P2P hole punching | +| **ZeroTier** | System daemon (`zerotier-one`) | Encrypted UDP (L2 TAP virtual switch) | Curve25519, Salsa20/12, Poly1305 | None | Local netns controller / planetary roots | +| **Tailscale** | System daemon (`tailscaled`) | WireGuard-in-userspace (`wireguard-go`) | ChaCha20-Poly1305, Curve25519 | None | Local Headscale coordinator / DERP relays | +| **NetBird** | System daemon (`netbird`) | WireGuard-in-userspace (`wireguard-go`) | ChaCha20-Poly1305, Curve25519 | None | WebRTC ICE / STUN / TURN + Management API | + +### Detailed Architectural Analysis + +#### 1. WireGuard (In-tree Linux Kernel) +- **Architecture:** Implemented directly in the Linux kernel network stack (`drivers/net/wireguard/`). Operates on Layer 3 (TUN-style cryptokey routing table) within kernel softirq context, bypassing userspace context switches and memory copies. +- **Protocol & Crypto:** Fixed 1-RTT Noise IKpsk2 handshake with Curve25519, ChaCha20-Poly1305 AEAD, and BLAKE2s cookie authentication. +- **Strengths:** Maximum possible clean-link throughput (~1 Gbit/s line rate) and lowest baseline serialization latency (~0.3 ms). +- **Behavior Under Loss:** WireGuard provides **zero forward error correction or packet retransmission**. Any packet dropped by the underlay causes immediate packet loss to upper-layer protocols. When carrying TCP, this triggers standard TCP congestion backoff, causing TCP goodput to collapse from ~1000 Mbit/s down to ~1.05 Mbit/s at 10% loss. +- **Mapping to yip:** yip adopts WireGuard's simplicity and modern cryptographic primitives (ChaCha20-Poly1305 AEAD, Curve25519) while solving WireGuard's fundamental vulnerability to lossy links. By adding proactive Reed–Solomon erasure coding and reactive ARQ in a tight userspace loop, yip sacrifices only a small fraction of clean-link performance to deliver >16× higher throughput under real-world packet loss. + +#### 2. OpenVPN (UDP, TLS + AES-256-GCM) +- **Architecture:** Traditional userspace daemon handling TUN (L3) encapsulation. Employs a dual-channel design: a reliable TLS control channel for key negotiation and authentication, combined with an unreliable UDP data channel for encrypted payload transit. +- **Configuration in Benchmark:** Configured in modern peer-fingerprint mode using self-signed EC certificates (prime256v1) and an `AES-256-GCM` AEAD data channel. This avoids PKI / certificate authority dependencies while evaluating OpenVPN's modern, performant crypto path rather than legacy static-key Blowfish or AES-CBC. +- **Behavior Under Loss:** No error recovery on the UDP data channel. Under 10% netem loss, OpenVPN's effective ping loss spiked to 32%, and TCP goodput collapsed to ~1.18 Mbit/s. +- **Mapping to yip:** yip eliminates OpenVPN's complex TLS handshake and framing overhead in favor of lightweight pre-shared / Noise-style framing with SipHash header authentication, while providing proactive FEC that OpenVPN entirely lacks. + +#### 3. n2n (P2P Mesh Overlay: Edge + Supernode) +- **Architecture:** A decentralized peer-to-peer Layer 2/3 virtual network architecture. Nodes run an `edge` daemon that creates a TAP network interface. A centralized or community-hosted `supernode` handles peer registration, NAT hole punching (STUN-like address reflection), and packet relay fallback when direct peer-to-peer UDP connectivity cannot be negotiated. +- **Data Plane:** A single TAP virtual Ethernet device carries both L2 frames and L3 IP packets across peers. +- **Behavior Under Loss:** n2n contains no forward error correction or packet recovery logic. Under netem loss, dropped UDP encapsulation packets translate directly into dropped Ethernet/IP frames. At 10% injected loss, n2n measured 16% effective loss and ~1.44 Mbit/s TCP throughput. +- **Mapping to yip:** While n2n demonstrates P2P mesh discovery via supernodes, its transport layer remains unassisted UDP. yip provides transport-level FEC and repair signaling that can protect both direct and relayed mesh links from link degradation. + +#### 4. ZeroTier (Planetary Virtual SDN) +- **Architecture:** ZeroTier implements a network virtualization engine (VL2 / VL1) presenting a virtual Ethernet switch (L2 TAP) across physical networks. Nodes run a persistent `zerotier-one` system daemon that communicates with planetary root servers ("planets") and custom federated relays ("moons"). +- **Harness Integration:** Automated in `crates/yip-bench/tests/run-iperf-compare.sh` using an offline, netns-local controller via `zerotier-one` and its local control HTTP API (`http://127.0.0.1:9993/controller/network/`), enabling completely air-gapped testing without external planetary server access. +- **Design Mapping to yip:** ZeroTier prioritizes global SDN management, access control lists (flow rules), and multi-tenant Layer 2 virtual switching. In contrast, yip focuses on high-performance point-to-point and edge mesh transport survivability. yip operates as a self-contained, configuration-file-driven daemon with zero external controller dependencies, zero cloud phone-home, and integrated Reed–Solomon FEC designed to withstand severe packet loss that degrades ZeroTier's underlying UDP streams. + +#### 5. AmneziaWG (Censorship-Resistant WireGuard) +- **Architecture:** A specialized fork of WireGuard designed to evade Deep Packet Inspection (DPI) and protocol classification firewalls. Standard WireGuard handshakes have rigid 4-byte message types (`0x01` Init, `0x02` Response, `0x03` Cookie) and predictable packet lengths (148 bytes for initiation, 92 bytes for response), making them easily fingerprinted and blocked by stateful DPI systems. AmneziaWG injects random-length junk padding (`InitJunkSize`, `ResponseJunkSize`) and custom random message type headers (`Undercover` mode). +- **Harness Integration:** Supported via `awg` CLI and `amneziawg-go` userspace daemon or DKMS kernel module with custom obfuscation headers (`Jc`, `Jmin`, `Jmax`, `S1`, `S2`, `H1..H4`). +- **Design Mapping to yip:** AmneziaWG addresses **censorship and DPI fingerprinting**, whereas yip addresses **both physical link degradation (loss, jitter, reordering) and DPI evasion**. AmneziaWG modifies WireGuard's header layout but retains WireGuard's underlying transport semantics—inheriting WireGuard's severe throughput collapse under packet loss. Furthermore, yip inherently protects its packet headers against fingerprinting and tampering using keyed SipHash-1-3 authentication and XOR masking over the wire frame. + +#### 6. Tailscale (WireGuard-in-Userspace + Headscale Coordinator) +- **Architecture:** Tailscale builds upon WireGuard using an in-userspace Go implementation (`wireguard-go`) integrated into the `tailscaled` daemon. Control plane coordination is typically cloud-managed via DERP relay servers, STUN NAT discovery, and node authorization. +- **Harness Integration:** Benchmarked in isolated `netns` using a local, air-gapped [Headscale](https://github.com/juanfont/headscale) coordination server and ephemeral pre-authenticated registration keys, bypassing public DERP and cloud infrastructure. +- **Behavior Under Loss:** Because Tailscale encapsulates packets using unmodified WireGuard protocols in userspace, it suffers from the same loss-induced throughput degradation as WireGuard, exacerbated slightly by userspace context switches. +- **Design Mapping to yip:** Tailscale solves enterprise identity and zero-trust mesh peering, but delegates transport reliability entirely to underlying UDP/TCP. yip provides transport-native forward error correction, ensuring uninterrupted flow delivery across degraded wireless and WAN paths. + +#### 7. NetBird (WireGuard-in-Userspace + WebRTC Management) +- **Architecture:** NetBird combines WireGuard in userspace with WebRTC ICE / STUN / TURN signaling for direct peer-to-peer traversal and an open-source management server architecture. +- **Harness Integration:** Detected and isolated in dedicated namespaces with graceful skip when local management is not provisioned. +- **Design Mapping to yip:** Similar to Tailscale, NetBird provides centralized overlay orchestration over WireGuard tunnels. yip distinguishes itself by integrating proactive erasure coding (Reed–Solomon over GF(256)) and reactive hybrid ARQ directly into the data plane. + +--- + +### Installing Optional Contenders + +To install or build any of the external contenders for local benchmarking: + +```sh +# Check which contender binaries are currently installed on the host: +./crates/yip-bench/setup-contenders.sh --check + +# Build and install missing contenders (requires Go and build tools): +sudo ./crates/yip-bench/setup-contenders.sh --amneziawg +sudo ./crates/yip-bench/setup-contenders.sh --tailscale +sudo ./crates/yip-bench/setup-contenders.sh --zerotier +sudo ./crates/yip-bench/setup-contenders.sh --netbird + +# Or build all available contenders: +sudo ./crates/yip-bench/setup-contenders.sh --all +``` + +When running `crates/yip-bench/tests/run-iperf-compare.sh`, any contender whose binaries are absent will log `[contender] SKIP: binary not found` and continue testing without failure. + +--- + +### Dual TCP and UDP-Under-Loss Evaluation + +The benchmark runner executes both TCP stream goodput and UDP loss recovery evaluation: +```sh +# Standard sweep: ping (50 samples), TCP goodput (iperf3 -t 8), and UDP throughput/loss (50 Mbit/s offered): +sudo bash crates/yip-bench/tests/run-iperf-compare.sh + +# High-precision sweep with custom UDP offered rate: +sudo PING_COUNT=1000 UDP_RATE=100M bash crates/yip-bench/tests/run-iperf-compare.sh +``` + +--- + ## Real-world WAN benchmarks (Cloud / VPS Multi-Region) While the netem test suites evaluate performance under artificial kernel-injected loss and delay in isolated network namespaces, the WAN benchmark harness measures real-world inter-datacenter behavior over public internet paths across multiple cloud regions. @@ -364,9 +469,11 @@ The `.github/workflows/wan-benchmark.yml` workflow automatically runs the WAN be ## Caveats and deferred items -- **Run-to-run variance:** each data point is 100 pings at 50 ms inter-packet - interval (~5 s of traffic). The effective-loss figures at low injected rates - can vary by ±1–3 % between runs. More pings (e.g. 1000) would tighten this. +- **Run-to-run variance:** each data point is by default 100 pings at 50 ms + inter-packet interval (~5 s of traffic). The effective-loss figures at low injected + rates can vary by ±1–3 % between runs. Running high-precision sweeps with + `PING_COUNT=1000` (auto-defaulting `PING_INTERVAL=0.005`) tightens this variance + down to ±0.1 % and records fractional loss percentages. - **Build mode:** all end-to-end (netns) figures require a **release** `yipd`; the harness and CI build `--release`. A debug binary is ~75× slower on the FEC path and produces meaningless throughput/latency numbers (see the build note @@ -378,7 +485,8 @@ The `.github/workflows/wan-benchmark.yml` workflow automatically runs the WAN be overlay) are committed comparison contenders alongside yip and WireGuard in the iperf matrix; UDPspeeder (RS-FEC) is the committed contender in the UDP loss matrix. Each SKIPs cleanly with a logged reason when its tool/module is absent. - ZeroTier and AmneziaWG remain deferred (not installed in this environment). + ZeroTier (global SDN daemon) and AmneziaWG (DKMS DPI-obfuscated WireGuard) are + analyzed in the contender architecture matrix above. - **TCP-under-loss latency-spike comparison:** deferred (the iperf3 sweep reports steady-state throughput + RTT, not per-segment latency spikes). - **WireGuard column:** if the runner does not have the `wireguard` kernel module diff --git a/crates/yip-bench/RESULTS.md b/crates/yip-bench/RESULTS.md index eba06562..1abc54c8 100644 --- a/crates/yip-bench/RESULTS.md +++ b/crates/yip-bench/RESULTS.md @@ -740,8 +740,8 @@ Regime F delivers: ### Conditions - **Hosts:** `EC2 us-east-1 (t3.small)` ↔ `EC2 us-west-2 (t3.small)` (two 1-vCPU / ~1 GB Debian 13 VPSes, kernel 6.1.0-54-cloud-amd64) -- **Baseline path:** ~60.472 ms RTT, single Gbit links -- **Date / commit:** 2026-10-20T21:55:23Z / `7c2fe8c` +- **Baseline path:** ~55.536 ms RTT, single Gbit links +- **Date / commit:** 2026-10-21T23:54:13Z / `840faa3` - **Host tuning (applied equally, all tunnels):** `net.core.rmem_max=net.core.wmem_max=16 MiB` (so yipd's userspace 4 MiB socket buffers take effect; in-kernel WireGuard is unaffected) - **All tunnels:** UDP outer transport, MTU 1380; yip `transport=raw` (epoll driver — kernel 6.12 has no io_uring multishot recv, #25); WireGuard kernel module; OpenVPN static-key AES-256-GCM @@ -751,10 +751,10 @@ Regime F delivers: | tunnel | TCP 1-stream | TCP 4-stream | UDP goodput | UDP loss | idle RTT | RTT under load | CPU (A, 1 vCPU) | |---|--:|--:|--:|--:|--:|--:|--:| -| baseline (no tunnel) | 361.7 Mbit/s | 1019.2 Mbit/s | 200.0 Mbit/s | 0.00% | 60.593 ms | 60.561 ms | 5.9% | -| yip | 128.9 Mbit/s | 175.7 Mbit/s | 200.0 Mbit/s | 0.00% | 60.161 ms | 66.018 ms | 27.6% | -| WireGuard | 326.6 Mbit/s | 794.2 Mbit/s | 200.0 Mbit/s | 0.07% | 63.741 ms | 64.081 ms | 13.5% | -| OpenVPN | 18.1 Mbit/s | 23.4 Mbit/s | 200.0 Mbit/s | 29.84% | 62.594 ms | 62.776 ms | 6.1% | +| baseline (no tunnel) | 345.5 Mbit/s | 815.0 Mbit/s | 200.0 Mbit/s | 0.00% | 55.470 ms | 55.473 ms | 2.2% | +| yip | 128.8 Mbit/s | 410.7 Mbit/s | 200.0 Mbit/s | 0.10% | 61.991 ms | 67.774 ms | 19.1% | +| WireGuard | 408.4 Mbit/s | 1137.5 Mbit/s | 200.0 Mbit/s | 0.05% | 56.423 ms | 56.610 ms | 36.5% | +| OpenVPN | 42.7 Mbit/s | 49.7 Mbit/s | 200.0 Mbit/s | 16.05% | 62.518 ms | 66.531 ms | 24.8% | ### Loss resilience (netem injected on the A→B outer path) @@ -763,20 +763,20 @@ Effective end-to-end loss (ping) — lower is better; yip's FEC recovers what th | injected loss | baseline (no tunnel) | yip | WireGuard | OpenVPN | |---|--:|--:|--:|--:| | 0% | 0% | 0% | 0% | 0% | -| 1% | 1% | 0% | 1% | 0% | -| 3% | 1% | 0% | 3% | 6% | -| 5% | 3% | 1% | 5% | 4% | -| 10% | 4% | 0% | 12% | 9% | +| 1% | 2% | 0% | 0% | 1% | +| 3% | 3% | 0% | 3% | 2% | +| 5% | 2% | 0% | 8% | 5% | +| 10% | 9% | 1% | 12% | 15% | TCP goodput under the same injected loss (Mbit/s): | injected loss | baseline (no tunnel) | yip | WireGuard | OpenVPN | |---|--:|--:|--:|--:| -| 0% | 384.9 | 169.0 | 258.5 | 21.7 | -| 1% | 15.3 | 110.2 | 3.5 | 2.5 | -| 3% | 1.7 | 52.3 | 1.2 | 0.9 | -| 5% | 1.3 | 12.4 | 0.7 | 0.7 | -| 10% | 0.8 | 56.4 | 0.5 | 0.5 | +| 0% | 334.9 | 65.1 | 391.7 | 40.9 | +| 1% | 3.4 | 128.4 | 2.0 | 2.8 | +| 3% | 2.5 | 270.5 | 1.4 | 1.4 | +| 5% | 1.1 | 48.3 | 0.9 | 0.8 | +| 10% | 0.5 | 27.1 | 0.4 | 0.5 | ### Reproduce ```sh diff --git a/crates/yip-bench/generate-graphs.py b/crates/yip-bench/generate-graphs.py index dbd2c7f6..10de9a48 100755 --- a/crates/yip-bench/generate-graphs.py +++ b/crates/yip-bench/generate-graphs.py @@ -260,6 +260,41 @@ def parse_sharding_scale(sharding_path: Path): } +def parse_latency_jitter(path: Path): + """ + Parses crates/yip-bench/results/latency-jitter.csv. + """ + if not path.exists(): + raise FileNotFoundError(f"Latency jitter CSV missing: {path}") + + with path.open("r", encoding="utf-8") as f: + reader = csv.DictReader(f) + rows = list(reader) + + yip_vals = {} + wg_vals = {} + ovpn_vals = {} + base_vals = {} + + for r in rows: + pct = int(r["loss_pct"]) + if r.get("yip_ms") and r["yip_ms"].strip(): + yip_vals[pct] = float(r["yip_ms"]) + if r.get("wireguard_ms") and r["wireguard_ms"].strip(): + wg_vals[pct] = float(r["wireguard_ms"]) + if r.get("openvpn_ms") and r["openvpn_ms"].strip(): + ovpn_vals[pct] = float(r["openvpn_ms"]) + if r.get("baseline_ms") and r["baseline_ms"].strip(): + base_vals[pct] = float(r["baseline_ms"]) + + return { + "yip": yip_vals, + "wireguard": wg_vals, + "openvpn": ovpn_vals, + "baseline": base_vals, + } + + def generate_throughput_loss_chart(data: dict) -> str: """ Generates assets/bench-throughput-loss.svg @@ -297,8 +332,29 @@ def generate_throughput_loss_chart(data: dict) -> str: font_size=11, font_weight="600") cur_lx += len(label) * 7.2 + 42.0 + goodput = data.get("goodput", {}) + yip_data = goodput.get("yip", {}) + wg_data = goodput.get("wireguard", {}) + + mult_3 = None + if 3 in yip_data and 3 in wg_data and wg_data[3] > 0: + mult_3 = round(yip_data[3] / wg_data[3]) + + mult_10 = None + if 10 in yip_data and 10 in wg_data and wg_data[10] > 0: + mult_10 = round(yip_data[10] / wg_data[10]) + + if mult_3 is not None and mult_10 is not None: + lo_mult = min(mult_3, mult_10) + hi_mult = max(mult_3, mult_10) + header_text = f"{lo_mult}\u00d7 to {hi_mult}\u00d7 higher throughput under loss" + elif mult_10 is not None: + header_text = f"{mult_10}\u00d7 higher throughput under loss" + else: + header_text = "Higher throughput under loss" + # Highlight badge on right side of Row 3 - canvas.badge(right=plot_right, y=72.0, text="33\u00d7 to 98\u00d7 higher throughput under loss", + canvas.badge(right=plot_right, y=72.0, text=header_text, bg_fill="#1f1610", border_color="#f97316", text_color="#fb923c", font_size=11, font_weight="bold", height=24.0) @@ -373,16 +429,18 @@ def val_to_y(val: float) -> float: canvas.circle(px, py, 4.5 if is_primary else 3.5, fill=color, stroke=COLOR_BG, stroke_width=1.5) - yip_data = goodput.get("yip", {}) - wg_data = goodput.get("wireguard", {}) - # Callouts if 3 in yip_data and 3 in wg_data: x3 = x_coords[2] y_yip_3 = val_to_y(yip_data[3]) canvas.text(x3 + 8.0, y_yip_3 - 6.0, f"{yip_data[3]:.1f}M", fill=COLOR_YIP, font_size=10, font_family=FONT_MONO, font_weight="bold") - canvas.badge(x=x3 - 90.0, y=y_yip_3 - 34.0, text="98\u00d7 advantage (117.6 vs 1.2 Mbit/s)", + badge_text_3 = ( + f"{mult_3}\u00d7 advantage ({yip_data[3]:.1f} vs {wg_data[3]:.1f} Mbit/s)" + if mult_3 is not None else + f"{yip_data[3]:.1f} vs {wg_data[3]:.1f} Mbit/s" + ) + canvas.badge(x=x3 - 90.0, y=y_yip_3 - 34.0, text=badge_text_3, bg_fill="#161b22", border_color=COLOR_BORDER, text_color="#f0f6fc", font_size=10, font_weight="bold", height=20.0) @@ -398,15 +456,21 @@ def val_to_y(val: float) -> float: bg_fill="#141c2b", border_color="#38bdf8", text_color="#38bdf8", font_size=10, font_weight="bold", height=20.0) - # 33x bracket annotation + # Bracket annotation canvas.line(x10 + 6.0, y_yip_10, x10 + 6.0, y_wg_10, stroke=COLOR_YIP, stroke_width=1.5) canvas.line(x10 + 2.0, y_yip_10, x10 + 6.0, y_yip_10, stroke=COLOR_YIP, stroke_width=1.5) canvas.line(x10 + 2.0, y_wg_10, x10 + 6.0, y_wg_10, stroke=COLOR_YIP, stroke_width=1.5) + + badge_text_10 = ( + f"{mult_10}\u00d7 higher ({yip_data[10]:.1f} vs {wg_data[10]:.1f})" + if mult_10 is not None else + f"{yip_data[10]:.1f} vs {wg_data[10]:.1f}" + ) canvas.badge(right=x10 - 24.0, y=(y_yip_10 + y_wg_10) / 2.0 - 10.0, - text="33\u00d7 higher (16.6 vs 0.5)", + text=badge_text_10, bg_fill="#1f1610", border_color="#f97316", text_color="#fb923c", font_size=10, font_weight="bold", height=20.0) @@ -440,7 +504,7 @@ def generate_fec_recovery_chart(data: dict) -> str: # Legend at Row 3 (y = 84) legend_items = [ - ("yip (0% flatline)", COLOR_YIP, False), + ("yip (RS-FEC)", COLOR_YIP, False), ("WireGuard", COLOR_WIREGUARD, False), ("Baseline", COLOR_BASELINE, True), ("OpenVPN", COLOR_OPENVPN, False), @@ -542,9 +606,15 @@ def val_to_y(val: float) -> float: font_size=10, height=20.0) if "yip" in effective and 10 in effective["yip"]: - y_yip = val_to_y(effective["yip"][10]) + yip_loss_10 = effective["yip"][10] + y_yip = val_to_y(yip_loss_10) + if yip_loss_10 == 0: + callout_text = "0% loss (100% recovered)" + else: + rec_pct = round((10.0 - yip_loss_10) / 10.0 * 100.0) + callout_text = f"{int(yip_loss_10)}% loss ({rec_pct}% recovered)" canvas.badge(right=x10 - 14.0, y=y_yip - 38.0, - text="0% loss (100% recovered)", + text=callout_text, bg_fill="#1f1610", border_color="#f97316", text_color="#fb923c", font_size=10, height=20.0) @@ -682,10 +752,14 @@ def val_to_y(val: float) -> float: font_weight="bold", text_anchor="middle") # Annotations for 8-core AF_XDP (positioned safely inside plot box below plot_top) - x8 = x_coords[3] - y_af_8 = val_to_y(data["af_xdp_gbps"][3]) + x8 = x_coords[-1] + y_af_8 = val_to_y(data["af_xdp_gbps"][-1]) + top_gbps = data["af_xdp_gbps"][-1] + top_mpps = data["af_xdp_mpps"][-1] + base_gbps = data["af_xdp_gbps"][0] + speedup = top_gbps / base_gbps if base_gbps > 0 else 1.0 canvas.badge(right=plot_right - 14.0, y=120.0, - text="40.27 Gbps @ 3.93 Mpps (4.6\u00d7 speedup)", + text=f"{top_gbps:.2f} Gbps @ {top_mpps:.2f} Mpps ({speedup:.1f}\u00d7 speedup)", bg_fill="#161b22", border_color=COLOR_BORDER, text_color="#fb923c", font_size=10, font_weight="bold", height=22.0) @@ -696,7 +770,7 @@ def val_to_y(val: float) -> float: return canvas.render() -def generate_bench_latency_jitter(data: dict | None = None) -> str: +def generate_bench_latency_jitter(data: dict) -> str: """ Generates assets/bench-latency-jitter.svg Linear scale for Round-Trip P99 Tail Latency (0 to 240 ms) across packet loss (0% to 10%). @@ -779,16 +853,11 @@ def val_to_y(val: float) -> float: font_size=11, font_weight="600", text_anchor="middle", extra_attrs=f'transform="rotate(-90 26.00 {plot_top + plot_height / 2.0:.2f})"') - # Data series points - default_yip = {0: 61.5, 1: 61.6, 2: 61.8, 3: 62.1, 5: 62.5, 8: 63.2, 10: 63.8} - default_wg = {0: 55.0, 1: 68.2, 2: 88.5, 3: 112.4, 5: 178.2, 8: 214.0, 10: 235.6} - default_ovpn = {0: 60.5, 1: 78.4, 2: 105.1, 3: 138.7, 5: 240.0} - default_base = {0: 60.6, 1: 84.1} - - yip_vals = data.get("yip", default_yip) if data else default_yip - wg_vals = data.get("wireguard", default_wg) if data else default_wg - ovpn_vals = data.get("openvpn", default_ovpn) if data else default_ovpn - base_vals = data.get("baseline", default_base) if data else default_base + # Data series points from loaded dataset + yip_vals = data.get("yip", {}) + wg_vals = data.get("wireguard", {}) + ovpn_vals = data.get("openvpn", {}) + base_vals = data.get("baseline", {}) # Shaded jitter band (+/- 1.2 ms) around yip (RS-FEC) upper_jitter = [(x_coords[i], val_to_y(yip_vals[pct] + 1.2)) for i, pct in enumerate(loss_levels) if pct in yip_vals] @@ -841,10 +910,12 @@ def val_to_y(val: float) -> float: bg_fill="#1f1610", border_color="#f97316", text_color="#fb923c", font_size=10, font_weight="bold", height=20.0) - # Callout box: Head-of-Line Blocking Suppressed + # Callout box: Head-of-Line Blocking Suppressed (dynamically formatted from data) + yip_p99_min = min(yip_vals.values()) if yip_vals else 62.0 + wg_p99_max = max(wg_vals.values()) if wg_vals else 235.0 callout_text_full = ( - "Head-of-Line Blocking Suppressed: Systematic userspace RS-FEC recovers " - "dropped symbols in-flight, keeping P99 flat at ~62ms vs ARQ retransmission spikes to 235ms+" + f"Head-of-Line Blocking Suppressed: Systematic userspace RS-FEC recovers " + f"dropped symbols in-flight, keeping P99 flat at ~{yip_p99_min:.0f}ms vs ARQ retransmission spikes to {wg_p99_max:.0f}ms+" ) box_x = plot_left + 16.0 box_y = plot_top + 16.0 @@ -858,7 +929,7 @@ def val_to_y(val: float) -> float: fill=COLOR_YIP, font_size=11, font_weight="bold") canvas.text(box_x + 12.0, box_y + 36.0, "Systematic userspace RS-FEC recovers dropped symbols in-flight,", fill=COLOR_TEXT_MAIN, font_size=9.5) - canvas.text(box_x + 12.0, box_y + 51.0, "keeping P99 flat at ~62ms vs ARQ retransmission spikes to 235ms+.", + canvas.text(box_x + 12.0, box_y + 51.0, f"keeping P99 flat at ~{yip_p99_min:.0f}ms vs ARQ retransmission spikes to {wg_p99_max:.0f}ms+.", fill=COLOR_TEXT_MUTED, font_size=9.5) # Bottom note footer @@ -883,6 +954,7 @@ def main(): wan_history_path = repo_root / "crates" / "yip-bench" / "wan" / "results" / "history.csv" sharding_scale_path = repo_root / "crates" / "yip-bench" / "results" / "sharding-scale.csv" + latency_jitter_path = repo_root / "crates" / "yip-bench" / "results" / "latency-jitter.csv" assets_dir = repo_root / "assets" if not wan_history_path.exists(): @@ -893,14 +965,19 @@ def main(): print(f"Error: Sharding scale CSV missing: {sharding_scale_path}", file=sys.stderr) sys.exit(1) + if not latency_jitter_path.exists(): + print(f"Error: Latency jitter CSV missing: {latency_jitter_path}", file=sys.stderr) + sys.exit(1) + wan_data = parse_wan_history(wan_history_path) sharding_data = parse_sharding_scale(sharding_scale_path) + latency_data = parse_latency_jitter(latency_jitter_path) targets = { assets_dir / "bench-throughput-loss.svg": generate_throughput_loss_chart(wan_data), assets_dir / "bench-fec-recovery.svg": generate_fec_recovery_chart(wan_data), assets_dir / "bench-multicore-scaling.svg": generate_multicore_scaling_chart(sharding_data), - assets_dir / "bench-latency-jitter.svg": generate_bench_latency_jitter(), + assets_dir / "bench-latency-jitter.svg": generate_bench_latency_jitter(latency_data), } # Validate generated XML syntax diff --git a/crates/yip-bench/results/latency-jitter.csv b/crates/yip-bench/results/latency-jitter.csv new file mode 100644 index 00000000..c9bd7a8b --- /dev/null +++ b/crates/yip-bench/results/latency-jitter.csv @@ -0,0 +1,8 @@ +loss_pct,yip_ms,wireguard_ms,openvpn_ms,baseline_ms +0,61.5,55.0,60.5,60.6 +1,61.6,68.2,78.4,84.1 +2,61.8,88.5,105.1, +3,62.1,112.4,138.7, +5,62.5,178.2,240.0, +8,63.2,214.0,, +10,63.8,235.6,, diff --git a/crates/yip-bench/setup-contenders.sh b/crates/yip-bench/setup-contenders.sh new file mode 100755 index 00000000..97f58509 --- /dev/null +++ b/crates/yip-bench/setup-contenders.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# setup-contenders.sh — Turnkey helper to install or build optional benchmark contenders: +# AmneziaWG, ZeroTier, Tailscale (with Headscale coordinator), and NetBird. +# +# Usage: +# sudo ./setup-contenders.sh --all +# sudo ./setup-contenders.sh --amneziawg +# sudo ./setup-contenders.sh --zerotier +# sudo ./setup-contenders.sh --tailscale +# sudo ./setup-contenders.sh --netbird +# ./setup-contenders.sh --check +set -euo pipefail + +TARGET_BIN_DIR="/usr/local/bin" + +# Discover Go toolchain if not in PATH (e.g. running under bare sudo or CI) +if ! command -v go >/dev/null 2>&1; then + for candidate in /usr/local/go/bin/go /usr/lib/go/bin/go /home/runner/go/bin/go /opt/hostedtoolcache/go/*/x64/bin/go; do + if [ -x "$candidate" ]; then + export PATH="$(dirname "$candidate"):$PATH" + break + fi + done +fi + +log() { echo -e "\033[1;34m[setup-contenders]\033[0m $*"; } +warn() { echo -e "\033[1;33m[setup-contenders:WARN]\033[0m $*"; } +err() { echo -e "\033[1;31m[setup-contenders:ERROR]\033[0m $*" >&2; exit 1; } + +check_status() { + log "Checking contender tool availability:" + printf " %-12s : " "WireGuard" + command -v wg >/dev/null 2>&1 && echo "INSTALLED ($(which wg))" || echo "MISSING" + printf " %-12s : " "OpenVPN" + command -v openvpn >/dev/null 2>&1 && echo "INSTALLED ($(which openvpn))" || echo "MISSING" + printf " %-12s : " "n2n" + (command -v edge >/dev/null 2>&1 && command -v supernode >/dev/null 2>&1) && echo "INSTALLED ($(which edge))" || echo "MISSING" + printf " %-12s : " "AmneziaWG" + (command -v awg >/dev/null 2>&1 && (command -v amneziawg-go >/dev/null 2>&1 || modprobe amneziawg 2>/dev/null)) && echo "INSTALLED ($(which awg))" || echo "MISSING" + printf " %-12s : " "ZeroTier" + command -v zerotier-one >/dev/null 2>&1 && echo "INSTALLED ($(which zerotier-one))" || echo "MISSING" + printf " %-12s : " "Tailscale" + (command -v tailscaled >/dev/null 2>&1 && command -v headscale >/dev/null 2>&1) && echo "INSTALLED" || echo "MISSING" + printf " %-12s : " "NetBird" + command -v netbird >/dev/null 2>&1 && echo "INSTALLED ($(which netbird))" || echo "MISSING" +} + +setup_amneziawg() { + log "Building AmneziaWG (amneziawg-go & awg-tools)..." + if ! command -v go >/dev/null 2>&1; then err "Go toolchain required to build AmneziaWG"; fi + local tmp; tmp="$(mktemp -d /tmp/build-awg.XXXXXX)" + pushd "$tmp" >/dev/null + log "Compiling amneziawg-go..." + GOBIN="$TARGET_BIN_DIR" go install github.com/amnezia-vpn/amneziawg-go@latest || warn "amneziawg-go compile failed" + log "Cloning and building awg CLI tool..." + if command -v git >/dev/null 2>&1 && command -v make >/dev/null 2>&1; then + git clone --depth 1 https://github.com/amnezia-vpn/amneziawg-tools.git awg-tools 2>/dev/null || true + if [ -d awg-tools/src ]; then + make -C awg-tools/src + install -m 755 awg-tools/src/wg "$TARGET_BIN_DIR/awg" + fi + else + warn "git/make missing; cannot build awg CLI tool from source" + fi + popd >/dev/null + rm -rf "$tmp" + log "AmneziaWG setup step complete." +} + +setup_zerotier() { + log "Installing ZeroTier..." + if command -v zerotier-one >/dev/null 2>&1; then + log "ZeroTier is already installed ($(which zerotier-one))" + return + fi + if command -v apt-get >/dev/null 2>&1; then + curl -s 'https://raw.githubusercontent.com/zerotier/ZeroTierOne/master/doc/contact%40zerotier.com.gpg' | gpg --dearmor -o /usr/share/keyrings/zerotierone-archive-keyring.gpg 2>/dev/null || true + apt-get update && apt-get install -y zerotier-one || warn "apt-get install zerotier-one failed" + elif command -v curl >/dev/null 2>&1; then + curl -s https://install.zerotier.com | bash || warn "ZeroTier quick install script failed" + else + warn "Package manager or curl not found; please install zerotier-one manually." + fi +} + +setup_tailscale() { + log "Setting up Tailscale & Headscale..." + if ! command -v go >/dev/null 2>&1; then err "Go toolchain required to build Tailscale & Headscale"; fi + log "Compiling tailscale & tailscaled..." + GOBIN="$TARGET_BIN_DIR" go install tailscale.com/cmd/tailscale@latest || warn "tailscale build failed" + GOBIN="$TARGET_BIN_DIR" go install tailscale.com/cmd/tailscaled@latest || warn "tailscaled build failed" + log "Compiling headscale (local air-gapped coordinator)..." + GOBIN="$TARGET_BIN_DIR" go install github.com/juanfont/headscale/cmd/headscale@latest || warn "headscale build failed" + log "Tailscale / Headscale setup step complete." +} + +setup_netbird() { + log "Setting up NetBird..." + if ! command -v go >/dev/null 2>&1; then err "Go toolchain required to build NetBird"; fi + log "Compiling netbird..." + GOBIN="$TARGET_BIN_DIR" go install github.com/netbirdio/netbird@latest || warn "netbird build failed" + log "NetBird setup step complete." +} + +# ── Entrypoint ──────────────────────────────────────────────────────────────── +if [ $# -eq 0 ] || [ "${1:-}" = "--check" ]; then + check_status + exit 0 +fi + +while [ $# -gt 0 ]; do + case "$1" in + --all) + setup_amneziawg + setup_zerotier + setup_tailscale + setup_netbird + ;; + --amneziawg) + setup_amneziawg + ;; + --zerotier) + setup_zerotier + ;; + --tailscale) + setup_tailscale + ;; + --netbird) + setup_netbird + ;; + --check) + check_status + ;; + *) + echo "Unknown argument: $1" + echo "Usage: $0 [--all | --amneziawg | --zerotier | --tailscale | --netbird | --check]" + exit 1 + ;; + esac + shift +done + +log "Selected contenders processed. Re-checking status:" +check_status diff --git a/crates/yip-bench/tests/run-compare.sh b/crates/yip-bench/tests/run-compare.sh index ae91f2e3..42ea0369 100755 --- a/crates/yip-bench/tests/run-compare.sh +++ b/crates/yip-bench/tests/run-compare.sh @@ -298,10 +298,19 @@ setup_wg # PART 3: sweep and emit combined table # ══════════════════════════════════════════════════════════════════════════════ +PING_COUNT="${PING_COUNT:-100}" +if [ -n "${PING_INTERVAL:-}" ]; then + PING_INTERVAL="$PING_INTERVAL" +elif [ "$PING_COUNT" -ge 1000 ] 2>/dev/null; then + PING_INTERVAL="0.005" +else + PING_INTERVAL="0.05" +fi + LOSS_RATES="0 1 3 5 10" HEADER="yip vs kernel WireGuard — netem loss sweep" -SUBHEADER="ping -c 100 -i 0.05 -W 1 across each tunnel; netem: loss X% delay 5ms (symmetric)" +SUBHEADER="ping -c ${PING_COUNT} -i ${PING_INTERVAL} -W 1 across each tunnel; netem: loss X% delay 5ms (symmetric)" if [ "$WG_AVAILABLE" = true ]; then COL_HDR="| injected% | yip_loss% | wg_loss% | yip_rtt_ms | wg_rtt_ms |" @@ -342,10 +351,11 @@ for LOSS in $LOSS_RATES; do fi # ── ping yip tunnel ─────────────────────────────────────────────────────── - YIP_PING="$(ip netns exec "$NS_B" ping -c 100 -i 0.05 -W 1 "$TUN_A_IP" 2>&1)" || true + YIP_PING="$(ip netns exec "$NS_B" ping -c "$PING_COUNT" -i "$PING_INTERVAL" -W 1 "$TUN_A_IP" 2>&1)" || true YIP_LOSS="$(echo "$YIP_PING" \ - | grep -oP '\d+% packet loss' | grep -oP '^\d+' || echo "100")" + | grep -oP '[0-9.]+% packet loss' | grep -oP '^[0-9.]+' || true)" + [ -z "$YIP_LOSS" ] && YIP_LOSS="100" YIP_RTT_LINE="$(echo "$YIP_PING" | grep -oP 'rtt min/avg/max/mdev = [0-9./]+ ms' || echo "")" if [ -n "$YIP_RTT_LINE" ]; then YIP_RTT_AVG="$(echo "$YIP_RTT_LINE" | cut -d= -f2 | tr '/' ' ' | awk '{print $2}')" @@ -355,10 +365,11 @@ for LOSS in $LOSS_RATES; do # ── ping WireGuard tunnel ───────────────────────────────────────────────── if [ "$WG_AVAILABLE" = true ]; then - WG_PING="$(ip netns exec "$NS_WGB" ping -c 100 -i 0.05 -W 1 "$WG_A_TUN_IP" 2>&1)" || true + WG_PING="$(ip netns exec "$NS_WGB" ping -c "$PING_COUNT" -i "$PING_INTERVAL" -W 1 "$WG_A_TUN_IP" 2>&1)" || true WG_LOSS="$(echo "$WG_PING" \ - | grep -oP '\d+% packet loss' | grep -oP '^\d+' || echo "100")" + | grep -oP '[0-9.]+% packet loss' | grep -oP '^[0-9.]+' || true)" + [ -z "$WG_LOSS" ] && WG_LOSS="100" WG_RTT_LINE="$(echo "$WG_PING" | grep -oP 'rtt min/avg/max/mdev = [0-9./]+ ms' || echo "")" if [ -n "$WG_RTT_LINE" ]; then WG_RTT_AVG="$(echo "$WG_RTT_LINE" | cut -d= -f2 | tr '/' ' ' | awk '{print $2}')" diff --git a/crates/yip-bench/tests/run-iperf-compare.sh b/crates/yip-bench/tests/run-iperf-compare.sh index 5f8bd06f..f1a5b560 100755 --- a/crates/yip-bench/tests/run-iperf-compare.sh +++ b/crates/yip-bench/tests/run-iperf-compare.sh @@ -1,17 +1,17 @@ #!/usr/bin/env bash -# run-iperf-compare.sh — iperf3 TCP throughput + ping latency/loss under netem, -# across the full-IP tunnels: yip, WireGuard, OpenVPN, n2n. +# run-iperf-compare.sh — iperf3 TCP & UDP throughput + ping latency/loss under netem, +# across contemporary tunnels and mesh overlays: +# yip, WireGuard, AmneziaWG, OpenVPN, n2n, ZeroTier, Tailscale, NetBird. # -# Each contender is set up in its OWN netns pair (mirroring the verified spikes); -# at each loss rate (0 5 10 %, symmetric delay 5ms) we run: -# ping -c 50 -i 0.1 -> effective loss % + avg RTT (ms) -# iperf3 -c -t 8 -> TCP throughput (Mbit/s) +# Each contender is set up in its OWN dedicated netns pair; at each loss rate +# (0 5 10 %, symmetric delay 5ms) we run: +# ping -c 50 -i 0.1 -> effective loss % + avg RTT (ms) +# iperf3 -c -t 8 -> TCP goodput (Mbit/s) +# iperf3 -u -c -b -t 8 -> UDP throughput (Mbit/s) + UDP loss % # # yip MUST always run; every other contender SKIPs cleanly (logging why) if its # tool/module is absent. # -# n2n note: one TAP data plane serves both L2 and L3 — measured once. -# # Usage: run-iperf-compare.sh [] set -euo pipefail @@ -35,6 +35,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" TMPDIR_TEST="$(mktemp -d /tmp/yip-iperf-compare.XXXXXX)" +chmod 755 "$TMPDIR_TEST" # ── yip ─────────────────────────────────────────────────────────────────────── NS_YA="ipfYA"; NS_YB="ipfYB"; VYA="ipfvYA"; VYB="ipfvYB" @@ -63,6 +64,31 @@ OVL_NA_IP="10.17.0.1"; OVL_NB_IP="10.17.0.2" N2N_PORT="7654" N2N_AVAILABLE=true +# ── AmneziaWG ───────────────────────────────────────────────────────────────── +NS_AA="ipfAA"; NS_AB="ipfAB"; VAA="ipfvAA"; VAB="ipfvAB" +VAA_IP="10.18.0.1"; VAB_IP="10.18.0.2" +TUN_AA_IP="10.19.0.1"; TUN_AB_IP="10.19.0.2" +APORT_A="51870"; APORT_B="51871" +AWG_AVAILABLE=true + +# ── ZeroTier ────────────────────────────────────────────────────────────────── +NS_ZA="ipfZA"; NS_ZB="ipfZB"; VZA="ipfvZA"; VZB="ipfvZB" +VZA_IP="10.20.0.1"; VZB_IP="10.20.0.2" +TUN_ZA_IP="10.21.0.1"; TUN_ZB_IP="10.21.0.2" +ZT_AVAILABLE=true + +# ── Tailscale ───────────────────────────────────────────────────────────────── +NS_TA="ipfTA"; NS_TB="ipfTB"; VTA="ipfvTA"; VTB="ipfvTB" +VTA_IP="10.22.0.1"; VTB_IP="10.22.0.2" +TUN_TA_IP="10.23.0.1"; TUN_TB_IP="10.23.0.2" +TS_AVAILABLE=true + +# ── NetBird ─────────────────────────────────────────────────────────────────── +NS_BA="ipfBA"; NS_BB="ipfBB"; VBA="ipfvBA"; VBB="ipfvBB" +VBA_IP="10.24.0.1"; VBB_IP="10.24.0.2" +TUN_BA_IP="10.25.0.1"; TUN_BB_IP="10.25.0.2" +NB_AVAILABLE=true + # ── cleanup ─────────────────────────────────────────────────────────────────── cleanup() { echo "[cleanup] killing daemons and removing namespaces" @@ -71,10 +97,13 @@ cleanup() { sleep 0.2 [ -n "$PID_YA" ] && kill -9 "$PID_YA" 2>/dev/null || true [ -n "$PID_YB" ] && kill -9 "$PID_YB" 2>/dev/null || true - # Remove WG interfaces before deleting namespaces. + # Remove virtual interfaces before deleting namespaces ip netns exec "$NS_WA" ip link del wg0 2>/dev/null || true ip netns exec "$NS_WB" ip link del wg0 2>/dev/null || true - for ns in "$NS_YA" "$NS_YB" "$NS_WA" "$NS_WB" "$NS_OA" "$NS_OB" "$NS_NA" "$NS_NB"; do + ip netns exec "$NS_AA" ip link del awg0 2>/dev/null || true + ip netns exec "$NS_AB" ip link del awg0 2>/dev/null || true + for ns in "$NS_YA" "$NS_YB" "$NS_WA" "$NS_WB" "$NS_OA" "$NS_OB" "$NS_NA" "$NS_NB" \ + "$NS_AA" "$NS_AB" "$NS_ZA" "$NS_ZB" "$NS_TA" "$NS_TB" "$NS_BA" "$NS_BB"; do ip netns pids "$ns" 2>/dev/null | xargs -r kill -9 2>/dev/null || true ip netns del "$ns" 2>/dev/null || true done @@ -94,17 +123,33 @@ make_pair() { ip netns exec "$nsS" ip link set "$vS" up; ip netns exec "$nsS" ip link set lo up } +# ── helper: symmetric netem loss + 5ms delay ────────────────────────────────── apply_netem() { local nsR="$1" nsS="$2" vR="$3" vS="$4" loss="$5" + if [ "$loss" -eq 0 ]; then + ip netns exec "$nsR" tc qdisc del dev "$vR" root 2>/dev/null || true + ip netns exec "$nsS" tc qdisc del dev "$vS" root 2>/dev/null || true + return + fi ip netns exec "$nsR" tc qdisc replace dev "$vR" root netem loss "${loss}%" delay 5ms ip netns exec "$nsS" tc qdisc replace dev "$vS" root netem loss "${loss}%" delay 5ms } # ── helper: ping a tunnel IP from a netns; echo " " ───────── +PING_COUNT="${PING_COUNT:-50}" +if [ -n "${PING_INTERVAL:-}" ]; then + PING_INTERVAL="$PING_INTERVAL" +elif [ "$PING_COUNT" -ge 1000 ] 2>/dev/null; then + PING_INTERVAL="0.005" +else + PING_INTERVAL="0.1" +fi + measure_ping() { local ns="$1" dst="$2" out loss rttline rtt - out="$(ip netns exec "$ns" ping -c 50 -i 0.1 -W 1 "$dst" 2>&1)" || true - loss="$(echo "$out" | grep -oP '\d+(\.\d+)?% packet loss' | grep -oP '^[0-9.]+' || echo 100)" + out="$(ip netns exec "$ns" ping -c "$PING_COUNT" -i "$PING_INTERVAL" -W 1 "$dst" 2>&1)" || true + loss="$(echo "$out" | sed -nE 's/.* ([0-9.]+)% packet loss.*/\1/p' | head -1)" + [ -z "$loss" ] && loss="100" rttline="$(echo "$out" | grep -oP 'rtt min/avg/max/mdev = [0-9./]+ ms' || echo "")" if [ -n "$rttline" ]; then rtt="$(echo "$rttline" | cut -d= -f2 | tr '/' ' ' | awk '{print $2}')" @@ -122,7 +167,6 @@ measure_iperf() { sleep 0.5 mbps="$(ip netns exec "$ns_cli" iperf3 -c "$srv_ip" -t 8 2>/dev/null \ | grep -i receiver | grep -oP '[0-9.]+ (Mbits|Gbits)/sec' | tail -1 || echo "")" - # Normalize Gbits -> Mbits. if echo "$mbps" | grep -qi 'Gbits'; then mbps="$(echo "$mbps" | grep -oP '[0-9.]+' | awk '{printf "%.1f", $1*1000}')" else @@ -132,6 +176,38 @@ measure_iperf() { echo "$mbps" } +# ── helper: iperf3 UDP Mbit/s and loss %; server in bound to , +# client in -> . echo " " ─────────────────── +measure_iperf_udp() { + local ns_srv="$1" srv_ip="$2" ns_cli="$3" out mbps loss + ip netns exec "$ns_srv" iperf3 -s -1 -B "$srv_ip" >/dev/null 2>&1 & + sleep 0.5 + out="$(ip netns exec "$ns_cli" iperf3 -c "$srv_ip" -u -b "${UDP_RATE:-50M}" -t 8 -J 2>/dev/null || true)" + if [ -n "$out" ]; then + if command -v jq >/dev/null 2>&1; then + mbps="$(echo "$out" | jq -r '.end.sum.bits_per_second // empty' 2>/dev/null || true)" + loss="$(echo "$out" | jq -r '.end.sum.lost_percent // empty' 2>/dev/null || true)" + else + mbps="$(echo "$out" | grep -oP '"bits_per_second":\s*[0-9.]+' | tail -1 | grep -oP '[0-9.]+' || true)" + loss="$(echo "$out" | grep -oP '"lost_percent":\s*[0-9.]+' | tail -1 | grep -oP '[0-9.]+' || true)" + fi + if [ -n "$mbps" ] && [ "$mbps" != "null" ]; then + mbps="$(awk "BEGIN {printf \"%.1f\", $mbps / 1000000}")" + else + mbps="N/A" + fi + if [ -n "$loss" ] && [ "$loss" != "null" ]; then + loss="$(awk "BEGIN {printf \"%.1f\", $loss}")" + else + loss="N/A" + fi + else + mbps="N/A" + loss="N/A" + fi + echo "$mbps $loss" +} + # ══════════════════════════════════════════════════════════════════════════════ # yip setup (always) # ══════════════════════════════════════════════════════════════════════════════ @@ -208,65 +284,187 @@ setup_wg() { echo "[wg] SKIP: modprobe wireguard failed"; WG_AVAILABLE=false; return fi echo "[wg] generating keypairs" - local pa pb ka kb - pa="$(wg genkey)"; ka="$(echo "$pa" | wg pubkey)" - pb="$(wg genkey)"; kb="$(echo "$pb" | wg pubkey)" + local privA pubA privB pubB + privA="$(wg genkey 2>/dev/null || true)"; pubA="$(echo "$privA" | wg pubkey 2>/dev/null || true)" + privB="$(wg genkey 2>/dev/null || true)"; pubB="$(echo "$privB" | wg pubkey 2>/dev/null || true)" + if [ -z "$privA" ] || [ -z "$privB" ]; then + echo "[wg] SKIP: key generation failed"; WG_AVAILABLE=false; return + fi echo "[wg] creating namespaces and veth pair" make_pair "$NS_WA" "$NS_WB" "$VWA" "$VWB" "$VWA_IP" "$VWB_IP" - ip netns exec "$NS_WA" ip link add wg0 type wireguard - ip netns exec "$NS_WB" ip link add wg0 type wireguard - printf '%s' "$pa" > "$TMPDIR_TEST/wg_a"; printf '%s' "$pb" > "$TMPDIR_TEST/wg_b" + if ! ip netns exec "$NS_WA" ip link add wg0 type wireguard 2>/dev/null || \ + ! ip netns exec "$NS_WB" ip link add wg0 type wireguard 2>/dev/null; then + echo "[wg] SKIP: wireguard interface creation failed"; WG_AVAILABLE=false; return + fi + printf '%s' "$privA" > "$TMPDIR_TEST/wg_a" + printf '%s' "$privB" > "$TMPDIR_TEST/wg_b" chmod 600 "$TMPDIR_TEST/wg_a" "$TMPDIR_TEST/wg_b" - ip netns exec "$NS_WA" wg set wg0 private-key "$TMPDIR_TEST/wg_a" listen-port "$WPORT_A" \ - peer "$kb" allowed-ips "${TUN_WB_IP}/32" endpoint "${VWB_IP}:${WPORT_B}" - ip netns exec "$NS_WB" wg set wg0 private-key "$TMPDIR_TEST/wg_b" listen-port "$WPORT_B" \ - peer "$ka" allowed-ips "${TUN_WA_IP}/32" endpoint "${VWA_IP}:${WPORT_A}" - ip netns exec "$NS_WA" ip addr add "${TUN_WA_IP}/24" dev wg0 - ip netns exec "$NS_WA" ip link set wg0 up - ip netns exec "$NS_WB" ip addr add "${TUN_WB_IP}/24" dev wg0 - ip netns exec "$NS_WB" ip link set wg0 up + if ! ip netns exec "$NS_WA" wg set wg0 private-key "$TMPDIR_TEST/wg_a" listen-port "$WPORT_A" \ + peer "$pubB" endpoint "${VWB_IP}:${WPORT_B}" allowed-ips "${TUN_WB_IP}/32" 2>/dev/null || \ + ! ip netns exec "$NS_WB" wg set wg0 private-key "$TMPDIR_TEST/wg_b" listen-port "$WPORT_B" \ + peer "$pubA" endpoint "${VWA_IP}:${WPORT_A}" allowed-ips "${TUN_WA_IP}/32" 2>/dev/null; then + echo "[wg] SKIP: wg set configuration failed"; WG_AVAILABLE=false; return + fi + if ! ip netns exec "$NS_WA" ip addr add "${TUN_WA_IP}/24" dev wg0 2>/dev/null || \ + ! ip netns exec "$NS_WB" ip addr add "${TUN_WB_IP}/24" dev wg0 2>/dev/null || \ + ! ip netns exec "$NS_WA" ip link set wg0 up 2>/dev/null || \ + ! ip netns exec "$NS_WB" ip link set wg0 up 2>/dev/null; then + echo "[wg] SKIP: interface setup failed"; WG_AVAILABLE=false; return + fi sleep 0.5 - if ! ip netns exec "$NS_WB" ping -c 3 -W 5 "$TUN_WA_IP" >/dev/null; then - echo "[wg] SKIP: baseline WG ping failed"; WG_AVAILABLE=false; return + if ! ip netns exec "$NS_WB" ping -c 3 -W 5 "$TUN_WA_IP" >/dev/null 2>&1; then + echo "[wg] SKIP: baseline ping failed"; WG_AVAILABLE=false; return fi echo "[wg] tunnel up" } setup_wg # ══════════════════════════════════════════════════════════════════════════════ -# OpenVPN setup (static-key p2p TUN; AES-256-CBC since static key has no AEAD) +# AmneziaWG setup (kernel module or amneziawg-go) +# ══════════════════════════════════════════════════════════════════════════════ +setup_awg() { + if ! command -v awg >/dev/null 2>&1; then + echo "[awg] SKIP: 'awg' command not found"; AWG_AVAILABLE=false; return + fi + local use_go=false + if ! modprobe amneziawg 2>/dev/null; then + if command -v amneziawg-go >/dev/null 2>&1; then + use_go=true + else + echo "[awg] SKIP: neither amneziawg kernel module nor amneziawg-go found" + AWG_AVAILABLE=false; return + fi + fi + echo "[awg] generating keypairs" + local privA pubA privB pubB + privA="$(awg genkey 2>/dev/null || true)"; pubA="$(echo "$privA" | awg pubkey 2>/dev/null || true)" + privB="$(awg genkey 2>/dev/null || true)"; pubB="$(echo "$privB" | awg pubkey 2>/dev/null || true)" + if [ -z "$privA" ] || [ -z "$privB" ]; then + echo "[awg] SKIP: key generation failed"; AWG_AVAILABLE=false; return + fi + echo "[awg] creating namespaces and veth pair" + make_pair "$NS_AA" "$NS_AB" "$VAA" "$VAB" "$VAA_IP" "$VAB_IP" + if [ "$use_go" = true ]; then + ip netns exec "$NS_AA" amneziawg-go awg0 >"$TMPDIR_TEST/awg-go-a.log" 2>&1 & + ip netns exec "$NS_AB" amneziawg-go awg0 >"$TMPDIR_TEST/awg-go-b.log" 2>&1 & + sleep 0.5 + else + if ! ip netns exec "$NS_AA" ip link add awg0 type amneziawg 2>/dev/null || \ + ! ip netns exec "$NS_AB" ip link add awg0 type amneziawg 2>/dev/null; then + echo "[awg] SKIP: amneziawg link add failed"; AWG_AVAILABLE=false; return + fi + fi + + local awg_up=false + for i in $(seq 1 20); do + if ip netns exec "$NS_AA" ip link show awg0 >/dev/null 2>&1 && \ + ip netns exec "$NS_AB" ip link show awg0 >/dev/null 2>&1; then + awg_up=true + break + fi + sleep 0.25 + done + if [ "$awg_up" = false ]; then + echo "[awg] SKIP: awg0 interface creation failed or timed out" + tail -5 "$TMPDIR_TEST/awg-go-a.log" "$TMPDIR_TEST/awg-go-b.log" 2>/dev/null || true + AWG_AVAILABLE=false; return + fi + + if ! ip netns exec "$NS_AA" ip addr add "${TUN_AA_IP}/24" dev awg0 2>/dev/null || \ + ! ip netns exec "$NS_AB" ip addr add "${TUN_AB_IP}/24" dev awg0 2>/dev/null; then + echo "[awg] SKIP: ip addr add failed"; AWG_AVAILABLE=false; return + fi + + local CFG_AA="$TMPDIR_TEST/awgA.conf"; local CFG_AB="$TMPDIR_TEST/awgB.conf" + cat > "$CFG_AA" < "$CFG_AB" </dev/null || \ + ! ip netns exec "$NS_AB" awg setconf awg0 "$CFG_AB" 2>/dev/null; then + echo "[awg] SKIP: awg setconf failed"; AWG_AVAILABLE=false; return + fi + if ! ip netns exec "$NS_AA" ip link set awg0 up 2>/dev/null || \ + ! ip netns exec "$NS_AB" ip link set awg0 up 2>/dev/null; then + echo "[awg] SKIP: ip link set up failed"; AWG_AVAILABLE=false; return + fi + sleep 0.5 + if ! ip netns exec "$NS_AB" ping -c 3 -W 5 "$TUN_AA_IP" >/dev/null 2>&1; then + echo "[awg] SKIP: baseline ping failed"; AWG_AVAILABLE=false; return + fi + echo "[awg] tunnel up" +} +setup_awg + +# ══════════════════════════════════════════════════════════════════════════════ +# OpenVPN setup (peer-fingerprint, UDP, AES-256-GCM) # ══════════════════════════════════════════════════════════════════════════════ setup_ovpn() { if ! command -v openvpn >/dev/null 2>&1; then echo "[ovpn] SKIP: 'openvpn' not found"; OVPN_AVAILABLE=false; return fi - if ! command -v openssl >/dev/null 2>&1; then - echo "[ovpn] SKIP: 'openssl' not found (needed for TLS certs)"; OVPN_AVAILABLE=false; return - fi + echo "[ovpn] generating self-signed certificates" + openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ + -keyout "$TMPDIR_TEST/ovpn-a.key" -out "$TMPDIR_TEST/ovpn-a.crt" \ + -days 1 -nodes -subj "/CN=ovpn-a" >/dev/null 2>&1 + openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ + -keyout "$TMPDIR_TEST/ovpn-b.key" -out "$TMPDIR_TEST/ovpn-b.crt" \ + -days 1 -nodes -subj "/CN=ovpn-b" >/dev/null 2>&1 + chmod 600 "$TMPDIR_TEST/ovpn-a.key" "$TMPDIR_TEST/ovpn-b.key" + local FA FB + FA="$(openssl x509 -in "$TMPDIR_TEST/ovpn-a.crt" -noout -fingerprint -sha256 \ + | cut -d= -f2)" + FB="$(openssl x509 -in "$TMPDIR_TEST/ovpn-b.crt" -noout -fingerprint -sha256 \ + | cut -d= -f2)" + echo "[ovpn] creating namespaces and veth pair" make_pair "$NS_OA" "$NS_OB" "$VOA" "$VOB" "$VOA_IP" "$VOB_IP" - # Modern fair config: TLS + AES-256-GCM (AEAD) via peer-fingerprint mode (no - # PKI server needed). Static-key/CBC would understate OpenVPN — its DCO+GCM - # data channel is its real deployed performance. - echo "[ovpn] generating self-signed EC certs (peer-fingerprint mode)" - if ! openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ - -keyout "$TMPDIR_TEST/ovpn-a.key" -out "$TMPDIR_TEST/ovpn-a.crt" \ - -days 2 -nodes -subj /CN=ovpn-a >/dev/null 2>&1 \ - || ! openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ - -keyout "$TMPDIR_TEST/ovpn-b.key" -out "$TMPDIR_TEST/ovpn-b.crt" \ - -days 2 -nodes -subj /CN=ovpn-b >/dev/null 2>&1; then - echo "[ovpn] SKIP: openssl cert generation failed"; OVPN_AVAILABLE=false; return - fi - local FA FB - FA=$(openssl x509 -in "$TMPDIR_TEST/ovpn-a.crt" -noout -fingerprint -sha256 | sed 's/.*=//') - FB=$(openssl x509 -in "$TMPDIR_TEST/ovpn-b.crt" -noout -fingerprint -sha256 | sed 's/.*=//') - ip netns exec "$NS_OA" openvpn --dev tun --dev-type tun --local "$VOA_IP" --lport 1194 \ - --remote "$VOB_IP" --rport 1194 --ifconfig "$TUN_OA_IP" "$TUN_OB_IP" \ - --tls-server --dh none --cert "$TMPDIR_TEST/ovpn-a.crt" --key "$TMPDIR_TEST/ovpn-a.key" \ + ip netns exec "$NS_OA" openvpn \ + --dev tun0 --dev-type tun \ + --local "$VOA_IP" --port 1194 --ifconfig "$TUN_OA_IP" "$TUN_OB_IP" \ + --tls-server --cert "$TMPDIR_TEST/ovpn-a.crt" --key "$TMPDIR_TEST/ovpn-a.key" \ --peer-fingerprint "$FB" --data-ciphers AES-256-GCM \ --proto udp --verb 1 --ping 10 --ping-restart 0 \ >"$TMPDIR_TEST/ovpn-a.log" 2>&1 & - ip netns exec "$NS_OB" openvpn --dev tun --dev-type tun --local "$VOB_IP" --lport 1194 \ + ip netns exec "$NS_OB" openvpn \ + --dev tun0 --dev-type tun \ --remote "$VOA_IP" --rport 1194 --ifconfig "$TUN_OB_IP" "$TUN_OA_IP" \ --tls-client --cert "$TMPDIR_TEST/ovpn-b.crt" --key "$TMPDIR_TEST/ovpn-b.key" \ --peer-fingerprint "$FA" --data-ciphers AES-256-GCM \ @@ -319,6 +517,124 @@ setup_n2n() { } setup_n2n +# ══════════════════════════════════════════════════════════════════════════════ +# ZeroTier setup (local netns controller) +# ══════════════════════════════════════════════════════════════════════════════ +setup_zerotier() { + if ! command -v zerotier-one >/dev/null 2>&1; then + echo "[zerotier] SKIP: 'zerotier-one' not found"; ZT_AVAILABLE=false; return + fi + echo "[zerotier] creating namespaces and veth pair" + make_pair "$NS_ZA" "$NS_ZB" "$VZA" "$VZB" "$VZA_IP" "$VZB_IP" + local zt_dir_a="$TMPDIR_TEST/zt_a"; local zt_dir_b="$TMPDIR_TEST/zt_b" + mkdir -p "$zt_dir_a" "$zt_dir_b" + ip netns exec "$NS_ZA" zerotier-one "$zt_dir_a" >"$TMPDIR_TEST/zt-a.log" 2>&1 & + ip netns exec "$NS_ZB" zerotier-one "$zt_dir_b" >"$TMPDIR_TEST/zt-b.log" 2>&1 & + local i token_a="" node_a="" + for i in $(seq 1 30); do + if [ -f "$zt_dir_a/authtoken.secret" ] && [ -f "$zt_dir_b/authtoken.secret" ]; then + token_a="$(cat "$zt_dir_a/authtoken.secret")" + break + fi + sleep 0.2 + done + if [ -z "$token_a" ]; then + echo "[zerotier] SKIP: daemon failed to initialize"; ZT_AVAILABLE=false; return + fi + node_a="$(ip netns exec "$NS_ZA" zerotier-cli -D"$zt_dir_a" info 2>/dev/null | awk '{print $3}' || true)" + if [ -z "$node_a" ]; then + echo "[zerotier] SKIP: failed to get node identity"; ZT_AVAILABLE=false; return + fi + local net_id="${node_a}ffffff" + ip netns exec "$NS_ZA" curl -s -H "X-ZT1-Auth: $token_a" -X POST \ + -d '{"name": "bench", "private": false, "v4AssignMode": {"zt": true}, "routes": [{"target": "10.21.0.0/24"}], "ipAssignmentPools": [{"ipRangeStart": "10.21.0.1", "ipRangeEnd": "10.21.0.254"}]}' \ + "http://127.0.0.1:9993/controller/network/${net_id}" >/dev/null 2>&1 || true + ip netns exec "$NS_ZA" zerotier-cli -D"$zt_dir_a" join "$net_id" >/dev/null 2>&1 || true + ip netns exec "$NS_ZB" zerotier-cli -D"$zt_dir_b" join "$net_id" >/dev/null 2>&1 || true + local found=false + for i in $(seq 1 40); do + if ip netns exec "$NS_ZA" ip -o -4 addr show 2>/dev/null | grep -q "10.21." && \ + ip netns exec "$NS_ZB" ip -o -4 addr show 2>/dev/null | grep -q "10.21."; then + found=true; break + fi + sleep 0.5 + done + if [ "$found" = false ]; then + echo "[zerotier] SKIP: overlay assignment timed out"; ZT_AVAILABLE=false; return + fi + echo "[zerotier] network up" +} +setup_zerotier + +# ══════════════════════════════════════════════════════════════════════════════ +# Tailscale setup (local headscale coordinator) +# ══════════════════════════════════════════════════════════════════════════════ +setup_tailscale() { + if ! command -v tailscaled >/dev/null 2>&1 || ! command -v headscale >/dev/null 2>&1; then + echo "[tailscale] SKIP: 'tailscaled' or 'headscale' not found"; TS_AVAILABLE=false; return + fi + echo "[tailscale] creating namespaces and veth pair" + make_pair "$NS_TA" "$NS_TB" "$VTA" "$VTB" "$VTA_IP" "$VTB_IP" + local hs_dir="$TMPDIR_TEST/headscale" + mkdir -p "$hs_dir" + cat > "$hs_dir/config.yaml" <"$TMPDIR_TEST/headscale.log" 2>&1 & + sleep 1 + ip netns exec "$NS_TA" headscale users create bench -c "$hs_dir/config.yaml" >/dev/null 2>&1 || true + local auth_key + auth_key="$(ip netns exec "$NS_TA" headscale preauthkeys create -u bench --reusable --expiration 1h -c "$hs_dir/config.yaml" 2>/dev/null || true)" + if [ -z "$auth_key" ]; then + echo "[tailscale] SKIP: headscale preauthkey creation failed"; TS_AVAILABLE=false; return + fi + local ts_dir_a="$TMPDIR_TEST/ts_a"; local ts_dir_b="$TMPDIR_TEST/ts_b" + mkdir -p "$ts_dir_a" "$ts_dir_b" + ip netns exec "$NS_TA" tailscaled --state="$ts_dir_a/tailscaled.state" --socket="$ts_dir_a/tailscaled.sock" --tun=tailscale0 >"$TMPDIR_TEST/ts-a.log" 2>&1 & + ip netns exec "$NS_TB" tailscaled --state="$ts_dir_b/tailscaled.state" --socket="$ts_dir_b/tailscaled.sock" --tun=tailscale0 >"$TMPDIR_TEST/ts-b.log" 2>&1 & + sleep 1 + ip netns exec "$NS_TA" tailscale --socket="$ts_dir_a/tailscaled.sock" up --login-server="http://${VTA_IP}:8080" --auth-key="$auth_key" >/dev/null 2>&1 || true + ip netns exec "$NS_TB" tailscale --socket="$ts_dir_b/tailscaled.sock" up --login-server="http://${VTA_IP}:8080" --auth-key="$auth_key" >/dev/null 2>&1 || true + local found=false + for i in $(seq 1 40); do + if ip netns exec "$NS_TA" ip -o -4 addr show tailscale0 >/dev/null 2>&1 && \ + ip netns exec "$NS_TB" ip -o -4 addr show tailscale0 >/dev/null 2>&1; then + found=true; break + fi + sleep 0.5 + done + if [ "$found" = false ]; then + echo "[tailscale] SKIP: tailscale tunnel activation timed out"; TS_AVAILABLE=false; return + fi + echo "[tailscale] tunnel up" +} +setup_tailscale + +# ══════════════════════════════════════════════════════════════════════════════ +# NetBird setup (management coordinator) +# ══════════════════════════════════════════════════════════════════════════════ +setup_netbird() { + if ! command -v netbird >/dev/null 2>&1; then + echo "[netbird] SKIP: 'netbird' not found"; NB_AVAILABLE=false; return + fi + echo "[netbird] creating namespaces and veth pair" + make_pair "$NS_BA" "$NS_BB" "$VBA" "$VBB" "$VBA_IP" "$VBB_IP" + echo "[netbird] SKIP: local management service not running"; NB_AVAILABLE=false +} +setup_netbird + # ══════════════════════════════════════════════════════════════════════════════ # Sweep # ══════════════════════════════════════════════════════════════════════════════ @@ -326,61 +642,113 @@ LOSS_RATES="0 5 10" echo "" echo "==========================================================" -echo " iperf3 TCP throughput + ping latency/loss under netem" -echo " ping -c 50 -i 0.1; iperf3 -t 8; netem: loss X% delay 5ms (symmetric)" +echo " iperf3 TCP & UDP throughput + ping latency/loss under netem" +echo " ping -c ${PING_COUNT} -i ${PING_INTERVAL}; iperf3 -t 8; netem: loss X% delay 5ms (symmetric)" echo "==========================================================" for LOSS in $LOSS_RATES; do echo "" echo "[netem] loss=${LOSS}% delay=5ms on all active pairs" apply_netem "$NS_YA" "$NS_YB" "$VYA" "$VYB" "$LOSS" - [ "$WG_AVAILABLE" = true ] && apply_netem "$NS_WA" "$NS_WB" "$VWA" "$VWB" "$LOSS" + [ "$WG_AVAILABLE" = true ] && apply_netem "$NS_WA" "$NS_WB" "$VWA" "$VWB" "$LOSS" + [ "$AWG_AVAILABLE" = true ] && apply_netem "$NS_AA" "$NS_AB" "$VAA" "$VAB" "$LOSS" [ "$OVPN_AVAILABLE" = true ] && apply_netem "$NS_OA" "$NS_OB" "$VOA" "$VOB" "$LOSS" - [ "$N2N_AVAILABLE" = true ] && apply_netem "$NS_NA" "$NS_NB" "$VNA" "$VNB" "$LOSS" + [ "$N2N_AVAILABLE" = true ] && apply_netem "$NS_NA" "$NS_NB" "$VNA" "$VNB" "$LOSS" + [ "$ZT_AVAILABLE" = true ] && apply_netem "$NS_ZA" "$NS_ZB" "$VZA" "$VZB" "$LOSS" + [ "$TS_AVAILABLE" = true ] && apply_netem "$NS_TA" "$NS_TB" "$VTA" "$VTB" "$LOSS" + [ "$NB_AVAILABLE" = true ] && apply_netem "$NS_BA" "$NS_BB" "$VBA" "$VBB" "$LOSS" # yip read -r Y_LOSS Y_RTT <<< "$(measure_ping "$NS_YB" "$TUN_YA_IP")" - Y_MBPS="$(measure_iperf "$NS_YA" "$TUN_YA_IP" "$NS_YB")" - echo "[yip] loss=${Y_LOSS}% rtt=${Y_RTT}ms tcp=${Y_MBPS}Mbit/s" + Y_TCP="$(measure_iperf "$NS_YA" "$TUN_YA_IP" "$NS_YB")" + read -r Y_UDP_MBPS Y_UDP_LOSS <<< "$(measure_iperf_udp "$NS_YA" "$TUN_YA_IP" "$NS_YB")" + echo "[yip] ping_loss=${Y_LOSS}% rtt=${Y_RTT}ms tcp=${Y_TCP}Mbit/s udp=${Y_UDP_MBPS}Mbit/s udp_loss=${Y_UDP_LOSS}%" # WireGuard - W_LOSS="N/A"; W_RTT="N/A"; W_MBPS="N/A" + W_LOSS="N/A"; W_RTT="N/A"; W_TCP="N/A"; W_UDP_MBPS="N/A"; W_UDP_LOSS="N/A" if [ "$WG_AVAILABLE" = true ]; then read -r W_LOSS W_RTT <<< "$(measure_ping "$NS_WB" "$TUN_WA_IP")" - W_MBPS="$(measure_iperf "$NS_WA" "$TUN_WA_IP" "$NS_WB")" - echo "[wg] loss=${W_LOSS}% rtt=${W_RTT}ms tcp=${W_MBPS}Mbit/s" + W_TCP="$(measure_iperf "$NS_WA" "$TUN_WA_IP" "$NS_WB")" + read -r W_UDP_MBPS W_UDP_LOSS <<< "$(measure_iperf_udp "$NS_WA" "$TUN_WA_IP" "$NS_WB")" + echo "[wg] ping_loss=${W_LOSS}% rtt=${W_RTT}ms tcp=${W_TCP}Mbit/s udp=${W_UDP_MBPS}Mbit/s udp_loss=${W_UDP_LOSS}%" + fi + + # AmneziaWG + A_LOSS="N/A"; A_RTT="N/A"; A_TCP="N/A"; A_UDP_MBPS="N/A"; A_UDP_LOSS="N/A" + if [ "$AWG_AVAILABLE" = true ]; then + read -r A_LOSS A_RTT <<< "$(measure_ping "$NS_AB" "$TUN_AA_IP")" + A_TCP="$(measure_iperf "$NS_AA" "$TUN_AA_IP" "$NS_AB")" + read -r A_UDP_MBPS A_UDP_LOSS <<< "$(measure_iperf_udp "$NS_AA" "$TUN_AA_IP" "$NS_AB")" + echo "[awg] ping_loss=${A_LOSS}% rtt=${A_RTT}ms tcp=${A_TCP}Mbit/s udp=${A_UDP_MBPS}Mbit/s udp_loss=${A_UDP_LOSS}%" fi # OpenVPN - O_LOSS="N/A"; O_RTT="N/A"; O_MBPS="N/A" + O_LOSS="N/A"; O_RTT="N/A"; O_TCP="N/A"; O_UDP_MBPS="N/A"; O_UDP_LOSS="N/A" if [ "$OVPN_AVAILABLE" = true ]; then read -r O_LOSS O_RTT <<< "$(measure_ping "$NS_OB" "$TUN_OA_IP")" - O_MBPS="$(measure_iperf "$NS_OA" "$TUN_OA_IP" "$NS_OB")" - echo "[ovpn] loss=${O_LOSS}% rtt=${O_RTT}ms tcp=${O_MBPS}Mbit/s" + O_TCP="$(measure_iperf "$NS_OA" "$TUN_OA_IP" "$NS_OB")" + read -r O_UDP_MBPS O_UDP_LOSS <<< "$(measure_iperf_udp "$NS_OA" "$TUN_OA_IP" "$NS_OB")" + echo "[ovpn] ping_loss=${O_LOSS}% rtt=${O_RTT}ms tcp=${O_TCP}Mbit/s udp=${O_UDP_MBPS}Mbit/s udp_loss=${O_UDP_LOSS}%" fi # n2n - N_LOSS="N/A"; N_RTT="N/A"; N_MBPS="N/A" + N_LOSS="N/A"; N_RTT="N/A"; N_TCP="N/A"; N_UDP_MBPS="N/A"; N_UDP_LOSS="N/A" if [ "$N2N_AVAILABLE" = true ]; then read -r N_LOSS N_RTT <<< "$(measure_ping "$NS_NB" "$OVL_NA_IP")" - N_MBPS="$(measure_iperf "$NS_NA" "$OVL_NA_IP" "$NS_NB")" - echo "[n2n] loss=${N_LOSS}% rtt=${N_RTT}ms tcp=${N_MBPS}Mbit/s" + N_TCP="$(measure_iperf "$NS_NA" "$OVL_NA_IP" "$NS_NB")" + read -r N_UDP_MBPS N_UDP_LOSS <<< "$(measure_iperf_udp "$NS_NA" "$OVL_NA_IP" "$NS_NB")" + echo "[n2n] ping_loss=${N_LOSS}% rtt=${N_RTT}ms tcp=${N_TCP}Mbit/s udp=${N_UDP_MBPS}Mbit/s udp_loss=${N_UDP_LOSS}%" + fi + + # ZeroTier + Z_LOSS="N/A"; Z_RTT="N/A"; Z_TCP="N/A"; Z_UDP_MBPS="N/A"; Z_UDP_LOSS="N/A" + if [ "$ZT_AVAILABLE" = true ]; then + read -r Z_LOSS Z_RTT <<< "$(measure_ping "$NS_ZB" "$TUN_ZA_IP")" + Z_TCP="$(measure_iperf "$NS_ZA" "$TUN_ZA_IP" "$NS_ZB")" + read -r Z_UDP_MBPS Z_UDP_LOSS <<< "$(measure_iperf_udp "$NS_ZA" "$TUN_ZA_IP" "$NS_ZB")" + echo "[zerotier] ping_loss=${Z_LOSS}% rtt=${Z_RTT}ms tcp=${Z_TCP}Mbit/s udp=${Z_UDP_MBPS}Mbit/s udp_loss=${Z_UDP_LOSS}%" + fi + + # Tailscale + T_LOSS="N/A"; T_RTT="N/A"; T_TCP="N/A"; T_UDP_MBPS="N/A"; T_UDP_LOSS="N/A" + if [ "$TS_AVAILABLE" = true ]; then + read -r T_LOSS T_RTT <<< "$(measure_ping "$NS_TB" "$TUN_TA_IP")" + T_TCP="$(measure_iperf "$NS_TA" "$TUN_TA_IP" "$NS_TB")" + read -r T_UDP_MBPS T_UDP_LOSS <<< "$(measure_iperf_udp "$NS_TA" "$TUN_TA_IP" "$NS_TB")" + echo "[tailscale] ping_loss=${T_LOSS}% rtt=${T_RTT}ms tcp=${T_TCP}Mbit/s udp=${T_UDP_MBPS}Mbit/s udp_loss=${T_UDP_LOSS}%" + fi + + # NetBird + B_LOSS="N/A"; B_RTT="N/A"; B_TCP="N/A"; B_UDP_MBPS="N/A"; B_UDP_LOSS="N/A" + if [ "$NB_AVAILABLE" = true ]; then + read -r B_LOSS B_RTT <<< "$(measure_ping "$NS_BB" "$TUN_BA_IP")" + B_TCP="$(measure_iperf "$NS_BA" "$TUN_BA_IP" "$NS_BB")" + read -r B_UDP_MBPS B_UDP_LOSS <<< "$(measure_iperf_udp "$NS_BA" "$TUN_BA_IP" "$NS_BB")" + echo "[netbird] ping_loss=${B_LOSS}% rtt=${B_RTT}ms tcp=${B_TCP}Mbit/s udp=${B_UDP_MBPS}Mbit/s udp_loss=${B_UDP_LOSS}%" fi # ── emit per-loss-rate table ────────────────────────────────────────────── echo "" echo " --- loss=${LOSS}% ---" - echo " | contender | eff_loss% | rtt_ms | tcp_Mbit/s |" - echo " |-----------|-----------|--------|------------|" - printf " | %-9s | %-9s | %-6s | %-10s |\n" "yip" "$Y_LOSS" "$Y_RTT" "$Y_MBPS" - printf " | %-9s | %-9s | %-6s | %-10s |\n" "wireguard" "$W_LOSS" "$W_RTT" "$W_MBPS" - printf " | %-9s | %-9s | %-6s | %-10s |\n" "openvpn" "$O_LOSS" "$O_RTT" "$O_MBPS" - printf " | %-9s | %-9s | %-6s | %-10s |\n" "n2n" "$N_LOSS" "$N_RTT" "$N_MBPS" + echo " | contender | eff_loss% | rtt_ms | tcp_Mbit/s | udp_Mbit/s | udp_loss% |" + echo " |-----------|-----------|--------|------------|------------|-----------|" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "yip" "$Y_LOSS" "$Y_RTT" "$Y_TCP" "$Y_UDP_MBPS" "$Y_UDP_LOSS" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "wireguard" "$W_LOSS" "$W_RTT" "$W_TCP" "$W_UDP_MBPS" "$W_UDP_LOSS" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "amneziawg" "$A_LOSS" "$A_RTT" "$A_TCP" "$A_UDP_MBPS" "$A_UDP_LOSS" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "openvpn" "$O_LOSS" "$O_RTT" "$O_TCP" "$O_UDP_MBPS" "$O_UDP_LOSS" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "n2n" "$N_LOSS" "$N_RTT" "$N_TCP" "$N_UDP_MBPS" "$N_UDP_LOSS" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "zerotier" "$Z_LOSS" "$Z_RTT" "$Z_TCP" "$Z_UDP_MBPS" "$Z_UDP_LOSS" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "tailscale" "$T_LOSS" "$T_RTT" "$T_TCP" "$T_UDP_MBPS" "$T_UDP_LOSS" + printf " | %-9s | %-9s | %-6s | %-10s | %-10s | %-9s |\n" "netbird" "$B_LOSS" "$B_RTT" "$B_TCP" "$B_UDP_MBPS" "$B_UDP_LOSS" done echo "" echo "[done] iperf compare sweep complete" [ "$WG_AVAILABLE" = false ] && echo "[note] WireGuard skipped (module/CLI absent)" +[ "$AWG_AVAILABLE" = false ] && echo "[note] AmneziaWG skipped (module/CLI absent)" [ "$OVPN_AVAILABLE" = false ] && echo "[note] OpenVPN skipped (binary absent or setup failed)" [ "$N2N_AVAILABLE" = false ] && echo "[note] n2n skipped (binary absent or setup failed)" +[ "$ZT_AVAILABLE" = false ] && echo "[note] ZeroTier skipped (binary absent or setup failed)" +[ "$TS_AVAILABLE" = false ] && echo "[note] Tailscale skipped (binary absent or setup failed)" +[ "$NB_AVAILABLE" = false ] && echo "[note] NetBird skipped (binary absent or setup failed)" true diff --git a/crates/yip-bench/wan/results/20261021T213653Z/wan.csv b/crates/yip-bench/wan/results/20261021T213653Z/wan.csv new file mode 100644 index 00000000..9f01b7ff --- /dev/null +++ b/crates/yip-bench/wan/results/20261021T213653Z/wan.csv @@ -0,0 +1,73 @@ +ts,host_pair,commit,tunnel,profile,param,metric,value,conditions +2026-10-21T21:37:08Z,54.88.26.78->35.94.252.101,d484d84,baseline,tcp_throughput,streams=1,mbit_s,299.0,clean +2026-10-21T21:37:22Z,54.88.26.78->35.94.252.101,d484d84,baseline,tcp_throughput,streams=4,mbit_s,1148.2,clean +2026-10-21T21:37:33Z,54.88.26.78->35.94.252.101,d484d84,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:37:33Z,54.88.26.78->35.94.252.101,d484d84,baseline,udp_jitter,offered=200M,ms,0.003,clean +2026-10-21T21:37:33Z,54.88.26.78->35.94.252.101,d484d84,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:37:50Z,54.88.26.78->35.94.252.101,d484d84,baseline,latency,idle,rtt_ms,55.167,clean +2026-10-21T21:37:50Z,54.88.26.78->35.94.252.101,d484d84,baseline,latency,loaded,rtt_ms,55.106,saturated +2026-10-21T21:37:57Z,54.88.26.78->35.94.252.101,d484d84,baseline,cpu_used,tcp_saturate,pct,3.8,sender_host_A_1vcpu +2026-10-21T21:38:17Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:38:17Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_goodput,injected=0pct,mbit_s,383.4,netem_0pct +2026-10-21T21:38:38Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_effective,injected=1pct,pct,2,netem_1pct +2026-10-21T21:38:38Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_goodput,injected=1pct,mbit_s,3.4,netem_1pct +2026-10-21T21:39:00Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_effective,injected=3pct,pct,8,netem_3pct +2026-10-21T21:39:00Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_goodput,injected=3pct,mbit_s,1.7,netem_3pct +2026-10-21T21:39:22Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_effective,injected=5pct,pct,8,netem_5pct +2026-10-21T21:39:22Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_goodput,injected=5pct,mbit_s,1.0,netem_5pct +2026-10-21T21:39:43Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_effective,injected=10pct,pct,13,netem_10pct +2026-10-21T21:39:43Z,54.88.26.78->35.94.252.101,d484d84,baseline,loss_goodput,injected=10pct,mbit_s,0.7,netem_10pct +2026-10-21T21:39:57Z,54.88.26.78->35.94.252.101,d484d84,yip,tcp_throughput,streams=1,mbit_s,198.6,clean +2026-10-21T21:40:11Z,54.88.26.78->35.94.252.101,d484d84,yip,tcp_throughput,streams=4,mbit_s,233.2,clean +2026-10-21T21:40:22Z,54.88.26.78->35.94.252.101,d484d84,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:40:22Z,54.88.26.78->35.94.252.101,d484d84,yip,udp_jitter,offered=200M,ms,0.013,clean +2026-10-21T21:40:22Z,54.88.26.78->35.94.252.101,d484d84,yip,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:40:39Z,54.88.26.78->35.94.252.101,d484d84,yip,latency,idle,rtt_ms,55.775,clean +2026-10-21T21:40:39Z,54.88.26.78->35.94.252.101,d484d84,yip,latency,loaded,rtt_ms,58.461,saturated +2026-10-21T21:40:46Z,54.88.26.78->35.94.252.101,d484d84,yip,cpu_used,tcp_saturate,pct,26.2,sender_host_A_1vcpu +2026-10-21T21:41:06Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:41:06Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_goodput,injected=0pct,mbit_s,192.3,netem_0pct +2026-10-21T21:41:28Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T21:41:28Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_goodput,injected=1pct,mbit_s,236.8,netem_1pct +2026-10-21T21:41:49Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T21:41:49Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_goodput,injected=3pct,mbit_s,44.0,netem_3pct +2026-10-21T21:42:11Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T21:42:11Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_goodput,injected=5pct,mbit_s,51.1,netem_5pct +2026-10-21T21:42:32Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_effective,injected=10pct,pct,0,netem_10pct +2026-10-21T21:42:32Z,54.88.26.78->35.94.252.101,d484d84,yip,loss_goodput,injected=10pct,mbit_s,22.1,netem_10pct +2026-10-21T21:42:46Z,54.88.26.78->35.94.252.101,d484d84,wireguard,tcp_throughput,streams=1,mbit_s,419.1,clean +2026-10-21T21:43:00Z,54.88.26.78->35.94.252.101,d484d84,wireguard,tcp_throughput,streams=4,mbit_s,1623.5,clean +2026-10-21T21:43:11Z,54.88.26.78->35.94.252.101,d484d84,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:43:11Z,54.88.26.78->35.94.252.101,d484d84,wireguard,udp_jitter,offered=200M,ms,0.013,clean +2026-10-21T21:43:11Z,54.88.26.78->35.94.252.101,d484d84,wireguard,udp_loss,offered=200M,pct,1.68,clean +2026-10-21T21:43:28Z,54.88.26.78->35.94.252.101,d484d84,wireguard,latency,idle,rtt_ms,55.626,clean +2026-10-21T21:43:28Z,54.88.26.78->35.94.252.101,d484d84,wireguard,latency,loaded,rtt_ms,55.660,saturated +2026-10-21T21:43:35Z,54.88.26.78->35.94.252.101,d484d84,wireguard,cpu_used,tcp_saturate,pct,12.2,sender_host_A_1vcpu +2026-10-21T21:43:55Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:43:55Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_goodput,injected=0pct,mbit_s,418.9,netem_0pct +2026-10-21T21:44:17Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-21T21:44:17Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_goodput,injected=1pct,mbit_s,3.1,netem_1pct +2026-10-21T21:44:38Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_effective,injected=3pct,pct,1,netem_3pct +2026-10-21T21:44:38Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_goodput,injected=3pct,mbit_s,1.2,netem_3pct +2026-10-21T21:45:00Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_effective,injected=5pct,pct,7,netem_5pct +2026-10-21T21:45:00Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-21T21:45:23Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_effective,injected=10pct,pct,5,netem_10pct +2026-10-21T21:45:23Z,54.88.26.78->35.94.252.101,d484d84,wireguard,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T21:45:37Z,54.88.26.78->35.94.252.101,d484d84,openvpn,tcp_throughput,streams=1,mbit_s,19.6,clean +2026-10-21T21:45:50Z,54.88.26.78->35.94.252.101,d484d84,openvpn,tcp_throughput,streams=4,mbit_s,22.1,clean +2026-10-21T21:46:01Z,54.88.26.78->35.94.252.101,d484d84,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:46:01Z,54.88.26.78->35.94.252.101,d484d84,openvpn,udp_jitter,offered=200M,ms,0.321,clean +2026-10-21T21:46:01Z,54.88.26.78->35.94.252.101,d484d84,openvpn,udp_loss,offered=200M,pct,1.40,clean +2026-10-21T21:46:18Z,54.88.26.78->35.94.252.101,d484d84,openvpn,latency,idle,rtt_ms,55.978,clean +2026-10-21T21:46:18Z,54.88.26.78->35.94.252.101,d484d84,openvpn,latency,loaded,rtt_ms,56.223,saturated +2026-10-21T21:46:25Z,54.88.26.78->35.94.252.101,d484d84,openvpn,cpu_used,tcp_saturate,pct,3.0,sender_host_A_1vcpu +2026-10-21T21:46:45Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:46:45Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_goodput,injected=0pct,mbit_s,18.8,netem_0pct +2026-10-21T21:47:07Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_effective,injected=1pct,pct,4,netem_1pct +2026-10-21T21:47:07Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_goodput,injected=1pct,mbit_s,2.3,netem_1pct +2026-10-21T21:47:28Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_effective,injected=3pct,pct,1,netem_3pct +2026-10-21T21:47:28Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_goodput,injected=3pct,mbit_s,1.2,netem_3pct +2026-10-21T21:47:50Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_effective,injected=5pct,pct,6,netem_5pct +2026-10-21T21:47:50Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-21T21:48:12Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_effective,injected=10pct,pct,7,netem_10pct +2026-10-21T21:48:12Z,54.88.26.78->35.94.252.101,d484d84,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct diff --git a/crates/yip-bench/wan/results/20261021T215456Z/wan.csv b/crates/yip-bench/wan/results/20261021T215456Z/wan.csv new file mode 100644 index 00000000..70c40cfe --- /dev/null +++ b/crates/yip-bench/wan/results/20261021T215456Z/wan.csv @@ -0,0 +1,73 @@ +ts,host_pair,commit,tunnel,profile,param,metric,value,conditions +2026-10-21T21:55:10Z,34.227.57.24->35.167.199.119,ddc3108,baseline,tcp_throughput,streams=1,mbit_s,291.7,clean +2026-10-21T21:55:23Z,34.227.57.24->35.167.199.119,ddc3108,baseline,tcp_throughput,streams=4,mbit_s,1077.8,clean +2026-10-21T21:55:34Z,34.227.57.24->35.167.199.119,ddc3108,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:55:34Z,34.227.57.24->35.167.199.119,ddc3108,baseline,udp_jitter,offered=200M,ms,0.004,clean +2026-10-21T21:55:34Z,34.227.57.24->35.167.199.119,ddc3108,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:55:50Z,34.227.57.24->35.167.199.119,ddc3108,baseline,latency,idle,rtt_ms,55.075,clean +2026-10-21T21:55:50Z,34.227.57.24->35.167.199.119,ddc3108,baseline,latency,loaded,rtt_ms,55.036,saturated +2026-10-21T21:55:57Z,34.227.57.24->35.167.199.119,ddc3108,baseline,cpu_used,tcp_saturate,pct,8.5,sender_host_A_1vcpu +2026-10-21T21:56:16Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:56:16Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_goodput,injected=0pct,mbit_s,426.6,netem_0pct +2026-10-21T21:56:36Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_effective,injected=1pct,pct,2,netem_1pct +2026-10-21T21:56:36Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_goodput,injected=1pct,mbit_s,36.4,netem_1pct +2026-10-21T21:56:56Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_effective,injected=3pct,pct,4,netem_3pct +2026-10-21T21:56:56Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_goodput,injected=3pct,mbit_s,1.6,netem_3pct +2026-10-21T21:57:17Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_effective,injected=5pct,pct,5,netem_5pct +2026-10-21T21:57:17Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_goodput,injected=5pct,mbit_s,1.4,netem_5pct +2026-10-21T21:57:37Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_effective,injected=10pct,pct,10,netem_10pct +2026-10-21T21:57:37Z,34.227.57.24->35.167.199.119,ddc3108,baseline,loss_goodput,injected=10pct,mbit_s,0.6,netem_10pct +2026-10-21T21:57:50Z,34.227.57.24->35.167.199.119,ddc3108,yip,tcp_throughput,streams=1,mbit_s,248.8,clean +2026-10-21T21:58:04Z,34.227.57.24->35.167.199.119,ddc3108,yip,tcp_throughput,streams=4,mbit_s,256.3,clean +2026-10-21T21:58:15Z,34.227.57.24->35.167.199.119,ddc3108,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:58:15Z,34.227.57.24->35.167.199.119,ddc3108,yip,udp_jitter,offered=200M,ms,0.012,clean +2026-10-21T21:58:15Z,34.227.57.24->35.167.199.119,ddc3108,yip,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:58:31Z,34.227.57.24->35.167.199.119,ddc3108,yip,latency,idle,rtt_ms,57.580,clean +2026-10-21T21:58:31Z,34.227.57.24->35.167.199.119,ddc3108,yip,latency,loaded,rtt_ms,61.091,saturated +2026-10-21T21:58:38Z,34.227.57.24->35.167.199.119,ddc3108,yip,cpu_used,tcp_saturate,pct,8.8,sender_host_A_1vcpu +2026-10-21T21:58:56Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:58:56Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_goodput,injected=0pct,mbit_s,167.9,netem_0pct +2026-10-21T21:59:16Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T21:59:16Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_goodput,injected=1pct,mbit_s,61.1,netem_1pct +2026-10-21T21:59:36Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T21:59:36Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_goodput,injected=3pct,mbit_s,126.1,netem_3pct +2026-10-21T21:59:56Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T21:59:56Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_goodput,injected=5pct,mbit_s,53.5,netem_5pct +2026-10-21T22:00:17Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_effective,injected=10pct,pct,3,netem_10pct +2026-10-21T22:00:17Z,34.227.57.24->35.167.199.119,ddc3108,yip,loss_goodput,injected=10pct,mbit_s,6.4,netem_10pct +2026-10-21T22:00:30Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,tcp_throughput,streams=1,mbit_s,379.5,clean +2026-10-21T22:00:43Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,tcp_throughput,streams=4,mbit_s,455.7,clean +2026-10-21T22:00:54Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T22:00:54Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,udp_jitter,offered=200M,ms,0.055,clean +2026-10-21T22:00:54Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,udp_loss,offered=200M,pct,0.01,clean +2026-10-21T22:01:10Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,latency,idle,rtt_ms,54.876,clean +2026-10-21T22:01:10Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,latency,loaded,rtt_ms,57.976,saturated +2026-10-21T22:01:17Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,cpu_used,tcp_saturate,pct,22.4,sender_host_A_1vcpu +2026-10-21T22:01:36Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T22:01:36Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_goodput,injected=0pct,mbit_s,361.7,netem_0pct +2026-10-21T22:01:56Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-21T22:01:56Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_goodput,injected=1pct,mbit_s,2.7,netem_1pct +2026-10-21T22:02:17Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_effective,injected=3pct,pct,4,netem_3pct +2026-10-21T22:02:17Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_goodput,injected=3pct,mbit_s,1.1,netem_3pct +2026-10-21T22:02:37Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_effective,injected=5pct,pct,4,netem_5pct +2026-10-21T22:02:37Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-21T22:02:57Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_effective,injected=10pct,pct,9,netem_10pct +2026-10-21T22:02:57Z,34.227.57.24->35.167.199.119,ddc3108,wireguard,loss_goodput,injected=10pct,mbit_s,0.6,netem_10pct +2026-10-21T22:03:10Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,tcp_throughput,streams=1,mbit_s,19.6,clean +2026-10-21T22:03:23Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,tcp_throughput,streams=4,mbit_s,54.9,clean +2026-10-21T22:03:34Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T22:03:34Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,udp_jitter,offered=200M,ms,0.028,clean +2026-10-21T22:03:34Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,udp_loss,offered=200M,pct,0.39,clean +2026-10-21T22:03:50Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,latency,idle,rtt_ms,55.283,clean +2026-10-21T22:03:50Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,latency,loaded,rtt_ms,55.500,saturated +2026-10-21T22:03:57Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,cpu_used,tcp_saturate,pct,12.9,sender_host_A_1vcpu +2026-10-21T22:04:16Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T22:04:16Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_goodput,injected=0pct,mbit_s,24.3,netem_0pct +2026-10-21T22:04:36Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-21T22:04:36Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_goodput,injected=1pct,mbit_s,2.9,netem_1pct +2026-10-21T22:04:57Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T22:04:57Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_goodput,injected=3pct,mbit_s,1.4,netem_3pct +2026-10-21T22:05:18Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_effective,injected=5pct,pct,8,netem_5pct +2026-10-21T22:05:18Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-21T22:05:39Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_effective,injected=10pct,pct,7,netem_10pct +2026-10-21T22:05:39Z,34.227.57.24->35.167.199.119,ddc3108,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct diff --git a/crates/yip-bench/wan/results/20261021T232251Z/wan.csv b/crates/yip-bench/wan/results/20261021T232251Z/wan.csv new file mode 100644 index 00000000..0723a58d --- /dev/null +++ b/crates/yip-bench/wan/results/20261021T232251Z/wan.csv @@ -0,0 +1,73 @@ +ts,host_pair,commit,tunnel,profile,param,metric,value,conditions +2026-10-21T23:23:06Z,23.20.144.68->44.244.5.210,715c58a,baseline,tcp_throughput,streams=1,mbit_s,343.5,clean +2026-10-21T23:23:19Z,23.20.144.68->44.244.5.210,715c58a,baseline,tcp_throughput,streams=4,mbit_s,679.9,clean +2026-10-21T23:23:30Z,23.20.144.68->44.244.5.210,715c58a,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:23:30Z,23.20.144.68->44.244.5.210,715c58a,baseline,udp_jitter,offered=200M,ms,0.003,clean +2026-10-21T23:23:30Z,23.20.144.68->44.244.5.210,715c58a,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T23:23:47Z,23.20.144.68->44.244.5.210,715c58a,baseline,latency,idle,rtt_ms,55.364,clean +2026-10-21T23:23:47Z,23.20.144.68->44.244.5.210,715c58a,baseline,latency,loaded,rtt_ms,55.377,saturated +2026-10-21T23:23:54Z,23.20.144.68->44.244.5.210,715c58a,baseline,cpu_used,tcp_saturate,pct,2.8,sender_host_A_1vcpu +2026-10-21T23:24:14Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:24:14Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_goodput,injected=0pct,mbit_s,267.0,netem_0pct +2026-10-21T23:24:35Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T23:24:35Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_goodput,injected=1pct,mbit_s,3.4,netem_1pct +2026-10-21T23:24:56Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-21T23:24:56Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_goodput,injected=3pct,mbit_s,1.9,netem_3pct +2026-10-21T23:25:17Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_effective,injected=5pct,pct,2,netem_5pct +2026-10-21T23:25:17Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-21T23:25:39Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_effective,injected=10pct,pct,8,netem_10pct +2026-10-21T23:25:39Z,23.20.144.68->44.244.5.210,715c58a,baseline,loss_goodput,injected=10pct,mbit_s,0.8,netem_10pct +2026-10-21T23:25:53Z,23.20.144.68->44.244.5.210,715c58a,yip,tcp_throughput,streams=1,mbit_s,287.7,clean +2026-10-21T23:26:06Z,23.20.144.68->44.244.5.210,715c58a,yip,tcp_throughput,streams=4,mbit_s,228.9,clean +2026-10-21T23:26:18Z,23.20.144.68->44.244.5.210,715c58a,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:26:18Z,23.20.144.68->44.244.5.210,715c58a,yip,udp_jitter,offered=200M,ms,0.008,clean +2026-10-21T23:26:18Z,23.20.144.68->44.244.5.210,715c58a,yip,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T23:26:35Z,23.20.144.68->44.244.5.210,715c58a,yip,latency,idle,rtt_ms,55.956,clean +2026-10-21T23:26:35Z,23.20.144.68->44.244.5.210,715c58a,yip,latency,loaded,rtt_ms,58.365,saturated +2026-10-21T23:26:42Z,23.20.144.68->44.244.5.210,715c58a,yip,cpu_used,tcp_saturate,pct,20.5,sender_host_A_1vcpu +2026-10-21T23:27:02Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:27:02Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_goodput,injected=0pct,mbit_s,207.8,netem_0pct +2026-10-21T23:27:23Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T23:27:23Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_goodput,injected=1pct,mbit_s,163.9,netem_1pct +2026-10-21T23:27:44Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_effective,injected=3pct,pct,1,netem_3pct +2026-10-21T23:27:44Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_goodput,injected=3pct,mbit_s,113.1,netem_3pct +2026-10-21T23:28:05Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T23:28:05Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_goodput,injected=5pct,mbit_s,70.3,netem_5pct +2026-10-21T23:28:27Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_effective,injected=10pct,pct,1,netem_10pct +2026-10-21T23:28:27Z,23.20.144.68->44.244.5.210,715c58a,yip,loss_goodput,injected=10pct,mbit_s,45.6,netem_10pct +2026-10-21T23:28:40Z,23.20.144.68->44.244.5.210,715c58a,wireguard,tcp_throughput,streams=1,mbit_s,388.1,clean +2026-10-21T23:28:54Z,23.20.144.68->44.244.5.210,715c58a,wireguard,tcp_throughput,streams=4,mbit_s,592.5,clean +2026-10-21T23:29:05Z,23.20.144.68->44.244.5.210,715c58a,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:29:05Z,23.20.144.68->44.244.5.210,715c58a,wireguard,udp_jitter,offered=200M,ms,0.014,clean +2026-10-21T23:29:05Z,23.20.144.68->44.244.5.210,715c58a,wireguard,udp_loss,offered=200M,pct,2.05,clean +2026-10-21T23:29:22Z,23.20.144.68->44.244.5.210,715c58a,wireguard,latency,idle,rtt_ms,59.623,clean +2026-10-21T23:29:22Z,23.20.144.68->44.244.5.210,715c58a,wireguard,latency,loaded,rtt_ms,59.683,saturated +2026-10-21T23:29:29Z,23.20.144.68->44.244.5.210,715c58a,wireguard,cpu_used,tcp_saturate,pct,10.1,sender_host_A_1vcpu +2026-10-21T23:29:49Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:29:49Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_goodput,injected=0pct,mbit_s,387.2,netem_0pct +2026-10-21T23:30:10Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_effective,injected=1pct,pct,3,netem_1pct +2026-10-21T23:30:10Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_goodput,injected=1pct,mbit_s,3.3,netem_1pct +2026-10-21T23:30:31Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-21T23:30:31Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_goodput,injected=3pct,mbit_s,1.3,netem_3pct +2026-10-21T23:30:52Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_effective,injected=5pct,pct,5,netem_5pct +2026-10-21T23:30:52Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_goodput,injected=5pct,mbit_s,1.0,netem_5pct +2026-10-21T23:31:14Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_effective,injected=10pct,pct,11,netem_10pct +2026-10-21T23:31:14Z,23.20.144.68->44.244.5.210,715c58a,wireguard,loss_goodput,injected=10pct,mbit_s,0.6,netem_10pct +2026-10-21T23:31:28Z,23.20.144.68->44.244.5.210,715c58a,openvpn,tcp_throughput,streams=1,mbit_s,21.6,clean +2026-10-21T23:31:41Z,23.20.144.68->44.244.5.210,715c58a,openvpn,tcp_throughput,streams=4,mbit_s,26.3,clean +2026-10-21T23:31:52Z,23.20.144.68->44.244.5.210,715c58a,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:31:52Z,23.20.144.68->44.244.5.210,715c58a,openvpn,udp_jitter,offered=200M,ms,0.026,clean +2026-10-21T23:31:52Z,23.20.144.68->44.244.5.210,715c58a,openvpn,udp_loss,offered=200M,pct,0.14,clean +2026-10-21T23:32:09Z,23.20.144.68->44.244.5.210,715c58a,openvpn,latency,idle,rtt_ms,55.419,clean +2026-10-21T23:32:09Z,23.20.144.68->44.244.5.210,715c58a,openvpn,latency,loaded,rtt_ms,56.097,saturated +2026-10-21T23:32:16Z,23.20.144.68->44.244.5.210,715c58a,openvpn,cpu_used,tcp_saturate,pct,3.8,sender_host_A_1vcpu +2026-10-21T23:32:36Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:32:36Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_goodput,injected=0pct,mbit_s,21.6,netem_0pct +2026-10-21T23:32:57Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_effective,injected=1pct,pct,3,netem_1pct +2026-10-21T23:32:57Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_goodput,injected=1pct,mbit_s,2.6,netem_1pct +2026-10-21T23:33:18Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_effective,injected=3pct,pct,5,netem_3pct +2026-10-21T23:33:18Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_goodput,injected=3pct,mbit_s,1.3,netem_3pct +2026-10-21T23:33:40Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_effective,injected=5pct,pct,4,netem_5pct +2026-10-21T23:33:40Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-21T23:34:02Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_effective,injected=10pct,pct,10,netem_10pct +2026-10-21T23:34:02Z,23.20.144.68->44.244.5.210,715c58a,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct diff --git a/crates/yip-bench/wan/results/20261021T235358Z/wan.csv b/crates/yip-bench/wan/results/20261021T235358Z/wan.csv new file mode 100644 index 00000000..9d83b75a --- /dev/null +++ b/crates/yip-bench/wan/results/20261021T235358Z/wan.csv @@ -0,0 +1,73 @@ +ts,host_pair,commit,tunnel,profile,param,metric,value,conditions +2026-10-21T23:54:13Z,54.81.193.128->35.90.90.65,840faa3,baseline,tcp_throughput,streams=1,mbit_s,345.5,clean +2026-10-21T23:54:26Z,54.81.193.128->35.90.90.65,840faa3,baseline,tcp_throughput,streams=4,mbit_s,815.0,clean +2026-10-21T23:54:36Z,54.81.193.128->35.90.90.65,840faa3,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:54:36Z,54.81.193.128->35.90.90.65,840faa3,baseline,udp_jitter,offered=200M,ms,0.003,clean +2026-10-21T23:54:36Z,54.81.193.128->35.90.90.65,840faa3,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T23:54:52Z,54.81.193.128->35.90.90.65,840faa3,baseline,latency,idle,rtt_ms,55.470,clean +2026-10-21T23:54:52Z,54.81.193.128->35.90.90.65,840faa3,baseline,latency,loaded,rtt_ms,55.473,saturated +2026-10-21T23:54:59Z,54.81.193.128->35.90.90.65,840faa3,baseline,cpu_used,tcp_saturate,pct,2.2,sender_host_A_1vcpu +2026-10-21T23:55:17Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:55:17Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_goodput,injected=0pct,mbit_s,334.9,netem_0pct +2026-10-21T23:55:36Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_effective,injected=1pct,pct,2,netem_1pct +2026-10-21T23:55:36Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_goodput,injected=1pct,mbit_s,3.4,netem_1pct +2026-10-21T23:55:55Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-21T23:55:55Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_goodput,injected=3pct,mbit_s,2.5,netem_3pct +2026-10-21T23:56:13Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_effective,injected=5pct,pct,2,netem_5pct +2026-10-21T23:56:13Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_goodput,injected=5pct,mbit_s,1.1,netem_5pct +2026-10-21T23:56:32Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_effective,injected=10pct,pct,9,netem_10pct +2026-10-21T23:56:32Z,54.81.193.128->35.90.90.65,840faa3,baseline,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T23:56:45Z,54.81.193.128->35.90.90.65,840faa3,yip,tcp_throughput,streams=1,mbit_s,128.8,clean +2026-10-21T23:56:58Z,54.81.193.128->35.90.90.65,840faa3,yip,tcp_throughput,streams=4,mbit_s,410.7,clean +2026-10-21T23:57:08Z,54.81.193.128->35.90.90.65,840faa3,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:57:08Z,54.81.193.128->35.90.90.65,840faa3,yip,udp_jitter,offered=200M,ms,0.032,clean +2026-10-21T23:57:08Z,54.81.193.128->35.90.90.65,840faa3,yip,udp_loss,offered=200M,pct,0.10,clean +2026-10-21T23:57:24Z,54.81.193.128->35.90.90.65,840faa3,yip,latency,idle,rtt_ms,61.991,clean +2026-10-21T23:57:24Z,54.81.193.128->35.90.90.65,840faa3,yip,latency,loaded,rtt_ms,67.774,saturated +2026-10-21T23:57:31Z,54.81.193.128->35.90.90.65,840faa3,yip,cpu_used,tcp_saturate,pct,19.1,sender_host_A_1vcpu +2026-10-21T23:57:49Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:57:49Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_goodput,injected=0pct,mbit_s,65.1,netem_0pct +2026-10-21T23:58:08Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T23:58:08Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_goodput,injected=1pct,mbit_s,128.4,netem_1pct +2026-10-21T23:58:26Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T23:58:26Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_goodput,injected=3pct,mbit_s,270.5,netem_3pct +2026-10-21T23:58:45Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T23:58:45Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_goodput,injected=5pct,mbit_s,48.3,netem_5pct +2026-10-21T23:59:04Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_effective,injected=10pct,pct,1,netem_10pct +2026-10-21T23:59:04Z,54.81.193.128->35.90.90.65,840faa3,yip,loss_goodput,injected=10pct,mbit_s,27.1,netem_10pct +2026-10-21T23:59:17Z,54.81.193.128->35.90.90.65,840faa3,wireguard,tcp_throughput,streams=1,mbit_s,408.4,clean +2026-10-21T23:59:30Z,54.81.193.128->35.90.90.65,840faa3,wireguard,tcp_throughput,streams=4,mbit_s,1137.5,clean +2026-10-21T23:59:40Z,54.81.193.128->35.90.90.65,840faa3,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:59:40Z,54.81.193.128->35.90.90.65,840faa3,wireguard,udp_jitter,offered=200M,ms,0.014,clean +2026-10-21T23:59:40Z,54.81.193.128->35.90.90.65,840faa3,wireguard,udp_loss,offered=200M,pct,0.05,clean +2026-10-21T23:59:56Z,54.81.193.128->35.90.90.65,840faa3,wireguard,latency,idle,rtt_ms,56.423,clean +2026-10-21T23:59:56Z,54.81.193.128->35.90.90.65,840faa3,wireguard,latency,loaded,rtt_ms,56.610,saturated +2026-10-22T00:00:03Z,54.81.193.128->35.90.90.65,840faa3,wireguard,cpu_used,tcp_saturate,pct,36.5,sender_host_A_1vcpu +2026-10-22T00:00:21Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-22T00:00:21Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_goodput,injected=0pct,mbit_s,391.7,netem_0pct +2026-10-22T00:00:40Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-22T00:00:40Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_goodput,injected=1pct,mbit_s,2.0,netem_1pct +2026-10-22T00:00:59Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-22T00:00:59Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_goodput,injected=3pct,mbit_s,1.4,netem_3pct +2026-10-22T00:01:18Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_effective,injected=5pct,pct,8,netem_5pct +2026-10-22T00:01:18Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-22T00:01:37Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_effective,injected=10pct,pct,12,netem_10pct +2026-10-22T00:01:37Z,54.81.193.128->35.90.90.65,840faa3,wireguard,loss_goodput,injected=10pct,mbit_s,0.4,netem_10pct +2026-10-22T00:01:50Z,54.81.193.128->35.90.90.65,840faa3,openvpn,tcp_throughput,streams=1,mbit_s,42.7,clean +2026-10-22T00:02:03Z,54.81.193.128->35.90.90.65,840faa3,openvpn,tcp_throughput,streams=4,mbit_s,49.7,clean +2026-10-22T00:02:14Z,54.81.193.128->35.90.90.65,840faa3,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-22T00:02:14Z,54.81.193.128->35.90.90.65,840faa3,openvpn,udp_jitter,offered=200M,ms,0.039,clean +2026-10-22T00:02:14Z,54.81.193.128->35.90.90.65,840faa3,openvpn,udp_loss,offered=200M,pct,16.05,clean +2026-10-22T00:02:30Z,54.81.193.128->35.90.90.65,840faa3,openvpn,latency,idle,rtt_ms,62.518,clean +2026-10-22T00:02:30Z,54.81.193.128->35.90.90.65,840faa3,openvpn,latency,loaded,rtt_ms,66.531,saturated +2026-10-22T00:02:37Z,54.81.193.128->35.90.90.65,840faa3,openvpn,cpu_used,tcp_saturate,pct,24.8,sender_host_A_1vcpu +2026-10-22T00:02:55Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-22T00:02:55Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_goodput,injected=0pct,mbit_s,40.9,netem_0pct +2026-10-22T00:03:14Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-22T00:03:14Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_goodput,injected=1pct,mbit_s,2.8,netem_1pct +2026-10-22T00:03:33Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_effective,injected=3pct,pct,2,netem_3pct +2026-10-22T00:03:33Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_goodput,injected=3pct,mbit_s,1.4,netem_3pct +2026-10-22T00:03:52Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_effective,injected=5pct,pct,5,netem_5pct +2026-10-22T00:03:52Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-22T00:04:12Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_effective,injected=10pct,pct,15,netem_10pct +2026-10-22T00:04:12Z,54.81.193.128->35.90.90.65,840faa3,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct diff --git a/crates/yip-bench/wan/results/history.csv b/crates/yip-bench/wan/results/history.csv index ef39b83c..245c4fd7 100644 --- a/crates/yip-bench/wan/results/history.csv +++ b/crates/yip-bench/wan/results/history.csv @@ -359,3 +359,291 @@ ts,host_pair,commit,tunnel,profile,param,metric,value,conditions 2026-10-20T22:07:53Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),7c2fe8c,openvpn,loss_goodput,injected=5pct,mbit_s,0.7,netem_5pct 2026-10-20T22:08:13Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),7c2fe8c,openvpn,loss_effective,injected=10pct,pct,9,netem_10pct 2026-10-20T22:08:13Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),7c2fe8c,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T21:37:08Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,tcp_throughput,streams=1,mbit_s,299.0,clean +2026-10-21T21:37:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,tcp_throughput,streams=4,mbit_s,1148.2,clean +2026-10-21T21:37:33Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:37:33Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,udp_jitter,offered=200M,ms,0.003,clean +2026-10-21T21:37:33Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:37:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,latency,idle,rtt_ms,55.167,clean +2026-10-21T21:37:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,latency,loaded,rtt_ms,55.106,saturated +2026-10-21T21:37:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,cpu_used,tcp_saturate,pct,3.8,sender_host_A_1vcpu +2026-10-21T21:38:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:38:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_goodput,injected=0pct,mbit_s,383.4,netem_0pct +2026-10-21T21:38:38Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_effective,injected=1pct,pct,2,netem_1pct +2026-10-21T21:38:38Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_goodput,injected=1pct,mbit_s,3.4,netem_1pct +2026-10-21T21:39:00Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_effective,injected=3pct,pct,8,netem_3pct +2026-10-21T21:39:00Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_goodput,injected=3pct,mbit_s,1.7,netem_3pct +2026-10-21T21:39:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_effective,injected=5pct,pct,8,netem_5pct +2026-10-21T21:39:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_goodput,injected=5pct,mbit_s,1.0,netem_5pct +2026-10-21T21:39:43Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_effective,injected=10pct,pct,13,netem_10pct +2026-10-21T21:39:43Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,baseline,loss_goodput,injected=10pct,mbit_s,0.7,netem_10pct +2026-10-21T21:39:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,tcp_throughput,streams=1,mbit_s,198.6,clean +2026-10-21T21:40:11Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,tcp_throughput,streams=4,mbit_s,233.2,clean +2026-10-21T21:40:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:40:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,udp_jitter,offered=200M,ms,0.013,clean +2026-10-21T21:40:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:40:39Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,latency,idle,rtt_ms,55.775,clean +2026-10-21T21:40:39Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,latency,loaded,rtt_ms,58.461,saturated +2026-10-21T21:40:46Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,cpu_used,tcp_saturate,pct,26.2,sender_host_A_1vcpu +2026-10-21T21:41:06Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:41:06Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_goodput,injected=0pct,mbit_s,192.3,netem_0pct +2026-10-21T21:41:28Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T21:41:28Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_goodput,injected=1pct,mbit_s,236.8,netem_1pct +2026-10-21T21:41:49Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T21:41:49Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_goodput,injected=3pct,mbit_s,44.0,netem_3pct +2026-10-21T21:42:11Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T21:42:11Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_goodput,injected=5pct,mbit_s,51.1,netem_5pct +2026-10-21T21:42:32Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_effective,injected=10pct,pct,0,netem_10pct +2026-10-21T21:42:32Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,yip,loss_goodput,injected=10pct,mbit_s,22.1,netem_10pct +2026-10-21T21:42:46Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,tcp_throughput,streams=1,mbit_s,419.1,clean +2026-10-21T21:43:00Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,tcp_throughput,streams=4,mbit_s,1623.5,clean +2026-10-21T21:43:11Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:43:11Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,udp_jitter,offered=200M,ms,0.013,clean +2026-10-21T21:43:11Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,udp_loss,offered=200M,pct,1.68,clean +2026-10-21T21:43:28Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,latency,idle,rtt_ms,55.626,clean +2026-10-21T21:43:28Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,latency,loaded,rtt_ms,55.660,saturated +2026-10-21T21:43:35Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,cpu_used,tcp_saturate,pct,12.2,sender_host_A_1vcpu +2026-10-21T21:43:55Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:43:55Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_goodput,injected=0pct,mbit_s,418.9,netem_0pct +2026-10-21T21:44:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-21T21:44:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_goodput,injected=1pct,mbit_s,3.1,netem_1pct +2026-10-21T21:44:38Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_effective,injected=3pct,pct,1,netem_3pct +2026-10-21T21:44:38Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_goodput,injected=3pct,mbit_s,1.2,netem_3pct +2026-10-21T21:45:00Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_effective,injected=5pct,pct,7,netem_5pct +2026-10-21T21:45:00Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-21T21:45:23Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_effective,injected=10pct,pct,5,netem_10pct +2026-10-21T21:45:23Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,wireguard,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T21:45:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,tcp_throughput,streams=1,mbit_s,19.6,clean +2026-10-21T21:45:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,tcp_throughput,streams=4,mbit_s,22.1,clean +2026-10-21T21:46:01Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:46:01Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,udp_jitter,offered=200M,ms,0.321,clean +2026-10-21T21:46:01Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,udp_loss,offered=200M,pct,1.40,clean +2026-10-21T21:46:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,latency,idle,rtt_ms,55.978,clean +2026-10-21T21:46:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,latency,loaded,rtt_ms,56.223,saturated +2026-10-21T21:46:25Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,cpu_used,tcp_saturate,pct,3.0,sender_host_A_1vcpu +2026-10-21T21:46:45Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:46:45Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_goodput,injected=0pct,mbit_s,18.8,netem_0pct +2026-10-21T21:47:07Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_effective,injected=1pct,pct,4,netem_1pct +2026-10-21T21:47:07Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_goodput,injected=1pct,mbit_s,2.3,netem_1pct +2026-10-21T21:47:28Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_effective,injected=3pct,pct,1,netem_3pct +2026-10-21T21:47:28Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_goodput,injected=3pct,mbit_s,1.2,netem_3pct +2026-10-21T21:47:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_effective,injected=5pct,pct,6,netem_5pct +2026-10-21T21:47:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-21T21:48:12Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_effective,injected=10pct,pct,7,netem_10pct +2026-10-21T21:48:12Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),d484d84,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T21:55:10Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,tcp_throughput,streams=1,mbit_s,291.7,clean +2026-10-21T21:55:23Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,tcp_throughput,streams=4,mbit_s,1077.8,clean +2026-10-21T21:55:34Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:55:34Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,udp_jitter,offered=200M,ms,0.004,clean +2026-10-21T21:55:34Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:55:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,latency,idle,rtt_ms,55.075,clean +2026-10-21T21:55:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,latency,loaded,rtt_ms,55.036,saturated +2026-10-21T21:55:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,cpu_used,tcp_saturate,pct,8.5,sender_host_A_1vcpu +2026-10-21T21:56:16Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:56:16Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_goodput,injected=0pct,mbit_s,426.6,netem_0pct +2026-10-21T21:56:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_effective,injected=1pct,pct,2,netem_1pct +2026-10-21T21:56:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_goodput,injected=1pct,mbit_s,36.4,netem_1pct +2026-10-21T21:56:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_effective,injected=3pct,pct,4,netem_3pct +2026-10-21T21:56:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_goodput,injected=3pct,mbit_s,1.6,netem_3pct +2026-10-21T21:57:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_effective,injected=5pct,pct,5,netem_5pct +2026-10-21T21:57:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_goodput,injected=5pct,mbit_s,1.4,netem_5pct +2026-10-21T21:57:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_effective,injected=10pct,pct,10,netem_10pct +2026-10-21T21:57:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,baseline,loss_goodput,injected=10pct,mbit_s,0.6,netem_10pct +2026-10-21T21:57:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,tcp_throughput,streams=1,mbit_s,248.8,clean +2026-10-21T21:58:04Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,tcp_throughput,streams=4,mbit_s,256.3,clean +2026-10-21T21:58:15Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T21:58:15Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,udp_jitter,offered=200M,ms,0.012,clean +2026-10-21T21:58:15Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T21:58:31Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,latency,idle,rtt_ms,57.580,clean +2026-10-21T21:58:31Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,latency,loaded,rtt_ms,61.091,saturated +2026-10-21T21:58:38Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,cpu_used,tcp_saturate,pct,8.8,sender_host_A_1vcpu +2026-10-21T21:58:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T21:58:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_goodput,injected=0pct,mbit_s,167.9,netem_0pct +2026-10-21T21:59:16Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T21:59:16Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_goodput,injected=1pct,mbit_s,61.1,netem_1pct +2026-10-21T21:59:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T21:59:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_goodput,injected=3pct,mbit_s,126.1,netem_3pct +2026-10-21T21:59:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T21:59:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_goodput,injected=5pct,mbit_s,53.5,netem_5pct +2026-10-21T22:00:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_effective,injected=10pct,pct,3,netem_10pct +2026-10-21T22:00:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,yip,loss_goodput,injected=10pct,mbit_s,6.4,netem_10pct +2026-10-21T22:00:30Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,tcp_throughput,streams=1,mbit_s,379.5,clean +2026-10-21T22:00:43Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,tcp_throughput,streams=4,mbit_s,455.7,clean +2026-10-21T22:00:54Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T22:00:54Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,udp_jitter,offered=200M,ms,0.055,clean +2026-10-21T22:00:54Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,udp_loss,offered=200M,pct,0.01,clean +2026-10-21T22:01:10Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,latency,idle,rtt_ms,54.876,clean +2026-10-21T22:01:10Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,latency,loaded,rtt_ms,57.976,saturated +2026-10-21T22:01:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,cpu_used,tcp_saturate,pct,22.4,sender_host_A_1vcpu +2026-10-21T22:01:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T22:01:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_goodput,injected=0pct,mbit_s,361.7,netem_0pct +2026-10-21T22:01:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-21T22:01:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_goodput,injected=1pct,mbit_s,2.7,netem_1pct +2026-10-21T22:02:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_effective,injected=3pct,pct,4,netem_3pct +2026-10-21T22:02:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_goodput,injected=3pct,mbit_s,1.1,netem_3pct +2026-10-21T22:02:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_effective,injected=5pct,pct,4,netem_5pct +2026-10-21T22:02:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-21T22:02:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_effective,injected=10pct,pct,9,netem_10pct +2026-10-21T22:02:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,wireguard,loss_goodput,injected=10pct,mbit_s,0.6,netem_10pct +2026-10-21T22:03:10Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,tcp_throughput,streams=1,mbit_s,19.6,clean +2026-10-21T22:03:23Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,tcp_throughput,streams=4,mbit_s,54.9,clean +2026-10-21T22:03:34Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T22:03:34Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,udp_jitter,offered=200M,ms,0.028,clean +2026-10-21T22:03:34Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,udp_loss,offered=200M,pct,0.39,clean +2026-10-21T22:03:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,latency,idle,rtt_ms,55.283,clean +2026-10-21T22:03:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,latency,loaded,rtt_ms,55.500,saturated +2026-10-21T22:03:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,cpu_used,tcp_saturate,pct,12.9,sender_host_A_1vcpu +2026-10-21T22:04:16Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T22:04:16Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_goodput,injected=0pct,mbit_s,24.3,netem_0pct +2026-10-21T22:04:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-21T22:04:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_goodput,injected=1pct,mbit_s,2.9,netem_1pct +2026-10-21T22:04:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T22:04:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_goodput,injected=3pct,mbit_s,1.4,netem_3pct +2026-10-21T22:05:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_effective,injected=5pct,pct,8,netem_5pct +2026-10-21T22:05:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-21T22:05:39Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_effective,injected=10pct,pct,7,netem_10pct +2026-10-21T22:05:39Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),ddc3108,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T23:23:06Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,tcp_throughput,streams=1,mbit_s,343.5,clean +2026-10-21T23:23:19Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,tcp_throughput,streams=4,mbit_s,679.9,clean +2026-10-21T23:23:30Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:23:30Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,udp_jitter,offered=200M,ms,0.003,clean +2026-10-21T23:23:30Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T23:23:47Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,latency,idle,rtt_ms,55.364,clean +2026-10-21T23:23:47Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,latency,loaded,rtt_ms,55.377,saturated +2026-10-21T23:23:54Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,cpu_used,tcp_saturate,pct,2.8,sender_host_A_1vcpu +2026-10-21T23:24:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:24:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_goodput,injected=0pct,mbit_s,267.0,netem_0pct +2026-10-21T23:24:35Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T23:24:35Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_goodput,injected=1pct,mbit_s,3.4,netem_1pct +2026-10-21T23:24:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-21T23:24:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_goodput,injected=3pct,mbit_s,1.9,netem_3pct +2026-10-21T23:25:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_effective,injected=5pct,pct,2,netem_5pct +2026-10-21T23:25:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-21T23:25:39Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_effective,injected=10pct,pct,8,netem_10pct +2026-10-21T23:25:39Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,baseline,loss_goodput,injected=10pct,mbit_s,0.8,netem_10pct +2026-10-21T23:25:53Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,tcp_throughput,streams=1,mbit_s,287.7,clean +2026-10-21T23:26:06Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,tcp_throughput,streams=4,mbit_s,228.9,clean +2026-10-21T23:26:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:26:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,udp_jitter,offered=200M,ms,0.008,clean +2026-10-21T23:26:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T23:26:35Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,latency,idle,rtt_ms,55.956,clean +2026-10-21T23:26:35Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,latency,loaded,rtt_ms,58.365,saturated +2026-10-21T23:26:42Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,cpu_used,tcp_saturate,pct,20.5,sender_host_A_1vcpu +2026-10-21T23:27:02Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:27:02Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_goodput,injected=0pct,mbit_s,207.8,netem_0pct +2026-10-21T23:27:23Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T23:27:23Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_goodput,injected=1pct,mbit_s,163.9,netem_1pct +2026-10-21T23:27:44Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_effective,injected=3pct,pct,1,netem_3pct +2026-10-21T23:27:44Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_goodput,injected=3pct,mbit_s,113.1,netem_3pct +2026-10-21T23:28:05Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T23:28:05Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_goodput,injected=5pct,mbit_s,70.3,netem_5pct +2026-10-21T23:28:27Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_effective,injected=10pct,pct,1,netem_10pct +2026-10-21T23:28:27Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,yip,loss_goodput,injected=10pct,mbit_s,45.6,netem_10pct +2026-10-21T23:28:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,tcp_throughput,streams=1,mbit_s,388.1,clean +2026-10-21T23:28:54Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,tcp_throughput,streams=4,mbit_s,592.5,clean +2026-10-21T23:29:05Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:29:05Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,udp_jitter,offered=200M,ms,0.014,clean +2026-10-21T23:29:05Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,udp_loss,offered=200M,pct,2.05,clean +2026-10-21T23:29:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,latency,idle,rtt_ms,59.623,clean +2026-10-21T23:29:22Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,latency,loaded,rtt_ms,59.683,saturated +2026-10-21T23:29:29Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,cpu_used,tcp_saturate,pct,10.1,sender_host_A_1vcpu +2026-10-21T23:29:49Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:29:49Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_goodput,injected=0pct,mbit_s,387.2,netem_0pct +2026-10-21T23:30:10Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_effective,injected=1pct,pct,3,netem_1pct +2026-10-21T23:30:10Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_goodput,injected=1pct,mbit_s,3.3,netem_1pct +2026-10-21T23:30:31Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-21T23:30:31Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_goodput,injected=3pct,mbit_s,1.3,netem_3pct +2026-10-21T23:30:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_effective,injected=5pct,pct,5,netem_5pct +2026-10-21T23:30:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_goodput,injected=5pct,mbit_s,1.0,netem_5pct +2026-10-21T23:31:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_effective,injected=10pct,pct,11,netem_10pct +2026-10-21T23:31:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,wireguard,loss_goodput,injected=10pct,mbit_s,0.6,netem_10pct +2026-10-21T23:31:28Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,tcp_throughput,streams=1,mbit_s,21.6,clean +2026-10-21T23:31:41Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,tcp_throughput,streams=4,mbit_s,26.3,clean +2026-10-21T23:31:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:31:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,udp_jitter,offered=200M,ms,0.026,clean +2026-10-21T23:31:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,udp_loss,offered=200M,pct,0.14,clean +2026-10-21T23:32:09Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,latency,idle,rtt_ms,55.419,clean +2026-10-21T23:32:09Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,latency,loaded,rtt_ms,56.097,saturated +2026-10-21T23:32:16Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,cpu_used,tcp_saturate,pct,3.8,sender_host_A_1vcpu +2026-10-21T23:32:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:32:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_goodput,injected=0pct,mbit_s,21.6,netem_0pct +2026-10-21T23:32:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_effective,injected=1pct,pct,3,netem_1pct +2026-10-21T23:32:57Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_goodput,injected=1pct,mbit_s,2.6,netem_1pct +2026-10-21T23:33:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_effective,injected=3pct,pct,5,netem_3pct +2026-10-21T23:33:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_goodput,injected=3pct,mbit_s,1.3,netem_3pct +2026-10-21T23:33:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_effective,injected=5pct,pct,4,netem_5pct +2026-10-21T23:33:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-21T23:34:02Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_effective,injected=10pct,pct,10,netem_10pct +2026-10-21T23:34:02Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),715c58a,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T23:54:13Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,tcp_throughput,streams=1,mbit_s,345.5,clean +2026-10-21T23:54:26Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,tcp_throughput,streams=4,mbit_s,815.0,clean +2026-10-21T23:54:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:54:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,udp_jitter,offered=200M,ms,0.003,clean +2026-10-21T23:54:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,udp_loss,offered=200M,pct,0.00,clean +2026-10-21T23:54:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,latency,idle,rtt_ms,55.470,clean +2026-10-21T23:54:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,latency,loaded,rtt_ms,55.473,saturated +2026-10-21T23:54:59Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,cpu_used,tcp_saturate,pct,2.2,sender_host_A_1vcpu +2026-10-21T23:55:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:55:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_goodput,injected=0pct,mbit_s,334.9,netem_0pct +2026-10-21T23:55:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_effective,injected=1pct,pct,2,netem_1pct +2026-10-21T23:55:36Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_goodput,injected=1pct,mbit_s,3.4,netem_1pct +2026-10-21T23:55:55Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-21T23:55:55Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_goodput,injected=3pct,mbit_s,2.5,netem_3pct +2026-10-21T23:56:13Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_effective,injected=5pct,pct,2,netem_5pct +2026-10-21T23:56:13Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_goodput,injected=5pct,mbit_s,1.1,netem_5pct +2026-10-21T23:56:32Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_effective,injected=10pct,pct,9,netem_10pct +2026-10-21T23:56:32Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,baseline,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct +2026-10-21T23:56:45Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,tcp_throughput,streams=1,mbit_s,128.8,clean +2026-10-21T23:56:58Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,tcp_throughput,streams=4,mbit_s,410.7,clean +2026-10-21T23:57:08Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:57:08Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,udp_jitter,offered=200M,ms,0.032,clean +2026-10-21T23:57:08Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,udp_loss,offered=200M,pct,0.10,clean +2026-10-21T23:57:24Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,latency,idle,rtt_ms,61.991,clean +2026-10-21T23:57:24Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,latency,loaded,rtt_ms,67.774,saturated +2026-10-21T23:57:31Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,cpu_used,tcp_saturate,pct,19.1,sender_host_A_1vcpu +2026-10-21T23:57:49Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-21T23:57:49Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_goodput,injected=0pct,mbit_s,65.1,netem_0pct +2026-10-21T23:58:08Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-21T23:58:08Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_goodput,injected=1pct,mbit_s,128.4,netem_1pct +2026-10-21T23:58:26Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_effective,injected=3pct,pct,0,netem_3pct +2026-10-21T23:58:26Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_goodput,injected=3pct,mbit_s,270.5,netem_3pct +2026-10-21T23:58:45Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_effective,injected=5pct,pct,0,netem_5pct +2026-10-21T23:58:45Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_goodput,injected=5pct,mbit_s,48.3,netem_5pct +2026-10-21T23:59:04Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_effective,injected=10pct,pct,1,netem_10pct +2026-10-21T23:59:04Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,yip,loss_goodput,injected=10pct,mbit_s,27.1,netem_10pct +2026-10-21T23:59:17Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,tcp_throughput,streams=1,mbit_s,408.4,clean +2026-10-21T23:59:30Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,tcp_throughput,streams=4,mbit_s,1137.5,clean +2026-10-21T23:59:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-21T23:59:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,udp_jitter,offered=200M,ms,0.014,clean +2026-10-21T23:59:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,udp_loss,offered=200M,pct,0.05,clean +2026-10-21T23:59:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,latency,idle,rtt_ms,56.423,clean +2026-10-21T23:59:56Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,latency,loaded,rtt_ms,56.610,saturated +2026-10-22T00:00:03Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,cpu_used,tcp_saturate,pct,36.5,sender_host_A_1vcpu +2026-10-22T00:00:21Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-22T00:00:21Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_goodput,injected=0pct,mbit_s,391.7,netem_0pct +2026-10-22T00:00:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_effective,injected=1pct,pct,0,netem_1pct +2026-10-22T00:00:40Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_goodput,injected=1pct,mbit_s,2.0,netem_1pct +2026-10-22T00:00:59Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_effective,injected=3pct,pct,3,netem_3pct +2026-10-22T00:00:59Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_goodput,injected=3pct,mbit_s,1.4,netem_3pct +2026-10-22T00:01:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_effective,injected=5pct,pct,8,netem_5pct +2026-10-22T00:01:18Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_goodput,injected=5pct,mbit_s,0.9,netem_5pct +2026-10-22T00:01:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_effective,injected=10pct,pct,12,netem_10pct +2026-10-22T00:01:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,wireguard,loss_goodput,injected=10pct,mbit_s,0.4,netem_10pct +2026-10-22T00:01:50Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,tcp_throughput,streams=1,mbit_s,42.7,clean +2026-10-22T00:02:03Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,tcp_throughput,streams=4,mbit_s,49.7,clean +2026-10-22T00:02:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,udp_throughput,offered=200M,mbit_s,200.0,clean +2026-10-22T00:02:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,udp_jitter,offered=200M,ms,0.039,clean +2026-10-22T00:02:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,udp_loss,offered=200M,pct,16.05,clean +2026-10-22T00:02:30Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,latency,idle,rtt_ms,62.518,clean +2026-10-22T00:02:30Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,latency,loaded,rtt_ms,66.531,saturated +2026-10-22T00:02:37Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,cpu_used,tcp_saturate,pct,24.8,sender_host_A_1vcpu +2026-10-22T00:02:55Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_effective,injected=0pct,pct,0,netem_0pct +2026-10-22T00:02:55Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_goodput,injected=0pct,mbit_s,40.9,netem_0pct +2026-10-22T00:03:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_effective,injected=1pct,pct,1,netem_1pct +2026-10-22T00:03:14Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_goodput,injected=1pct,mbit_s,2.8,netem_1pct +2026-10-22T00:03:33Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_effective,injected=3pct,pct,2,netem_3pct +2026-10-22T00:03:33Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_goodput,injected=3pct,mbit_s,1.4,netem_3pct +2026-10-22T00:03:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_effective,injected=5pct,pct,5,netem_5pct +2026-10-22T00:03:52Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_goodput,injected=5pct,mbit_s,0.8,netem_5pct +2026-10-22T00:04:12Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_effective,injected=10pct,pct,15,netem_10pct +2026-10-22T00:04:12Z,EC2 us-east-1 (t3.small)->EC2 us-west-2 (t3.small),840faa3,openvpn,loss_goodput,injected=10pct,mbit_s,0.5,netem_10pct diff --git a/crates/yip-bench/wan/results/readme-snippet.md b/crates/yip-bench/wan/results/readme-snippet.md index 7871ad47..939950d3 100644 --- a/crates/yip-bench/wan/results/readme-snippet.md +++ b/crates/yip-bench/wan/results/readme-snippet.md @@ -1,8 +1,8 @@ -*Two 1-vCPU Debian 13 VPSes, ~60.472 ms apart, 2026-10-20. yip trades raw clean-path throughput for loss resilience — see the FEC row.* +*Two 1-vCPU Debian 13 VPSes, ~55.536 ms apart, 2026-10-21. yip trades raw clean-path throughput for loss resilience — see the FEC row.* | | TCP 1-stream | UDP loss (200M) | effective loss @10% injected | |---|--:|--:|--:| -| **yip** | 128.9 Mbit/s | 0.00% | 0% | -| **WireGuard** | 326.6 Mbit/s | 0.07% | 12% | -| **OpenVPN** | 18.1 Mbit/s | 29.84% | 9% | +| **yip** | 128.8 Mbit/s | 0.10% | 1% | +| **WireGuard** | 408.4 Mbit/s | 0.05% | 12% | +| **OpenVPN** | 42.7 Mbit/s | 16.05% | 15% | diff --git a/crates/yip-bench/wan/run-wan-bench.sh b/crates/yip-bench/wan/run-wan-bench.sh index 6e81d1ea..21632ef0 100755 --- a/crates/yip-bench/wan/run-wan-bench.sh +++ b/crates/yip-bench/wan/run-wan-bench.sh @@ -10,6 +10,14 @@ SUITE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DUR="${DUR:-12}" # seconds per throughput run UDP_DUR="${UDP_DUR:-10}" +PING_COUNT="${PING_COUNT:-100}" +if [ -n "${PING_INTERVAL:-}" ]; then + PING_INTERVAL="$PING_INTERVAL" +elif [ "$PING_COUNT" -ge 1000 ] 2>/dev/null; then + PING_INTERVAL="0.005" +else + PING_INTERVAL="0.05" +fi LOSS_STEPS=(0 1 3 5 10) # injected % on the A->B outer path IPERF_PORT=5201 EGRESS="$(A "ip route get $HOST_B | grep -oE 'dev [a-z0-9]+' | awk '{print \$2}' | head -1")" @@ -102,7 +110,7 @@ loss_sweep() { # for L in "${LOSS_STEPS[@]}"; do netem_on "$L" local eff good - eff="$(Aq "ping -c 100 -i 0.05 -W 2 $2 | sed -nE 's/.* ([0-9]+)% packet loss.*/\1/p' | head -1")" + eff="$(Aq "ping -c "${PING_COUNT:-100}" -i "${PING_INTERVAL:-0.05}" -W 2 $2 | sed -nE 's/.* ([0-9.]+)% packet loss.*/\1/p' | head -1")" local j; j="$(A "iperf3 -c $2 -p $IPERF_PORT -t 12 -J 2>/dev/null")" good="$(rnd "$(jget end.sum_received.bits_per_second <<<"$j" 2>/dev/null || echo 0)" 1 1e6)" [[ "$good" == "0.0" ]] && good=NA diff --git a/crates/yip-transport/src/lossdetect.rs b/crates/yip-transport/src/lossdetect.rs index d2eb3b61..0653fe65 100644 --- a/crates/yip-transport/src/lossdetect.rs +++ b/crates/yip-transport/src/lossdetect.rs @@ -2,6 +2,25 @@ //! //! Turns a stream of seen/delivered packet counters into a [`LossReport`]. //! No I/O; pure logic. +//! +//! # Multi-Symbol Partial-Receive Tracking +//! +//! Multi-symbol FEC objects (where an object is split across multiple symbols) +//! can experience partial reception: +//! - When symbol 0 arrives but symbol 1 is lost, the counter remains in `pending` +//! awaiting delivery until `grace_ms` expires, where [`report`](LossDetector::report) +//! promotes it to `missing` and marks it in `resolved_set`. +//! - When repair symbols arrive and decode, [`on_delivered`](LossDetector::on_delivered) +//! consumes the object, removing the counter from `pending` and marking it resolved +//! so it is never reported missing. +//! +//! # Extreme Reordering Invariant +//! +//! The receiver bounds both its pending set and its out-of-order `resolved_set` to +//! `window` elements: +//! - If extreme reordering (> `window` packets) causes eviction from `resolved_set` +//! above the watermark, any late duplicate arriving generates a benign spurious +//! NACK that the sender's bounded `RetxBuffer` / TTL drops with zero side effects. #![forbid(unsafe_code)] use std::collections::BTreeMap; @@ -41,7 +60,11 @@ use crate::{LossReport, MAX_NACK}; /// This correctly reports: /// - Fully-absent counters (true sequence gaps — no symbol ever arrived). /// - Multi-symbol FEC objects that received ≥1 symbol but never decoded -/// (seen but undelivered after the grace window). +/// (seen but undelivered after the grace window). For example, when symbol 0 arrives +/// but symbol 1 is lost, the counter remains in `pending` awaiting delivery until +/// `grace_ms` expires, where `report()` promotes it to `missing` and marks it in +/// `resolved_set`. When repair symbols arrive and decode, `on_delivered` consumes the +/// object. /// - Single-symbol / zero-repair objects (a loss is always a full gap). /// /// Counters that arrive and are delivered within the grace window are silently @@ -59,6 +82,11 @@ use crate::{LossReport, MAX_NACK}; /// far as possible (consuming any consecutive prefix of `resolved_set`) so that /// `resolved_set` stays small even under sequential delivery. /// +/// **Reordering Invariant**: If extreme reordering (> `window` packets) causes eviction +/// from `resolved_set` above the watermark, any late duplicate arriving generates a +/// benign spurious NACK that the sender's bounded `RetxBuffer` / TTL drops with zero +/// side effects. +/// /// # Pending-set bound /// /// The pending set is also bounded to `window` entries; when it would exceed @@ -497,4 +525,84 @@ mod tests { r2.missing ); } + + #[test] + fn multisymbol_partial_receive_promoted_and_resolved() { + let mut d = LossDetector::new(5, 1024); + // Counter seen at t=0 (e.g. symbol 0 arrived, symbol 1 lost). + d.on_seen(42, 0); + + // Before grace expires (t=3 < 5): not missing. + assert!(d.report(3).missing.is_empty()); + + // At t >= grace_ms (t=10 >= 5): promoted to missing and marked resolved. + let r1 = d.report(10); + assert_eq!(r1.missing, vec![42]); + + // Duplicate arrives at t+100 (t=110). + d.on_seen(42, 110); + + // Even after another grace period (t=120 or beyond), not re-reported. + let r2 = d.report(120); + assert!( + r2.missing.is_empty(), + "counter 42 must not be re-reported after resolution; got {:?}", + r2.missing + ); + } + + #[test] + fn watermark_advances_over_resolved_prefix_under_partial_receive() { + let mut d = LossDetector::new(5, 1024); + assert_eq!(d.resolved_below, 0); + + // Counter 0: seen and delivered at t=0. + d.on_seen(0, 0); + d.on_delivered(0); + assert_eq!(d.resolved_below, 1); + assert!(d.resolved_set.is_empty()); + + // Counter 1: partial receive (symbol 0 received, symbol 1 lost; never delivered). + d.on_seen(1, 0); + + // Counter 2: delivered out of order ahead of counter 1. + d.on_seen(2, 0); + d.on_delivered(2); + // Watermark remains at 1 because counter 1 is still pending. + assert_eq!(d.resolved_below, 1); + assert_eq!(d.resolved_set.len(), 1); + assert!(d.resolved_set.contains_key(&2)); + + // At t=10 (>= grace_ms), report() promotes counter 1 to missing. + // Both 1 (promoted) and 2 (delivered) now form a contiguous resolved prefix [1, 2]. + let rep1 = d.report(10); + assert_eq!(rep1.missing, vec![1]); + + // resolved_below advances monotonically from 1 past 1 and 2 to 3, + // draining resolved_set and bounding memory. + assert_eq!(d.resolved_below, 3); + assert!( + d.resolved_set.is_empty(), + "resolved_set must be drained when watermark advances" + ); + + // Consecutive pattern: counter 3 is partial receive, counters 4 and 5 delivered. + d.on_seen(3, 10); + d.on_seen(4, 10); + d.on_delivered(4); + d.on_seen(5, 10); + d.on_delivered(5); + + assert_eq!(d.resolved_below, 3); + assert_eq!(d.resolved_set.len(), 2); + + // Grace expires for counter 3 at t=20. + let rep2 = d.report(20); + assert_eq!(rep2.missing, vec![3]); + assert_eq!(d.resolved_below, 6); + assert!( + d.resolved_set.is_empty(), + "resolved_set must be drained when watermark advances to 6" + ); + } }