Robert McMahon - 2026-07-03
  1. New evidence for the open UDP-L4S SourceForge ticket — RunUDPL4S() (lines 1290–1365 vs. 1410–1415)

This is the piece that ties directly back to the ticket I filed earlier (client's relayed "Server Report" showing a garbage ~UINT32_MAX Lost count). RunUDPL4S()'s main loop sends a small per-packet L4S ack (struct udp_l4s_ack, via sendmsg() at line 1356) on the same connected UDP socket throughout the transfer. Only after that loop ends does write_UDP_AckFIN() (line 1415) send the much larger, structurally different final stats packet (struct server_hdr) that the client's AwaitServerFinPacket() parses into the "Server Report" line.

On the client side, ack_poll() (Client.cpp) reads udp_l4s_ack-sized packets off that same socket throughout the run. If the server's last per-packet ack is still in flight (or queued in the socket's receive buffer) right as the loop ends and write_UDP_AckFIN() fires, the client's dedicated single read() in AwaitServerFinPacket() could consume that leftover small udp_l4s_ack instead of the real server_hdr — and blindly reinterpreting a udp_l4s_ack's bytes as a server_hdr would produce exactly the kind of nonsense field values (wrong flags, garbage error_cnt) I saw in the original investigation. This significantly narrows down the root cause from "some stray packet" to "the L4S per-packet ack channel racing the final-stats handoff on the same socket." Worth adding to the ticket, but I'd want to actually reproduce with this specific hypothesis (e.g. instrument for udp_l4s_ack vs server_hdr sized reads) before calling it confirmed.