diff --git a/Cargo.lock b/Cargo.lock index 7388607..013fbea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4212,7 +4212,7 @@ dependencies = [ "hmac 0.13.0", "iroh", "mainline", - "netdev 0.45.1", + "netdev 0.46.3", "netwatch", "postcard", "rand 0.10.3", diff --git a/crates/tsunagi-cli/src/main.rs b/crates/tsunagi-cli/src/main.rs index 6691e2c..091a9b3 100644 --- a/crates/tsunagi-cli/src/main.rs +++ b/crates/tsunagi-cli/src/main.rs @@ -2810,7 +2810,7 @@ fn member_row(row: &MemberRow<'_>) -> report::Row { // ordinary condition, so it is stated rather than flagged. let mut detail = "offline".to_string(); if let Some(v4) = row.overlay_address_v4 { - detail.push_str(&format!(" · {v4} still reserved for it")); + detail = format!("{v4:<15} offline · {v4} still reserved for it"); } let out = Row::new(Health::Info, row.label(), detail); return if row.failed_dials > 0 { @@ -2829,13 +2829,14 @@ fn member_row(row: &MemberRow<'_>) -> report::Row { let tunnel_up = row.tunnel.is_some_and(|tunnel| tunnel.is_up()); let has_overlay = row.tunnel.is_some(); - let mut detail = transport.to_lowercase(); + let mut detail = String::new(); + if let Some(v4) = row.overlay_address_v4 { + detail.push_str(&format!("{v4:<15} ")); + } + detail.push_str(&transport.to_lowercase()); if let Some(rtt) = row.rtt_ms { detail.push_str(&format!(" rtt {rtt}ms")); } - if let Some(v4) = row.overlay_address_v4 { - detail.push_str(&format!(" · {v4}")); - } let health = if !direct || (has_overlay && !tunnel_up) { Health::Degraded @@ -2851,22 +2852,24 @@ fn member_row(row: &MemberRow<'_>) -> report::Row { &tunnel.protocol }; out = out.with_note(match tunnel.handshake_secs_ago { - Some(secs) => format!( - "tunnel up ({proto}), {} tx {} rx {}{} · {}", - if proto == "tcp-tls" { - format!("uptime {secs}s,") - } else { - format!("handshake {secs}s ago,") - }, - tunnel.tx_packets, - tunnel.rx_packets, - if tunnel.dropped > 0 { - format!(", {} dropped", tunnel.dropped) - } else { - String::new() - }, - tunnel.path - ), + Some(secs) => { + let stats = format!( + "tunnel up ({proto}), {} tx {} rx {}{}", + if proto == "tcp-tls" { + format!("uptime {secs}s,") + } else { + format!("handshake {secs}s ago,") + }, + tunnel.tx_packets, + tunnel.rx_packets, + if tunnel.dropped > 0 { + format!(", {} dropped", tunnel.dropped) + } else { + String::new() + }, + ); + format!("{stats:<48} · {}", tunnel.path) + } None => { if proto == "wg-quic" { "no WireGuard handshake yet; the tunnel cannot carry traffic".to_string() @@ -3408,7 +3411,16 @@ fn program_path() -> String { async fn netwatch_addresses() -> Vec { // Best effort; used for diagnostics only. let state = netwatch::interfaces::State::new().await; - let mut addresses = state.local_addresses.regular; + let mut addresses = Vec::new(); + for (name, iface) in &state.interfaces { + let name_lower = name.to_lowercase(); + if name_lower.starts_with("tsun") || name_lower.contains("tsunagi") { + continue; + } + for prefix in iface.addrs() { + addresses.push(prefix.addr()); + } + } addresses.sort(); addresses.dedup(); addresses diff --git a/crates/tsunagi-tcp-tls/src/plugin.rs b/crates/tsunagi-tcp-tls/src/plugin.rs index 8a0daf0..7f986c5 100644 --- a/crates/tsunagi-tcp-tls/src/plugin.rs +++ b/crates/tsunagi-tcp-tls/src/plugin.rs @@ -66,6 +66,10 @@ pub fn local_ip_candidates() -> Vec { let mut ips = Vec::new(); for iface in netdev::get_interfaces() { if iface.is_up() && !iface.is_loopback() { + let name_lower = iface.name.to_lowercase(); + if name_lower.starts_with("tsun") || name_lower.contains("tsunagi") { + continue; + } for ip in iface.ipv4 { let addr = ip.addr(); if !addr.is_loopback() && !addr.is_unspecified() { diff --git a/crates/tsunagi/Cargo.toml b/crates/tsunagi/Cargo.toml index 470ef89..1c50875 100644 --- a/crates/tsunagi/Cargo.toml +++ b/crates/tsunagi/Cargo.toml @@ -22,7 +22,6 @@ tun-device = [ "dep:rtnetlink", "dep:caps", "dep:futures-util", - "dep:netdev", ] # Telling the operating system where to send its DNS questions. systemd-resolved # on Linux, the Name Resolution Policy Table on Windows; the zone and the @@ -67,6 +66,7 @@ directories = "6.0" # forbidden `unsafe` and will not make one. gethostname = "1.1" netwatch = "0.19.3" +netdev = "0.46" mainline = "8.0" futures-lite = "2.6" tun = { version = "0.8", features = ["async"], optional = true } @@ -93,7 +93,6 @@ futures-util = { version = "0.3", default-features = false, optional = true } # before deciding what to change. Creating an adapter needs the process to be # elevated and `wintun.dll` to be reachable at run time. [target.'cfg(target_os = "windows")'.dependencies] -netdev = { version = "0.45", optional = true } windows-sys = { version = "0.61", features = [ "Win32_Security", "Win32_Security_Authorization", diff --git a/crates/tsunagi/src/net.rs b/crates/tsunagi/src/net.rs index 6fdbebf..c3ab9a1 100644 --- a/crates/tsunagi/src/net.rs +++ b/crates/tsunagi/src/net.rs @@ -240,6 +240,39 @@ impl EndpointAdapter { TransportPolicy::N0Defaults => builder.preset(presets::N0), }; + if matches!( + config.transport, + TransportPolicy::N0Defaults | TransportPolicy::DirectOnly + ) { + let filter = iroh::address_lookup::AddrFilter::new(|addrs| { + let mut overlay_ips = std::collections::HashSet::new(); + for iface in netdev::get_interfaces() { + let name = iface.name.to_lowercase(); + if name.starts_with("tsun") || name.contains("tsunagi") { + for ip in iface.ipv4 { + overlay_ips.insert(std::net::IpAddr::V4(ip.addr())); + } + for ip in iface.ipv6 { + overlay_ips.insert(std::net::IpAddr::V6(ip.addr())); + } + } + } + if overlay_ips.is_empty() { + return std::borrow::Cow::Borrowed(addrs); + } + let filtered: Vec = addrs + .iter() + .filter(|addr| match addr { + iroh::TransportAddr::Ip(sock) => !overlay_ips.contains(&sock.ip()), + _ => true, + }) + .cloned() + .collect(); + std::borrow::Cow::Owned(filtered) + }); + builder = builder.addr_filter(filter); + } + #[cfg(feature = "testing")] if let Some(transport) = &config.test_quic_transport { builder = builder.transport_config(transport.clone());