From bbe12e851ae75dec2e6af0b85a066a1718f2d60f Mon Sep 17 00:00:00 2001 From: ddidderr Date: Thu, 21 May 2026 19:28:14 +0200 Subject: [PATCH] feat(client): disable TAP default routes while running The Windows client now holds scoped default-route suppression guards for the TAP interface while the frame pump is active. IPv4 protection is required, matching the relay-route safety path. IPv6 protection is still best-effort so IPv4-only Windows TAP setups do not fail startup just because there is no IPv6 interface row to update. This completes the current client-side route-policy wiring from PLAN.md: the relay host route is pinned before TAP activation, TAP interface metrics are raised while running, and TAP default routes are disabled until the client exits or startup unwinds. Automatic TAP MAC and MTU configuration remain follow-up work. The full Windows client target check still cannot complete on this Linux host: `ring` fails while compiling for `x86_64-pc-windows-msvc` because the Windows C header `assert.h` is unavailable, before `lanparty-client-win` is typechecked. The independent Windows-target route crate checks do pass. Test Plan: - cargo fmt --check - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc - cargo clippy -p lanparty-client-route --target x86_64-pc-windows-msvc --all-targets -- -D warnings - git diff --check Refs: PLAN.md --- README.md | 6 +-- crates/lanparty-client-win/src/main.rs | 70 ++++++++++++++++++++------ 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 3b8ae9e..813c23d 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,6 @@ adapter until shutdown. `--virtual-mac` can still override the stored identity for manual testing. On Windows it marks the TAP media connected and reports the driver MAC/MTU before forwarding frames, along with the TAP interface index/LUID. The client applies -a scoped TAP interface metric while it runs and restores the previous metric on -exit. Default-route takeover detection and automatic TAP MAC/MTU configuration -are not wired yet. +a scoped TAP interface metric and disables TAP default routes while it runs, +then restores the previous route policy on exit. Automatic TAP MAC/MTU +configuration is not wired yet. diff --git a/crates/lanparty-client-win/src/main.rs b/crates/lanparty-client-win/src/main.rs index 75913d3..29bb227 100644 --- a/crates/lanparty-client-win/src/main.rs +++ b/crates/lanparty-client-win/src/main.rs @@ -15,7 +15,7 @@ use lanparty_client_core::{ #[cfg(windows)] use lanparty_client_route::{ IpInterfaceFamily, NetworkInterfaceIdentity, PinnedRelayRoute, RouteSnapshot, - ScopedInterfaceMetric, + ScopedDefaultRoutes, ScopedInterfaceMetric, }; #[cfg(windows)] use lanparty_client_tap::TapAdapter; @@ -124,9 +124,9 @@ async fn main() -> Result<()> { #[cfg(windows)] async fn run_client(session: &ClientSession) -> Result<()> { - let OpenedTapAdapter { tap, _metric_guard } = open_tap_adapter(session)?; + let OpenedTapAdapter { tap, _route_guard } = open_tap_adapter(session)?; println!( - "bridging TAP frames; relay route is pinned and TAP metrics are scoped; default-route takeover detection is not wired yet; press Ctrl-C to stop" + "bridging TAP frames; relay route is pinned and TAP route policy is scoped; press Ctrl-C to stop" ); tokio::select! { @@ -190,13 +190,15 @@ fn print_pinned_relay_route(route: &PinnedRelayRoute) { #[cfg(windows)] struct OpenedTapAdapter { tap: TapAdapter, - _metric_guard: TapMetricGuard, + _route_guard: TapRouteProtectionGuard, } #[cfg(windows)] -struct TapMetricGuard { - _ipv4: ScopedInterfaceMetric, - _ipv6: Option, +struct TapRouteProtectionGuard { + _ipv4_metric: ScopedInterfaceMetric, + _ipv4_default_routes: ScopedDefaultRoutes, + _ipv6_metric: Option, + _ipv6_default_routes: Option, } #[cfg(windows)] @@ -206,7 +208,7 @@ fn open_tap_adapter(session: &ClientSession) -> Result { let tap_interface = lanparty_client_route::interface_identity_from_guid(tap.info().instance_id()) .context("failed to resolve TAP interface identity")?; - let metric_guard = protect_tap_metrics(tap_interface)?; + let route_guard = protect_tap_routes(tap_interface)?; let driver_mac = tap.driver_mac()?; let driver_mtu = tap.driver_mtu()?; @@ -242,21 +244,29 @@ fn open_tap_adapter(session: &ClientSession) -> Result { Ok(OpenedTapAdapter { tap, - _metric_guard: metric_guard, + _route_guard: route_guard, }) } #[cfg(windows)] -fn protect_tap_metrics(identity: NetworkInterfaceIdentity) -> Result { - let ipv4 = lanparty_client_route::set_scoped_interface_metric( +fn protect_tap_routes(identity: NetworkInterfaceIdentity) -> Result { + let ipv4_metric = lanparty_client_route::set_scoped_interface_metric( identity, IpInterfaceFamily::Ipv4, TAP_INTERFACE_METRIC, ) .context("failed to set TAP IPv4 interface metric")?; - print_tap_metric_override(IpInterfaceFamily::Ipv4, &ipv4); + print_tap_metric_override(IpInterfaceFamily::Ipv4, &ipv4_metric); - let ipv6 = match lanparty_client_route::set_scoped_interface_metric( + let ipv4_default_routes = lanparty_client_route::set_scoped_default_routes_disabled( + identity, + IpInterfaceFamily::Ipv4, + true, + ) + .context("failed to disable TAP IPv4 default routes")?; + print_tap_default_routes_override(IpInterfaceFamily::Ipv4, &ipv4_default_routes); + + let ipv6_metric = match lanparty_client_route::set_scoped_interface_metric( identity, IpInterfaceFamily::Ipv6, TAP_INTERFACE_METRIC, @@ -273,9 +283,28 @@ fn protect_tap_metrics(identity: NetworkInterfaceIdentity) -> Result { + print_tap_default_routes_override(IpInterfaceFamily::Ipv6, &default_routes); + Some(default_routes) + } + Err(error) => { + eprintln!( + "failed to disable TAP IPv6 default routes; IPv6 route protection may be incomplete: {error:#}" + ); + None + } + }; + + Ok(TapRouteProtectionGuard { + _ipv4_metric: ipv4_metric, + _ipv4_default_routes: ipv4_default_routes, + _ipv6_metric: ipv6_metric, + _ipv6_default_routes: ipv6_default_routes, }) } @@ -290,6 +319,15 @@ fn print_tap_metric_override(family: IpInterfaceFamily, metric: &ScopedInterface ); } +#[cfg(windows)] +fn print_tap_default_routes_override(family: IpInterfaceFamily, routes: &ScopedDefaultRoutes) { + let previous = routes.previous(); + println!( + "TAP {family:?} default routes disabled; previous default-routes-disabled {}", + previous.disable_default_routes() + ); +} + #[cfg(windows)] async fn run_tap_frame_pump(relay_io: ClientRelayIo, tap: TapAdapter) -> Result<()> { let tap = Arc::new(tap);