fix: UDP port allocation race causing link self-loop (one-way links)

PortManager.get_free_udp_port had an unguarded find-then-add sequence.
A link allocates both ends concurrently (asyncio.gather in
UDPLink._prepare -> two POST /ports/udp) and FastAPI runs the sync
route handler in a threadpool, so both threads could probe the same
'free' port before either recorded it — handing lport == rport to both
ends. uBridge sets SO_REUSEADDR on UDP NIO sockets, so the double bind
succeeds silently and the kernel delivers everything to the last-bound
socket: one node starves, the other echoes to itself.

Make every TCP/UDP allocate/reserve/release path atomic with an RLock,
and rebuild _link_data in UDPLink._prepare so reset() commits the fresh
port pair instead of re-sending the stale, already-released one.

Regression tests: threaded barrier allocation never returns duplicates
(red on the old code, UDP and TCP); reset() leaves exactly one mirrored
NIO pair per side with lport != rport (red on the old code).
This commit is contained in:
YueGuobin committed 2026-08-14 21:31:58 +08:00
1 parent 1cddeb8c3f
commit 350f2b24e7
7 files changed
+285 -89

No files matched your search

+7
View File
@@ -98,6 +98,13 @@ class UDPLink(Link):
tuples, ready to be POSTed to each node's compute.
"""
# Start from a clean slate: reset() re-creates the link on the same
# object (delete() + create()), and _commit_nios()/update() always
# address indices 0/1 — appending onto the previous run's entries
# would re-commit the stale (already released) port pair and orphan
# the freshly allocated ports.
self._link_data = []
node1 = self._nodes[0]["node"]
adapter_number1 = self._nodes[0]["adapter_number"]
port_number1 = self._nodes[0]["port_number"]