//! Lifecycle owner for the portable EasyTier runtime. mod build_capabilities; mod config; #[cfg(feature = "proxy-smoltcp-stack")] mod data_plane_extension; mod lifecycle; mod management; #[cfg(feature = "web-client")] mod management_extension; mod management_state; pub mod manager; mod packet_io; mod packet_plane; #[cfg(feature = "proxy-packet")] mod packet_proxy_extension; #[cfg(feature = "public-ipv6-provider")] mod public_ipv6_extension; #[cfg(feature = "vpn-portal")] mod vpn_portal_extension; use std::sync::{ Arc, atomic::{AtomicU8, Ordering}, }; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; #[cfg(feature = "test-utils")] use std::sync::atomic::AtomicUsize; use tokio::sync::Mutex; use tokio_util::sync::CancellationToken; use url::Url; #[cfg(feature = "tcp-hole-punch")] use crate::connectivity::hole_punch::tcp::TcpHolePunchConnector; use crate::{ config::runtime::{CoreInstanceRuntimeConfig, CoreRuntimeConfig, CoreRuntimeConfigStore}, config::toml::TomlConfig, connectivity::hole_punch::port_mapping::UdpPortMappingPlatform, connectivity::hole_punch::tcp::TcpHolePunchHost, connectivity::stun::{ StunDnsRuntime, StunInfoCollector, StunInfoProvider, StunServerConfig, StunSocketMapper, }, connectivity::{ direct::{ DirectConnectorHost, DirectConnectorManager, DirectConnectorOptions, ForeignDirectConnectorRpcRegistrar, }, hole_punch::udp::CoreUdpHolePunchService, manual::{ ManualConnectorManager, ManualConnectorOptions, discovery::{CoreManualEndpointResolver, ManualEndpointDiscoveryConfig}, }, protocol::{ ClientProtocolUpgrader, CoreClientProtocolConfig, CoreClientProtocolUpgrader, ServerProtocolUpgrader, }, }, events::CoreEventSink, gateway::dhcp::DhcpIpv4Host, host::{ dns::{DnsRecordResolver, DnsResolver}, packet::{HostPacketReceiver, PacketSink, host_packet_channel}, }, listener::{ AcceptedSocketHandler, ExternalListenerFactory, ExternalListenerRequest, ListenerFactory, RunningListenerRegistry, plan::{ListenerRuntimeConfig, PreparedListenerPlan, prepare_listener_plan}, transport::{ AcceptedTransport, CoreListenerRuntime, HostAcceptedTcpSocket, ProtocolAcceptedTransportHandler, }, }, peers::peer_center::instance::PeerCenterInstance, peers::{ admission::{PeerAcceptedTunnelHandler, RawAcceptedTransportHandler}, context::PeerStunInfoSource, credential_manager::CredentialStorage, peer_manager::{PeerManagerCore, PortablePeerManagerConfig}, public_ipv6::{CorePublicIpv6Runtime, PublicIpv6Host}, }, process_runtime::CoreProcessRuntime, socket::{tcp::VirtualTcpSocketFactory, udp::VirtualUdpSocketFactory}, }; use crate::gateway::proxy::cidr_table::{ProxyCidrSnapshot, ProxyCidrTable}; #[cfg(feature = "proxy-packet")] use crate::gateway::proxy::icmp_host::IcmpProxyHost; #[cfg(feature = "wrapped-transport")] use crate::gateway::proxy::wrapped_transport::WrappedTransportEngines; #[cfg(feature = "vpn-portal")] use crate::gateway::vpn_portal::PortalHost; use crate::gateway::vpn_portal::PortalRuntimeConfig; #[cfg(feature = "public-ipv6-provider")] use crate::peers::public_ipv6::provider::PublicIpv6ProviderPlatform; #[cfg(feature = "dhcp-ipv4")] use crate::gateway::dhcp::DhcpIpv4Runtime; #[cfg(feature = "proxy-cidr-monitor")] use crate::gateway::proxy::cidr_monitor::ProxyCidrMonitorRuntime; #[cfg(feature = "proxy-packet")] use crate::gateway::proxy::service::CoreProxyModule; #[cfg(feature = "wrapped-transport")] use crate::gateway::proxy::wrapped_transport::WrappedTransportProxyModule; #[cfg(feature = "vpn-portal")] use crate::gateway::vpn_portal::PortalModule; #[cfg(feature = "proxy-smoltcp-stack")] use crate::gateway::{ DataPlaneRuntime, DataPlaneSession, PortForwardAdapter, Socks5GatewayAdapter, }; #[cfg(feature = "public-ipv6-provider")] use crate::peers::public_ipv6::provider::PublicIpv6ProviderRuntime; pub use config::CoreInstanceHostConfig; use management_state::ManagementState; pub use packet_io::PacketEgressHost; use packet_io::PacketSinkEgress; pub use packet_plane::CorePacketPlane; /// Complete Host capability set required by one portable core instance. pub trait CoreInstanceHost: DirectConnectorHost + TcpHolePunchHost {} impl CoreInstanceHost for T where T: DirectConnectorHost + TcpHolePunchHost {} #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum CoreInstanceState { Created, Starting, Running, Stopping, Stopped, } impl CoreInstanceState { fn from_u8(value: u8) -> Self { match value { value if value == Self::Created as u8 => Self::Created, value if value == Self::Starting as u8 => Self::Starting, value if value == Self::Running as u8 => Self::Running, value if value == Self::Stopping as u8 => Self::Stopping, value if value == Self::Stopped as u8 => Self::Stopped, _ => unreachable!("invalid core instance state"), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct CoreInstanceStartupPlan { pub gateway: bool, } impl CoreInstanceStartupPlan { fn is_default(&self) -> bool { self == &Self::default() } } impl Default for CoreInstanceStartupPlan { fn default() -> Self { Self { gateway: true } } } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct CoreConnectivityConfig { pub initial_peers: Vec, pub listeners: Option, pub runtime: CoreRuntimeConfig, #[serde(default, skip_serializing_if = "CoreInstanceStartupPlan::is_default")] pub startup_plan: CoreInstanceStartupPlan, pub stun: StunServerConfig, pub endpoint_discovery: ManualEndpointDiscoveryConfig, pub manual: ManualConnectorOptions, pub direct: DirectConnectorOptions, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CoreInstanceConfig { #[serde(default = "crate::config::toml::default_instance_name")] pub instance_name: String, pub peer: PortablePeerManagerConfig, pub connectivity: CoreConnectivityConfig, #[serde(default, skip_serializing_if = "Option::is_none")] pub vpn_portal: Option, } #[cfg(any(test, feature = "test-utils"))] #[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PeerRelaySessionSnapshot { pub has_state: bool, pub has_session: bool, } fn proxy_cidr_snapshot(config: &CoreInstanceRuntimeConfig) -> ProxyCidrSnapshot { ProxyCidrSnapshot::from_proxy_networks(&config.peer.runtime.core.routes.proxy_networks) } /// Host-owned resources that must be prepared for the complete Instance /// lifetime, such as a native packet interface. #[async_trait::async_trait] pub trait InstanceRuntimeHost: std::any::Any + Send + Sync + 'static { async fn prepare( &self, packet_plane: Arc, ) -> anyhow::Result>>; async fn shutdown(&self); /// Requests prompt Host cleanup when the canonical instance owner is /// dropped without an opportunity to await [`Self::shutdown`]. fn request_shutdown(&self) {} /// Returns the bounded, serialized event journal exposed by process /// management. Hosts that do not produce events keep the default empty /// journal. fn management_events(&self) -> Vec { Vec::new() } /// Applies Host-side cached views of fields already committed to the /// shared TOML model. #[cfg(feature = "web-client")] fn synchronize_config( &self, _patch: &crate::proto::api::config::InstanceConfigPatch, _config: &CoreInstanceRuntimeConfig, ) { } #[cfg(feature = "web-client")] fn publish_config_patch(&self, _patch: crate::proto::api::config::InstanceConfigPatch) {} fn attach_tun_fd(&self, _fd: i32) -> anyhow::Result<()> { anyhow::bail!("external TUN attachment is not supported by this Host") } } #[async_trait::async_trait] impl InstanceRuntimeHost for () { async fn prepare( &self, _packet_plane: Arc, ) -> anyhow::Result>> { Ok(None) } async fn shutdown(&self) {} } /// Host Adapters and optional native capabilities for one core instance. /// /// Callers provide this bundle and one normalized [`CoreInstanceConfig`] to /// [`CoreInstance::new`]. Core constructs and owns every portable runtime /// Module behind that seam. pub struct CoreHostAdapters where H: CoreInstanceHost, { host: Arc, /// Host OS policy and build capabilities used during configuration /// normalization. It contains no portable TOML-derived state. pub config: CoreInstanceHostConfig, #[cfg(any(test, feature = "test-utils"))] /// Optional construction-time STUN provider used by deterministic tests. stun_override: Option::Socket>>>, dns: Arc, process_runtime: Arc, pub packet_egress: Arc, pub instance_runtime: Arc, pub events: Arc, pub credential_storage: Option>, #[cfg(feature = "wrapped-transport")] pub wrapped_transports: WrappedTransportEngines, pub protocol: Option::Socket>>>, pub external_listener_factory: Option>>>>, pub server_protocol: Option>>>, /// Optional OS port-mapping adapter. STUN-only hole punching remains /// available when the host does not provide one. pub udp_hole_punch_platform: Option>, #[cfg(feature = "proxy-packet")] pub icmp_proxy_host: Option>, #[cfg(feature = "proxy-cidr-monitor")] pub proxy_cidr_monitor_enabled: bool, #[cfg(feature = "public-ipv6-provider")] pub public_ipv6_host: Option>, #[cfg(feature = "public-ipv6-provider")] pub public_ipv6_provider: Option>, #[cfg(feature = "vpn-portal")] pub vpn_portal: Option>, } impl CoreHostAdapters where H: CoreInstanceHost, { /// Creates the minimal host bundle. Optional native capabilities can be /// installed on the returned value before constructing the instance. pub fn new( host: Arc, dns: Arc, packet_sink: Arc, process_runtime: Arc, ) -> Self { Self::new_with_packet_egress( host, dns, Arc::new(PacketSinkEgress::new(packet_sink)), process_runtime, ) } /// Creates a host bundle whose packet runtime owns the core's single /// bounded egress receiver directly. pub fn new_with_packet_egress( host: Arc, dns: Arc, packet_egress: Arc, process_runtime: Arc, ) -> Self { Self { host, config: CoreInstanceHostConfig::default(), #[cfg(any(test, feature = "test-utils"))] stun_override: None, dns, process_runtime, packet_egress, instance_runtime: Arc::new(()), events: Arc::new(()), credential_storage: None, #[cfg(feature = "wrapped-transport")] wrapped_transports: WrappedTransportEngines::default(), protocol: None, external_listener_factory: None, server_protocol: None, udp_hole_punch_platform: None, #[cfg(feature = "proxy-packet")] icmp_proxy_host: None, #[cfg(feature = "proxy-cidr-monitor")] proxy_cidr_monitor_enabled: false, #[cfg(feature = "public-ipv6-provider")] public_ipv6_host: None, #[cfg(feature = "public-ipv6-provider")] public_ipv6_provider: None, #[cfg(feature = "vpn-portal")] vpn_portal: None, } } } struct CoreStunPeerInfoSource(Arc); impl PeerStunInfoSource for CoreStunPeerInfoSource { fn stun_info(&self) -> crate::proto::common::StunInfo { self.0.get_stun_info() } } /// Owns the portable peer and connectivity runtime for one EasyTier instance. /// /// An instance is intentionally one-shot: after it is stopped, construct a new /// instance rather than trying to rebuild partially consumed peer-manager state. pub struct CoreInstance where H: CoreInstanceHost, { instance_name: String, #[allow(dead_code)] management: ManagementState, pub(super) instance_runtime: Arc, state: AtomicU8, latest_error: RwLock>, pub(super) operation: Mutex<()>, pub(super) cancel: CancellationToken, pub(super) peer_manager: Arc, packet_plane: Arc, pub(super) manual: ManualConnectorManager, pub(super) direct: DirectConnectorManager, #[cfg(feature = "tcp-hole-punch")] tcp_hole_punch: TcpHolePunchConnector, pub(super) listener: Option>>, running_listeners: Arc, pub(super) udp_hole_punch: CoreUdpHolePunchService, #[cfg(feature = "wrapped-transport")] wrapped_transport: Option>, #[cfg(feature = "proxy-smoltcp-stack")] data_plane_runtime: Arc>, #[cfg(feature = "proxy-smoltcp-stack")] data_plane_session: Arc>, #[cfg(feature = "proxy-smoltcp-stack")] socks5_adapter: Arc>, #[cfg(feature = "proxy-smoltcp-stack")] port_forward_adapter: Arc>, proxy_cidr_table: Arc, #[cfg(feature = "proxy-packet")] packet_proxy: Arc>, #[cfg(feature = "proxy-cidr-monitor")] proxy_cidr_monitor: ProxyCidrMonitorRuntime, #[cfg(feature = "dhcp-ipv4")] dhcp_ipv4: DhcpIpv4Runtime, pub(super) packet_egress: Arc, pub(super) packet_receiver: Mutex>, pub(super) peer_center: Arc, #[cfg(feature = "public-ipv6-provider")] public_ipv6_provider: PublicIpv6ProviderRuntime, #[cfg(feature = "vpn-portal")] vpn_portal: Arc, #[cfg(feature = "proxy-smoltcp-stack")] pub(super) startup_plan: CoreInstanceStartupPlan, pub(super) runtime_config: CoreRuntimeConfigStore, #[cfg(feature = "test-utils")] acl_reload_count: AtomicUsize, } impl CoreInstance where H: CoreInstanceHost, { fn prepare_stun( adapters: &CoreHostAdapters, config: &CoreConnectivityConfig, ) -> Arc::Socket>> { #[cfg(any(test, feature = "test-utils"))] if let Some(stun_override) = &adapters.stun_override { return stun_override.clone(); } Arc::new(StunInfoCollector::new_with_socket_contexts( adapters.host.clone(), adapters.dns.clone(), config.direct.udp_bind.context.clone(), config.direct.tcp_bind.context.clone(), config.stun.udp_servers.clone(), config.stun.tcp_servers.clone(), config.stun.udp_v6_servers.clone(), )) } /// Constructs the complete portable runtime for one EasyTier instance. /// /// This is the only instance construction entry. The normalized config is /// authoritative after creation; all platform behavior enters through the /// supplied Host Adapters. pub fn new( config: CoreInstanceConfig, adapters: CoreHostAdapters, ) -> anyhow::Result> { let host_config = adapters.config.clone(); Self::new_inner(config, None, host_config, adapters) } /// Constructs an instance from the shared TOML model and retains that /// model as the authoritative management configuration. pub fn from_toml( toml_config: TomlConfig, adapters: CoreHostAdapters, ) -> anyhow::Result> { let host_config = adapters.config.clone(); let config = CoreInstanceConfig::from_toml_with_host(&toml_config, &host_config)?; Self::new_inner(config, Some(toml_config), host_config, adapters) } fn new_inner( config: CoreInstanceConfig, toml_config: Option, host_config: CoreInstanceHostConfig, mut adapters: CoreHostAdapters, ) -> anyhow::Result> { build_capabilities::validate(&config)?; let instance_name = config.instance_name; #[cfg(feature = "vpn-portal")] let vpn_portal_config = config.vpn_portal.clone(); let (packet_tx, packet_rx) = host_packet_channel(); let runtime_config = CoreRuntimeConfigStore::new( config.connectivity.runtime.clone(), Arc::new(config.peer.snapshot.clone()), ); let events = adapters.events.clone(); #[cfg(feature = "public-ipv6-provider")] let public_ipv6_host: Arc = adapters .public_ipv6_host .take() .unwrap_or_else(|| Arc::new(())); #[cfg(not(feature = "public-ipv6-provider"))] let public_ipv6_host: Arc = Arc::new(()); #[cfg(feature = "public-ipv6-provider")] let public_ipv6_events = events.clone(); #[cfg(not(feature = "public-ipv6-provider"))] let public_ipv6_events: Arc = Arc::new(()); let public_ipv6_runtime = CorePublicIpv6Runtime::new( runtime_config.clone(), public_ipv6_host, public_ipv6_events, ); let stun = Self::prepare_stun(&adapters, &config.connectivity); let peer_stun: Arc = stun.clone(); let foreign_rpc_registrar = Arc::new(ForeignDirectConnectorRpcRegistrar::new( adapters.host.clone(), stun.clone(), )); let peer_manager = Arc::new(PeerManagerCore::new( config.peer, runtime_config.clone(), Arc::new(CoreStunPeerInfoSource(peer_stun)), packet_tx, public_ipv6_runtime.clone(), events.clone(), adapters.credential_storage.take(), foreign_rpc_registrar, )?); let config = config.connectivity; let listener_plan = prepare_listener_plan( config.listeners.as_ref(), peer_manager.instance_id(), adapters.server_protocol.as_deref(), adapters.external_listener_factory.as_deref(), )?; let CoreHostAdapters { host, config: _, #[cfg(any(test, feature = "test-utils"))] stun_override: _, dns, process_runtime, packet_egress, instance_runtime, events, credential_storage: _, #[cfg(feature = "wrapped-transport")] wrapped_transports, protocol, external_listener_factory, server_protocol, udp_hole_punch_platform, #[cfg(feature = "proxy-packet")] icmp_proxy_host, #[cfg(feature = "proxy-cidr-monitor")] proxy_cidr_monitor_enabled, #[cfg(feature = "public-ipv6-provider")] public_ipv6_host: _, #[cfg(feature = "public-ipv6-provider")] public_ipv6_provider, #[cfg(feature = "vpn-portal")] vpn_portal, } = adapters; let dns_records: Arc = dns.clone(); let dns: Arc = dns; let ring_registry = process_runtime.ring_registry(); let protected_tcp_ports = process_runtime.protected_tcp_ports(); let CoreConnectivityConfig { initial_peers, listeners: _, runtime: _, startup_plan, stun: _, endpoint_discovery, manual: manual_options, direct: direct_options, } = config; #[cfg(not(feature = "proxy-smoltcp-stack"))] let _ = startup_plan; let accepted_transport_handler: Arc< dyn AcceptedSocketHandler>>, > = match server_protocol { Some(server_protocol) => { let tunnel_handler = PeerAcceptedTunnelHandler::new(&peer_manager, events.clone()); Arc::new(ProtocolAcceptedTransportHandler::new( &tunnel_handler, server_protocol, )) } None => Arc::new(RawAcceptedTransportHandler::new(&peer_manager)), }; let running_listeners = Arc::new(RunningListenerRegistry::default()); let PreparedListenerPlan { transports, external, failures, } = listener_plan; let mut external_factories = Vec::with_capacity(external.len()); if !external.is_empty() && external_listener_factory.is_none() { anyhow::bail!("listener plan requires an external listener factory"); } for (listener, socket_context) in external { let factory = external_listener_factory.clone().unwrap(); let request = ExternalListenerRequest { url: listener.url, socket_context, }; external_factories.push(ListenerFactory::new( move || factory.create(request.clone()), listener.must_succeed, )); } let has_listener_work = !transports.is_empty() || !external_factories.is_empty() || !failures.is_empty(); let listener = has_listener_work.then(|| { Arc::new(CoreListenerRuntime::new_with_events( host.clone(), dns.clone(), ring_registry.clone(), transports, external_factories, failures, accepted_transport_handler, events.clone(), running_listeners.clone(), )) }); let protocol = protocol.unwrap_or_else(|| { Arc::new(CoreClientProtocolUpgrader::new( CoreClientProtocolConfig::default(), )) }); let endpoint_resolver = Arc::new(CoreManualEndpointResolver::new( host.clone(), dns.clone(), dns_records, endpoint_discovery, )); let manual = ManualConnectorManager::new( peer_manager.clone(), host.clone(), dns.clone(), endpoint_resolver, protocol.clone(), ring_registry, manual_options, events.clone(), ); for url in initial_peers { manual.add_connector(url)?; } let udp_hole_punch_socket_context = direct_options.udp_bind.context.clone(); let udp_hole_punch = CoreUdpHolePunchService::new( peer_manager.clone(), host.clone(), stun.clone(), udp_hole_punch_platform, events.clone(), udp_hole_punch_socket_context, protocol.clone(), ); let proxy_cidr_table = Arc::new(ProxyCidrTable::from_snapshot(proxy_cidr_snapshot( runtime_config.snapshot().as_ref(), ))); #[cfg(feature = "wrapped-transport")] let tcp_proxy_socket_context = direct_options.tcp_bind.context.clone(); #[cfg(feature = "proxy-packet")] let packet_proxy = CoreProxyModule::new( peer_manager.clone(), host.clone(), protected_tcp_ports.clone(), running_listeners.clone(), runtime_config.clone(), proxy_cidr_table.clone(), tcp_proxy_socket_context.clone(), direct_options.udp_bind.context.clone(), // Raw ICMP shares the datagram/network-layer routing context. direct_options.udp_bind.context.clone(), icmp_proxy_host, ); #[cfg(feature = "wrapped-transport")] let wrapped_transport = { let WrappedTransportEngines { kcp, quic } = wrapped_transports; WrappedTransportProxyModule::new( peer_manager.clone(), runtime_config.clone(), kcp, quic, host.clone(), protected_tcp_ports.clone(), running_listeners.clone(), proxy_cidr_table.clone(), tcp_proxy_socket_context, ) }; #[cfg(feature = "proxy-smoltcp-stack")] let data_plane_runtime = DataPlaneRuntime::new( runtime_config.clone(), peer_manager.clone(), wrapped_transport.as_ref(), host.clone(), direct_options.tcp_bind.context.clone(), ); #[cfg(feature = "proxy-smoltcp-stack")] let data_plane_session = DataPlaneSession::new(&data_plane_runtime); #[cfg(feature = "proxy-smoltcp-stack")] let socks5_adapter = Socks5GatewayAdapter::new( runtime_config.clone(), data_plane_runtime.clone(), host.clone(), dns.clone(), direct_options.tcp_bind.context.clone(), ); #[cfg(feature = "proxy-smoltcp-stack")] let port_forward_adapter = PortForwardAdapter::new( runtime_config.clone(), data_plane_runtime.clone(), host.clone(), direct_options.tcp_bind.context.clone(), events.clone(), ); #[cfg(feature = "tcp-hole-punch")] let tcp_hole_punch = TcpHolePunchConnector::new( peer_manager.clone(), host.clone(), stun.clone(), direct_options.tcp_bind.context.clone(), protocol.clone(), Arc::new(crate::connectivity::protocol::CoreServerProtocolUpgrader::< HostAcceptedTcpSocket, >::new( crate::connectivity::protocol::CoreServerProtocolConfig::default(), )), ); let direct = DirectConnectorManager::new_with_running_listeners( peer_manager.clone(), host.clone(), protected_tcp_ports, stun.clone(), running_listeners.clone(), dns, protocol, direct_options, ); let peer_center = Arc::new(PeerCenterInstance::new(peer_manager.clone())); #[cfg(feature = "public-ipv6-provider")] let public_ipv6_provider = PublicIpv6ProviderRuntime::new( public_ipv6_provider, runtime_config.clone(), public_ipv6_runtime, ); #[cfg(feature = "vpn-portal")] let vpn_portal = PortalModule::new( peer_manager.clone(), runtime_config.clone(), vpn_portal_config, vpn_portal, events.clone(), )?; #[cfg(feature = "proxy-cidr-monitor")] let proxy_cidr_monitor = ProxyCidrMonitorRuntime::new(proxy_cidr_monitor_enabled, events.clone()); #[cfg(feature = "proxy-cidr-monitor")] let proxy_cidr_monitor_available = proxy_cidr_monitor.is_enabled(); #[cfg(not(feature = "proxy-cidr-monitor"))] let proxy_cidr_monitor_available = false; let packet_plane = Arc::new(CorePacketPlane::new( peer_manager.clone(), runtime_config.clone(), proxy_cidr_monitor_available, )); Ok(Arc::new(Self { instance_name, management: ManagementState::new(toml_config, host_config), instance_runtime, state: AtomicU8::new(CoreInstanceState::Created as u8), latest_error: RwLock::new(None), operation: Mutex::new(()), cancel: CancellationToken::new(), peer_manager, packet_plane, manual, direct, #[cfg(feature = "tcp-hole-punch")] tcp_hole_punch, listener, running_listeners, udp_hole_punch, #[cfg(feature = "wrapped-transport")] wrapped_transport, #[cfg(feature = "proxy-smoltcp-stack")] data_plane_runtime, #[cfg(feature = "proxy-smoltcp-stack")] data_plane_session, #[cfg(feature = "proxy-smoltcp-stack")] socks5_adapter, #[cfg(feature = "proxy-smoltcp-stack")] port_forward_adapter, proxy_cidr_table, #[cfg(feature = "proxy-packet")] packet_proxy, #[cfg(feature = "proxy-cidr-monitor")] proxy_cidr_monitor, #[cfg(feature = "dhcp-ipv4")] dhcp_ipv4: DhcpIpv4Runtime::new(), packet_egress, packet_receiver: Mutex::new(Some(packet_rx)), peer_center, #[cfg(feature = "public-ipv6-provider")] public_ipv6_provider, #[cfg(feature = "vpn-portal")] vpn_portal, #[cfg(feature = "proxy-smoltcp-stack")] startup_plan, runtime_config, #[cfg(feature = "test-utils")] acl_reload_count: AtomicUsize::new(0), })) } pub fn state(&self) -> CoreInstanceState { CoreInstanceState::from_u8(self.state.load(Ordering::Acquire)) } fn set_state(&self, state: CoreInstanceState) { self.state.store(state as u8, Ordering::Release); } /// Publishes one complete instance configuration version. Host changes have /// no effect until submitted through this method. pub async fn update_runtime_config( &self, config: CoreInstanceRuntimeConfig, ) -> anyhow::Result<()> { let _operation = self.operation.lock().await; self.update_runtime_config_under_operation(config).await } pub(crate) async fn update_runtime_config_under_operation( &self, config: CoreInstanceRuntimeConfig, ) -> anyhow::Result<()> { if matches!( self.state(), CoreInstanceState::Stopping | CoreInstanceState::Stopped ) { anyhow::bail!("runtime config cannot update while instance is stopping or stopped"); } self.validate_runtime_config_capabilities(&config)?; #[cfg(feature = "test-utils")] let reload_acl = self.runtime_config.snapshot().services.acl != config.services.acl; let published = self.peer_manager.update_runtime_config(config).await?; #[cfg(feature = "test-utils")] if reload_acl { self.acl_reload_count.fetch_add(1, Ordering::Relaxed); } self.proxy_cidr_table .update_snapshot(proxy_cidr_snapshot(&published)); #[cfg(feature = "proxy-smoltcp-stack")] self.port_forward_adapter .reload( &self .runtime_config .snapshot() .services .gateway .port_forwards, ) .await?; Ok(()) } pub(crate) fn validate_runtime_config_capabilities( &self, config: &CoreInstanceRuntimeConfig, ) -> anyhow::Result<()> { build_capabilities::validate_runtime(config)?; #[cfg(feature = "vpn-portal")] self.vpn_portal.validate_runtime_config(config)?; Ok(()) } pub async fn wait(&self) { self.peer_manager.wait().await; } } impl Drop for CoreInstance where H: CoreInstanceHost, { fn drop(&mut self) { self.cancel.cancel(); self.instance_runtime.request_shutdown(); self.packet_egress.request_stop(); } } #[cfg(any(test, feature = "test-utils"))] mod test_utils; #[cfg(test)] mod tests;