Skip to content

Commit fe7240d

Browse files
authored
fix(iroh): Use valid available IPv6 address, ignoring have_ipv6 (#3419)
## Description Sometimes net_report says `udp_v6: false`, even though I can clearly send and receive to/from the other iroh endpoint I want to talk to. Here are some logs from that situation: ``` 2025-07-29T10:15:43.281088Z DEBUG ep{me=68b3f4f4cc}:actor: iroh::net_report: generated report in 728ms report=Report { udp_v4: true, udp_v6: false, mapping_varies_by_dest_ipv4: Some(false), mapping_varies_by_dest_ipv6: None, preferred_relay: Some(RelayUrl("[https://euc1-1.relay.n0.iroh.iroh.link./")](https://euc1-1.relay.n0.iroh.iroh.link./%22))), relay_latency: RelayLatencies { ipv4: {RelayUrl("[https://aps1-1.relay.n0.iroh.iroh.link./"](https://aps1-1.relay.n0.iroh.iroh.link./%22)): 182.188896ms, RelayUrl("[https://euc1-1.relay.n0.iroh.iroh.link./"](https://euc1-1.relay.n0.iroh.iroh.link./%22)): 21.317467ms, RelayUrl("[https://use1-1.relay.n0.iroh.iroh.link./"](https://use1-1.relay.n0.iroh.iroh.link./%22)): 117.29417ms}, ipv6: {}, https: {} }, global_v4: Some(176.199.xxx.xxx:26608), global_v6: None, captive_portal: None } 2025-07-29T10:15:55.933530Z DEBUG ep{me=68b3f4f4cc}:disco_in{node=e10b5cdea1 src=Ip([2a01:599:b2f:a1ad:495a:8b14:xxxx:xxxx]:49154)}:handle_disco{dm=Pong(Pong { tx_id: TransactionId(0xBC381DA98EFD2B20CAE0A96E), ping_observed_addr: Udp([2a02:8071:d85:19c0:9676:965:xxxx:xxxx]:38391) })}:handle_pong{m=Pong { tx_id: TransactionId(0xBC381DA98EFD2B20CAE0A96E), ping_observed_addr: Udp([2a02:8071:d85:19c0:9676:965:xxxx:xxxx]:38391) } src=Udp([2a01:599:b2f:a1ad:495a:8b14:xxxx:xxxx]:49154)}: iroh::_events::holepunched: remote_node=e10b5cdea1 path=[2a01:599:b2f:a1ad:495a:8b14:xxxx:xxxx]:49154 direction="outgoing" ``` Now, the `udp_v6: false` value originally comes from `netwatch`'s `InterfaceState`, as it sees my (what is now and probably was at the time fe80-prefixed) link-local address and characterizes it as "not reachable from the public internet". But this PR instead ignores that value *iff* we have any `UdpSendAddr::Valid`, since that would mean it's an address that we've successfully sent and received from with the particular peer we care about. ## Change checklist <!-- Remove any that are not relevant. --> - [x] Self-review.
1 parent 36842d5 commit fe7240d

File tree

3 files changed

+17
-7
lines changed

3 files changed

+17
-7
lines changed

iroh/src/magicsock.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,8 +1879,13 @@ impl Actor {
18791879
continue;
18801880
};
18811881
let is_major = state.is_major_change(&current_netmon_state);
1882+
event!(
1883+
target: "iroh::_events::link_change",
1884+
Level::DEBUG,
1885+
?state,
1886+
is_major
1887+
);
18821888
current_netmon_state = state;
1883-
trace!("tick: link change {}", is_major);
18841889
self.msock.metrics.magicsock.actor_link_change.inc();
18851890
self.handle_network_change(is_major).await;
18861891
}
@@ -1912,11 +1917,11 @@ impl Actor {
19121917
}
19131918

19141919
async fn handle_network_change(&mut self, is_major: bool) {
1915-
debug!("link change detected: major? {}", is_major);
1920+
debug!(is_major, "link change detected");
19161921

19171922
if is_major {
19181923
if let Err(err) = self.network_change_sender.rebind() {
1919-
warn!("failed to rebind transports: {:?}", err);
1924+
warn!("failed to rebind transports: {err:?}");
19201925
}
19211926

19221927
#[cfg(not(wasm_browser))]

iroh/src/magicsock/node_map/node_state.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,22 +294,22 @@ impl NodeState {
294294
let (best_addr, relay_url) = match self.udp_paths.send_addr(have_ipv6) {
295295
UdpSendAddr::Valid(addr) => {
296296
// If we have a valid address we use it.
297-
trace!(%addr, "UdpSendAddr is valid, use it");
297+
trace!(%addr, ?have_ipv6, "UdpSendAddr is valid, use it");
298298
(Some(*addr), None)
299299
}
300300
UdpSendAddr::Outdated(addr) => {
301301
// If the address is outdated we use it, but send via relay at the same time.
302302
// We also send disco pings so that it will become valid again if it still
303303
// works (i.e. we don't need to holepunch again).
304-
trace!(%addr, "UdpSendAddr is outdated, use it together with relay");
304+
trace!(%addr, ?have_ipv6, "UdpSendAddr is outdated, use it together with relay");
305305
(Some(*addr), self.relay_url())
306306
}
307307
UdpSendAddr::Unconfirmed(addr) => {
308-
trace!(%addr, "UdpSendAddr is unconfirmed, use it together with relay");
308+
trace!(%addr, ?have_ipv6, "UdpSendAddr is unconfirmed, use it together with relay");
309309
(Some(*addr), self.relay_url())
310310
}
311311
UdpSendAddr::None => {
312-
trace!("No UdpSendAddr, use relay");
312+
trace!(?have_ipv6, "No UdpSendAddr, use relay");
313313
(None, self.relay_url())
314314
}
315315
};

iroh/src/magicsock/node_map/udp_paths.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ impl NodeUdpPaths {
132132
/// Returns the current UDP address to send on.
133133
pub(super) fn send_addr(&self, have_ipv6: bool) -> &UdpSendAddr {
134134
if !have_ipv6 {
135+
// If it's a valid address, it doesn't matter if our interface scan determined that we
136+
// "probably" don't have IPv6, because we clearly were able to send and receive a ping/pong over IPv6.
137+
if matches!(&self.best, UdpSendAddr::Valid(_)) {
138+
return &self.best;
139+
}
135140
return &self.best_ipv4;
136141
}
137142
&self.best

0 commit comments

Comments
 (0)