refactor: decouple virtual nic from global ctx

Move TUN-specific configuration into VirtualNicConfig so VirtualNic and SharedVirtualNic no longer depend on ArcGlobalCtx.

Keep global context side effects in NicCtx, including the Windows generated device-name writeback.
This commit is contained in:
sijie.sun
2026-06-14 01:26:19 +08:00
parent c0bd050742
commit e533b48244
2 changed files with 77 additions and 65 deletions
+18 -33
View File
@@ -7,9 +7,7 @@ use std::{
use cidr::{Ipv4Inet, Ipv6Inet}; use cidr::{Ipv4Inet, Ipv6Inet};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::common::global_ctx::ArcGlobalCtx; use super::virtual_nic::{VirtualNic, VirtualNicConfig};
use super::virtual_nic::VirtualNic;
pub type SharedVirtualNicMemberId = uuid::Uuid; pub type SharedVirtualNicMemberId = uuid::Uuid;
@@ -237,40 +235,21 @@ pub struct SharedVirtualNic {
} }
impl SharedVirtualNic { impl SharedVirtualNic {
pub fn new(global_ctx: ArcGlobalCtx) -> Self { pub fn new(config: VirtualNicConfig) -> Self {
Self { Self {
nic: Arc::new(Mutex::new(VirtualNic::new(global_ctx))), nic: Arc::new(Mutex::new(VirtualNic::new(config))),
ifcfg: SharedIfConfig::default(), ifcfg: SharedIfConfig::default(),
} }
} }
pub fn attach_member(
&mut self,
member_id: SharedVirtualNicMemberId,
claims: SharedIfConfigClaims,
) -> SharedIfConfigDelta {
self.ifcfg.apply_member_claims(member_id, claims)
}
pub fn update_member_claims(
&mut self,
member_id: SharedVirtualNicMemberId,
claims: SharedIfConfigClaims,
) -> SharedIfConfigDelta {
self.ifcfg.apply_member_claims(member_id, claims)
}
pub fn detach_member(
&mut self,
member_id: SharedVirtualNicMemberId,
) -> Option<SharedIfConfigDelta> {
self.ifcfg.remove_member(member_id)
}
pub fn ifcfg(&self) -> &SharedIfConfig { pub fn ifcfg(&self) -> &SharedIfConfig {
&self.ifcfg &self.ifcfg
} }
pub fn ifcfg_mut(&mut self) -> &mut SharedIfConfig {
&mut self.ifcfg
}
pub fn nic(&self) -> Arc<Mutex<VirtualNic>> { pub fn nic(&self) -> Arc<Mutex<VirtualNic>> {
self.nic.clone() self.nic.clone()
} }
@@ -389,7 +368,7 @@ where
mod tests { mod tests {
use std::str::FromStr as _; use std::str::FromStr as _;
use crate::common::global_ctx::tests::get_mock_global_ctx; use crate::common::netns::NetNS;
use super::*; use super::*;
@@ -405,6 +384,10 @@ mod tests {
} }
} }
fn virtual_nic_config() -> VirtualNicConfig {
VirtualNicConfig::new(String::new(), 1500, NetNS::new(None))
}
#[test] #[test]
fn duplicate_routes_keep_owner_sets_and_single_os_delta() { fn duplicate_routes_keep_owner_sets_and_single_os_delta() {
let route = SharedIpv4Route::new(Ipv4Addr::new(10, 10, 0, 0), 24, None); let route = SharedIpv4Route::new(Ipv4Addr::new(10, 10, 0, 0), 24, None);
@@ -486,13 +469,15 @@ mod tests {
); );
} }
#[tokio::test] #[test]
async fn shared_virtual_nic_wraps_virtual_nic_and_tracks_ifcfg() { fn shared_virtual_nic_wraps_virtual_nic_and_tracks_ifcfg() {
let mut shared_nic = SharedVirtualNic::new(get_mock_global_ctx()); let mut shared_nic = SharedVirtualNic::new(virtual_nic_config());
let member = member_id(1); let member = member_id(1);
let route = SharedIpv4Route::new(Ipv4Addr::new(10, 40, 0, 0), 24, None); let route = SharedIpv4Route::new(Ipv4Addr::new(10, 40, 0, 0), 24, None);
shared_nic.attach_member(member, claims_with_ipv4_route(route.clone(), None)); shared_nic
.ifcfg_mut()
.apply_member_claims(member, claims_with_ipv4_route(route.clone(), None));
assert_eq!( assert_eq!(
shared_nic.ifcfg().owners_of_ipv4_route(&route), shared_nic.ifcfg().owners_of_ipv4_route(&route),
+59 -32
View File
@@ -13,6 +13,7 @@ use crate::{
global_ctx::{ArcGlobalCtx, GlobalCtxEvent}, global_ctx::{ArcGlobalCtx, GlobalCtxEvent},
ifcfg::{IfConfiger, IfConfiguerTrait}, ifcfg::{IfConfiger, IfConfiguerTrait},
log, log,
netns::NetNS,
}, },
instance::proxy_cidrs_monitor::ProxyCidrsMonitor, instance::proxy_cidrs_monitor::ProxyCidrsMonitor,
peers::{PacketRecvChanReceiver, peer_manager::PeerManager, recv_packet_from_chan}, peers::{PacketRecvChanReceiver, peer_manager::PeerManager, recv_packet_from_chan},
@@ -239,8 +240,24 @@ impl AsyncWrite for TunAsyncWrite {
} }
} }
pub struct VirtualNicConfig {
dev_name: String,
mtu: u32,
net_ns: NetNS,
}
impl VirtualNicConfig {
pub fn new(dev_name: String, mtu: u32, net_ns: NetNS) -> Self {
Self {
dev_name,
mtu,
net_ns,
}
}
}
pub struct VirtualNic { pub struct VirtualNic {
global_ctx: ArcGlobalCtx, config: VirtualNicConfig,
ifname: Option<String>, ifname: Option<String>,
ifcfg: Box<dyn IfConfiguerTrait + Send + Sync + 'static>, ifcfg: Box<dyn IfConfiguerTrait + Send + Sync + 'static>,
@@ -265,9 +282,9 @@ impl Drop for VirtualNic {
} }
impl VirtualNic { impl VirtualNic {
pub fn new(global_ctx: ArcGlobalCtx) -> Self { pub fn new(config: VirtualNicConfig) -> Self {
Self { Self {
global_ctx, config,
ifname: None, ifname: None,
ifcfg: Box::new(IfConfiger {}), ifcfg: Box::new(IfConfiger {}),
} }
@@ -492,14 +509,14 @@ impl VirtualNic {
Ok(()) Ok(())
} }
async fn create_tun(&self) -> Result<tun::platform::Device, Error> { async fn create_tun(&mut self) -> Result<tun::platform::Device, Error> {
let mut config = Configuration::default(); let mut config = Configuration::default();
config.layer(Layer::L3); config.layer(Layer::L3);
// FreeBSD specific: Check and restore TUN interfaces before creating new one // FreeBSD specific: Check and restore TUN interfaces before creating new one
#[cfg(target_os = "freebsd")] #[cfg(target_os = "freebsd")]
{ {
let dev_name = self.global_ctx.get_flags().dev_name; let dev_name = self.config.dev_name.clone();
if !dev_name.is_empty() { if !dev_name.is_empty() {
// Restore TUN interface name if needed, ignoring errors as it's not critical // Restore TUN interface name if needed, ignoring errors as it's not critical
@@ -512,7 +529,7 @@ impl VirtualNic {
// Check and create TUN device node if necessary (Linux only) // Check and create TUN device node if necessary (Linux only)
Self::ensure_tun_device_node().await; Self::ensure_tun_device_node().await;
let dev_name = self.global_ctx.get_flags().dev_name; let dev_name = self.config.dev_name.clone();
if !dev_name.is_empty() { if !dev_name.is_empty() {
config.tun_name(&dev_name); config.tun_name(&dev_name);
} }
@@ -526,7 +543,7 @@ impl VirtualNic {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
let dev_name = self.global_ctx.get_flags().dev_name; let dev_name = self.config.dev_name.clone();
match crate::arch::windows::add_self_to_firewall_allowlist() { match crate::arch::windows::add_self_to_firewall_allowlist() {
Ok(_) => tracing::info!("add_self_to_firewall_allowlist successful!"), Ok(_) => tracing::info!("add_self_to_firewall_allowlist successful!"),
@@ -558,10 +575,7 @@ impl VirtualNic {
let random_dev_name = format!("et_{}_{}", c, s); let random_dev_name = format!("et_{}_{}", c, s);
config.tun_name(random_dev_name.clone()); config.tun_name(random_dev_name.clone());
self.config.dev_name = random_dev_name;
let mut flags = self.global_ctx.get_flags();
flags.dev_name = random_dev_name.clone();
self.global_ctx.set_flags(flags);
} }
config.platform_config(|config| { config.platform_config(|config| {
@@ -575,7 +589,7 @@ impl VirtualNic {
config.up(); config.up();
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
Ok(tun::create(&config)?) Ok(tun::create(&config)?)
} }
@@ -632,7 +646,7 @@ impl VirtualNic {
// FreeBSD TUN interface rename functionality // FreeBSD TUN interface rename functionality
#[cfg(target_os = "freebsd")] #[cfg(target_os = "freebsd")]
{ {
let dev_name = self.global_ctx.get_flags().dev_name; let dev_name = self.config.dev_name.clone();
if !dev_name.is_empty() && dev_name != ifname { if !dev_name.is_empty() && dev_name != ifname {
// Use ifconfig to rename the TUN interface // Use ifconfig to rename the TUN interface
@@ -668,15 +682,10 @@ impl VirtualNic {
let dev = AsyncDevice::new(dev)?; let dev = AsyncDevice::new(dev)?;
let flags = self.global_ctx.config.get_flags();
let mut mtu_in_config = flags.mtu;
if flags.enable_encryption {
mtu_in_config -= 20;
}
{ {
// set mtu by ourselves, rust-tun does not handle it correctly on windows // set mtu by ourselves, rust-tun does not handle it correctly on windows
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg.set_mtu(ifname.as_str(), mtu_in_config).await?; self.ifcfg.set_mtu(ifname.as_str(), self.config.mtu).await?;
} }
let has_packet_info = cfg!(all(target_os = "macos", not(feature = "macos-ne"))); let has_packet_info = cfg!(all(target_os = "macos", not(feature = "macos-ne")));
@@ -723,13 +732,13 @@ impl VirtualNic {
} }
pub async fn link_up(&self) -> Result<(), Error> { pub async fn link_up(&self) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg.set_link_status(self.ifname(), true).await?; self.ifcfg.set_link_status(self.ifname(), true).await?;
Ok(()) Ok(())
} }
pub async fn add_route(&self, address: Ipv4Addr, cidr: u8) -> Result<(), Error> { pub async fn add_route(&self, address: Ipv4Addr, cidr: u8) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg self.ifcfg
.add_ipv4_route(self.ifname(), address, cidr, None) .add_ipv4_route(self.ifname(), address, cidr, None)
.await?; .await?;
@@ -746,7 +755,7 @@ impl VirtualNic {
cidr: u8, cidr: u8,
cost: Option<i32>, cost: Option<i32>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg self.ifcfg
.add_ipv6_route(self.ifname(), address, cidr, cost) .add_ipv6_route(self.ifname(), address, cidr, cost)
.await?; .await?;
@@ -754,7 +763,7 @@ impl VirtualNic {
} }
pub async fn remove_ipv6_route(&self, address: Ipv6Addr, cidr: u8) -> Result<(), Error> { pub async fn remove_ipv6_route(&self, address: Ipv6Addr, cidr: u8) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg self.ifcfg
.remove_ipv6_route(self.ifname(), address, cidr) .remove_ipv6_route(self.ifname(), address, cidr)
.await?; .await?;
@@ -762,19 +771,19 @@ impl VirtualNic {
} }
pub async fn remove_ip(&self, ip: Option<Ipv4Inet>) -> Result<(), Error> { pub async fn remove_ip(&self, ip: Option<Ipv4Inet>) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg.remove_ip(self.ifname(), ip).await?; self.ifcfg.remove_ip(self.ifname(), ip).await?;
Ok(()) Ok(())
} }
pub async fn remove_ipv6(&self, ip: Option<Ipv6Inet>) -> Result<(), Error> { pub async fn remove_ipv6(&self, ip: Option<Ipv6Inet>) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg.remove_ipv6(self.ifname(), ip).await?; self.ifcfg.remove_ipv6(self.ifname(), ip).await?;
Ok(()) Ok(())
} }
pub async fn add_ip(&self, ip: Ipv4Addr, cidr: i32) -> Result<(), Error> { pub async fn add_ip(&self, ip: Ipv4Addr, cidr: i32) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg self.ifcfg
.add_ipv4_ip(self.ifname(), ip, cidr as u8) .add_ipv4_ip(self.ifname(), ip, cidr as u8)
.await?; .await?;
@@ -782,7 +791,7 @@ impl VirtualNic {
} }
pub async fn add_ipv6(&self, ip: Ipv6Addr, cidr: i32) -> Result<(), Error> { pub async fn add_ipv6(&self, ip: Ipv6Addr, cidr: i32) -> Result<(), Error> {
let _g = self.global_ctx.net_ns.guard(); let _g = self.config.net_ns.guard();
self.ifcfg self.ifcfg
.add_ipv6_ip(self.ifname(), ip, cidr as u8) .add_ipv6_ip(self.ifname(), ip, cidr as u8)
.await?; .await?;
@@ -809,12 +818,24 @@ pub struct NicCtx {
} }
impl NicCtx { impl NicCtx {
fn virtual_nic_config(global_ctx: &ArcGlobalCtx) -> VirtualNicConfig {
let flags = global_ctx.get_flags();
let mut mtu = flags.mtu;
if flags.enable_encryption {
mtu -= 20;
}
VirtualNicConfig::new(flags.dev_name, mtu, global_ctx.net_ns.clone())
}
pub fn new( pub fn new(
global_ctx: ArcGlobalCtx, global_ctx: ArcGlobalCtx,
peer_manager: &Arc<PeerManager>, peer_manager: &Arc<PeerManager>,
peer_packet_receiver: Arc<Mutex<PacketRecvChanReceiver>>, peer_packet_receiver: Arc<Mutex<PacketRecvChanReceiver>>,
close_notifier: Arc<Notify>, close_notifier: Arc<Notify>,
) -> Self { ) -> Self {
let nic_config = Self::virtual_nic_config(&global_ctx);
NicCtx { NicCtx {
global_ctx: global_ctx.clone(), global_ctx: global_ctx.clone(),
peer_mgr: Arc::downgrade(peer_manager), peer_mgr: Arc::downgrade(peer_manager),
@@ -822,7 +843,7 @@ impl NicCtx {
close_notifier, close_notifier,
nic: Arc::new(Mutex::new(VirtualNic::new(global_ctx))), nic: Arc::new(Mutex::new(VirtualNic::new(nic_config))),
tasks: JoinSet::new(), tasks: JoinSet::new(),
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@@ -1344,7 +1365,12 @@ impl NicCtx {
Ok(ret) => { Ok(ret) => {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
let dev_name = self.global_ctx.get_flags().dev_name; let dev_name = nic.ifname().to_string();
let mut flags = self.global_ctx.get_flags();
if flags.dev_name.is_empty() {
flags.dev_name = dev_name.clone();
self.global_ctx.set_flags(flags);
}
let _ = RegistryManager::reg_change_catrgory_in_profile(&dev_name); let _ = RegistryManager::reg_change_catrgory_in_profile(&dev_name);
} }
@@ -1429,10 +1455,11 @@ impl NicCtx {
mod tests { mod tests {
use crate::common::{error::Error, global_ctx::tests::get_mock_global_ctx}; use crate::common::{error::Error, global_ctx::tests::get_mock_global_ctx};
use super::VirtualNic; use super::{NicCtx, VirtualNic};
async fn run_test_helper() -> Result<VirtualNic, Error> { async fn run_test_helper() -> Result<VirtualNic, Error> {
let mut dev = VirtualNic::new(get_mock_global_ctx()); let global_ctx = get_mock_global_ctx();
let mut dev = VirtualNic::new(NicCtx::virtual_nic_config(&global_ctx));
let _tunnel = dev.create_dev().await?; let _tunnel = dev.create_dev().await?;
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;