From ac03bf16160ef493c1a60958626cc0b4699838a4 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Fri, 22 May 2026 04:44:13 +0200 Subject: [PATCH] fix(client): clear TAP media before relay connect If the Windows client is killed hard, the TAP adapter can be left in a connected media state. A retry should not resolve or connect to the relay while stale TAP state might still influence Windows routing. Select and open the intended TAP adapter before relay endpoint resolution, force its media state to disconnected, then proceed with the existing relay connect, route pin, TAP route protection, and bridge startup flow. This also makes missing or ambiguous TAP adapters fail before the client joins the relay room. The README and MVP test guide now show the new startup line and the early TAP preflight troubleshooting checks. Test Plan: All cargo commands used these environment variables: RUSTUP_HOME=/tmp/softlan-vpn-rustup CARGO_HOME=/tmp/softlan-vpn-cargo - cargo fmt --check - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - cargo check -p lanparty-client-tap --tests --target x86_64-pc-windows-gnu - cargo check -p lanparty-client-tap --tests --target x86_64-pc-windows-msvc - cargo check -p lanparty-client-route --tests --target x86_64-pc-windows-gnu - cargo check -p lanparty-client-route --tests --target x86_64-pc-windows-msvc - git diff --check Known limitation: full lanparty-client-win Windows cross-check is still blocked on this Linux host by the external ring toolchain setup. The default GNU target lacks x86_64-w64-mingw32-gcc, the default MSVC target lacks lib.exe, and the LLVM MSVC attempt gets as far as ring C compilation but lacks Windows CRT headers such as assert.h. Refs: PLAN.md route-protection startup requirement --- README.md | 19 +++++++++++-------- TESTING.md | 6 ++++++ crates/lanparty-client-win/src/main.rs | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 48d2deb..d03306f 100644 --- a/README.md +++ b/README.md @@ -207,14 +207,17 @@ cargo run -p lanparty-client-win -- \ The Windows client binary currently connects to the relay as `role = client` with a generated locally administered virtual MAC persisted in -`lanparty-client-identity.json`, resolves the relay DNS name before TAP -activation, completes the control-stream hello/welcome handshake, pins a host -route for the resolved relay IP on the current pre-TAP interface, verifies that -the relay route still uses that pinned host route after TAP activation, and then -bridges Ethernet frames between the relay and the first TAP-Windows6 adapter -until shutdown. `--relay` accepts a DNS name or socket address; bare hosts -default to UDP/443. Before opening the adapter, it writes the -generated tunnel MAC to the TAP driver's `NetworkAddress` registry setting. +`lanparty-client-identity.json`. Before resolving or connecting to the relay, +it selects the TAP-Windows6 adapter and marks TAP media disconnected to clear +stale connected state from a previous crashed run. It then resolves the relay +DNS name before TAP activation, completes the control-stream hello/welcome +handshake, pins a host route for the resolved relay IP on the current pre-TAP +interface, verifies that the relay route still uses that pinned host route +after TAP activation, and then bridges Ethernet frames between the relay and +the first TAP-Windows6 adapter until shutdown. `--relay` accepts a DNS name or +socket address; bare hosts default to UDP/443. Before opening the adapter for +bridging, it writes the generated tunnel MAC to the TAP driver's +`NetworkAddress` registry setting. If the exact relay host route already exists, the client uses it and leaves it alone on exit. The startup status reports whether the relay already has a LAN gateway for the room. diff --git a/TESTING.md b/TESTING.md index b4bc4bc..b8421a3 100644 --- a/TESTING.md +++ b/TESTING.md @@ -118,6 +118,7 @@ one explicitly: Expected client output: ```text +prepared TAP adapter ... media disconnected before relay connect lanparty-client-win connected as peer ... relay route pinned before TAP ... relay route verified after TAP activation ... @@ -218,6 +219,11 @@ drop_reason=ControlPlaneEtherType If the client says `Waiting for LAN gateway`, check that the gateway uses the same room code and is connected to the same relay. +If startup fails before the relay connection while preparing the TAP adapter, +check that the terminal is elevated, TAP-Windows6 is installed, and +`--tap-instance-id` selects the intended adapter when more than one TAP adapter +exists. + If the client says `Waiting for TAP IP`, DHCP is not making the full round trip. Check relay/gateway frame logs for broadcast traffic and check that the gateway is on wired Ethernet. diff --git a/crates/lanparty-client-win/src/main.rs b/crates/lanparty-client-win/src/main.rs index a89ea13..8753287 100644 --- a/crates/lanparty-client-win/src/main.rs +++ b/crates/lanparty-client-win/src/main.rs @@ -145,6 +145,9 @@ async fn main() -> Result<()> { return Ok(()); } + #[cfg(windows)] + prepare_tap_before_relay_connect(args.tap_instance_id.as_deref())?; + let config = args.into_config()?; println!( "lanparty-client-win connecting virtual MAC {} to relay {} room {}", @@ -197,6 +200,21 @@ fn print_available_tap_adapters() -> Result<()> { Ok(()) } +#[cfg(windows)] +fn prepare_tap_before_relay_connect(tap_instance_id: Option<&str>) -> Result<()> { + let adapters = lanparty_client_tap::available_adapters() + .context("failed to list TAP-Windows6 adapters before relay connect")?; + let info = select_tap_adapter(adapters, tap_instance_id)?; + let device_path = info.device_path(); + let tap = TapAdapter::open(info)?; + tap.set_media_connected(false) + .with_context(|| format!("failed to mark TAP media disconnected for {device_path}"))?; + + println!("prepared TAP adapter {device_path}: media disconnected before relay connect"); + + Ok(()) +} + #[cfg(windows)] async fn run_client( session: &ClientSession,