mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-06 03:28:24 +00:00
A NAT traversal binds its base socket with a plain port-zero bind, and a successful punch adopts that socket as the peer's transport. Connected-UDP activation then opens a per-peer connected socket on the transport's local address. Linux admits that second bind only when the socket already holding the port set a reuse flag, and the traversal socket set neither, so every activation attempt failed with EADDRINUSE at bind. The rx-loop tick retried it on every pass, and the peer never left the unconnected path. Set SO_REUSEPORT and SO_REUSEADDR inside UdpRawSocket::adopt, after the socket is already bound. The order matters. Flags set before a port-zero bind let the kernel hand out a port another flagged socket already holds, so two concurrent traversals could share a port and the kernel would silently split one peer's datagrams across two transports. Flags set after the bind cannot change which port the socket was given; they only let a later socket join it. The listen socket in UdpRawSocket::open already sets its flags after its bind for the same reason. adopt has one caller chain, which ends at traversal adoption, so the STUN probe and configured listeners are untouched. The flags are best-effort, as in open: if setting them fails, adoption still succeeds and only the connected fast path is refused, rather than a working punched peer being torn down. Two tests cover the change, and both fail without it. The first adopts a socket bound the way the traversal path binds, checks that it arrives with neither flag, and requires a connected socket to open on its port and both flags to read back; without the change the open fails with EADDRINUSE at bind. The second adopts a traversal socket into a node peered with a node on a configured listener, runs activation on both, and requires a connected socket on each side, the adopted side's on the adopted socket's own port; without the change the configured side activates, the adopted side does not, and the failure carries the bind error from a direct open. Each part of the change was also removed in turn. Without SO_REUSEPORT the first test fails on that readback while the connected open still succeeds, because either flag on the holder admits the join; without SO_REUSEADDR it fails on that readback the same way; without both, both tests fail at the bind. Flagging the socket before adoption fails the first test's precondition, and running the second with FIPS_CONNECTED_UDP=0 fails its configured-listener assertion first, so neither can pass vacuously. Not established: Darwin behaviour. adopt is shared by the Unix backends, so macOS builds get the flags too, and no measurement covers SO_REUSEPORT set after bind there. FIPS_MACOS_CONNECTED_UDP=0 or FIPS_CONNECTED_UDP=0 turns the fast path off without a rebuild.