Files
EasyTier/easytier-core/src/instance/build_capabilities.rs
T
KKRainbow 03078b4791 feat(dhcp): support automatic IPv6 address assignment (#2688)
Accept a boolean or an IPv6 CIDR in dhcpv6 and --dhcpv6, using
fd6b:8c2d:91e4::/64 when no initial subnet is supplied. Keep IPv4
configuration serialization compatible and DHCPv6 disabled by default.

Share the DHCP lifecycle and host update between both address families,
with typed allocation state, peer subnet selection, conflict recovery,
and bounded IPv6 probing. Preserve static addresses of the other family
and prevent static config patches from replacing DHCP-owned leases.

Carry IPv6 settings, leases, and events through management, web config,
and mobile VPN setup. Reuse the shared NIC registry and mobile address
list for IPv6-only and dual-stack operation without extra FD owners.

Cover config round trips, allocation and recovery, mobile
reconciliation, three-node IPv6 and dual-stack connectivity, and shared
TUN member exit. Validate with workspace clippy, Docker TUN tests,
frontend tests/builds, WASI and Android Rust checks, and Android VPN
Kotlin compilation.
2026-10-14 00:36:47 +08:00

82 lines
2.7 KiB
Rust

//! Compile-time capability selection for portable Instance validation.
//!
//! Cargo features are localized here so configuration validation remains one
//! unconditional path in `CoreInstance`.
use crate::config::{peers::PeerRuntimeSnapshot, runtime::CoreRuntimeConfig};
use super::CoreInstanceConfig;
const DHCP_IPV4_AVAILABLE: bool = cfg!(feature = "dhcp-ipv4");
const DHCP_IPV6_AVAILABLE: bool = cfg!(feature = "dhcp-ipv6");
const SMOLTCP_GATEWAY_AVAILABLE: bool = cfg!(feature = "proxy-smoltcp-stack");
const PACKET_PROXY_AVAILABLE: bool = cfg!(feature = "proxy-packet");
const PROXY_CIDR_MONITOR_AVAILABLE: bool = cfg!(feature = "proxy-cidr-monitor");
const WRAPPED_TRANSPORT_AVAILABLE: bool = cfg!(feature = "wrapped-transport");
const PUBLIC_IPV6_AVAILABLE: bool = cfg!(feature = "public-ipv6-provider");
const VPN_PORTAL_AVAILABLE: bool = cfg!(feature = "vpn-portal");
fn require(available: bool, requested: bool, capability: &str) -> anyhow::Result<()> {
if requested && !available {
anyhow::bail!("this build does not include {capability}");
}
Ok(())
}
fn validate_snapshot(
runtime: &CoreRuntimeConfig,
peer: &PeerRuntimeSnapshot,
) -> anyhow::Result<()> {
let core = &peer.runtime.core;
require(DHCP_IPV4_AVAILABLE, runtime.dhcp_ipv4, "DHCP IPv4")?;
require(DHCP_IPV6_AVAILABLE, runtime.dhcp_ipv6, "DHCP IPv6")?;
require(
SMOLTCP_GATEWAY_AVAILABLE,
runtime.gateway.socks5_bind.is_some() || !runtime.gateway.port_forwards.is_empty(),
"the smoltcp gateway",
)?;
require(
PROXY_CIDR_MONITOR_AVAILABLE,
runtime
.manual_routes
.as_ref()
.is_some_and(|routes| !routes.is_empty()),
"the proxy CIDR monitor",
)?;
require(
WRAPPED_TRANSPORT_AVAILABLE,
!core.routes.proxy_networks.is_empty(),
"proxy routing services",
)?;
require(
PACKET_PROXY_AVAILABLE,
runtime
.proxy
.should_start(!core.routes.proxy_networks.is_empty()),
"packet proxy services",
)?;
require(
PUBLIC_IPV6_AVAILABLE,
runtime.public_ipv6_auto
|| runtime.public_ipv6_provider.provider_enabled
|| runtime.public_ipv6_provider.configured_prefix.is_some(),
"public IPv6 services",
)?;
Ok(())
}
pub(super) fn validate(config: &CoreInstanceConfig) -> anyhow::Result<()> {
validate_snapshot(&config.connectivity.runtime, &config.peer.snapshot)?;
require(
VPN_PORTAL_AVAILABLE,
config.vpn_portal.is_some(),
"the VPN portal",
)
}
pub(super) fn validate_runtime(
config: &crate::config::runtime::CoreInstanceRuntimeConfig,
) -> anyhow::Result<()> {
validate_snapshot(&config.services, &config.peer)
}