diff --git a/README.md b/README.md index bb62e1b..3b8ae9e 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Windows route-table boundary: - selected source address, next hop, interface index/LUID, prefix, and metric - interface index/LUID lookup from Windows network adapter GUIDs - scoped IP interface metric overrides with restore-on-drop behavior +- scoped default-route suppression with restore-on-drop behavior - scoped host-route pinning for the relay IP on the pre-TAP interface - non-Windows builds return a clear unsupported-platform error diff --git a/crates/lanparty-client-route/src/lib.rs b/crates/lanparty-client-route/src/lib.rs index 718a3af..cefdb7a 100644 --- a/crates/lanparty-client-route/src/lib.rs +++ b/crates/lanparty-client-route/src/lib.rs @@ -2,7 +2,7 @@ //! //! The client binary uses this crate to keep Win32 route/metric calls out of //! the relay session code. The crate can snapshot the current relay route and -//! install a scoped host route that pins the relay IP to the pre-TAP interface. +//! install scoped route/interface overrides that are restored when dropped. use std::net::IpAddr; @@ -175,7 +175,10 @@ mod windows; #[cfg(windows)] pub use windows::{PinnedRelayRoute, best_route_to, interface_identity_from_guid, pin_relay_route}; #[cfg(windows)] -pub use windows::{ScopedInterfaceMetric, interface_metric, set_scoped_interface_metric}; +pub use windows::{ + ScopedDefaultRoutes, ScopedInterfaceMetric, interface_metric, + set_scoped_default_routes_disabled, set_scoped_interface_metric, +}; #[cfg(not(windows))] pub fn best_route_to(_destination: IpAddr) -> Result { @@ -221,6 +224,21 @@ pub fn set_scoped_interface_metric( bail!("Windows interface metric updates are only available on Windows"); } +#[cfg(not(windows))] +#[derive(Debug)] +pub struct ScopedDefaultRoutes { + _private: (), +} + +#[cfg(not(windows))] +pub fn set_scoped_default_routes_disabled( + _identity: NetworkInterfaceIdentity, + _family: IpInterfaceFamily, + _disabled: bool, +) -> Result { + bail!("Windows interface default-route updates are only available on Windows"); +} + #[cfg(test)] mod tests { use super::*; @@ -305,6 +323,9 @@ mod tests { assert!(interface_metric(identity, IpInterfaceFamily::Ipv4).is_err()); assert!(set_scoped_interface_metric(identity, IpInterfaceFamily::Ipv4, 500).is_err()); + assert!( + set_scoped_default_routes_disabled(identity, IpInterfaceFamily::Ipv4, true).is_err() + ); } fn ip(value: &str) -> IpAddr { diff --git a/crates/lanparty-client-route/src/windows.rs b/crates/lanparty-client-route/src/windows.rs index ea5a026..8728496 100644 --- a/crates/lanparty-client-route/src/windows.rs +++ b/crates/lanparty-client-route/src/windows.rs @@ -78,6 +78,24 @@ pub fn set_scoped_interface_metric( }) } +pub fn set_scoped_default_routes_disabled( + identity: NetworkInterfaceIdentity, + family: IpInterfaceFamily, + disabled: bool, +) -> Result { + let previous = interface_metric(identity, family)?; + let mut row = get_interface_row(identity, family)?; + row.DisableDefaultRoutes = disabled; + set_interface_row(&mut row).with_context(|| { + format!("failed to set {family:?} default-route disabled state to {disabled}") + })?; + + Ok(ScopedDefaultRoutes { + previous, + active: true, + }) +} + pub struct ScopedInterfaceMetric { previous: InterfaceMetricSnapshot, active: bool, @@ -109,6 +127,37 @@ impl Drop for ScopedInterfaceMetric { } } +pub struct ScopedDefaultRoutes { + previous: InterfaceMetricSnapshot, + active: bool, +} + +impl ScopedDefaultRoutes { + #[must_use] + pub const fn previous(&self) -> InterfaceMetricSnapshot { + self.previous + } +} + +impl fmt::Debug for ScopedDefaultRoutes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ScopedDefaultRoutes") + .field("previous", &self.previous) + .field("active", &self.active) + .finish() + } +} + +impl Drop for ScopedDefaultRoutes { + fn drop(&mut self) { + if !self.active { + return; + } + + let _ = restore_default_routes(self.previous); + } +} + pub fn best_route_to(destination: IpAddr) -> Result { let destination_sockaddr = sockaddr_from_ip(destination); let mut route = MIB_IPFORWARD_ROW2::default(); @@ -183,6 +232,12 @@ fn restore_interface_metric(snapshot: InterfaceMetricSnapshot) -> Result<()> { set_interface_row(&mut row) } +fn restore_default_routes(snapshot: InterfaceMetricSnapshot) -> Result<()> { + let mut row = get_interface_row(snapshot.identity(), snapshot.family())?; + row.DisableDefaultRoutes = snapshot.disable_default_routes(); + set_interface_row(&mut row) +} + fn interface_row_key( identity: NetworkInterfaceIdentity, family: IpInterfaceFamily,