diff --git a/easytier/src/common/ifcfg/darwin.rs b/easytier/src/common/ifcfg/darwin.rs index 0412d9e2..fe8cec98 100644 --- a/easytier/src/common/ifcfg/darwin.rs +++ b/easytier/src/common/ifcfg/darwin.rs @@ -1,10 +1,42 @@ -use std::net::Ipv4Addr; +use std::{ + collections::{BTreeMap, BTreeSet}, + net::Ipv4Addr, + sync::Arc, +}; use super::{Error, IfConfiguerTrait, cidr_to_subnet_mask, run_shell_cmd}; use async_trait::async_trait; use cidr::{Ipv4Inet, Ipv6Inet}; +use tokio::sync::Mutex; + +#[derive(Default)] +pub struct MacIfConfiger { + configured_ipv4: Arc>>>, +} + +impl MacIfConfiger { + fn build_add_ipv4_cmd(name: &str, addr: Ipv4Inet, has_configured_ipv4: bool) -> String { + let address = addr.address(); + if has_configured_ipv4 { + format!( + "ifconfig {} alias {:?} {:?} netmask {}", + name, + address, + address, + cidr_to_subnet_mask(addr.network_length()) + ) + } else { + format!( + "ifconfig {} {:?}/{:?} {:?} up", + name, + address, + addr.network_length(), + address, + ) + } + } +} -pub struct MacIfConfiger {} #[async_trait] impl IfConfiguerTrait for MacIfConfiger { async fn add_ipv4_route( @@ -51,14 +83,23 @@ impl IfConfiguerTrait for MacIfConfiger { address: Ipv4Addr, cidr_prefix: u8, ) -> Result<(), Error> { - run_shell_cmd( - format!( - "ifconfig {} {:?}/{:?} {:?} up", - name, address, cidr_prefix, address, - ) - .as_str(), - ) - .await + let addr = Ipv4Inet::new(address, cidr_prefix).map_err(|err| { + Error::RouteError(Some(format!( + "invalid IPv4 address {address}/{cidr_prefix}: {err:?}" + ))) + })?; + let mut configured_ipv4 = self.configured_ipv4.lock().await; + let has_configured_ipv4 = configured_ipv4 + .get(name) + .is_some_and(|addresses| !addresses.is_empty()); + let cmd = Self::build_add_ipv4_cmd(name, addr, has_configured_ipv4); + + run_shell_cmd(cmd.as_str()).await?; + configured_ipv4 + .entry(name.to_owned()) + .or_default() + .insert(addr); + Ok(()) } async fn set_link_status(&self, name: &str, up: bool) -> Result<(), Error> { @@ -67,11 +108,33 @@ impl IfConfiguerTrait for MacIfConfiger { } async fn remove_ip(&self, name: &str, ip: Option) -> Result<(), Error> { + let mut configured_ipv4 = self.configured_ipv4.lock().await; if let Some(ip) = ip { - run_shell_cmd(format!("ifconfig {} inet {} delete", name, ip.address()).as_str()).await + run_shell_cmd(format!("ifconfig {} inet {} delete", name, ip.address()).as_str()) + .await?; + if let Some(addresses) = configured_ipv4.get_mut(name) { + addresses.remove(&ip); + if addresses.is_empty() { + configured_ipv4.remove(name); + } + } } else { - run_shell_cmd(format!("ifconfig {} inet delete", name).as_str()).await + if let Some(addresses) = configured_ipv4.get(name).cloned() { + for ip in addresses { + run_shell_cmd( + format!("ifconfig {} inet {} delete", name, ip.address()).as_str(), + ) + .await?; + if let Some(addresses) = configured_ipv4.get_mut(name) { + addresses.remove(&ip); + } + } + configured_ipv4.remove(name); + } else { + run_shell_cmd(format!("ifconfig {} inet delete", name).as_str()).await?; + } } + Ok(()) } async fn set_mtu(&self, name: &str, mtu: u32) -> Result<(), Error> { diff --git a/easytier/src/common/ifcfg/mod.rs b/easytier/src/common/ifcfg/mod.rs index 3c2b20b5..7f97744c 100644 --- a/easytier/src/common/ifcfg/mod.rs +++ b/easytier/src/common/ifcfg/mod.rs @@ -140,6 +140,7 @@ async fn run_shell_cmd(cmd: &str) -> Result<(), Error> { Ok(()) } +#[derive(Default)] pub struct DummyIfConfiger {} #[async_trait] impl IfConfiguerTrait for DummyIfConfiger {} diff --git a/easytier/src/common/ifcfg/netlink.rs b/easytier/src/common/ifcfg/netlink.rs index b620c215..13c0ec65 100644 --- a/easytier/src/common/ifcfg/netlink.rs +++ b/easytier/src/common/ifcfg/netlink.rs @@ -157,6 +157,7 @@ impl From for Route { } } +#[derive(Default)] pub struct NetlinkIfConfiger {} impl NetlinkIfConfiger { diff --git a/easytier/src/common/ifcfg/windows.rs b/easytier/src/common/ifcfg/windows.rs index db3ca512..88648fad 100644 --- a/easytier/src/common/ifcfg/windows.rs +++ b/easytier/src/common/ifcfg/windows.rs @@ -22,6 +22,7 @@ use winreg::{ }; use super::{Error, IfConfiguerTrait}; +#[derive(Default)] pub struct WindowsIfConfiger {} fn format_win_error(error: u32) -> String { diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index 189afba8..0ee7acef 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -292,7 +292,7 @@ impl VirtualNic { Self { config, ifname: None, - ifcfg: Box::new(IfConfiger {}), + ifcfg: Box::new(IfConfiger::default()), } } @@ -832,7 +832,7 @@ impl VirtualNic { } pub fn get_ifcfg(&self) -> IfConfiger { - IfConfiger {} + IfConfiger::default() } }