From 97e7da22fb162b286e08cf0c0b0fa9eb5074e0e6 Mon Sep 17 00:00:00 2001 From: Nikolay Edigaryev Date: Thu, 28 May 2026 00:56:08 +0100 Subject: [PATCH] WIP --- lib/proxy/mod.rs | 42 +++++++++++++++++++++++--------------- src/main.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/lib/proxy/mod.rs b/lib/proxy/mod.rs index 8efcc46..e091b48 100644 --- a/lib/proxy/mod.rs +++ b/lib/proxy/mod.rs @@ -42,6 +42,16 @@ pub enum Target { Host, } +impl Target { + pub fn is_ipv4_default_route(&self) -> bool { + matches!(self, Target::Prefix(prefix) if *prefix == Ipv4Net::zero()) + } + + pub fn is_mac_address(&self) -> bool { + matches!(self, Target::MacAddress(_)) + } +} + impl FromStr for Target { type Err = ipnet::AddrParseError; @@ -69,17 +79,11 @@ impl Proxy<'_> { vm_fd: RawFd, vm_mac_address: MacAddress, vm_net_type: NetType, + enable_isolation: bool, allow: Vec, block: Vec, exposed_ports: Vec, ) -> Result> { - let allowing_all_ipv4 = allow.contains(&Target::Prefix(Ipv4Net::zero())); - let using_mac_filtering = allow - .iter() - .chain(block.iter()) - .any(|target| matches!(target, Target::MacAddress(_))); - let enable_isolation = !allowing_all_ipv4 && !using_mac_filtering; - let vm = VM::new(vm_fd)?; let host = Host::new(vm_net_type, enable_isolation)?; let poller_timeout = Duration::from_millis(100); @@ -226,7 +230,7 @@ impl Proxy<'_> { mod tests { use crate::NetType; use crate::dhcp_snooper::Lease; - use crate::proxy::{Action, Proxy}; + use crate::proxy::{Action, Proxy, Target}; use ipnet::Ipv4Net; use mac_address::MacAddress; use nix::sys::socket::{AddressFamily, SockFlag, SockType, socketpair}; @@ -329,18 +333,24 @@ mod tests { .unwrap(); let vm_fd = Box::leak(Box::new(vm_fd)); + let allow: Vec = allow + .into_iter() + .map(|cidr| cidr.parse().unwrap()) + .collect(); + let block: Vec = block + .into_iter() + .map(|cidr| cidr.parse().unwrap()) + .collect(); + let enable_isolation = !allow.iter().any(Target::is_ipv4_default_route) + && !allow.iter().chain(block.iter()).any(Target::is_mac_address); + let mut proxy = Proxy::new( vm_fd.as_raw_fd(), MacAddress::from_str("02:00:00:00:00:01").unwrap(), NetType::Nat, - allow - .into_iter() - .map(|cidr| cidr.parse().unwrap()) - .collect(), - block - .into_iter() - .map(|cidr| cidr.parse().unwrap()) - .collect(), + enable_isolation, + allow, + block, Vec::default(), ) .unwrap(); diff --git a/src/main.rs b/src/main.rs index c4011be..49b0f51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -194,11 +194,14 @@ fn try_main() -> anyhow::Result<()> { // Set bootpd(8) min/max lease time while still having the root privileges set_bootpd_lease_time(args.bootpd_lease_time); + let enable_isolation = bridge_isolation_enabled(&args.allow, &args.block); + // Initialize the proxy while still having the root privileges let mut proxy = Proxy::new( args.vm_fd as RawFd, args.vm_mac_address, args.vm_net_type, + enable_isolation, args.allow, args.block, args.expose, @@ -231,6 +234,14 @@ fn sudo_escalation_works() -> bool { .unwrap_or(false) } +fn bridge_isolation_enabled(allow: &[Target], block: &[Target]) -> bool { + let disables_isolation_for_compat = allow.iter().any(Target::is_ipv4_default_route); + let disables_isolation_for_mac_filtering = + allow.iter().chain(block.iter()).any(Target::is_mac_address); + + !(disables_isolation_for_compat || disables_isolation_for_mac_filtering) +} + fn set_bootpd_lease_time(lease_time: u32) { let prefs = SCPreferences::group( &CFString::new("softnet"), @@ -252,3 +263,44 @@ fn set_bootpd_lease_time(lease_time: u32) { SCPreferencesCommitChanges(prefs.as_concrete_TypeRef()); } } + +#[cfg(test)] +mod tests { + use super::*; + + fn targets(values: &[&str]) -> Vec { + values.iter().map(|value| value.parse().unwrap()).collect() + } + + #[test] + fn bridge_isolation_disabled_for_legacy_allow_default_route() { + assert!(!bridge_isolation_enabled( + &targets(&["0.0.0.0/0"]), + &targets(&[]) + )); + } + + #[test] + fn bridge_isolation_disabled_for_legacy_allow_default_route_even_when_blocked() { + assert!(!bridge_isolation_enabled( + &targets(&["0.0.0.0/0"]), + &targets(&["0.0.0.0/0"]) + )); + } + + #[test] + fn bridge_isolation_kept_for_block_default_route_only() { + assert!(bridge_isolation_enabled( + &targets(&[]), + &targets(&["0.0.0.0/0"]) + )); + } + + #[test] + fn bridge_isolation_disabled_for_mac_filtering() { + assert!(!bridge_isolation_enabled( + &targets(&[]), + &targets(&["02:00:00:00:00:02"]) + )); + } +}