Replace the one-intermediate-peer relay with protocol-scoped connectivity graphs and precomputed shortest-path/ECMP forwarding snapshots. Independent transport readers forward opaque transit frames without a plugin or TUN round trip. Carry source, destination, a bounded hop limit and stable flow tags; preserve end-to-end WireGuard links across topology changes. Classify IP flows before encryption and preserve their tags through the WireGuard pending queue. Add offline four-agent path-change coverage, loop and isolation tests, and an opt-in release forwarding microbenchmark. Bump control/data ALPNs while preserving persistent network identities and state. Also include the pending Windows Mainline idle-timeout fix and its regression test, using a reproducible vendored dependency patch. Validation: fmt, workspace Clippy with warnings denied, and 307 release tests passed. Two pre-existing Windows SQLite wipe failures were excluded; public DHT and the manual benchmark remain ignored by default. The forwarding microbenchmark measured 103 ns (64 B) and 202 ns (1280 B) per transit packet, excluding encryption and socket I/O.
69 lines
2.7 KiB
Rust
69 lines
2.7 KiB
Rust
//! The real Mainline UDP receive loop must be quiet while idle on Windows too.
|
|
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
|
|
|
use std::net::Ipv4Addr;
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
use std::time::Duration;
|
|
|
|
use tracing_subscriber::layer::SubscriberExt;
|
|
|
|
struct SocketWarnings(Arc<AtomicUsize>);
|
|
|
|
impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for SocketWarnings {
|
|
fn on_event(&self, event: &tracing::Event<'_>, _: tracing_subscriber::layer::Context<'_, S>) {
|
|
if event.metadata().target() == "mainline::rpc::socket"
|
|
&& *event.metadata().level() <= tracing::Level::WARN
|
|
{
|
|
self.0.fetch_add(1, Ordering::Relaxed);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn an_idle_dht_socket_stays_quiet_and_still_answers_packets() {
|
|
// Mainline owns an OS thread, so a thread-local subscriber cannot see its
|
|
// events. This integration-test binary has just this test and installs
|
|
// its own subscriber; it cannot affect the application or other binaries.
|
|
let warnings = Arc::new(AtomicUsize::new(0));
|
|
tracing::subscriber::set_global_default(
|
|
tracing_subscriber::registry().with(SocketWarnings(Arc::clone(&warnings))),
|
|
)
|
|
.unwrap();
|
|
|
|
tokio::time::timeout(Duration::from_secs(5), async {
|
|
let dht = mainline::Dht::builder()
|
|
.no_bootstrap()
|
|
.server_mode()
|
|
.bind_address(Ipv4Addr::LOCALHOST)
|
|
.port(0)
|
|
.build()
|
|
.unwrap()
|
|
.as_async();
|
|
|
|
let address = dht.info().await.local_addr();
|
|
// Each info response comes from the actor after another receive-loop
|
|
// iteration. With no peers or packets, these exercise real socket
|
|
// read timeouts, without an arbitrary sleep or public bootstrap.
|
|
for _ in 0..8 {
|
|
assert_eq!(dht.info().await.local_addr(), address);
|
|
}
|
|
assert_eq!(warnings.load(Ordering::Relaxed), 0, "idle receive warnings");
|
|
|
|
let client = tokio::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0))
|
|
.await
|
|
.unwrap();
|
|
let ping = b"d1:ad2:id20:abcdefghijklmnopqrste1:q4:ping1:t4:ping1:y1:qe";
|
|
client.send_to(ping, address).await.unwrap();
|
|
let mut response = [0u8; 2048];
|
|
let (len, from) = client.recv_from(&mut response).await.unwrap();
|
|
assert_eq!(from, std::net::SocketAddr::V4(address));
|
|
let response = &response[..len];
|
|
assert!(response.windows(9).any(|field| field == b"1:t4:ping"));
|
|
assert!(response.windows(6).any(|field| field == b"1:y1:r"));
|
|
assert_eq!(warnings.load(Ordering::Relaxed), 0);
|
|
})
|
|
.await
|
|
.expect("the DHT receive loop keeps making progress");
|
|
}
|