Skip to content

Commit d3ddb83

Browse files
committed
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.
1 parent 51ac57a commit d3ddb83

2 files changed

Lines changed: 5 additions & 6 deletions

File tree

netwatch/src/interfaces/linux.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ pub enum Error {
4141
}
4242

4343
pub async fn default_route() -> Option<DefaultRouteDetails> {
44-
let route = default_route_proc().await;
45-
if let Ok(route) = route {
46-
return route;
44+
// /proc/net/route only contains IPv4 routes. If it finds one, return it.
45+
// If it returns Ok(None) (no IPv4 default route) or Err (file unreadable),
46+
// fall through to netlink which checks both IPv4 and IPv6.
47+
if let Ok(Some(route)) = default_route_proc().await {
48+
return Some(route);
4749
}
4850

4951
#[cfg(target_os = "android")]

netwatch/tests/patchbay.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ async fn default_route_v4_only() -> TestResult {
7676
/// `/proc/net/route` contains only IPv4 entries, so the default route must be
7777
/// detected through the netlink fallback.
7878
#[tokio::test]
79-
#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"]
8079
async fn default_route_v6_only() -> TestResult {
8180
let (default_route, have_v4, have_v6) =
8281
with_device(RouterPreset::IspV6, IpSupport::V6Only).await?;
@@ -112,7 +111,6 @@ async fn default_route_dual_stack() -> TestResult {
112111
/// iroh considers a network usable when `default_route_interface.is_some() &&
113112
/// (have_v4 || have_v6)`. On a v6-only network this must return true.
114113
#[tokio::test]
115-
#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"]
116114
async fn has_usable_network_v6_only() -> TestResult {
117115
let (default_route, have_v4, have_v6) =
118116
with_device(RouterPreset::IspV6, IpSupport::V6Only).await?;
@@ -128,7 +126,6 @@ async fn has_usable_network_v6_only() -> TestResult {
128126
/// After replugging from a v4 router to a v6 router, netwatch detects the new
129127
/// default route.
130128
#[tokio::test]
131-
#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"]
132129
async fn default_route_after_replug_v4_to_v6() -> TestResult {
133130
let lab = Lab::new().await?;
134131
let v4_router = lab

0 commit comments

Comments
 (0)