fix(client): restore TAP media status on exit
The Windows client already treats TAP route, metric, and MTU changes as scoped runtime state that is restored when the process exits. TAP media status was the odd one out: startup marked the adapter connected, but there was no matching cleanup path to mark it disconnected again. Add a small TAP media guard that owns an Arc to the opened adapter and marks media disconnected on drop. The frame pump now receives an Arc<TapAdapter>, so the guard can run on normal Ctrl-C, startup errors after TAP open, route-check failures, or frame-pump failures without fighting ownership of the TAP reader thread. Cleanup errors are logged because Drop cannot return them. Update the README and MVP test guide so the documented cleanup behavior matches the client runtime. The full Windows client cross-check is still blocked on this host before project code by ring needing x86_64-w64-mingw32-gcc. The TAP and route helper crates still check for the Windows GNU target. Test Plan: - cargo fmt --check - cargo test -p lanparty-client-win -p lanparty-client-tap -p lanparty-client-route - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - cargo check --target x86_64-pc-windows-gnu -p lanparty-client-route -p lanparty-client-tap - git diff --check Refs: MVP Windows TAP cleanup
This commit is contained in:
@@ -218,11 +218,12 @@ The startup status reports whether the relay already has a LAN gateway for the
|
|||||||
room.
|
room.
|
||||||
`--virtual-mac` can still override the stored identity for manual testing. On
|
`--virtual-mac` can still override the stored identity for manual testing. On
|
||||||
Windows it sets the TAP IP interface MTU to the relay-selected MTU, marks the
|
Windows it sets the TAP IP interface MTU to the relay-selected MTU, marks the
|
||||||
TAP media connected, and reports the driver MAC/MTU before forwarding frames,
|
TAP media connected for the scoped client run, and reports the driver MAC/MTU
|
||||||
along with the TAP interface index/LUID. The client applies a scoped TAP
|
before forwarding frames, along with the TAP interface index/LUID. The client
|
||||||
interface metric and disables TAP default routes while it runs, periodically
|
applies a scoped TAP interface metric and disables TAP default routes while it
|
||||||
rechecks that the relay route remains pinned, then restores the previous route
|
runs, periodically rechecks that the relay route remains pinned, then restores
|
||||||
policy on exit. Startup prints a warning when TAP default routes were enabled
|
the previous route policy and TAP media status on exit. Startup prints a warning
|
||||||
|
when TAP default routes were enabled
|
||||||
before the scoped protection was applied. Startup still fails before bridging
|
before the scoped protection was applied. Startup still fails before bridging
|
||||||
if the driver-reported MAC does not match the tunnel identity, because an
|
if the driver-reported MAC does not match the tunnel identity, because an
|
||||||
already-initialized Windows TAP adapter may need to be disabled/enabled or
|
already-initialized Windows TAP adapter may need to be disabled/enabled or
|
||||||
|
|||||||
+1
-1
@@ -232,7 +232,7 @@ Uncommon LAN subnets such as `10.73.42.0/24` are safer than `192.168.0.0/24`.
|
|||||||
## Cleanup
|
## Cleanup
|
||||||
|
|
||||||
Stop client, gateway, and relay with Ctrl-C. The Windows client restores the TAP
|
Stop client, gateway, and relay with Ctrl-C. The Windows client restores the TAP
|
||||||
route policy when it exits normally.
|
route policy and marks TAP media disconnected when it exits normally.
|
||||||
|
|
||||||
Keep `lanparty-client-identity.json` if you want the same virtual MAC on the
|
Keep `lanparty-client-identity.json` if you want the same virtual MAC on the
|
||||||
next run. Delete it only when you intentionally want a new client identity.
|
next run. Delete it only when you intentionally want a new client identity.
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ async fn run_client(
|
|||||||
tap,
|
tap,
|
||||||
tap_diagnostics,
|
tap_diagnostics,
|
||||||
tap_interface,
|
tap_interface,
|
||||||
|
_media_guard,
|
||||||
_route_guard,
|
_route_guard,
|
||||||
} = open_tap_adapter(session, tap_instance_id)?;
|
} = open_tap_adapter(session, tap_instance_id)?;
|
||||||
let relay_route =
|
let relay_route =
|
||||||
@@ -396,9 +397,10 @@ fn route_next_hop_label(next_hop: Option<std::net::IpAddr>) -> String {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
struct OpenedTapAdapter {
|
struct OpenedTapAdapter {
|
||||||
tap: TapAdapter,
|
tap: Arc<TapAdapter>,
|
||||||
tap_diagnostics: TapDiagnostics,
|
tap_diagnostics: TapDiagnostics,
|
||||||
tap_interface: NetworkInterfaceIdentity,
|
tap_interface: NetworkInterfaceIdentity,
|
||||||
|
_media_guard: TapMediaStatusGuard,
|
||||||
_route_guard: TapRouteProtectionGuard,
|
_route_guard: TapRouteProtectionGuard,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,12 +414,38 @@ struct TapRouteProtectionGuard {
|
|||||||
_ipv6_mtu: Option<ScopedInterfaceMtu>,
|
_ipv6_mtu: Option<ScopedInterfaceMtu>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
struct TapMediaStatusGuard {
|
||||||
|
tap: Arc<TapAdapter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl TapMediaStatusGuard {
|
||||||
|
fn connected(tap: Arc<TapAdapter>) -> Result<Self> {
|
||||||
|
tap.set_media_connected(true)?;
|
||||||
|
|
||||||
|
Ok(Self { tap })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl Drop for TapMediaStatusGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Err(error) = self.tap.set_media_connected(false) {
|
||||||
|
eprintln!("failed to mark TAP media disconnected during cleanup: {error:#}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn open_tap_adapter(
|
fn open_tap_adapter(
|
||||||
session: &ClientSession,
|
session: &ClientSession,
|
||||||
tap_instance_id: Option<&str>,
|
tap_instance_id: Option<&str>,
|
||||||
) -> Result<OpenedTapAdapter> {
|
) -> Result<OpenedTapAdapter> {
|
||||||
let tap = open_configured_tap_adapter(session.config().virtual_mac(), tap_instance_id)?;
|
let tap = Arc::new(open_configured_tap_adapter(
|
||||||
|
session.config().virtual_mac(),
|
||||||
|
tap_instance_id,
|
||||||
|
)?);
|
||||||
let tap_interface =
|
let tap_interface =
|
||||||
lanparty_client_route::interface_identity_from_guid(tap.info().instance_id())
|
lanparty_client_route::interface_identity_from_guid(tap.info().instance_id())
|
||||||
.context("failed to resolve TAP interface identity")?;
|
.context("failed to resolve TAP interface identity")?;
|
||||||
@@ -425,7 +453,7 @@ fn open_tap_adapter(
|
|||||||
let driver_mac = tap.driver_mac()?;
|
let driver_mac = tap.driver_mac()?;
|
||||||
let driver_mtu = tap.driver_mtu()?;
|
let driver_mtu = tap.driver_mtu()?;
|
||||||
validate_tap_driver_mac(session.config().virtual_mac(), driver_mac)?;
|
validate_tap_driver_mac(session.config().virtual_mac(), driver_mac)?;
|
||||||
tap.set_media_connected(true)?;
|
let media_guard = TapMediaStatusGuard::connected(Arc::clone(&tap))?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"lanparty-client-win opened TAP adapter {}",
|
"lanparty-client-win opened TAP adapter {}",
|
||||||
@@ -453,6 +481,7 @@ fn open_tap_adapter(
|
|||||||
tap,
|
tap,
|
||||||
tap_diagnostics,
|
tap_diagnostics,
|
||||||
tap_interface,
|
tap_interface,
|
||||||
|
_media_guard: media_guard,
|
||||||
_route_guard: route_guard,
|
_route_guard: route_guard,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -908,8 +937,7 @@ fn tap_default_routes_override_message(family: &str, previous_disabled: bool) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
async fn run_tap_frame_pump(relay_io: ClientRelayIo, tap: TapAdapter) -> Result<()> {
|
async fn run_tap_frame_pump(relay_io: ClientRelayIo, tap: Arc<TapAdapter>) -> Result<()> {
|
||||||
let tap = Arc::new(tap);
|
|
||||||
let (tap_error_tx, tap_error_rx) = mpsc::channel();
|
let (tap_error_tx, tap_error_rx) = mpsc::channel();
|
||||||
spawn_tap_reader(Arc::clone(&tap), relay_io.clone(), tap_error_tx)?;
|
spawn_tap_reader(Arc::clone(&tap), relay_io.clone(), tap_error_tx)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user