mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-03 09:35:41 +00:00
feat: add interface address fallback for IPv6 prefix detection (DHCPv6 IA_NA / SLAAC) (#2334)
feat: add IPv6 prefix fallback and NDP proxy for SLAAC/IA_NA Add interface address fallback for IPv6 prefix detection when route-based detection fails (no delegated prefix on LAN). Covers DHCPv6 IA_NA / SLAAC where prefix is assigned to WAN without PD. Uses getifaddrs() and Ipv6Inet to handle host bits. Add NDP proxy sync for on-link prefixes. ISP router uses NDP to resolve MACs; EasyTier /128 addresses on tun0 won't answer on physical WAN. Periodic sync (30s) reads /128 routes via netlink and manages proxy neigh entries. Auto-enables proxy_ndp sysctl. Returns WAN ifindex from detection; static NDP_WAN_IFINDEX for sync task. Spawns from provider reconcile when auto-detection enabled. Add tests: fallback default route interface, on-link NDP proxy, interface prefix selection. Fixes #2333 Co-authored-by: ririyeye <200610237@qq.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: KKRainbow <443152178@qq.com>
This commit is contained in:
co-authored by
Claude
KKRainbow
parent
741460e1e4
commit
7756a15cbe
@@ -65,9 +65,9 @@ use crate::vpn_portal::{self, VpnPortal};
|
||||
use super::dns_server::{MAGIC_DNS_FAKE_IP, runner::DnsRunner};
|
||||
use super::listeners::ListenerManager;
|
||||
use super::public_ipv6_provider::{
|
||||
reconcile_public_ipv6_provider_runtime, run_public_ipv6_provider_reconcile_task,
|
||||
should_run_public_ipv6_provider_reconcile, validate_public_ipv6_config,
|
||||
validate_public_ipv6_config_values,
|
||||
PublicIpv6ProviderReconcileTask, reconcile_public_ipv6_provider_runtime,
|
||||
run_public_ipv6_provider_reconcile_task, should_run_public_ipv6_provider_reconcile,
|
||||
validate_public_ipv6_config, validate_public_ipv6_config_values,
|
||||
};
|
||||
|
||||
#[cfg(feature = "socks5")]
|
||||
@@ -194,6 +194,44 @@ impl NicCtxContainer {
|
||||
|
||||
#[cfg(feature = "tun")]
|
||||
type ArcNicCtx = Arc<Mutex<Option<NicCtxContainer>>>;
|
||||
type ArcPublicIpv6ProviderTaskSlot = Arc<PublicIpv6ProviderTaskSlot>;
|
||||
|
||||
struct PublicIpv6ProviderTaskSlot {
|
||||
task: Mutex<Option<PublicIpv6ProviderReconcileTask>>,
|
||||
closing: AtomicBool,
|
||||
}
|
||||
|
||||
impl PublicIpv6ProviderTaskSlot {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
task: Mutex::new(None),
|
||||
closing: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_started(&self, global_ctx: &ArcGlobalCtx) {
|
||||
let mut task = self.task.lock().await;
|
||||
if self.closing.load(Ordering::Acquire) || task.is_some() {
|
||||
return;
|
||||
}
|
||||
*task = run_public_ipv6_provider_reconcile_task(global_ctx);
|
||||
}
|
||||
|
||||
async fn shutdown(&self) {
|
||||
self.closing.store(true, Ordering::Release);
|
||||
let task = self.task.lock().await.take();
|
||||
if let Some(task) = task {
|
||||
task.shutdown().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_public_ipv6_provider_reconcile_task(
|
||||
global_ctx: &ArcGlobalCtx,
|
||||
task_slot: &ArcPublicIpv6ProviderTaskSlot,
|
||||
) {
|
||||
task_slot.ensure_started(global_ctx).await;
|
||||
}
|
||||
|
||||
pub struct InstanceRpcServerHook {
|
||||
rpc_portal_whitelist: Vec<IpCidr>,
|
||||
@@ -254,6 +292,7 @@ pub struct InstanceConfigPatcher {
|
||||
socks5_server: Weak<Socks5Server>,
|
||||
peer_manager: Weak<PeerManager>,
|
||||
conn_manager: Weak<ManualConnectorManager>,
|
||||
public_ipv6_provider_task: ArcPublicIpv6ProviderTaskSlot,
|
||||
}
|
||||
|
||||
impl InstanceConfigPatcher {
|
||||
@@ -324,7 +363,6 @@ impl InstanceConfigPatcher {
|
||||
self.patch_mapped_listeners(patch.mapped_listeners).await?;
|
||||
self.patch_connector(patch.connectors).await?;
|
||||
|
||||
let provider_reconcile_was_running = should_run_public_ipv6_provider_reconcile(&global_ctx);
|
||||
let mut provider_config_changed = false;
|
||||
if let Some(hostname) = patch.hostname {
|
||||
global_ctx.set_hostname(hostname.clone());
|
||||
@@ -362,10 +400,12 @@ impl InstanceConfigPatcher {
|
||||
if provider_config_changed {
|
||||
reconcile_public_ipv6_provider_runtime(&global_ctx).await;
|
||||
|
||||
let provider_reconcile_should_run =
|
||||
should_run_public_ipv6_provider_reconcile(&global_ctx);
|
||||
if !provider_reconcile_was_running && provider_reconcile_should_run {
|
||||
run_public_ipv6_provider_reconcile_task(&global_ctx);
|
||||
if should_run_public_ipv6_provider_reconcile(&global_ctx) {
|
||||
ensure_public_ipv6_provider_reconcile_task(
|
||||
&global_ctx,
|
||||
&self.public_ipv6_provider_task,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,6 +687,7 @@ pub struct Instance {
|
||||
socks5_server: Arc<Socks5Server>,
|
||||
|
||||
proxy_cidrs_monitor: Option<AbortOnDropHandle<()>>,
|
||||
public_ipv6_provider_task: ArcPublicIpv6ProviderTaskSlot,
|
||||
|
||||
global_ctx: ArcGlobalCtx,
|
||||
}
|
||||
@@ -734,6 +775,7 @@ impl Instance {
|
||||
socks5_server,
|
||||
|
||||
proxy_cidrs_monitor: None,
|
||||
public_ipv6_provider_task: Arc::new(PublicIpv6ProviderTaskSlot::new()),
|
||||
|
||||
global_ctx,
|
||||
}
|
||||
@@ -1034,7 +1076,11 @@ impl Instance {
|
||||
.await?;
|
||||
self.listener_manager.lock().await.run().await?;
|
||||
self.peer_manager.run().await?;
|
||||
run_public_ipv6_provider_reconcile_task(&self.global_ctx);
|
||||
ensure_public_ipv6_provider_reconcile_task(
|
||||
&self.global_ctx,
|
||||
&self.public_ipv6_provider_task,
|
||||
)
|
||||
.await;
|
||||
|
||||
#[cfg(feature = "tun")]
|
||||
{
|
||||
@@ -1347,6 +1393,7 @@ impl Instance {
|
||||
socks5_server: Arc::downgrade(&self.socks5_server),
|
||||
peer_manager: Arc::downgrade(&self.peer_manager),
|
||||
conn_manager: Arc::downgrade(&self.conn_manager),
|
||||
public_ipv6_provider_task: self.public_ipv6_provider_task.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1602,6 +1649,7 @@ impl Instance {
|
||||
}
|
||||
|
||||
pub async fn clear_resources(&mut self) {
|
||||
self.public_ipv6_provider_task.shutdown().await;
|
||||
self.peer_manager.clear_resources().await;
|
||||
#[cfg(feature = "tun")]
|
||||
let _ = self.nic_ctx.lock().await.take();
|
||||
@@ -1787,6 +1835,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn public_ipv6_provider_task_slot_does_not_restart_after_shutdown() {
|
||||
let global_ctx = get_mock_global_ctx();
|
||||
let slot = std::sync::Arc::new(super::PublicIpv6ProviderTaskSlot::new());
|
||||
global_ctx.config.set_ipv6_public_addr_provider(true);
|
||||
global_ctx
|
||||
.config
|
||||
.set_ipv6_public_addr_prefix(Some("2001:db8::/48".parse().unwrap()));
|
||||
|
||||
slot.shutdown().await;
|
||||
super::ensure_public_ipv6_provider_reconcile_task(&global_ctx, &slot).await;
|
||||
|
||||
assert!(slot.task.lock().await.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_public_ipv6_patch_allows_enabling_auto_with_manual_ipv6() {
|
||||
let global_ctx = get_mock_global_ctx();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1361,12 +1361,11 @@ impl NicCtx {
|
||||
}
|
||||
|
||||
self.global_ctx
|
||||
.issue_event(GlobalCtxEvent::TunDeviceReady(nic.ifname().to_string()));
|
||||
.set_tun_device_ready(nic.ifname().to_string());
|
||||
ret
|
||||
}
|
||||
Err(err) => {
|
||||
self.global_ctx
|
||||
.issue_event(GlobalCtxEvent::TunDeviceError(err.to_string()));
|
||||
self.global_ctx.set_tun_device_error(err.to_string());
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
@@ -1405,12 +1404,11 @@ impl NicCtx {
|
||||
match nic.create_dev_for_mobile(tun_fd).await {
|
||||
Ok(ret) => {
|
||||
self.global_ctx
|
||||
.issue_event(GlobalCtxEvent::TunDeviceReady(nic.ifname().to_string()));
|
||||
.set_tun_device_ready(nic.ifname().to_string());
|
||||
ret
|
||||
}
|
||||
Err(err) => {
|
||||
self.global_ctx
|
||||
.issue_event(GlobalCtxEvent::TunDeviceError(err.to_string()));
|
||||
self.global_ctx.set_tun_device_error(err.to_string());
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user