diff --git a/README.md b/README.md index dc69388..bb62e1b 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ and then bridges Ethernet frames between the relay and the first TAP-Windows6 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. Default-route -takeover neutralization and automatic TAP MAC/MTU configuration are not wired -yet. +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. diff --git a/crates/lanparty-client-win/src/main.rs b/crates/lanparty-client-win/src/main.rs index baca39d..75913d3 100644 --- a/crates/lanparty-client-win/src/main.rs +++ b/crates/lanparty-client-win/src/main.rs @@ -13,12 +13,18 @@ use lanparty_client_core::{ ClientIdentity, ClientIdentityStore, ClientSession, ClientSessionConfig, connect_client, }; #[cfg(windows)] -use lanparty_client_route::{PinnedRelayRoute, RouteSnapshot}; +use lanparty_client_route::{ + IpInterfaceFamily, NetworkInterfaceIdentity, PinnedRelayRoute, RouteSnapshot, + ScopedInterfaceMetric, +}; #[cfg(windows)] use lanparty_client_tap::TapAdapter; use lanparty_ctrl::RoomCode; use lanparty_proto::MacAddr; +#[cfg(windows)] +const TAP_INTERFACE_METRIC: u32 = 9_000; + #[derive(Debug, Parser)] #[command( name = "lanparty-client-win", @@ -118,9 +124,9 @@ async fn main() -> Result<()> { #[cfg(windows)] async fn run_client(session: &ClientSession) -> Result<()> { - let tap = open_tap_adapter(session)?; + let OpenedTapAdapter { tap, _metric_guard } = open_tap_adapter(session)?; println!( - "bridging TAP frames; relay route is pinned; default-route neutralization is not wired yet; press Ctrl-C to stop" + "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" ); tokio::select! { @@ -182,12 +188,25 @@ fn print_pinned_relay_route(route: &PinnedRelayRoute) { } #[cfg(windows)] -fn open_tap_adapter(session: &ClientSession) -> Result { +struct OpenedTapAdapter { + tap: TapAdapter, + _metric_guard: TapMetricGuard, +} + +#[cfg(windows)] +struct TapMetricGuard { + _ipv4: ScopedInterfaceMetric, + _ipv6: Option, +} + +#[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 metric_guard = protect_tap_metrics(tap_interface)?; let driver_mac = tap.driver_mac()?; let driver_mtu = tap.driver_mtu()?; @@ -221,7 +240,54 @@ fn open_tap_adapter(session: &ClientSession) -> Result Result { + let ipv4 = 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); + + let ipv6 = match lanparty_client_route::set_scoped_interface_metric( + identity, + IpInterfaceFamily::Ipv6, + TAP_INTERFACE_METRIC, + ) { + Ok(metric) => { + print_tap_metric_override(IpInterfaceFamily::Ipv6, &metric); + Some(metric) + } + Err(error) => { + eprintln!( + "failed to set TAP IPv6 interface metric; IPv6 route protection may be incomplete: {error:#}" + ); + None + } + }; + + Ok(TapMetricGuard { + _ipv4: ipv4, + _ipv6: ipv6, + }) +} + +#[cfg(windows)] +fn print_tap_metric_override(family: IpInterfaceFamily, metric: &ScopedInterfaceMetric) { + let previous = metric.previous(); + println!( + "TAP {family:?} interface metric set to {TAP_INTERFACE_METRIC}; previous metric {} automatic {} default-routes-disabled {}", + previous.metric(), + previous.automatic_metric(), + previous.disable_default_routes() + ); } #[cfg(windows)]