Description
When I2P tunnels are degraded (frequent during bootstrapping or network instability), peer connection establishment hangs indefinitely because the handshake I/O operations (read_framed / write_framed) have no timeouts.
The I2P streaming layer accepts the inbound connection, but the application-level handshake never completes because data can't flow through broken tunnels — and nothing ever times out to report the failure.
Evidence from logs
- The embedded I2P router starts and builds tunnels, but tunnel health checks fail continuously:
tunnel test failed name=exploratory outbound=958854281 inbound=2225195137 error=Timeout
This repeats every ~20 seconds for the entire session, meaning the tunnels are essentially non-functional.
- A peer connects at the I2P streaming level (06:00:34):
inbound stream accepted local=f8SDGXUx remote=0-RohxV8 recv_stream_id=1233202003 send_stream_id=0 payload_len=0
- The peer retries the SYN 10 seconds later (06:00:44) because it never got a response — the handshake ACK from
handle_incoming couldn't make it through the degraded tunnels:
received `SYN` to an active session local=f8SDGXUx remote=0-RohxV8 recv_id=1233202003 send_id=932169142
-
This repeats again at 06:00:54, confirming the handshake is stuck.
-
Eventually all NTCP2 sessions are forcibly reset (os error 10054 / WSAECONNRESET) at 06:01:48, killing all connectivity.
Root cause
In session.rs, both sides of the handshake lack timeouts:
Responder (handle_incoming, line ~274):
let frame = read_framed(&mut reader).await?; // No timeout — blocks forever
// ...
write_framed(&mut writer, &ack).await?; // No timeout
Initiator (initiate_session, line ~400-407):
write_framed(&mut writer, &init_msg).await?; // No timeout
let ack_frame = read_framed(&mut reader).await?; // No timeout — blocks forever
When I2P tunnels are degraded, these operations hang indefinitely. This also blocks the accept_loop since handle_incoming runs inline — no new connections can be accepted while one is stuck.
Additional problems visible in the logs
- No I2P tunnel health feedback to user: The router status is set to
"ready" once the SAM session is created, but the underlying tunnels may still be unusable. The user sees "connected" but connections fail silently.
- No SAM session recovery: When tunnels degrade to the point of being non-functional (all tunnel tests failing), there's no mechanism to tear down and recreate the SAM session.
Proposed solution
- Add timeouts to all handshake I/O — wrap
read_framed and write_framed calls in tokio::time::timeout (e.g., 30-60 seconds given I2P latency).
- Run
handle_incoming with a timeout — so the accept loop isn't blocked by a stuck handshake.
- Emit a user-visible error when the handshake times out, instead of silently dropping the connection.
- Consider monitoring tunnel health — if tunnel tests fail continuously for an extended period, update
router_status to reflect degraded connectivity so the user knows.
Description
When I2P tunnels are degraded (frequent during bootstrapping or network instability), peer connection establishment hangs indefinitely because the handshake I/O operations (
read_framed/write_framed) have no timeouts.The I2P streaming layer accepts the inbound connection, but the application-level handshake never completes because data can't flow through broken tunnels — and nothing ever times out to report the failure.
Evidence from logs
This repeats every ~20 seconds for the entire session, meaning the tunnels are essentially non-functional.
handle_incomingcouldn't make it through the degraded tunnels:This repeats again at 06:00:54, confirming the handshake is stuck.
Eventually all NTCP2 sessions are forcibly reset (os error 10054 / WSAECONNRESET) at 06:01:48, killing all connectivity.
Root cause
In
session.rs, both sides of the handshake lack timeouts:Responder (
handle_incoming, line ~274):Initiator (
initiate_session, line ~400-407):When I2P tunnels are degraded, these operations hang indefinitely. This also blocks the
accept_loopsincehandle_incomingruns inline — no new connections can be accepted while one is stuck.Additional problems visible in the logs
"ready"once the SAM session is created, but the underlying tunnels may still be unusable. The user sees "connected" but connections fail silently.Proposed solution
read_framedandwrite_framedcalls intokio::time::timeout(e.g., 30-60 seconds given I2P latency).handle_incomingwith a timeout — so the accept loop isn't blocked by a stuck handshake.router_statusto reflect degraded connectivity so the user knows.