From dc85b1dbd6ff803544c0187fdcae0eeb645bea14 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Thu, 21 May 2026 19:30:57 +0200 Subject: [PATCH] fix(client): fail fast on TAP identity mismatch The Windows client now validates the driver-reported TAP MAC and MTU before it marks the adapter media connected or starts bridging frames. If the TAP driver settings do not match the tunnel identity and relay-selected MTU, startup fails with a direct error instead of continuing into a session that would drop frames or advertise the wrong L2 identity. This is intentionally a correctness guard, not automatic configuration yet. Until TAP MAC and MTU configuration are wired, the safe behavior is to fail before traffic can flow. Route protection is still applied before validation and restored if validation fails during startup. 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. Test Plan: - cargo fmt --check - cargo test -p lanparty-client-win - cargo clippy -p lanparty-client-win --all-targets -- -D warnings - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md --- README.md | 5 +- crates/lanparty-client-win/src/main.rs | 79 ++++++++++++++++++++------ 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 813c23d..9fc485a 100644 --- a/README.md +++ b/README.md @@ -141,5 +141,6 @@ adapter until shutdown. 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 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. +then restores the previous route policy on exit. Until automatic TAP MAC/MTU +configuration is wired, startup fails before bridging if the driver-reported +MAC or MTU does not match the tunnel settings. diff --git a/crates/lanparty-client-win/src/main.rs b/crates/lanparty-client-win/src/main.rs index 29bb227..97214fa 100644 --- a/crates/lanparty-client-win/src/main.rs +++ b/crates/lanparty-client-win/src/main.rs @@ -5,7 +5,7 @@ use std::{ thread, }; -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use clap::Parser; #[cfg(windows)] use lanparty_client_core::ClientRelayIo; @@ -204,13 +204,19 @@ struct TapRouteProtectionGuard { #[cfg(windows)] fn open_tap_adapter(session: &ClientSession) -> Result { let tap = lanparty_client_tap::open_first_adapter()?; - tap.set_media_connected(true)?; let tap_interface = lanparty_client_route::interface_identity_from_guid(tap.info().instance_id()) .context("failed to resolve TAP interface identity")?; let route_guard = protect_tap_routes(tap_interface)?; let driver_mac = tap.driver_mac()?; let driver_mtu = tap.driver_mtu()?; + validate_tap_driver_settings( + session.config().virtual_mac(), + session.welcome().effective_tap_mtu(), + driver_mac, + driver_mtu, + )?; + tap.set_media_connected(true)?; println!( "lanparty-client-win opened TAP adapter {}", @@ -227,20 +233,6 @@ fn open_tap_adapter(session: &ClientSession) -> Result { tap_interface.index(), tap_interface.luid() ); - if driver_mac != session.config().virtual_mac() { - eprintln!( - "TAP driver MAC {} does not match tunnel identity {}; MAC configuration is not wired yet", - driver_mac, - session.config().virtual_mac() - ); - } - if driver_mtu != u32::from(session.welcome().effective_tap_mtu()) { - eprintln!( - "TAP driver MTU {} does not match relay-selected MTU {}; MTU configuration is not wired yet", - driver_mtu, - session.welcome().effective_tap_mtu() - ); - } Ok(OpenedTapAdapter { tap, @@ -248,6 +240,29 @@ fn open_tap_adapter(session: &ClientSession) -> Result { }) } +#[cfg_attr(not(windows), allow(dead_code))] +fn validate_tap_driver_settings( + expected_mac: MacAddr, + expected_mtu: u16, + driver_mac: MacAddr, + driver_mtu: u32, +) -> Result<()> { + if driver_mac != expected_mac { + bail!( + "TAP driver MAC {driver_mac} does not match tunnel identity {expected_mac}; automatic MAC configuration is not wired yet" + ); + } + + let expected_mtu = u32::from(expected_mtu); + if driver_mtu != expected_mtu { + bail!( + "TAP driver MTU {driver_mtu} does not match relay-selected MTU {expected_mtu}; automatic MTU configuration is not wired yet" + ); + } + + Ok(()) +} + #[cfg(windows)] fn protect_tap_routes(identity: NetworkInterfaceIdentity) -> Result { let ipv4_metric = lanparty_client_route::set_scoped_interface_metric( @@ -403,3 +418,35 @@ fn read_and_relay_tap_frame( fn open_tap_adapter(_session: &ClientSession) { println!("Windows TAP adapter opening is compiled only on Windows"); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn accepts_matching_tap_driver_settings() { + assert!(validate_tap_driver_settings(mac(1), 1200, mac(1), 1200).is_ok()); + } + + #[test] + fn rejects_tap_driver_mac_mismatch() { + let error = validate_tap_driver_settings(mac(1), 1200, mac(2), 1200).unwrap_err(); + + assert!(error.to_string().contains("does not match tunnel identity")); + } + + #[test] + fn rejects_tap_driver_mtu_mismatch() { + let error = validate_tap_driver_settings(mac(1), 1200, mac(1), 1500).unwrap_err(); + + assert!( + error + .to_string() + .contains("does not match relay-selected MTU") + ); + } + + const fn mac(last_octet: u8) -> MacAddr { + MacAddr::new([0x02, 0, 0, 0, 0, last_octet]) + } +}