Skip to content

Commit 42d8b42

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 45080f0 commit 42d8b42

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
@@ -72,7 +72,6 @@ async fn default_route_v4_only() -> TestResult {
7272
/// `/proc/net/route` contains only IPv4 entries, so the default route must be
7373
/// detected through the netlink fallback.
7474
#[tokio::test]
75-
#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"]
7675
async fn default_route_v6_only() -> TestResult {
7776
let (default_route, have_v4, have_v6) =
7877
with_device(RouterPreset::IspV6, IpSupport::V6Only).await?;
@@ -108,7 +107,6 @@ async fn default_route_dual_stack() -> TestResult {
108107
/// iroh considers a network usable when `default_route_interface.is_some() &&
109108
/// (have_v4 || have_v6)`. On a v6-only network this must return true.
110109
#[tokio::test]
111-
#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"]
112110
async fn has_usable_network_v6_only() -> TestResult {
113111
let (default_route, have_v4, have_v6) =
114112
with_device(RouterPreset::IspV6, IpSupport::V6Only).await?;
@@ -124,7 +122,6 @@ async fn has_usable_network_v6_only() -> TestResult {
124122
/// After replugging from a v4 router to a v6 router, netwatch detects the new
125123
/// default route.
126124
#[tokio::test]
127-
#[ignore = "default_route() does not fall through to netlink when /proc/net/route has no IPv4 default"]
128125
async fn default_route_after_replug_v4_to_v6() -> TestResult {
129126
let lab = Lab::new().await?;
130127
let v4_router = lab

0 commit comments

Comments
 (0)