A user's log showed the same two failures on all 9 connects, 198 route errors
total:
Added 0 bypass routes via 192.168.88.1 (if_index=11)
Could not find ostp_tun index in routing table after 15s — traffic will NOT be captured
Two independent bugs in the Windows route layer:
1. The TUN interface index was looked up by matching FriendlyName == "ostp_tun"
through GetAdaptersAddresses. WinTun does not set the FriendlyName to the
adapter name, so the match never succeeded — every connect burned the full
15s window and gave up. The tun crate hands the real index back directly via
AbstractDevice::tun_index() (WinTun's own adapter index), which is instant and
correct; the name lookup remains only as a fallback.
2. Every route add — the server-IP bypass and the TUN default route alike — went
through the legacy CreateIpForwardEntry, which failed with error 160
(ERROR_BAD_ARGUMENTS) on this machine for all of them. With the server-IP
bypass never installed, the server's own packets were routed INTO the tunnel:
a loop that stalls the link for seconds under load (the reported "VPN drops
~8s into a game" — the tunnel never actually disconnected, it froze; the log
showed gap recovery skipping up to 402 frames with no packet loss on the
wire). add_ipv4_route now shells to route.exe, which resolves the interface
and validates the gateway itself and is already what the teardown path uses;
its command form was verified to be accepted (fails only on elevation, not
syntax). CREATE_NO_WINDOW keeps it from flashing a console per route.
Cannot be verified without the user's elevated TUN environment; the next log
will read "Added N bypass routes" and "Default route via TUN ... added" instead
of the failures.
Ported only the fixes that actually apply to the pre-refactor base
(the handshake fixes 6eb7b36/d65af35 fix bugs the 0.3.1 multi-server
refactor introduced into the new outbounds/ostp.rs; the base bridge.rs
already waits for the handshake response with retransmit + NAT64
fallback, so they are intentionally skipped).
- EMFILE (922cf0b): rlimit::increase_nofile_limit at CLI startup.
- Logs (1151726): UoT connect/disconnect → debug; rate-limit the
unauthorized-probe log to one line / ~30s so a junk/probe flood can't
spam the log (and a client running junk-over-UDP can't self-ban).
- Helper lifecycle + bypass routes (b6e78c1):
* ostp-tun-helper forces std::process::exit(0) after run_server so the
WinTun adapter and its metric-0 default route are reclaimed instead
of lingering as a zombie that breaks the next connect.
* windows_route: delete_routes_for_dest() purges stale /32s, dedupe
bypass IPs, and log add failures at warn!.
* windows: retry tun::create through the transient ERROR_INVALID_
PARAMETER window and widen the adapter-index lookup to ~15s.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>