'auto' mode for detect_address_changes

This commit is contained in:
Christien Rioux 2025-06-14 09:12:59 -04:00
parent b7d725ab12
commit 5e11f945ef
16 changed files with 197 additions and 88 deletions

View file

@ -10,6 +10,10 @@
- Add private route example - Add private route example
- Add `require_inbound_relay` option in VeilidConfig. Default is false, but if enabled, forces OutboundOnly/InboundRelay mode. Can be used as an extra layer of IP address obscurity for some threat models. (@neequ57) - Add `require_inbound_relay` option in VeilidConfig. Default is false, but if enabled, forces OutboundOnly/InboundRelay mode. Can be used as an extra layer of IP address obscurity for some threat models. (@neequ57)
- Fix crash when peer info has missing or unsupported node ids - Fix crash when peer info has missing or unsupported node ids
- Add 'auto' mode for detect_address_changes
- veilid-server:
- Use `detect_address_changes: auto` by default
**Changed in Veilid 0.4.7** **Changed in Veilid 0.4.7**

View file

@ -94,7 +94,7 @@ core:
member_watch_limit: 8 member_watch_limit: 8
max_watch_expiration_ms: 600000 max_watch_expiration_ms: 600000
upnp: true upnp: true
detect_address_changes: true detect_address_changes: auto
restricted_nat_retries: 0 restricted_nat_retries: 0
tls: tls:
certificate_path: '%CERTIFICATE_PATH%' certificate_path: '%CERTIFICATE_PATH%'

View file

@ -74,7 +74,7 @@ impl AddressCheck {
let (detect_address_changes, ip6_prefix_size, require_inbound_relay) = let (detect_address_changes, ip6_prefix_size, require_inbound_relay) =
registry.config().with(|c| { registry.config().with(|c| {
( (
c.network.detect_address_changes, net.resolved_detect_address_changes(),
c.network.max_connections_per_ip6_prefix_size as usize, c.network.max_connections_per_ip6_prefix_size as usize,
c.network.privacy.require_inbound_relay, c.network.privacy.require_inbound_relay,
) )

View file

@ -91,6 +91,8 @@ struct NetworkInner {
dial_info_failure_count: BTreeMap<RoutingDomain, usize>, dial_info_failure_count: BTreeMap<RoutingDomain, usize>,
/// if we need to redo the publicinternet network class /// if we need to redo the publicinternet network class
needs_update_network_class: bool, needs_update_network_class: bool,
/// result of resolving 'auto'/None detect_address_changes mode
resolved_detect_address_changes: bool,
/// the next time we are allowed to check for better dialinfo when we are OutboundOnly /// the next time we are allowed to check for better dialinfo when we are OutboundOnly
next_outbound_only_dial_info_check: Timestamp, next_outbound_only_dial_info_check: Timestamp,
/// join handles for all the low level network background tasks /// join handles for all the low level network background tasks
@ -157,6 +159,7 @@ impl Network {
network_needs_restart: false, network_needs_restart: false,
dial_info_failure_count: BTreeMap::new(), dial_info_failure_count: BTreeMap::new(),
needs_update_network_class: false, needs_update_network_class: false,
resolved_detect_address_changes: false,
next_outbound_only_dial_info_check: Timestamp::default(), next_outbound_only_dial_info_check: Timestamp::default(),
join_handles: Vec::new(), join_handles: Vec::new(),
stop_source: None, stop_source: None,
@ -743,7 +746,8 @@ impl Network {
// Caution: this -must- happen first because we use unwrap() in last_network_state() // Caution: this -must- happen first because we use unwrap() in last_network_state()
let network_state = self.make_network_state().await?; let network_state = self.make_network_state().await?;
{ // Resolve 'auto'/None config fo detect_address_changes
let resolved_detect_address_changes = {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
// Create the shutdown stopper // Create the shutdown stopper
@ -751,7 +755,43 @@ impl Network {
// Store the first network state snapshot // Store the first network state snapshot
inner.network_state = Some(network_state.clone()); inner.network_state = Some(network_state.clone());
}
// Process the detect_address_changes 'auto' mode
let detect_address_changes = self.config().with(|c| c.network.detect_address_changes);
if let Some(detect_address_changes) = detect_address_changes {
inner.resolved_detect_address_changes = detect_address_changes;
if inner.resolved_detect_address_changes {
veilid_log!(self info "Manually-enabled detection of address changes");
} else {
veilid_log!(self info "Manually-disabled detection of address changes");
}
} else {
// Check for publicly routable IPv4 and IPv6 addresses
let mut global_ipv4 = false;
let mut global_ipv6 = false;
for siaddr in network_state.stable_interface_addresses {
if Address::from_ip_addr(siaddr).is_global() {
match siaddr {
IpAddr::V4(_ipv4_addr) => {
global_ipv4 = true;
}
IpAddr::V6(_ipv6_addr) => {
global_ipv6 = true;
}
}
}
}
// If both ipv4 and ipv6 global addresses are present, turn off detect_address_changes otherwise turn it on
inner.resolved_detect_address_changes = !(global_ipv4 && global_ipv6);
if inner.resolved_detect_address_changes {
veilid_log!(self info "Auto-enabled detection of address changes: global_ipv4={}, global_ipv6={}", global_ipv4, global_ipv6);
} else {
veilid_log!(self info "Auto-disabled detection of address changes because this node has global IPv4 and IPv6 addresses");
}
}
inner.resolved_detect_address_changes
};
// Start editing routing table // Start editing routing table
let routing_table = self.routing_table(); let routing_table = self.routing_table();
@ -768,9 +808,10 @@ impl Network {
true, true,
); );
let confirmed_public_internet = self let confirmed_public_internet = !resolved_detect_address_changes
.config() || self
.with(|c| !c.network.detect_address_changes || c.network.privacy.require_inbound_relay); .config()
.with(|c| c.network.privacy.require_inbound_relay);
editor_public_internet.setup_network( editor_public_internet.setup_network(
network_state.protocol_config.outbound, network_state.protocol_config.outbound,
network_state.protocol_config.inbound, network_state.protocol_config.inbound,
@ -991,6 +1032,15 @@ impl Network {
self.inner.lock().needs_update_network_class self.inner.lock().needs_update_network_class
} }
pub fn resolved_detect_address_changes(&self) -> bool {
let Ok(_guard) = self.startup_lock.enter() else {
veilid_log!(self debug "ignoring due to not started up");
return false;
};
self.inner.lock().resolved_detect_address_changes
}
pub fn trigger_update_network_class(&self, routing_domain: RoutingDomain) { pub fn trigger_update_network_class(&self, routing_domain: RoutingDomain) {
let Ok(_guard) = self.startup_lock.enter() else { let Ok(_guard) = self.startup_lock.enter() else {
veilid_log!(self debug "ignoring due to not started up"); veilid_log!(self debug "ignoring due to not started up");

View file

@ -140,15 +140,13 @@ impl Network {
#[instrument(level = "trace", skip_all)] #[instrument(level = "trace", skip_all)]
pub(super) async fn bind_udp_protocol_handlers(&self) -> EyreResult<StartupDisposition> { pub(super) async fn bind_udp_protocol_handlers(&self) -> EyreResult<StartupDisposition> {
veilid_log!(self trace "UDP: binding protocol handlers"); veilid_log!(self trace "UDP: binding protocol handlers");
let (listen_address, public_address, detect_address_changes, require_inbound_relay) = let (listen_address, public_address, require_inbound_relay) = self.config().with(|c| {
self.config().with(|c| { (
( c.network.protocol.udp.listen_address.clone(),
c.network.protocol.udp.listen_address.clone(), c.network.protocol.udp.public_address.clone(),
c.network.protocol.udp.public_address.clone(), c.network.privacy.require_inbound_relay,
c.network.detect_address_changes, )
c.network.privacy.require_inbound_relay, });
)
});
// Get the binding parameters from the user-specified listen address // Get the binding parameters from the user-specified listen address
let bind_set = self let bind_set = self
@ -177,7 +175,10 @@ impl Network {
{ {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if public_address.is_some() && !detect_address_changes && !require_inbound_relay { if public_address.is_some()
&& !inner.resolved_detect_address_changes
&& !require_inbound_relay
{
inner.static_public_dial_info.insert(ProtocolType::UDP); inner.static_public_dial_info.insert(ProtocolType::UDP);
} }
} }
@ -193,11 +194,11 @@ impl Network {
) -> EyreResult<()> { ) -> EyreResult<()> {
veilid_log!(self trace "UDP: registering dial info"); veilid_log!(self trace "UDP: registering dial info");
let (public_address, detect_address_changes, require_inbound_relay) = let (public_address, resolved_detect_address_changes, require_inbound_relay) =
self.config().with(|c| { self.config().with(|c| {
( (
c.network.protocol.udp.public_address.clone(), c.network.protocol.udp.public_address.clone(),
c.network.detect_address_changes, self.inner.lock().resolved_detect_address_changes,
c.network.privacy.require_inbound_relay, c.network.privacy.require_inbound_relay,
) )
}); });
@ -251,7 +252,7 @@ impl Network {
for di in &local_dial_info_list { for di in &local_dial_info_list {
// If the local interface address is global, then register global dial info // If the local interface address is global, then register global dial info
// if no other public address is specified // if no other public address is specified
if !detect_address_changes if !resolved_detect_address_changes
&& !require_inbound_relay && !require_inbound_relay
&& public_address.is_none() && public_address.is_none()
&& di.address().is_global() && di.address().is_global()
@ -269,15 +270,13 @@ impl Network {
#[instrument(level = "trace", skip_all)] #[instrument(level = "trace", skip_all)]
pub(super) async fn start_ws_listeners(&self) -> EyreResult<StartupDisposition> { pub(super) async fn start_ws_listeners(&self) -> EyreResult<StartupDisposition> {
veilid_log!(self trace "WS: binding protocol handlers"); veilid_log!(self trace "WS: binding protocol handlers");
let (listen_address, url, detect_address_changes, require_inbound_relay) = let (listen_address, url, require_inbound_relay) = self.config().with(|c| {
self.config().with(|c| { (
( c.network.protocol.ws.listen_address.clone(),
c.network.protocol.ws.listen_address.clone(), c.network.protocol.ws.url.clone(),
c.network.protocol.ws.url.clone(), c.network.privacy.require_inbound_relay,
c.network.detect_address_changes, )
c.network.privacy.require_inbound_relay, });
)
});
// Get the binding parameters from the user-specified listen address // Get the binding parameters from the user-specified listen address
let bind_set = self let bind_set = self
@ -309,7 +308,7 @@ impl Network {
{ {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if url.is_some() && !detect_address_changes && !require_inbound_relay { if url.is_some() && !inner.resolved_detect_address_changes && !require_inbound_relay {
inner.static_public_dial_info.insert(ProtocolType::WS); inner.static_public_dial_info.insert(ProtocolType::WS);
} }
} }
@ -324,14 +323,15 @@ impl Network {
editor_local_network: &mut RoutingDomainEditorLocalNetwork<'_>, editor_local_network: &mut RoutingDomainEditorLocalNetwork<'_>,
) -> EyreResult<()> { ) -> EyreResult<()> {
veilid_log!(self trace "WS: registering dial info"); veilid_log!(self trace "WS: registering dial info");
let (url, path, detect_address_changes, require_inbound_relay) = self.config().with(|c| { let (url, path, resolved_detect_address_changes, require_inbound_relay) =
( self.config().with(|c| {
c.network.protocol.ws.url.clone(), (
c.network.protocol.ws.path.clone(), c.network.protocol.ws.url.clone(),
c.network.detect_address_changes, c.network.protocol.ws.path.clone(),
c.network.privacy.require_inbound_relay, self.inner.lock().resolved_detect_address_changes,
) c.network.privacy.require_inbound_relay,
}); )
});
let mut registered_addresses: HashSet<IpAddr> = HashSet::new(); let mut registered_addresses: HashSet<IpAddr> = HashSet::new();
@ -400,7 +400,7 @@ impl Network {
let local_di = let local_di =
DialInfo::try_ws(*socket_address, local_url).wrap_err("try_ws failed")?; DialInfo::try_ws(*socket_address, local_url).wrap_err("try_ws failed")?;
if !detect_address_changes if !resolved_detect_address_changes
&& !require_inbound_relay && !require_inbound_relay
&& url.is_none() && url.is_none()
&& local_di.address().is_global() && local_di.address().is_global()
@ -420,12 +420,12 @@ impl Network {
pub(super) async fn start_wss_listeners(&self) -> EyreResult<StartupDisposition> { pub(super) async fn start_wss_listeners(&self) -> EyreResult<StartupDisposition> {
veilid_log!(self trace "WSS: binding protocol handlers"); veilid_log!(self trace "WSS: binding protocol handlers");
let (listen_address, url, detect_address_changes, require_inbound_relay) = let (listen_address, url, resolved_detect_address_changes, require_inbound_relay) =
self.config().with(|c| { self.config().with(|c| {
( (
c.network.protocol.wss.listen_address.clone(), c.network.protocol.wss.listen_address.clone(),
c.network.protocol.wss.url.clone(), c.network.protocol.wss.url.clone(),
c.network.detect_address_changes, self.inner.lock().resolved_detect_address_changes,
c.network.privacy.require_inbound_relay, c.network.privacy.require_inbound_relay,
) )
}); });
@ -461,7 +461,7 @@ impl Network {
{ {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if url.is_some() && !detect_address_changes && !require_inbound_relay { if url.is_some() && !resolved_detect_address_changes && !require_inbound_relay {
inner.static_public_dial_info.insert(ProtocolType::WSS); inner.static_public_dial_info.insert(ProtocolType::WSS);
} }
} }
@ -531,15 +531,19 @@ impl Network {
pub(super) async fn start_tcp_listeners(&self) -> EyreResult<StartupDisposition> { pub(super) async fn start_tcp_listeners(&self) -> EyreResult<StartupDisposition> {
veilid_log!(self trace "TCP: binding protocol handlers"); veilid_log!(self trace "TCP: binding protocol handlers");
let (listen_address, public_address, detect_address_changes, require_inbound_relay) = let (
self.config().with(|c| { listen_address,
( public_address,
c.network.protocol.tcp.listen_address.clone(), resolved_detect_address_changes,
c.network.protocol.tcp.public_address.clone(), require_inbound_relay,
c.network.detect_address_changes, ) = self.config().with(|c| {
c.network.privacy.require_inbound_relay, (
) c.network.protocol.tcp.listen_address.clone(),
}); c.network.protocol.tcp.public_address.clone(),
self.inner.lock().resolved_detect_address_changes,
c.network.privacy.require_inbound_relay,
)
});
// Get the binding parameters from the user-specified listen address // Get the binding parameters from the user-specified listen address
let bind_set = self let bind_set = self
@ -571,7 +575,10 @@ impl Network {
{ {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if public_address.is_some() && !detect_address_changes && !require_inbound_relay { if public_address.is_some()
&& !resolved_detect_address_changes
&& !require_inbound_relay
{
inner.static_public_dial_info.insert(ProtocolType::TCP); inner.static_public_dial_info.insert(ProtocolType::TCP);
} }
} }
@ -587,11 +594,11 @@ impl Network {
) -> EyreResult<()> { ) -> EyreResult<()> {
veilid_log!(self trace "TCP: registering dialinfo"); veilid_log!(self trace "TCP: registering dialinfo");
let (public_address, detect_address_changes, require_inbound_relay) = let (public_address, resolved_detect_address_changes, require_inbound_relay) =
self.config().with(|c| { self.config().with(|c| {
( (
c.network.protocol.tcp.public_address.clone(), c.network.protocol.tcp.public_address.clone(),
c.network.detect_address_changes, self.inner.lock().resolved_detect_address_changes,
c.network.privacy.require_inbound_relay, c.network.privacy.require_inbound_relay,
) )
}); });
@ -650,7 +657,7 @@ impl Network {
let di = DialInfo::tcp(*socket_address); let di = DialInfo::tcp(*socket_address);
// Register global dial info if no public address is specified // Register global dial info if no public address is specified
if !detect_address_changes if !resolved_detect_address_changes
&& !require_inbound_relay && !require_inbound_relay
&& public_address.is_none() && public_address.is_none()
&& di.address().is_global() && di.address().is_global()

View file

@ -87,14 +87,10 @@ impl Network {
return Ok(()); return Ok(());
} }
let (detect_address_changes, upnp, require_inbound_relay) = { let (upnp, require_inbound_relay) = {
let config = self.network_manager().config(); let config = self.network_manager().config();
let c = config.get(); let c = config.get();
( (c.network.upnp, c.network.privacy.require_inbound_relay)
c.network.detect_address_changes,
c.network.upnp,
c.network.privacy.require_inbound_relay,
)
}; };
if require_inbound_relay { if require_inbound_relay {
@ -104,7 +100,7 @@ impl Network {
} }
// If we need to figure out our network class, tick the task for it // If we need to figure out our network class, tick the task for it
if detect_address_changes { if self.resolved_detect_address_changes() {
// Check our network interfaces to see if they have changed // Check our network interfaces to see if they have changed
self.network_interfaces_task.tick().await?; self.network_interfaces_task.tick().await?;

View file

@ -500,6 +500,15 @@ impl Network {
false false
} }
pub fn resolved_detect_address_changes(&self) -> bool {
let Ok(_guard) = self.startup_lock.enter() else {
veilid_log!(self debug "ignoring due to not started up");
return false;
};
false
}
pub fn trigger_update_network_class(&self, _routing_domain: RoutingDomain) { pub fn trigger_update_network_class(&self, _routing_domain: RoutingDomain) {
let Ok(_guard) = self.startup_lock.enter() else { let Ok(_guard) = self.startup_lock.enter() else {
veilid_log!(self debug "ignoring due to not started up"); veilid_log!(self debug "ignoring due to not started up");

View file

@ -255,7 +255,7 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn {
"network.dht.member_watch_limit" => Ok(Box::new(8u32)), "network.dht.member_watch_limit" => Ok(Box::new(8u32)),
"network.dht.max_watch_expiration_ms" => Ok(Box::new(600_000u32)), "network.dht.max_watch_expiration_ms" => Ok(Box::new(600_000u32)),
"network.upnp" => Ok(Box::new(false)), "network.upnp" => Ok(Box::new(false)),
"network.detect_address_changes" => Ok(Box::new(true)), "network.detect_address_changes" => Ok(Box::new(Some(true))),
"network.restricted_nat_retries" => Ok(Box::new(0u32)), "network.restricted_nat_retries" => Ok(Box::new(0u32)),
"network.tls.certificate_path" => Ok(Box::new(get_certfile_path())), "network.tls.certificate_path" => Ok(Box::new(get_certfile_path())),
"network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())), "network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())),
@ -400,7 +400,7 @@ pub fn test_config() {
); );
assert!(!inner.network.upnp); assert!(!inner.network.upnp);
assert!(inner.network.detect_address_changes); assert_eq!(inner.network.detect_address_changes, Some(true));
assert_eq!(inner.network.restricted_nat_retries, 0u32); assert_eq!(inner.network.restricted_nat_retries, 0u32);
assert_eq!(inner.network.tls.certificate_path, get_certfile_path()); assert_eq!(inner.network.tls.certificate_path, get_certfile_path());
assert_eq!(inner.network.tls.private_key_path, get_keyfile_path()); assert_eq!(inner.network.tls.private_key_path, get_keyfile_path());

View file

@ -228,7 +228,7 @@ pub fn fix_veilidconfig() -> VeilidConfig {
max_watch_expiration_ms: 22, max_watch_expiration_ms: 22,
}, },
upnp: true, upnp: true,
detect_address_changes: false, detect_address_changes: Some(false),
restricted_nat_retries: 10000, restricted_nat_retries: 10000,
tls: VeilidConfigTLS { tls: VeilidConfigTLS {
certificate_path: "/etc/ssl/certs/cert.pem".to_string(), certificate_path: "/etc/ssl/certs/cert.pem".to_string(),

View file

@ -556,7 +556,7 @@ pub struct VeilidConfigNetwork {
pub rpc: VeilidConfigRPC, pub rpc: VeilidConfigRPC,
pub dht: VeilidConfigDHT, pub dht: VeilidConfigDHT,
pub upnp: bool, pub upnp: bool,
pub detect_address_changes: bool, pub detect_address_changes: Option<bool>,
pub restricted_nat_retries: u32, pub restricted_nat_retries: u32,
pub tls: VeilidConfigTLS, pub tls: VeilidConfigTLS,
pub application: VeilidConfigApplication, pub application: VeilidConfigApplication,
@ -583,7 +583,7 @@ impl Default for VeilidConfigNetwork {
rpc: VeilidConfigRPC::default(), rpc: VeilidConfigRPC::default(),
dht: VeilidConfigDHT::default(), dht: VeilidConfigDHT::default(),
upnp: true, upnp: true,
detect_address_changes: true, detect_address_changes: Some(true),
restricted_nat_retries: 0, restricted_nat_retries: 0,
tls: VeilidConfigTLS::default(), tls: VeilidConfigTLS::default(),
application: VeilidConfigApplication::default(), application: VeilidConfigApplication::default(),

View file

@ -377,7 +377,7 @@ sealed class VeilidConfigNetwork with _$VeilidConfigNetwork {
required VeilidConfigRPC rpc, required VeilidConfigRPC rpc,
required VeilidConfigDHT dht, required VeilidConfigDHT dht,
required bool upnp, required bool upnp,
required bool detectAddressChanges, required bool? detectAddressChanges,
required int restrictedNatRetries, required int restrictedNatRetries,
required VeilidConfigTLS tls, required VeilidConfigTLS tls,
required VeilidConfigApplication application, required VeilidConfigApplication application,

View file

@ -6080,7 +6080,7 @@ mixin _$VeilidConfigNetwork implements DiagnosticableTreeMixin {
VeilidConfigRPC get rpc; VeilidConfigRPC get rpc;
VeilidConfigDHT get dht; VeilidConfigDHT get dht;
bool get upnp; bool get upnp;
bool get detectAddressChanges; bool? get detectAddressChanges;
int get restrictedNatRetries; int get restrictedNatRetries;
VeilidConfigTLS get tls; VeilidConfigTLS get tls;
VeilidConfigApplication get application; VeilidConfigApplication get application;
@ -6234,7 +6234,7 @@ abstract mixin class $VeilidConfigNetworkCopyWith<$Res> {
VeilidConfigRPC rpc, VeilidConfigRPC rpc,
VeilidConfigDHT dht, VeilidConfigDHT dht,
bool upnp, bool upnp,
bool detectAddressChanges, bool? detectAddressChanges,
int restrictedNatRetries, int restrictedNatRetries,
VeilidConfigTLS tls, VeilidConfigTLS tls,
VeilidConfigApplication application, VeilidConfigApplication application,
@ -6277,7 +6277,7 @@ class _$VeilidConfigNetworkCopyWithImpl<$Res>
Object? rpc = null, Object? rpc = null,
Object? dht = null, Object? dht = null,
Object? upnp = null, Object? upnp = null,
Object? detectAddressChanges = null, Object? detectAddressChanges = freezed,
Object? restrictedNatRetries = null, Object? restrictedNatRetries = null,
Object? tls = null, Object? tls = null,
Object? application = null, Object? application = null,
@ -6338,10 +6338,10 @@ class _$VeilidConfigNetworkCopyWithImpl<$Res>
? _self.upnp ? _self.upnp
: upnp // ignore: cast_nullable_to_non_nullable : upnp // ignore: cast_nullable_to_non_nullable
as bool, as bool,
detectAddressChanges: null == detectAddressChanges detectAddressChanges: freezed == detectAddressChanges
? _self.detectAddressChanges ? _self.detectAddressChanges
: detectAddressChanges // ignore: cast_nullable_to_non_nullable : detectAddressChanges // ignore: cast_nullable_to_non_nullable
as bool, as bool?,
restrictedNatRetries: null == restrictedNatRetries restrictedNatRetries: null == restrictedNatRetries
? _self.restrictedNatRetries ? _self.restrictedNatRetries
: restrictedNatRetries // ignore: cast_nullable_to_non_nullable : restrictedNatRetries // ignore: cast_nullable_to_non_nullable
@ -6496,7 +6496,7 @@ class _VeilidConfigNetwork
@override @override
final bool upnp; final bool upnp;
@override @override
final bool detectAddressChanges; final bool? detectAddressChanges;
@override @override
final int restrictedNatRetries; final int restrictedNatRetries;
@override @override
@ -6663,7 +6663,7 @@ abstract mixin class _$VeilidConfigNetworkCopyWith<$Res>
VeilidConfigRPC rpc, VeilidConfigRPC rpc,
VeilidConfigDHT dht, VeilidConfigDHT dht,
bool upnp, bool upnp,
bool detectAddressChanges, bool? detectAddressChanges,
int restrictedNatRetries, int restrictedNatRetries,
VeilidConfigTLS tls, VeilidConfigTLS tls,
VeilidConfigApplication application, VeilidConfigApplication application,
@ -6713,7 +6713,7 @@ class __$VeilidConfigNetworkCopyWithImpl<$Res>
Object? rpc = null, Object? rpc = null,
Object? dht = null, Object? dht = null,
Object? upnp = null, Object? upnp = null,
Object? detectAddressChanges = null, Object? detectAddressChanges = freezed,
Object? restrictedNatRetries = null, Object? restrictedNatRetries = null,
Object? tls = null, Object? tls = null,
Object? application = null, Object? application = null,
@ -6774,10 +6774,10 @@ class __$VeilidConfigNetworkCopyWithImpl<$Res>
? _self.upnp ? _self.upnp
: upnp // ignore: cast_nullable_to_non_nullable : upnp // ignore: cast_nullable_to_non_nullable
as bool, as bool,
detectAddressChanges: null == detectAddressChanges detectAddressChanges: freezed == detectAddressChanges
? _self.detectAddressChanges ? _self.detectAddressChanges
: detectAddressChanges // ignore: cast_nullable_to_non_nullable : detectAddressChanges // ignore: cast_nullable_to_non_nullable
as bool, as bool?,
restrictedNatRetries: null == restrictedNatRetries restrictedNatRetries: null == restrictedNatRetries
? _self.restrictedNatRetries ? _self.restrictedNatRetries
: restrictedNatRetries // ignore: cast_nullable_to_non_nullable : restrictedNatRetries // ignore: cast_nullable_to_non_nullable

View file

@ -478,7 +478,7 @@ _VeilidConfigNetwork _$VeilidConfigNetworkFromJson(Map<String, dynamic> json) =>
rpc: VeilidConfigRPC.fromJson(json['rpc']), rpc: VeilidConfigRPC.fromJson(json['rpc']),
dht: VeilidConfigDHT.fromJson(json['dht']), dht: VeilidConfigDHT.fromJson(json['dht']),
upnp: json['upnp'] as bool, upnp: json['upnp'] as bool,
detectAddressChanges: json['detect_address_changes'] as bool, detectAddressChanges: json['detect_address_changes'] as bool?,
restrictedNatRetries: (json['restricted_nat_retries'] as num).toInt(), restrictedNatRetries: (json['restricted_nat_retries'] as num).toInt(),
tls: VeilidConfigTLS.fromJson(json['tls']), tls: VeilidConfigTLS.fromJson(json['tls']),
application: VeilidConfigApplication.fromJson(json['application']), application: VeilidConfigApplication.fromJson(json['application']),

View file

@ -25,7 +25,7 @@ class ConfigBase:
value = json_data[key] value = json_data[key]
try: try:
# See if this field's type knows how to load itself from JSON input. # See if this field's type knows how to load itself from JSON input.
loader = field.type.from_json loader = field.type.from_json # type: ignore
except AttributeError: except AttributeError:
# No, it doesn't. Use the raw value. # No, it doesn't. Use the raw value.
args[key] = value args[key] = value
@ -210,7 +210,7 @@ class VeilidConfigNetwork(ConfigBase):
rpc: VeilidConfigRPC rpc: VeilidConfigRPC
dht: VeilidConfigDHT dht: VeilidConfigDHT
upnp: bool upnp: bool
detect_address_changes: bool detect_address_changes: Optional[bool]
restricted_nat_retries: int restricted_nat_retries: int
tls: VeilidConfigTLS tls: VeilidConfigTLS
application: VeilidConfigApplication application: VeilidConfigApplication

View file

@ -4261,7 +4261,6 @@
"client_allowlist_timeout_ms", "client_allowlist_timeout_ms",
"connection_inactivity_timeout_ms", "connection_inactivity_timeout_ms",
"connection_initial_timeout_ms", "connection_initial_timeout_ms",
"detect_address_changes",
"dht", "dht",
"hole_punch_receipt_time_ms", "hole_punch_receipt_time_ms",
"max_connection_frequency_per_min", "max_connection_frequency_per_min",
@ -4297,7 +4296,10 @@
"minimum": 0.0 "minimum": 0.0
}, },
"detect_address_changes": { "detect_address_changes": {
"type": "boolean" "type": [
"boolean",
"null"
]
}, },
"dht": { "dht": {
"$ref": "#/definitions/VeilidConfigDHT" "$ref": "#/definitions/VeilidConfigDHT"

View file

@ -180,7 +180,7 @@ core:
member_watch_limit: 8 member_watch_limit: 8
max_watch_expiration_ms: 600000 max_watch_expiration_ms: 600000
upnp: false upnp: false
detect_address_changes: false detect_address_changes: auto
restricted_nat_retries: 0 restricted_nat_retries: 0
tls: tls:
certificate_path: '%CERTIFICATE_PATH%' certificate_path: '%CERTIFICATE_PATH%'
@ -711,6 +711,20 @@ pub struct RoutingTable {
pub limit_attached_weak: u32, pub limit_attached_weak: u32,
} }
fn auto_bool_from_str(s: &str) -> Result<Option<bool>, String> {
match s {
"auto" => Ok(None),
"true" => Ok(Some(true)),
"false" => Ok(Some(false)),
_ => Err("Expected 'auto', 'true', or 'false'".to_owned()),
}
}
fn auto_bool<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Option<bool>, D::Error> {
let s: String = serde::de::Deserialize::deserialize(deserializer)?;
auto_bool_from_str(s.as_str()).map_err(serde::de::Error::custom)
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Network { pub struct Network {
pub connection_initial_timeout_ms: u32, pub connection_initial_timeout_ms: u32,
@ -727,7 +741,8 @@ pub struct Network {
pub rpc: Rpc, pub rpc: Rpc,
pub dht: Dht, pub dht: Dht,
pub upnp: bool, pub upnp: bool,
pub detect_address_changes: bool, #[serde(deserialize_with = "auto_bool")]
pub detect_address_changes: Option<bool>,
pub restricted_nat_retries: u32, pub restricted_nat_retries: u32,
pub tls: Tls, pub tls: Tls,
pub application: Application, pub application: Application,
@ -1068,6 +1083,28 @@ impl Settings {
}}; }};
} }
macro_rules! set_config_value_custom {
($innerkey:expr, $value:expr, $deserializer:expr) => {{
let innerkeyname = &stringify!($innerkey)[6..];
if innerkeyname == key {
match $deserializer(value) {
Ok(v) => {
$innerkey = v;
return Ok(());
}
Err(e) => {
return Err(eyre!(
"invalid type for key {}, value: {}: {}",
key,
value,
e
))
}
}
}
}};
}
set_config_value!(inner.daemon.enabled, value); set_config_value!(inner.daemon.enabled, value);
set_config_value!(inner.daemon.pid_file, value); set_config_value!(inner.daemon.pid_file, value);
set_config_value!(inner.daemon.chroot, value); set_config_value!(inner.daemon.chroot, value);
@ -1219,7 +1256,11 @@ impl Settings {
set_config_value!(inner.core.network.dht.member_watch_limit, value); set_config_value!(inner.core.network.dht.member_watch_limit, value);
set_config_value!(inner.core.network.dht.max_watch_expiration_ms, value); set_config_value!(inner.core.network.dht.max_watch_expiration_ms, value);
set_config_value!(inner.core.network.upnp, value); set_config_value!(inner.core.network.upnp, value);
set_config_value!(inner.core.network.detect_address_changes, value); set_config_value_custom!(
inner.core.network.detect_address_changes,
value,
auto_bool_from_str
);
set_config_value!(inner.core.network.restricted_nat_retries, value); set_config_value!(inner.core.network.restricted_nat_retries, value);
set_config_value!(inner.core.network.tls.certificate_path, value); set_config_value!(inner.core.network.tls.certificate_path, value);
set_config_value!(inner.core.network.tls.private_key_path, value); set_config_value!(inner.core.network.tls.private_key_path, value);
@ -1892,7 +1933,7 @@ mod tests {
assert_eq!(s.core.network.dht.max_watch_expiration_ms, 600_000u32); assert_eq!(s.core.network.dht.max_watch_expiration_ms, 600_000u32);
// //
assert!(!s.core.network.upnp); assert!(!s.core.network.upnp);
assert!(!s.core.network.detect_address_changes); assert_eq!(s.core.network.detect_address_changes, None);
assert_eq!(s.core.network.restricted_nat_retries, 0u32); assert_eq!(s.core.network.restricted_nat_retries, 0u32);
// //
assert_eq!( assert_eq!(
@ -1986,7 +2027,7 @@ mod tests {
); );
assert_eq!(s.core.network.protocol.wss.url, None); assert_eq!(s.core.network.protocol.wss.url, None);
// //
assert_eq!(s.core.network.privacy.require_inbound_relay, false); assert!(!s.core.network.privacy.require_inbound_relay);
#[cfg(feature = "geolocation")] #[cfg(feature = "geolocation")]
assert_eq!(s.core.network.privacy.country_code_denylist, &[]); assert_eq!(s.core.network.privacy.country_code_denylist, &[]);
#[cfg(feature = "virtual-network")] #[cfg(feature = "virtual-network")]