diff --git a/easytier/src/instance/dns_server/tests.rs b/easytier/src/instance/dns_server/tests.rs index 2f5a3d84..709a29fb 100644 --- a/easytier/src/instance/dns_server/tests.rs +++ b/easytier/src/instance/dns_server/tests.rs @@ -74,7 +74,11 @@ pub async fn prepare_env_with_tld_dns_zone( core_instance.packet_plane(), host_packet_rx, Arc::new(Notify::new()), - ); + Arc::new(tokio::sync::Mutex::new( + crate::instance::shared_virtual_nic::SharedVirtualNicRegistry::new(), + )), + ) + .await; virtual_nic.run(Some(tun_ip), None).await.unwrap(); (ctx, core_instance, virtual_nic) diff --git a/easytier/src/instance/factory/mobile_tun.rs b/easytier/src/instance/factory/mobile_tun.rs index da1f9ad9..1327a467 100644 --- a/easytier/src/instance/factory/mobile_tun.rs +++ b/easytier/src/instance/factory/mobile_tun.rs @@ -58,7 +58,7 @@ fn comparable_claims(mut claims: SharedIfConfigClaims) -> SharedIfConfigClaims { fn select_members( mut members: Vec, mut rejected: Vec, -) -> (Vec, MobileTunConfig) { +) -> (Vec, Vec) { // Keep unchanged attached members ahead of new or reconfigured members. members.sort_by_key(|member| { !member.current_claims.as_ref().is_some_and(|current| { @@ -82,8 +82,7 @@ fn select_members( ifcfg.apply_member_claims(member.instance_id, claims); accepted.push(member); } - let config = config_for_members(&accepted, rejected); - (accepted, config) + (accepted, rejected) } #[cfg(any(mobile, test))] @@ -100,45 +99,21 @@ fn config_for_members( for member in members { config.instance_ids.push(member.instance_id); for address in &member.claims.ipv4_addresses { - addresses.insert(format!( - "{}/{}", - address.address(), - address.network_length() - )); - routes.insert(format!( - "{}/{}", - address.network().first_address(), - address.network_length() - )); + addresses.insert(format!("{address:#}")); + routes.insert(format!("{:#}", address.network())); } for address in &member.claims.ipv6_addresses { - addresses.insert(format!( - "{}/{}", - address.address(), - address.network_length() - )); - routes.insert(format!( - "{}/{}", - address.network().first_address(), - address.network_length() - )); + addresses.insert(format!("{address:#}")); + routes.insert(format!("{:#}", address.network())); } for route in &member.claims.ipv4_routes { if let Ok(route) = cidr::Ipv4Inet::new(route.address, route.prefix) { - routes.insert(format!( - "{}/{}", - route.network().first_address(), - route.network_length() - )); + routes.insert(format!("{:#}", route.network())); } } for route in &member.claims.ipv6_routes { if let Ok(route) = cidr::Ipv6Inet::new(route.address, route.prefix) { - routes.insert(format!( - "{}/{}", - route.network().first_address(), - route.network_length() - )); + routes.insert(format!("{:#}", route.network())); } } if member.dns { @@ -199,7 +174,8 @@ pub async fn get_mobile_tun_config( instance_ids: &[uuid::Uuid], ) -> MobileTunConfig { let (members, rejected) = collect_members(manager, instance_ids).await; - select_members(members, rejected).1 + let (members, rejected) = select_members(members, rejected); + config_for_members(&members, rejected) } #[cfg(any(mobile, test))] @@ -243,7 +219,7 @@ pub async fn attach_mobile_tun_fd( return Ok(MobileTunConfig::default()); } let (members, rejected) = collect_members(manager, instance_ids.unwrap_or(&all_ids)).await; - let (members, mut config) = select_members(members, rejected); + let (members, mut rejected) = select_members(members, rejected); let mut attached = Vec::new(); // Open once; a rejected member never replaces or tears down this shared fd. for member in members { @@ -259,7 +235,7 @@ pub async fn attach_mobile_tun_fd( Ok(()) => attached.push(member), Err(error) if is_claim_conflict(&error) => { attach_member(manager, member.instance_id, 0, false, None).await?; - config.rejected.push(MobileTunMemberError { + rejected.push(MobileTunMemberError { instance_id: member.instance_id, error: error.to_string(), }); @@ -272,7 +248,7 @@ pub async fn attach_mobile_tun_fd( } } } - Ok(config_for_members(&attached, config.rejected)) + Ok(config_for_members(&attached, rejected)) } #[cfg(mobile)] @@ -315,7 +291,9 @@ mod tests { fn keeps_attached_member_when_bootstrap_subnets_overlap() { let mut existing = member(2, "10.126.126.2/24"); existing.current_claims = Some(existing.claims.clone()); - let (_, config) = select_members(vec![member(1, "10.126.126.1/24"), existing], Vec::new()); + let (members, rejected) = + select_members(vec![member(1, "10.126.126.1/24"), existing], Vec::new()); + let config = config_for_members(&members, rejected); assert_eq!(config.instance_ids, vec![uuid::Uuid::from_u128(2)]); assert_eq!(config.addresses, vec!["10.126.126.2/24"]); assert_eq!(config.routes, vec!["10.126.126.0/24"]); @@ -329,7 +307,8 @@ mod tests { let mut unchanged = member(2, "10.0.2.2/24"); unchanged.current_claims = Some(unchanged.claims.clone()); unchanged.current_claims.as_mut().unwrap().mtu = Some(1300); - let (_, config) = select_members(vec![changed, unchanged], Vec::new()); + let (members, rejected) = select_members(vec![changed, unchanged], Vec::new()); + let config = config_for_members(&members, rejected); assert_eq!(config.instance_ids, vec![uuid::Uuid::from_u128(2)]); assert_eq!(config.rejected[0].instance_id, uuid::Uuid::from_u128(1)); } @@ -338,6 +317,10 @@ mod tests { fn projects_ipv6_and_shared_dns_and_rejects_overlapping_proxy_routes() { let mut first = member(1, "10.0.1.1/24"); first.dns = true; + first + .claims + .ipv4_addresses + .insert("10.0.4.1/32".parse().unwrap()); first .claims .ipv6_addresses @@ -348,12 +331,16 @@ mod tests { .insert(SharedIpv6Route::new("::".parse().unwrap(), 0, Some(5))); let mut second = member(2, "10.0.2.1/24"); second.dns = true; - let (_, config) = select_members( + let (members, rejected) = select_members( vec![first, second, member(3, "100.100.100.101/32")], Vec::new(), ); + let config = config_for_members(&members, rejected); assert_eq!(config.instance_ids.len(), 2); + assert!(config.addresses.contains(&"10.0.4.1/32".to_owned())); + assert!(config.routes.contains(&"10.0.4.1/32".to_owned())); assert!(config.addresses.contains(&"2001:db8::1/128".to_owned())); + assert!(config.routes.contains(&"2001:db8::1/128".to_owned())); assert!(config.routes.contains(&"::/0".to_owned())); assert_eq!( config @@ -371,7 +358,9 @@ mod tests { 24, None, )); - let (_, config) = select_members(vec![member(1, "10.0.1.1/24"), route_owner], Vec::new()); + let (members, rejected) = + select_members(vec![member(1, "10.0.1.1/24"), route_owner], Vec::new()); + let config = config_for_members(&members, rejected); assert_eq!(config.instance_ids.len(), 1); assert_eq!(config.rejected[0].instance_id, uuid::Uuid::from_u128(2)); } diff --git a/easytier/src/instance/runtime_host/tun_common.rs b/easytier/src/instance/runtime_host/tun_common.rs index 053a14db..d51eb5ed 100644 --- a/easytier/src/instance/runtime_host/tun_common.rs +++ b/easytier/src/instance/runtime_host/tun_common.rs @@ -3,44 +3,28 @@ use std::{ sync::{Arc, OnceLock}, }; -use easytier_core::{host::packet::HostPacketReceiver, instance::CorePacketPlane}; -use tokio::{sync::Mutex, task::JoinSet}; +use cidr::Ipv4Inet; +#[cfg(not(mobile))] +use easytier_core::instance::CorePacketPlane; +use easytier_core::{ + gateway::dhcp::{DhcpIpv4ApplyOutcome, DhcpIpv4ApplyPermit, DhcpIpv4Host}, + host::packet::HostPacketReceiver, +}; +use futures::FutureExt as _; +use tokio::{ + sync::{Mutex, Notify}, + task::JoinSet, +}; +use tokio_util::sync::CancellationToken; use super::MagicDnsRuntime; +#[cfg(not(mobile))] +use crate::instance::shared_virtual_nic::ArcSharedVirtualNicRegistry; use crate::{ - common::{error::Error, global_ctx::ArcGlobalCtx}, - instance::{shared_virtual_nic::ArcSharedVirtualNicRegistry, virtual_nic::NicCtx}, + common::global_ctx::{ArcGlobalCtx, GlobalCtxEvent}, + instance::virtual_nic::NicCtx, }; -pub(super) async fn create_nic_ctx( - global_ctx: ArcGlobalCtx, - packet_plane: Arc, - receiver: Arc>, - close_notifier: Arc, - registry: ArcSharedVirtualNicRegistry, -) -> Result { - #[cfg(not(mobile))] - if global_ctx.get_flags().dev_name.is_empty() { - return Ok(NicCtx::new( - global_ctx, - packet_plane, - receiver, - close_notifier, - )); - } - - let member_id = global_ctx.get_id(); - NicCtx::new_shared( - global_ctx, - packet_plane, - receiver, - close_notifier, - registry, - member_id, - ) - .await -} - struct NicCtxContainer { _nic_ctx: Option>, magic_dns: MagicDnsRuntime, @@ -130,3 +114,161 @@ impl TunNicState { .replace(NicCtxContainer::new(nic, magic_dns)); } } + +pub(super) struct NativeDhcpIpv4Host { + pub(super) global_ctx: ArcGlobalCtx, + pub(super) operation: Arc>, + pub(super) cancel: CancellationToken, + pub(super) nic: TunNicState, + pub(super) closed: Arc, + #[cfg(not(mobile))] + pub(super) packet_plane: Arc, + #[cfg(not(mobile))] + pub(super) shared_virtual_nic_registry: ArcSharedVirtualNicRegistry, +} + +impl NativeDhcpIpv4Host { + fn ensure_open(&self) -> anyhow::Result<()> { + if self.cancel.is_cancelled() { + anyhow::bail!("instance is closing; DHCP update cancelled"); + } + Ok(()) + } + + async fn apply(&self, next: Option) -> anyhow::Result> { + self.ensure_open()?; + tokio::select! { + _ = self.cancel.cancelled() => anyhow::bail!("instance is closing; DHCP update cancelled"), + _ = self.nic.drain() => {} + } + self.ensure_open()?; + + #[cfg(not(mobile))] + if let Some(ip) = next + && !self.global_ctx.no_tun() + { + let mut nic = NicCtx::new( + self.global_ctx.clone(), + self.packet_plane.clone(), + self.nic.receiver(), + self.closed.clone(), + self.shared_virtual_nic_registry.clone(), + ) + .await; + tokio::select! { + _ = self.cancel.cancelled() => anyhow::bail!("instance is closing; DHCP update cancelled"), + result = nic.run(Some(ip), self.global_ctx.get_ipv6()) => result?, + } + let shared_route_backend = nic.shared_route_backend_for_dns(); + let magic_dns = MagicDnsRuntime::start( + self.global_ctx.clone(), + self.packet_plane.clone(), + nic.ifname().await, + ip, + shared_route_backend, + ); + self.nic.install(nic, magic_dns).await; + } + self.global_ctx.set_ipv4(next); + Ok(next) + } +} + +#[async_trait::async_trait] +impl DhcpIpv4Host for NativeDhcpIpv4Host { + fn take_interface_closed(&self) -> bool { + self.closed.notified().now_or_never().is_some() + } + + async fn apply_dhcp_ipv4( + &self, + _previous: Option, + next: Option, + ) -> DhcpIpv4ApplyOutcome { + let permit = self.operation.clone().lock_owned().await; + let outcome = match self.apply(next).await { + Ok(actual) => DhcpIpv4ApplyOutcome::applied(actual), + Err(error) => DhcpIpv4ApplyOutcome::failed(self.global_ctx.get_ipv4(), error), + }; + outcome.with_permit(DhcpIpv4ApplyPermit::new(permit)) + } + + fn publish_dhcp_ipv4( + &self, + previous: Option, + requested: Option, + actual: Option, + ) { + let event = if requested.is_none() { + GlobalCtxEvent::DhcpIpv4Conflicted(previous) + } else { + GlobalCtxEvent::DhcpIpv4Changed(previous, actual) + }; + self.global_ctx.issue_event(event); + } +} + +#[cfg(test)] +mod tests { + use easytier_core::{config::toml::TomlConfig, process_runtime::CoreProcessRuntime}; + + use super::*; + use crate::instance::test_instance::TestInstance; + + #[tokio::test] + async fn dhcp_preserves_publication_permit_and_cancelled_address() { + let instance = TestInstance::new_with_process_runtime( + TomlConfig::default(), + CoreProcessRuntime::new(), + ); + let global_ctx = instance.get_global_ctx(); + let mut flags = global_ctx.get_flags(); + flags.no_tun = true; + global_ctx.set_flags(flags); + let operation = Arc::new(Mutex::new(())); + let cancel = CancellationToken::new(); + let runtime = super::super::NativeTunRuntime::new( + global_ctx.clone(), + cancel.clone(), + TestInstance::new_shared_virtual_nic_registry(), + ); + let (_sender, receiver) = tokio::sync::mpsc::channel(1); + runtime + .install_packet_receiver(HostPacketReceiver::new(receiver)) + .unwrap(); + let host = runtime.dhcp_host( + operation.clone(), + instance.get_core_instance().packet_plane(), + ); + let mut events = global_ctx.subscribe(); + let address = "10.20.0.7/24".parse().unwrap(); + let mut previous = None; + for next in [Some(address), None] { + let outcome = host.apply_dhcp_ipv4(previous, next).await; + assert!(outcome.result.is_ok()); + assert_eq!(outcome.actual, next); + assert_eq!(global_ctx.get_ipv4(), next); + assert!(operation.try_lock().is_err()); + host.publish_dhcp_ipv4(previous, next, outcome.actual); + assert_eq!( + events.try_recv().unwrap(), + if next.is_none() { + GlobalCtxEvent::DhcpIpv4Conflicted(previous) + } else { + GlobalCtxEvent::DhcpIpv4Changed(previous, next) + } + ); + drop(outcome); + assert!(operation.try_lock().is_ok()); + previous = next; + } + + global_ctx.set_ipv4(Some(address)); + cancel.cancel(); + let outcome = host.apply_dhcp_ipv4(Some(address), None).await; + assert!(outcome.result.is_err()); + assert_eq!(outcome.actual, Some(address)); + assert_eq!(global_ctx.get_ipv4(), Some(address)); + runtime.shutdown().await; + } +} diff --git a/easytier/src/instance/runtime_host/tun_desktop.rs b/easytier/src/instance/runtime_host/tun_desktop.rs index f179750c..36aab57d 100644 --- a/easytier/src/instance/runtime_host/tun_desktop.rs +++ b/easytier/src/instance/runtime_host/tun_desktop.rs @@ -1,13 +1,9 @@ use std::{sync::Arc, time::Duration}; use anyhow::Context as _; -use cidr::Ipv4Inet; use easytier_core::{ - gateway::dhcp::{DhcpIpv4ApplyOutcome, DhcpIpv4ApplyPermit, DhcpIpv4Host}, - host::packet::HostPacketReceiver, - instance::CorePacketPlane, + gateway::dhcp::DhcpIpv4Host, host::packet::HostPacketReceiver, instance::CorePacketPlane, }; -use futures::FutureExt as _; use tokio::{ sync::{Mutex, Notify, oneshot}, task::JoinHandle, @@ -16,14 +12,11 @@ use tokio_util::sync::CancellationToken; use super::{ MagicDnsRuntime, - tun_common::{TunNicState, create_nic_ctx}, + tun_common::{NativeDhcpIpv4Host, TunNicState}, }; use crate::{ - common::{ - error::Error, - global_ctx::{ArcGlobalCtx, GlobalCtxEvent}, - }, - instance::shared_virtual_nic::ArcSharedVirtualNicRegistry, + common::{error::Error, global_ctx::ArcGlobalCtx}, + instance::{shared_virtual_nic::ArcSharedVirtualNicRegistry, virtual_nic::NicCtx}, }; pub(super) struct NativeTunRuntime { @@ -86,29 +79,14 @@ impl NativeTunRuntime { return; } let closed = Arc::new(Notify::new()); - let mut nic = match create_nic_ctx( + let mut nic = NicCtx::new( global_ctx.clone(), packet_plane.clone(), receiver.clone(), closed.clone(), shared_virtual_nic_registry.clone(), ) - .await - { - Ok(nic) => nic, - Err(error) => { - if let Some(output) = output.take() { - let _ = output.send(Err(error)); - return; - } - tracing::error!(?error, "failed to create native interface context"); - tokio::select! { - _ = cancel.cancelled() => return, - _ = tokio::time::sleep(Duration::from_secs(1)) => {} - } - continue; - } - }; + .await; let result = tokio::select! { biased; _ = cancel.cancelled() => { @@ -194,98 +172,3 @@ impl NativeTunRuntime { }) } } - -struct NativeDhcpIpv4Host { - global_ctx: ArcGlobalCtx, - operation: Arc>, - cancel: CancellationToken, - nic: TunNicState, - closed: Arc, - packet_plane: Arc, - shared_virtual_nic_registry: ArcSharedVirtualNicRegistry, -} - -impl NativeDhcpIpv4Host { - fn ensure_open(&self) -> anyhow::Result<()> { - if self.cancel.is_cancelled() { - anyhow::bail!("instance is closing; DHCP update cancelled"); - } - Ok(()) - } - - async fn apply(&self, next: Option) -> anyhow::Result> { - self.ensure_open()?; - tokio::select! { - _ = self.cancel.cancelled() => anyhow::bail!("instance is closing; DHCP update cancelled"), - _ = self.nic.drain() => {} - } - self.ensure_open()?; - - let Some(ip) = next else { - self.global_ctx.set_ipv4(None); - return Ok(None); - }; - if self.global_ctx.no_tun() { - self.global_ctx.set_ipv4(Some(ip)); - return Ok(Some(ip)); - } - - let mut nic = create_nic_ctx( - self.global_ctx.clone(), - self.packet_plane.clone(), - self.nic.receiver(), - self.closed.clone(), - self.shared_virtual_nic_registry.clone(), - ) - .await?; - tokio::select! { - _ = self.cancel.cancelled() => anyhow::bail!("instance is closing; DHCP update cancelled"), - result = nic.run(Some(ip), self.global_ctx.get_ipv6()) => result?, - } - let shared_route_backend = nic.shared_route_backend_for_dns(); - let magic_dns = MagicDnsRuntime::start( - self.global_ctx.clone(), - self.packet_plane.clone(), - nic.ifname().await, - ip, - shared_route_backend, - ); - self.nic.install(nic, magic_dns).await; - self.global_ctx.set_ipv4(Some(ip)); - Ok(Some(ip)) - } -} - -#[async_trait::async_trait] -impl DhcpIpv4Host for NativeDhcpIpv4Host { - fn take_interface_closed(&self) -> bool { - self.closed.notified().now_or_never().is_some() - } - - async fn apply_dhcp_ipv4( - &self, - _previous: Option, - next: Option, - ) -> DhcpIpv4ApplyOutcome { - let permit = self.operation.clone().lock_owned().await; - let outcome = match self.apply(next).await { - Ok(actual) => DhcpIpv4ApplyOutcome::applied(actual), - Err(error) => DhcpIpv4ApplyOutcome::failed(self.global_ctx.get_ipv4(), error), - }; - outcome.with_permit(DhcpIpv4ApplyPermit::new(permit)) - } - - fn publish_dhcp_ipv4( - &self, - previous: Option, - requested: Option, - actual: Option, - ) { - let event = if requested.is_none() { - GlobalCtxEvent::DhcpIpv4Conflicted(previous) - } else { - GlobalCtxEvent::DhcpIpv4Changed(previous, actual) - }; - self.global_ctx.issue_event(event); - } -} diff --git a/easytier/src/instance/runtime_host/tun_mobile.rs b/easytier/src/instance/runtime_host/tun_mobile.rs index 1200f2ae..c75535ee 100644 --- a/easytier/src/instance/runtime_host/tun_mobile.rs +++ b/easytier/src/instance/runtime_host/tun_mobile.rs @@ -1,23 +1,19 @@ use std::sync::Arc; use anyhow::Context as _; -use cidr::Ipv4Inet; use easytier_core::{ - gateway::dhcp::{DhcpIpv4ApplyOutcome, DhcpIpv4ApplyPermit, DhcpIpv4Host}, - host::packet::HostPacketReceiver, - instance::CorePacketPlane, + gateway::dhcp::DhcpIpv4Host, host::packet::HostPacketReceiver, instance::CorePacketPlane, }; -use futures::FutureExt as _; use tokio::sync::{Mutex, Notify, mpsc, oneshot}; use tokio_util::sync::CancellationToken; use super::{ MagicDnsRuntime, - tun_common::{TunNicState, create_nic_ctx}, + tun_common::{NativeDhcpIpv4Host, TunNicState}, }; use crate::{ - common::global_ctx::{ArcGlobalCtx, GlobalCtxEvent}, - instance::shared_virtual_nic::ArcSharedVirtualNicRegistry, + common::global_ctx::ArcGlobalCtx, + instance::{shared_virtual_nic::ArcSharedVirtualNicRegistry, virtual_nic::NicCtx}, }; struct MobileTunAttachment { @@ -76,20 +72,17 @@ impl NativeTunRuntime { return Ok(()); } let closed = Arc::new(Notify::new()); - let mut nic = create_nic_ctx( + let mut nic = NicCtx::new( global_ctx.clone(), packet_plane.clone(), nic_state.receiver(), closed, shared_virtual_nic_registry, ) - .await?; + .await; let claims = match claims { Some(claims) => claims, - None => { - crate::instance::virtual_nic::NicCtx::mobile_tun_claims(&global_ctx, &packet_plane) - .await - } + None => NicCtx::mobile_tun_claims(&global_ctx, &packet_plane).await, }; nic.run_for_mobile(fd, replace_tun_fd, claims) .await @@ -122,21 +115,16 @@ impl NativeTunRuntime { None => return, }, }; - let result = if attachment.fd <= 0 { - nic_state.drain().await; - Ok(()) - } else { - Self::install_mobile_tun( - nic_state.clone(), - global_ctx.clone(), - packet_plane.clone(), - attachment.fd, - attachment.replace_tun_fd, - shared_virtual_nic_registry.clone(), - attachment.claims, - ) - .await - }; + let result = Self::install_mobile_tun( + nic_state.clone(), + global_ctx.clone(), + packet_plane.clone(), + attachment.fd, + attachment.replace_tun_fd, + shared_virtual_nic_registry.clone(), + attachment.claims, + ) + .await; if let Err(error) = &result { tracing::error!(?error, "failed to attach mobile TUN fd"); } @@ -215,70 +203,3 @@ impl NativeTunRuntime { self.nic.mobile_tun_claims().await } } - -struct NativeDhcpIpv4Host { - global_ctx: ArcGlobalCtx, - operation: Arc>, - cancel: CancellationToken, - nic: TunNicState, - closed: Arc, -} - -impl NativeDhcpIpv4Host { - fn ensure_open(&self) -> anyhow::Result<()> { - if self.cancel.is_cancelled() { - anyhow::bail!("instance is closing; DHCP update cancelled"); - } - Ok(()) - } - - async fn apply(&self, next: Option) -> anyhow::Result> { - self.ensure_open()?; - tokio::select! { - _ = self.cancel.cancelled() => anyhow::bail!("instance is closing; DHCP update cancelled"), - _ = self.nic.drain() => {} - } - self.ensure_open()?; - - let Some(ip) = next else { - self.global_ctx.set_ipv4(None); - return Ok(None); - }; - self.global_ctx.set_ipv4(Some(ip)); - Ok(Some(ip)) - } -} - -#[async_trait::async_trait] -impl DhcpIpv4Host for NativeDhcpIpv4Host { - fn take_interface_closed(&self) -> bool { - self.closed.notified().now_or_never().is_some() - } - - async fn apply_dhcp_ipv4( - &self, - _previous: Option, - next: Option, - ) -> DhcpIpv4ApplyOutcome { - let permit = self.operation.clone().lock_owned().await; - let outcome = match self.apply(next).await { - Ok(actual) => DhcpIpv4ApplyOutcome::applied(actual), - Err(error) => DhcpIpv4ApplyOutcome::failed(self.global_ctx.get_ipv4(), error), - }; - outcome.with_permit(DhcpIpv4ApplyPermit::new(permit)) - } - - fn publish_dhcp_ipv4( - &self, - previous: Option, - requested: Option, - actual: Option, - ) { - let event = if requested.is_none() { - GlobalCtxEvent::DhcpIpv4Conflicted(previous) - } else { - GlobalCtxEvent::DhcpIpv4Changed(previous, actual) - }; - self.global_ctx.issue_event(event); - } -} diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index 0a0a9c2f..69c0e260 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -44,9 +44,7 @@ use zerocopy::{NativeEndian, NetworkEndian}; #[cfg(target_os = "windows")] use crate::common::ifcfg::RegistryManager; -use super::shared_virtual_nic::{ - ArcSharedVirtualNicRegistry, SharedVirtualNicMember, SharedVirtualNicMemberId, -}; +use super::shared_virtual_nic::{ArcSharedVirtualNicRegistry, SharedVirtualNicMember}; pin_project! { pub struct TunStream { @@ -905,14 +903,6 @@ pub enum NicBackend { } impl NicBackend { - pub fn dedicated(nic: Arc>) -> Self { - Self::Dedicated(nic) - } - - pub fn shared(member: SharedVirtualNicMember) -> Self { - Self::Shared(member) - } - pub async fn create_dev(&self) -> Result, Error> { match self { Self::Dedicated(nic) => nic.lock().await.create_dev().await, @@ -1083,27 +1073,14 @@ pub struct NicCtx { } impl NicCtx { - fn virtual_nic_config_from_parts( - dev_name: String, - mut mtu: u32, - enable_encryption: bool, - net_ns: NetNS, - ) -> VirtualNicConfig { - if enable_encryption { + 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(dev_name, mtu, net_ns) - } - - fn virtual_nic_config(global_ctx: &ArcGlobalCtx) -> VirtualNicConfig { - let flags = global_ctx.get_flags(); - Self::virtual_nic_config_from_parts( - flags.dev_name, - flags.mtu, - flags.enable_encryption, - global_ctx.net_ns.clone(), - ) + VirtualNicConfig::new(flags.dev_name, mtu, global_ctx.net_ns.clone()) } pub(crate) fn shared_route_backend_for_dns(&self) -> Option { @@ -1113,11 +1090,6 @@ impl NicCtx { } } - fn dedicated_backend(global_ctx: &ArcGlobalCtx) -> NicBackend { - let nic_config = Self::virtual_nic_config(global_ctx); - NicBackend::dedicated(Arc::new(Mutex::new(VirtualNic::new(nic_config)))) - } - fn new_with_backend( global_ctx: ArcGlobalCtx, packet_plane: Arc, @@ -1140,13 +1112,30 @@ impl NicCtx { } } - pub(crate) fn new( + pub(crate) async fn new( global_ctx: ArcGlobalCtx, packet_plane: Arc, peer_packet_receiver: Arc>, close_notifier: Arc, + registry: ArcSharedVirtualNicRegistry, ) -> Self { - let backend = Self::dedicated_backend(&global_ctx); + let nic_config = Self::virtual_nic_config(&global_ctx); + #[cfg(mobile)] + let nic_config = VirtualNicConfig { + dev_name: String::new(), + net_ns: NetNS::new(None), + ..nic_config + }; + let backend = if cfg!(mobile) || !nic_config.dev_name.is_empty() { + NicBackend::Shared(registry.lock().await.create_member( + nic_config.dev_name.clone(), + nic_config, + global_ctx.get_id(), + close_notifier.clone(), + )) + } else { + NicBackend::Dedicated(Arc::new(Mutex::new(VirtualNic::new(nic_config)))) + }; Self::new_with_backend( global_ctx, @@ -1157,51 +1146,6 @@ impl NicCtx { ) } - pub(crate) async fn new_shared( - global_ctx: ArcGlobalCtx, - packet_plane: Arc, - peer_packet_receiver: Arc>, - close_notifier: Arc, - registry: ArcSharedVirtualNicRegistry, - member_id: SharedVirtualNicMemberId, - ) -> Result { - let flags = global_ctx.get_flags(); - #[cfg(mobile)] - let dev_name = String::new(); - #[cfg(not(mobile))] - let dev_name = flags.dev_name.clone(); - #[cfg(not(mobile))] - if dev_name.is_empty() { - return Err(anyhow::anyhow!("shared virtual nic requires dev_name").into()); - } - #[cfg(mobile)] - let net_ns = NetNS::new(None); - #[cfg(not(mobile))] - let net_ns = global_ctx.net_ns.clone(); - let nic_config = Self::virtual_nic_config_from_parts( - dev_name.clone(), - flags.mtu, - flags.enable_encryption, - net_ns, - ); - - let member = registry.lock().await.create_member( - dev_name, - nic_config, - member_id, - close_notifier.clone(), - ); - let backend = NicBackend::shared(member); - - Ok(Self::new_with_backend( - global_ctx, - packet_plane, - peer_packet_receiver, - close_notifier, - backend, - )) - } - pub async fn ifname(&self) -> Option { self.backend.ifname().await } @@ -1845,7 +1789,7 @@ mod tests { core.packet_plane(), Arc::new(Mutex::new(HostPacketReceiver::new(packet_receiver))), Arc::new(Notify::new()), - NicBackend::shared(members[1].clone()), + NicBackend::Shared(members[1].clone()), ); nic.configure_mobile_tun().await.unwrap();