ares-novacom: two fixes to --forward — throughput and a forward that cannot tell a dead tunnel from an idle one - #18
Merged
Conversation
The pump waited 50ms on the device after every buffer it sent, whether or not the device had anything to say. On a busy connection that is 50ms per 16KB, which caps a forward at a few hundred KB/s: 5MB through a forward took 32s. Poll for 1ms while bytes are still moving and fall back to 50ms once both sides go quiet, so an idle connection still does not spin. The same 5MB now takes 1.9s, about 2.6 MB/s, measured against a UP7560. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gde2tcn5SbUHKhesuTNB7a
A forward found running on a workstation had spent two hours pointed at a TV that had stopped listening: the host still called the socket ESTABLISHED, the TV had no such connection, and the forwarded port was not open on the device. Nothing looked wrong from the forward's side, so it went on waiting for connections that could no longer arrive, and the person relying on it had no way to tell. That is the shape of a device dropping off Wi-Fi rather than closing the session: no FIN, no RST, nothing for libssh to report. The connection reads as live because as far as TCP is concerned it is. Keep it alive so the silence becomes an error - probe after 30s idle, every 10s, three times - and the socket fails instead of lying. Verified with `ss -tno` that the timer is armed on the session socket. Windows counts its own probe retries, so the retry count is set only where there is a knob for it; that path is written but not built here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gde2tcn5SbUHKhesuTNB7a
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent fixes to
--forwardas it ships today. Split out of #17, which builds on them.1. Throughput
The pump waited 50 ms on the device after every buffer it sent, whether or not the device had anything to say. On a busy connection that's 50 ms per 16 KB, which caps a forward at a few hundred KB/s — 5 MB through a forward took 32 s.
It now polls for 1 ms while bytes are still moving and falls back to 50 ms once both sides go quiet, so an idle connection still doesn't spin. The same 5 MB takes 1.9 s (~2.6 MB/s), measured against a UP7560.
2. A forward that can't tell a dead tunnel from an idle one
A forward found running on a workstation had spent two hours pointed at a TV that had stopped listening:
ESTAB 192.168.89.16:46389 → 192.168.89.31:22, owned byares-novacomNothing looked wrong from the forward's side, so it went on waiting for connections that could no longer arrive — and the person relying on it had no way to tell.
That's the shape of a device dropping off Wi-Fi rather than closing the session: no FIN, no RST, nothing for libssh to report. The connection reads as live because as far as TCP is concerned it is.
TCP keepalive makes the silence into an error — probe after 30 s idle, every 10 s, three times — so the socket fails instead of lying. Verified with
ss -tnothat the timer is armed on the session socket (timer:(keepalive,27sec,0)).socket2is added as a direct dependency; it was already in the lockfile transitively. The Windows path for the fd handling and probe count is written and cfg-gated, but only the Linux build was exercised here — worth a CI check.🤖 Generated with Claude Code
https://claude.ai/code/session_01Gde2tcn5SbUHKhesuTNB7a