Skip to content

Reverse forwarding (-R) + event-driven migration detection - #11

Merged
lacraig2 merged 2 commits into
mainfrom
feature/netwatch-and-reverse
Jun 19, 2026
Merged

Reverse forwarding (-R) + event-driven migration detection#11
lacraig2 merged 2 commits into
mainfrom
feature/netwatch-and-reverse

Conversation

@lacraig2

Copy link
Copy Markdown
Collaborator

Builds out two features on top of the TUI/SOCKS/via-ssh work.

1. Event-driven migration detection (netwatch v2)

Replaces the 2s routing-table poll with OS routing events — Linux NETLINK_ROUTE multicast, macOS PF_ROUTE — hand-rolled over nix::libc + tokio::AsyncFd (no new dependency). The event is only a trigger; source_ip_for stays the decision, so the rebind logic and netwatch::run's signature are unchanged (supervisor untouched). Debounced to one rebind check per burst; any unsupported platform or socket-setup failure falls back to the original poll.

2. Reverse forwarding (-R, the ssh -R equivalent)

Expose a local service on the remote: the agent binds a remote listener and opens a QUIC stream back to the client for each connection, which the client dials to a local target and splices.

  • Grammar (forward.rs): ReverseSpec = [NS@][BINDADDR:]REMOTEPORT->[HOST:]LOCALPORT, reusing existing parse helpers.
  • Protocol (reverse.rs, conn.rs, agent.rs): reuses the discovery convention — the client registers reverse forwards over an @reverse control stream; the agent binds remote listeners and opens streams back via the new Conn::accept_bi. No wire-version bump.
  • Surface (cli/control/config/main/tui): -R launch flag, add/drop --reverse, list/status output, persisted reverse_forwards, dimmed ← R:<port> rows in the TUI, per-epoch re-registration across reconnects.
  • Scope: QUIC-only in v1 — rejected over --via-ssh with a clear message; remote bind in a namespace is deferred.

Tests / CI parity

  • Reverse e2e + reverse-reconnect loopback tests; ReverseSpec parse/roundtrip; control-protocol JSON roundtrip incl. legacy back-compat; netwatch source_ip_for + debounce units.
  • cargo fmt --check, cargo clippy --all-targets -D warnings, full test suite, and the Linux musl cross-build (netlink path) all pass.

Not covered

Manual smoke test against a real SSH host (portmanager myhost -R 3000->3000) and observing a real interface-toggle migration event — both need a live host/interface and are left to manual verification.

Luke Craig added 2 commits June 18, 2026 19:08
Replace the 2s routing-table poll with OS routing events (Linux
NETLINK_ROUTE multicast, macOS PF_ROUTE), debounced to one rebind check
per burst. Events are only a trigger; source_ip_for stays the decision.
run()'s signature is unchanged, so supervisor.rs is untouched. Any
unsupported platform or socket-setup failure falls back to polling.
Expose a local service on the remote: the agent binds a remote listener
and opens a QUIC stream back to the client for each connection, which the
client dials to a local target and splices.

- forward.rs: ReverseSpec grammar [NS@][BINDADDR:]REMOTEPORT->[HOST:]LOCALPORT
- conn.rs: Conn::accept_bi (QUIC; errors on --via-ssh)
- reverse.rs: agent-side remote bind + stream-back; client ReverseSet +
  per-epoch registration over an @reverse control stream, reusing the
  discovery convention and the forward health model
- agent.rs: thread the Connection into stream dispatch for open-bi-back
- cli/control/config/main/tui: -R flag, AddReverse/DropReverse, persisted
  reverse_forwards, dimmed reverse rows in the TUI table
- QUIC-only in v1; reverse over --via-ssh is rejected with a clear message

Tests: reverse e2e + reverse reconnect (loopback), ReverseSpec parse/
roundtrip, control JSON roundtrip + legacy back-compat.
@lacraig2
lacraig2 merged commit 8de7065 into main Jun 19, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant