From 1995f3770f437df36bfd6f1dabff7fac4bcfa4d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 22:38:09 +0000 Subject: [PATCH] fix(wgbridge): rebind UDP sockets to a fresh ephemeral port gotatun 0.10 records the port it actually bound, so Tunnel::rebind() restored that concrete port and re-bound it while the old socket could still be held by tasks that were shutting down. This failed intermittently with EADDRINUSE, as in the instrumented test tunnelStatsRebindAndStopUseRealJni on master. End the toggle on port 0 instead so each rebind gets a fresh ephemeral port. Claude-Session: https://claude.ai/code/session_012jPc9ojxUPCR3f3hR4ycTP --- wgbridge-rs/src/tunnel.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/wgbridge-rs/src/tunnel.rs b/wgbridge-rs/src/tunnel.rs index c25118d55..79c6f6625 100644 --- a/wgbridge-rs/src/tunnel.rs +++ b/wgbridge-rs/src/tunnel.rs @@ -299,13 +299,17 @@ impl Tunnel { let device = guard.as_ref().ok_or("tunnel stopped")?; device .write(async |d| { - // Toggling the port marks the connection dirty so the + // Changing the port marks the connection dirty so the // write commit re-runs Connection::set_up, which re-binds - // through ProtectedUdpFactory. The final value is the - // original port again. - let port = d.listen_port(); - d.set_listen_port(if port == 0 { 1 } else { 0 }); - d.set_listen_port(port); + // through ProtectedUdpFactory. Since gotatun 0.10 the + // device remembers the port it actually bound, so restoring + // it would re-bind that exact port while the old socket may + // still be closing (EADDRINUSE). End on 0 instead to get a + // fresh ephemeral port; peers roam to it on the next packet. + if d.listen_port() == 0 { + d.set_listen_port(1); + } + d.set_listen_port(0); }) .await .map_err(|e| e.to_string())?;