How iroh Net Report Optimizes Connection Performance: Network-Aware Relay Selection
The iroh net report feature continuously probes network conditions to detect UDP reachability, identify stable NAT mappings, select the lowest-latency relay, and detect captive portals, enabling the protocol stack to choose the fastest available path for peer-to-peer connections.
The iroh crate in the n0-computer/iroh repository provides a sophisticated network reporting subsystem that transforms raw connectivity measurements into actionable routing decisions. By analyzing real-time data from QUIC address discovery probes and relay latency measurements, the net report feature eliminates guesswork from connection establishment, ensuring that the protocol always prefers the most efficient transport path available.
Detecting UDP and QUIC Reachability
The first optimization layer determines whether direct UDP communication is possible. The net report runs QUIC address discovery (QAD) round-trips to verify IPv4 and IPv6 connectivity, storing results in udp_v4 and udp_v6 boolean fields.
In iroh/src/net_report/report.rs, the Report::has_udp method checks these flags:
// From iroh/src/net_report/report.rs
pub fn has_udp(&self) -> bool {
self.udp_v4 || self.udp_v6
}
When has_udp() returns true, the stack knows it can establish direct QUIC connections, allowing it to skip costly fallback paths like HTTPS relays. This detection happens continuously, so the system adapts immediately when network conditions change.
Analyzing NAT Mapping Stability
The report identifies whether the host's NAT mapping is stable across different destinations. By comparing public addresses observed when probing different relays, the system sets mapping_varies_by_dest_ipv4 and mapping_varies_by_dest_ipv6 flags.
The Report::mapping_varies_by_dest method in iroh/src/net_report/report.rs merges these observations:
// Implementation merges optional booleans to determine stability
pub fn mapping_varies_by_dest(&self) -> bool {
self.mapping_varies_by_dest_ipv4.unwrap_or(false)
|| self.mapping_varies_by_dest_ipv6.unwrap_or(false)
}
When mappings are stable, the client can reuse discovered addresses without redundant probes, reducing connection setup latency and network overhead.
Selecting the Fastest Relay
Net report maintains a rolling history of relay latencies using a 5-minute window. Each probe reports timing data via RelayLatencies, and the system aggregates this to identify the preferred relay—the one with the lowest recent latency.
According to the source code in iroh/src/net_report.rs, the function add_report_history_and_set_preferred_relay merges historic measurements and updates the preferred relay selection:
// From iroh/src/net_report.rs#L47-L84
fn add_report_history_and_set_preferred_relay(&mut self, report: &Report) {
// Updates latency history and sets preferred_relay based on
// the lowest recent latency measurements
}
This minimizes round-trip time for subsequent data transfers by ensuring packets route through the geographically and topologically nearest healthy relay.
Captive Portal Detection
To avoid connection stalls behind restricted networks, the net report optionally checks for captive portals on the first report. The check_captive_portal function in iroh/src/net_report/reportgen.rs fetches /generate_204 from each relay:
// From iroh/src/net_report/reportgen.rs#L61-L66
async fn check_captive_portal(&self) -> bool {
// Returns true if response is not 204 No Content, indicating
// a captive portal is intercepting traffic
}
If the response is not 204 No Content, the client assumes a captive portal is blocking traffic and can adapt its retry logic, delaying heavy uploads until the network clears.
Incremental Reporting for Efficiency
Rather than running full network scans continuously, the net report uses an incremental architecture. The Client::get_report method in iroh/src/net_report.rs decides between full and incremental scans based on elapsed time, previous results, and current UDP status:
// From iroh/src/net_report.rs#L99-L108
pub async fn get_report(&mut self) -> Result<Report> {
// Logic to determine if full scan needed or if we can use
// incremental updates based on staleness
}
This approach reduces bandwidth and CPU usage while keeping latency data fresh, as subsequent reports only probe relays that have changed or when network state appears stale.
Accessing Net Report Data via the API
Applications can query the current report through the public API exposed in iroh/src/endpoint.rs. The Endpoint::net_report() method returns a watcher for the latest Report, allowing programmatic access to performance metrics.
Here is a practical example for optimizing connection logic:
use iroh::endpoint::Endpoint;
use std::time::Duration;
// Create an endpoint (using default config here)
let ep = Endpoint::new().await?;
// Request a fresh network report – this runs the probes in the background
let report = ep.net_report().initialized().await;
// Use the preferred relay for a new upload
if let Some(relay) = report.preferred_relay {
println!("Best relay is {}", relay);
// e.g. pass the relay URL to a transfer builder
// let tx = ep.send_file(...).relay(relay);
}
// Check whether UDP is usable (QUIC) – if false, fall back to HTTPS only
if report.has_udp() {
println!("UDP reachable – we can use QUIC");
} else {
println!("UDP not reachable – will use HTTPS latency probes only");
}
// Detect captive‑portal status
if let Some(true) = report.captive_portal {
println!("A captive portal is blocking traffic – delay heavy uploads");
}
This pattern allows applications to prefill connection attempts with the best-known relay address, toggle transport choices between QUIC and HTTPS based on UDP reachability, and react to captive portal detection before attempting heavy transfers.
Summary
The iroh net report subsystem provides continuous network intelligence that drives connection optimization:
- UDP Detection:
Report::has_udpiniroh/src/net_report/report.rsverifies direct QUIC capability, enabling the stack to bypass costly fallbacks. - NAT Stability Analysis:
mapping_varies_by_destfields identify stable mappings for address reuse, reducing probe overhead. - Intelligent Relay Selection: A 5-minute latency history maintained via
add_report_history_and_set_preferred_relayiniroh/src/net_report.rsensures traffic routes through the fastest available relay. - Captive Portal Awareness:
check_captive_portaliniroh/src/net_report/reportgen.rsprevents stalled connections behind restricted networks. - Incremental Efficiency:
Client::get_reportoptimizes scan frequency to minimize resource usage while maintaining accurate metrics.
Frequently Asked Questions
What is iroh net report?
The iroh net report is a continuous network measurement subsystem within the n0-computer/iroh repository that probes UDP reachability, relay latencies, and NAT mappings to provide real-time connection optimization data. It exposes this information through the Endpoint::net_report() API, allowing applications to make informed routing decisions based on current network conditions.
How does iroh detect captive portals?
Iroh detects captive portals by requesting /generate_204 from each relay during the initial report generation, as implemented in iroh/src/net_report/reportgen.rs. If the response code is not 204 No Content, the system sets the captive_portal flag in the Report struct, signaling that the network may be intercepting traffic and requiring alternative connection strategies.
What is the preferred relay in iroh?
The preferred relay is the relay server with the lowest recent latency as determined by a rolling 5-minute measurement window. The add_report_history_and_set_preferred_relay function in iroh/src/net_report.rs updates this selection after each report, ensuring that subsequent connections use the currently fastest available relay path.
How does iroh handle NAT mapping variations?
Iroh tracks NAT mapping stability by comparing public addresses observed when probing different relays, storing results in mapping_varies_by_dest_ipv4 and mapping_varies_by_dest_ipv6 fields. The Report::mapping_varies_by_dest method in iroh/src/net_report/report.rs exposes this data, allowing the stack to determine whether it can reuse discovered addresses or must perform additional discovery probes for each new destination.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →