From c4cf8cf1d883900e73a66bcea58ff9ce1c1e4e93 Mon Sep 17 00:00:00 2001 From: Luna Yao <40349250+ZnqbuZ@users.noreply.github.com> Date: Wed, 18 Feb 2026 20:47:34 +0100 Subject: [PATCH] rpc: let peers generate their own dedicated zones --- easytier/src/dns/client.rs | 59 +----------------- easytier/src/dns/config.rs | 86 +++++++++++++++++++++++---- easytier/src/peers/peer_ospf_route.rs | 5 +- easytier/src/proto/dns.proto | 3 +- 4 files changed, 82 insertions(+), 71 deletions(-) diff --git a/easytier/src/dns/client.rs b/easytier/src/dns/client.rs index 9e948460..fb89e57b 100644 --- a/easytier/src/dns/client.rs +++ b/easytier/src/dns/client.rs @@ -2,7 +2,7 @@ use std::net::{Ipv4Addr, Ipv6Addr}; use std::sync::Arc; use std::time::Duration; -use super::config::{DnsConfig, DNS_SERVER_RPC_ADDR}; +use super::config::{DnsConfig, DnsGlobalCtxExt, DNS_SERVER_RPC_ADDR}; use crate::common::config::ConfigLoader; use crate::common::PeerId; use crate::peers::peer_manager::PeerManager; @@ -16,7 +16,6 @@ use crate::proto::rpc_types::controller::BaseController; use crate::tunnel::tcp::TcpTunnelConnector; use dashmap::DashMap; use derivative::Derivative; -use hickory_proto::rr::Name; use tokio::sync::Mutex; use url::Url; @@ -86,27 +85,6 @@ impl DnsClient { Ok(()) } - fn create_dedicated_zone( - origin: &str, - ipv4: Option, - ipv6: Vec, - ) -> Option { - let mut records = Vec::new(); - - if let Some(ipv4) = ipv4 { - records.push(format!("@ IN A {}", ipv4)); - } - for ipv6 in ipv6 { - records.push(format!("@ IN AAAA {}", ipv6)); - } - (!records.is_empty()).then_some(ZoneConfigPb { - origin: origin.to_string(), - records, - - ..Default::default() - }) - } - async fn build_snapshot(&self) -> DnsSnapshot { let global_ctx = self.peer_mgr.get_global_ctx_ref(); @@ -116,17 +94,7 @@ impl DnsClient { // 1. Local zones zones.extend(config.zones.iter().map(Into::into)); - - // 1.5. Specialized zone for self (domain.tld -> self_ip) - if let Ok(origin) = Name::from(config.get_name()).append_domain(&*config.domain) { - let ipv4 = global_ctx.get_ipv4().map(|ip| ip.address()); - let ipv6 = global_ctx.get_ipv6().map(|ip| ip.address()); - let ipv6 = ipv6.map(|a| vec![a]).unwrap_or_default(); - if let Some(zone) = Self::create_dedicated_zone(origin.to_string().as_str(), ipv4, ipv6) - { - zones.push(zone); - } - } + zones.extend(global_ctx.dns_self_zone().as_ref().map(Into::into)); // 2. Peer zones for ref_multi in self.peer_configs.iter() { @@ -135,29 +103,6 @@ impl DnsClient { zones.extend(config.zones.clone()); } - // 3. Specialized zones for peers (domain.tld -> peer_ip) - let routes = self.peer_mgr.list_routes().await; - for route in routes.iter() { - let peer_id = route.peer_id; - if let Some(peer_config) = self.peer_configs.get(&peer_id) { - let origin = format!("{}.{}", peer_config.name, peer_config.domain); - let ipv4 = route - .ipv4_addr - .map(|ip| ip.address) - .flatten() - .map(Into::into); - let ipv6 = route - .ipv6_addr - .map(|ip| ip.address) - .flatten() - .map(Into::into); - let ipv6 = ipv6.map(|a| vec![a]).unwrap_or_default(); - if let Some(zone) = Self::create_dedicated_zone(origin.as_str(), ipv4, ipv6) { - zones.push(zone); - } - } - } - DnsSnapshot { zones, addresses: self diff --git a/easytier/src/dns/config.rs b/easytier/src/dns/config.rs index e51db272..5009da7c 100644 --- a/easytier/src/dns/config.rs +++ b/easytier/src/dns/config.rs @@ -1,11 +1,14 @@ -use crate::dns::utils::{parse, sanitize, NameServerAddr}; +use crate::common::config::ConfigLoader; +use crate::common::global_ctx::GlobalCtx; +use crate::dns::utils::{parse, NameServerAddr}; use crate::proto::dns::{DnsConfigPb, ZoneConfigPb}; use derive_more::{Deref, DerefMut}; use gethostname::gethostname; -use hickory_proto::rr::{IntoName, LowerName, Name}; +use hickory_proto::rr::{LowerName, Name}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::iter; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4}; use std::str::FromStr; use std::sync::LazyLock; use url::Url; @@ -44,17 +47,49 @@ impl DnsConfig { self.name = parse(name); } - pub fn export(&self) -> DnsConfigPb { + pub fn get_fqdn(&self) -> LowerName { + Name::from(self.get_name()) + .append_domain(&self.domain) + .unwrap() + .into() + } + + pub fn set_fqdn(&mut self, fqdn: &str) { + let mut fqdn = Name::from(parse(fqdn)); + fqdn.set_fqdn(true); + self.name = Name::from_labels(iter::once(fqdn.iter().next().unwrap_or_default())) + .unwrap_or_default() + .into(); + self.domain = fqdn.base_name().into(); + } +} + +pub trait DnsGlobalCtxExt { + fn dns_self_zone(&self) -> Option; + fn dns_export_config(&self) -> DnsConfigPb; +} + +impl DnsGlobalCtxExt for GlobalCtx { + fn dns_self_zone(&self) -> Option { + let fqdn = self.config.get_dns().get_fqdn(); + let ipv4 = self.get_ipv4().map(|ip| ip.address()); + let ipv6 = self.get_ipv6().map(|ip| ip.address()); + let ipv6 = ipv6.map(|a| vec![a]).unwrap_or_default(); + + ZoneConfig::dedicated(Some(self.get_id()), fqdn.clone(), ipv4, ipv6) + } + + fn dns_export_config(&self) -> DnsConfigPb { + let config = self.config.get_dns(); + let zone = self.dns_self_zone(); + let zones = config.zones.iter().chain(zone.iter()); + DnsConfigPb { - zones: self - .zones - .iter() + zones: zones .filter(|z| z.policy.export.is_some()) // TODO: check policies of parent zones .map(Into::into) .collect(), - - name: self.get_name().to_string(), - domain: self.domain.to_string(), + fqdn: config.get_fqdn().to_string(), } } } @@ -88,6 +123,37 @@ pub struct ZoneConfig { pub policy: ZonePolicyConfig, } +impl ZoneConfig { + fn dedicated( + id: Option, + origin: LowerName, + ipv4: Option, + ipv6: Vec, + ) -> Option { + let mut records = Vec::new(); + + if let Some(ipv4) = ipv4 { + records.push(format!("@ IN A {}", ipv4)); + } + for ipv6 in ipv6 { + records.push(format!("@ IN AAAA {}", ipv6)); + } + + let policy = ZonePolicyConfig { + export: Some(DnsExportPolicy::default()), + }; + + (!records.is_empty()).then_some(Self { + id: id.unwrap_or(Uuid::new_v4()), + origin, + records, + policy, + + ..Default::default() + }) + } +} + impl From<&ZoneConfig> for ZoneConfigPb { fn from(value: &ZoneConfig) -> Self { Self { diff --git a/easytier/src/peers/peer_ospf_route.rs b/easytier/src/peers/peer_ospf_route.rs index 55bff36e..18084b21 100644 --- a/easytier/src/peers/peer_ospf_route.rs +++ b/easytier/src/peers/peer_ospf_route.rs @@ -40,6 +40,8 @@ use super::{ PeerPacketFilter, }; use crate::common::config::ConfigLoader; +use crate::dns::config::DnsGlobalCtxExt; +use crate::proto::dns::DeterministicDigest; use crate::{ common::{ config::NetworkIdentity, @@ -69,7 +71,6 @@ use crate::{ }, use_global_var, }; -use crate::proto::dns::DeterministicDigest; use super::{ graph_algo::dijkstra_with_first_hop, @@ -261,7 +262,7 @@ impl RoutePeerInfo { ipv6_addr: global_ctx.get_ipv6().map(|x| x.into()), groups: global_ctx.get_acl_groups(my_peer_id), - dns: global_ctx.config.get_dns().export().digest(), + dns: global_ctx.dns_export_config().digest(), noise_static_pubkey, diff --git a/easytier/src/proto/dns.proto b/easytier/src/proto/dns.proto index 8907704e..05bf29d3 100644 --- a/easytier/src/proto/dns.proto +++ b/easytier/src/proto/dns.proto @@ -6,8 +6,7 @@ package dns; message DnsConfigPb { repeated ZoneConfigPb zones = 1; - string name = 2; - string domain = 3; + string fqdn = 2; } message ZoneConfigPb {