From 3e2f8970fa90f53bf9b59b60171c9d13caa4e87c Mon Sep 17 00:00:00 2001 From: Frando Date: Fri, 10 Apr 2026 14:25:17 +0200 Subject: [PATCH] fix(netwatch): detect IPv6 default routes on Linux default_route() reads /proc/net/route first, which contains only IPv4 entries. When no IPv4 default route exists it returns Ok(None), and the code treated any Ok result as final without falling through to the netlink-based detection that handles both address families. On a v6-only network this made default_route_interface permanently None, which caused iroh's has_usable_network() to return false after network switches. The 5-second debounce timeout would expire before the QUIC stack learned about the change, leaving connections stuck on dead paths. The fix narrows the early return to Ok(Some(..)) so that Ok(None) falls through to the netlink path. --- netwatch/src/interfaces/linux.rs | 8 +++++--- netwatch/tests/patchbay.rs | 2 -- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/netwatch/src/interfaces/linux.rs b/netwatch/src/interfaces/linux.rs index 2cf55c75..0dce28a4 100644 --- a/netwatch/src/interfaces/linux.rs +++ b/netwatch/src/interfaces/linux.rs @@ -41,9 +41,11 @@ pub enum Error { } pub async fn default_route() -> Option { - let route = default_route_proc().await; - if let Ok(route) = route { - return route; + // /proc/net/route only contains IPv4 routes. If it finds one, return it. + // If it returns Ok(None) (no IPv4 default route) or Err (file unreadable), + // fall through to netlink which checks both IPv4 and IPv6. + if let Ok(Some(route)) = default_route_proc().await { + return Some(route); } #[cfg(target_os = "android")] diff --git a/netwatch/tests/patchbay.rs b/netwatch/tests/patchbay.rs index 139a46ba..0f11130d 100644 --- a/netwatch/tests/patchbay.rs +++ b/netwatch/tests/patchbay.rs @@ -51,7 +51,6 @@ async fn default_route_v4_only() -> TestResult { /// Netwatch detects a default route on a v6-only network. #[tokio::test] -#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"] async fn default_route_v6_only() -> TestResult { let state = state_for_routed_device(IpSupport::V6Only).await?; @@ -75,7 +74,6 @@ async fn default_route_dual_stack() -> TestResult { /// After replugging from a v4 router to a v6 router, netwatch detects the new /// default route. #[tokio::test] -#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"] async fn default_route_after_replug_v4_to_v6() -> TestResult { let lab = Lab::new().await?; let v4_router = lab