v0.3.6
Third round from the 2026-08 stability audit: TCP hot-path hygiene and long-run state bounds. Verified per-commit on Python 3.9 with the full test suite (4167 tests) and smoke-tested end-to-end against a physical iOS device over the pymobiledevice3 userspace tunnel (zero stranded tasks, latency unchanged at ~0.8 s).
Highlights
- Out-of-order queue hygiene. Entries stranded below RCV.NXT by a re-segmented retransmission were unreachable by the exact-key consume path: each pinned its full packet buffer for the session's remaining life and kept being advertised as a SACK block below the peer's SND.UNA — which RFC 2883 peers parse as DSACK, wrongly undoing their cwnd reductions. The consume path now purges them, and the queue is bounded (64 entries, Linux
tcp_prune_ofo_queue-style) so disjoint tiny out-of-order segments can no longer pin one buffer per in-window byte. - Tail Loss Probe actually fires under long SRTT. The RFC 8985 §7.2 "do not outlast RTO" clamp read a timer field that no longer exists, so it was dead code: whenever 2×SRTT reached past the remaining RTO, the RTO always preempted the probe and every tail loss degraded to a full RTO + slow-start. The clamp now reads the live per-session retransmit deadline.
- Datagram rx queues are bounded (512 datagrams, POSIX full-buffer newest-drop). Worst prior case: the IPv4 ACD defense socket buffered every ARP frame on the segment between DHCP BOUND polls — up to T1, i.e. hours.
- Slow-growth state tables fixed for the runs-for-days profile: the IPv6 DAD registry now drops entries when their address is removed (each rotated RFC 8981 temp address previously leaked one forever); the TCP Fast-Open negative cache is FIFO-bounded like the cookie cache; timed-out default routers are removed per RFC 4861 §6.3.5 instead of merely hidden (an on-link RA spoofing attacker could previously grow the list without bound).