diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index db4be4c5..bad3033f 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -1651,6 +1651,10 @@ impl NetworkManager { let routing_table = inner.routing_table.as_ref().unwrap().clone(); (net, routing_table) }; + let detect_address_changes = { + let c = self.config.get(); + c.network.detect_address_changes + }; let network_class = net.get_network_class().unwrap_or(NetworkClass::Invalid); // Determine if our external address has likely changed @@ -1709,15 +1713,22 @@ impl NetworkManager { }; if needs_public_address_detection { - // Reset the address check cache now so we can start detecting fresh - info!("Public address has changed, detecting public dial info"); + if detect_address_changes { + // Reset the address check cache now so we can start detecting fresh + info!("Public address has changed, detecting public dial info"); - let mut inner = self.inner.lock(); - inner.public_address_check_cache.clear(); + let mut inner = self.inner.lock(); + inner.public_address_check_cache.clear(); - // Reset the network class and dial info so we can re-detect it - routing_table.clear_dial_info_details(RoutingDomain::PublicInternet); - net.reset_network_class(); + // Reset the network class and dial info so we can re-detect it + //routing_table.clear_dial_info_details(RoutingDomain::PublicInternet); + //net.reset_network_class(); + + // Do a full network reset since this doesn't take that long and ensures we get the local network stuff correct too + net.restart_network(); + } else { + warn!("Public address may have changed. Restarting the server may be required."); + } } } diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index e92340a9..28fc15b9 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -653,13 +653,17 @@ impl Network { self.free_bound_first_ports(); // If we have static public dialinfo, upgrade our network class - // xxx: force public address detection - // { - // let mut inner = self.inner.lock(); - // if !inner.static_public_dialinfo.is_empty() { - // inner.network_class = Some(NetworkClass::InboundCapable); - // } - // } + let detect_address_changes = { + let c = self.config.get(); + c.network.detect_address_changes + }; + + if !detect_address_changes { + let mut inner = self.inner.lock(); + if !inner.static_public_dialinfo.is_empty() { + inner.network_class = Some(NetworkClass::InboundCapable); + } + } info!("network started"); self.inner.lock().network_started = true; @@ -746,23 +750,30 @@ impl Network { } pub async fn tick(&self) -> EyreResult<()> { - let network_class = self.get_network_class().unwrap_or(NetworkClass::Invalid); - let routing_table = self.routing_table(); + let detect_address_changes = { + let config = self.network_manager().config(); + let c = config.get(); + c.network.detect_address_changes + }; // If we need to figure out our network class, tick the task for it - if network_class == NetworkClass::Invalid { - let rth = routing_table.get_routing_table_health(); + if detect_address_changes { + let network_class = self.get_network_class().unwrap_or(NetworkClass::Invalid); + if network_class == NetworkClass::Invalid { + let routing_table = self.routing_table(); + let rth = routing_table.get_routing_table_health(); - // Need at least two entries to do this - if rth.unreliable_entry_count + rth.reliable_entry_count >= 2 { - self.unlocked_inner.update_network_class_task.tick().await?; + // Need at least two entries to do this + if rth.unreliable_entry_count + rth.reliable_entry_count >= 2 { + self.unlocked_inner.update_network_class_task.tick().await?; + } } - } - // If we aren't resetting the network already, - // check our network interfaces to see if they have changed - if !self.needs_restart() { - self.unlocked_inner.network_interfaces_task.tick().await?; + // If we aren't resetting the network already, + // check our network interfaces to see if they have changed + if !self.needs_restart() { + self.unlocked_inner.network_interfaces_task.tick().await?; + } } Ok(()) diff --git a/veilid-core/src/network_manager/native/network_class_discovery.rs b/veilid-core/src/network_manager/native/network_class_discovery.rs index da3a2eeb..dde6ce19 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -439,6 +439,7 @@ impl Network { "Skipping detection for public dialinfo for {:?}:IPV4", protocol_type ); + context.set_detected_network_class(NetworkClass::InboundCapable); return Ok(()); } @@ -511,6 +512,7 @@ impl Network { "Skipping detection for public dialinfo for {:?}:IPV6", protocol_type ); + context.set_detected_network_class(NetworkClass::InboundCapable); return Ok(()); } // Start doing ipv6 protocol diff --git a/veilid-core/src/network_manager/native/start_protocols.rs b/veilid-core/src/network_manager/native/start_protocols.rs index 7f21f10a..41b1ac38 100644 --- a/veilid-core/src/network_manager/native/start_protocols.rs +++ b/veilid-core/src/network_manager/native/start_protocols.rs @@ -253,12 +253,13 @@ impl Network { pub(super) async fn start_udp_listeners(&self) -> EyreResult<()> { trace!("starting udp listeners"); let routing_table = self.routing_table(); - let (listen_address, public_address, enable_local_peer_scope) = { + let (listen_address, public_address, enable_local_peer_scope, detect_address_changes) = { let c = self.config.get(); ( c.network.protocol.udp.listen_address.clone(), c.network.protocol.udp.public_address.clone(), c.network.enable_local_peer_scope, + c.network.detect_address_changes, ) }; @@ -289,7 +290,10 @@ impl Network { for di in &local_dial_info_list { // If the local interface address is global, or we are enabling local peer scope // register global dial info if no public address is specified - if public_address.is_none() && (di.is_global() || enable_local_peer_scope) { + if !detect_address_changes + && public_address.is_none() + && (di.is_global() || enable_local_peer_scope) + { routing_table.register_dial_info( RoutingDomain::PublicInternet, di.clone(), @@ -318,11 +322,14 @@ impl Network { let pdi = DialInfo::udp_from_socketaddr(pdi_addr); // Register the public address - routing_table.register_dial_info( - RoutingDomain::PublicInternet, - pdi.clone(), - DialInfoClass::Direct, - )?; + if !detect_address_changes { + routing_table.register_dial_info( + RoutingDomain::PublicInternet, + pdi.clone(), + DialInfoClass::Direct, + )?; + static_public = true; + } // See if this public address is also a local interface address we haven't registered yet let is_interface_address = self.with_interface_addresses(|ip_addrs| { @@ -340,8 +347,6 @@ impl Network { DialInfoClass::Direct, )?; } - - static_public = true; } } @@ -359,13 +364,14 @@ impl Network { pub(super) async fn start_ws_listeners(&self) -> EyreResult<()> { trace!("starting ws listeners"); let routing_table = self.routing_table(); - let (listen_address, url, path, enable_local_peer_scope) = { + let (listen_address, url, path, enable_local_peer_scope, detect_address_changes) = { let c = self.config.get(); ( c.network.protocol.ws.listen_address.clone(), c.network.protocol.ws.url.clone(), c.network.protocol.ws.path.clone(), c.network.enable_local_peer_scope, + c.network.detect_address_changes, ) }; @@ -413,12 +419,14 @@ impl Network { let pdi = DialInfo::try_ws(SocketAddress::from_socket_addr(gsa), url.clone()) .wrap_err("try_ws failed")?; - routing_table.register_dial_info( - RoutingDomain::PublicInternet, - pdi.clone(), - DialInfoClass::Direct, - )?; - static_public = true; + if !detect_address_changes { + routing_table.register_dial_info( + RoutingDomain::PublicInternet, + pdi.clone(), + DialInfoClass::Direct, + )?; + static_public = true; + } // See if this public address is also a local interface address let is_interface_address = self.with_interface_addresses(|ip_addrs| { @@ -450,7 +458,10 @@ impl Network { let local_url = format!("ws://{}/{}", socket_address, path); let local_di = DialInfo::try_ws(socket_address, local_url).wrap_err("try_ws failed")?; - if url.is_none() && (socket_address.address().is_global() || enable_local_peer_scope) { + if !detect_address_changes + && url.is_none() + && (socket_address.address().is_global() || enable_local_peer_scope) + { // Register public dial info routing_table.register_dial_info( RoutingDomain::PublicInternet, @@ -482,11 +493,12 @@ impl Network { trace!("starting wss listeners"); let routing_table = self.routing_table(); - let (listen_address, url) = { + let (listen_address, url, detect_address_changes) = { let c = self.config.get(); ( c.network.protocol.wss.listen_address.clone(), c.network.protocol.wss.url.clone(), + c.network.detect_address_changes, ) }; @@ -539,12 +551,14 @@ impl Network { let pdi = DialInfo::try_wss(SocketAddress::from_socket_addr(gsa), url.clone()) .wrap_err("try_wss failed")?; - routing_table.register_dial_info( - RoutingDomain::PublicInternet, - pdi.clone(), - DialInfoClass::Direct, - )?; - static_public = true; + if !detect_address_changes { + routing_table.register_dial_info( + RoutingDomain::PublicInternet, + pdi.clone(), + DialInfoClass::Direct, + )?; + static_public = true; + } // See if this public address is also a local interface address let is_interface_address = self.with_interface_addresses(|ip_addrs| { @@ -583,12 +597,13 @@ impl Network { trace!("starting tcp listeners"); let routing_table = self.routing_table(); - let (listen_address, public_address, enable_local_peer_scope) = { + let (listen_address, public_address, enable_local_peer_scope, detect_address_changes) = { let c = self.config.get(); ( c.network.protocol.tcp.listen_address.clone(), c.network.protocol.tcp.public_address.clone(), c.network.enable_local_peer_scope, + c.network.detect_address_changes, ) }; @@ -622,7 +637,10 @@ impl Network { let di = DialInfo::tcp(socket_address); // Register global dial info if no public address is specified - if public_address.is_none() && (di.is_global() || enable_local_peer_scope) { + if !detect_address_changes + && public_address.is_none() + && (di.is_global() || enable_local_peer_scope) + { routing_table.register_dial_info( RoutingDomain::PublicInternet, di.clone(), @@ -654,12 +672,14 @@ impl Network { } let pdi = DialInfo::tcp_from_socketaddr(pdi_addr); - routing_table.register_dial_info( - RoutingDomain::PublicInternet, - pdi.clone(), - DialInfoClass::Direct, - )?; - static_public = true; + if !detect_address_changes { + routing_table.register_dial_info( + RoutingDomain::PublicInternet, + pdi.clone(), + DialInfoClass::Direct, + )?; + static_public = true; + } // See if this public address is also a local interface address let is_interface_address = self.with_interface_addresses(|ip_addrs| { diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index cbf30d1b..47793a48 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -223,6 +223,7 @@ fn config_callback(key: String) -> ConfigCallbackReturn { "network.dht.validate_dial_info_receipt_time_ms" => Ok(Box::new(5_000u32)), "network.upnp" => Ok(Box::new(false)), "network.natpmp" => Ok(Box::new(false)), + "network.detect_address_changes" => Ok(Box::new(true)), "network.enable_local_peer_scope" => Ok(Box::new(false)), "network.restricted_nat_retries" => Ok(Box::new(3u32)), "network.tls.certificate_path" => Ok(Box::new(get_certfile_path())), @@ -352,6 +353,7 @@ pub async fn test_config() { assert_eq!(inner.network.upnp, false); assert_eq!(inner.network.natpmp, false); + assert_eq!(inner.network.detect_address_changes, true); assert_eq!(inner.network.enable_local_peer_scope, false); assert_eq!(inner.network.restricted_nat_retries, 3u32); assert_eq!(inner.network.tls.certificate_path, get_certfile_path()); diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index ac8df820..db9b469c 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -136,6 +136,7 @@ pub struct VeilidConfigNetwork { pub dht: VeilidConfigDHT, pub upnp: bool, pub natpmp: bool, + pub detect_address_changes: bool, pub enable_local_peer_scope: bool, pub restricted_nat_retries: u32, pub tls: VeilidConfigTLS, @@ -354,6 +355,7 @@ impl VeilidConfig { get_config!(inner.network.rpc.max_route_hop_count); get_config!(inner.network.upnp); get_config!(inner.network.natpmp); + get_config!(inner.network.detect_address_changes); get_config!(inner.network.enable_local_peer_scope); get_config!(inner.network.restricted_nat_retries); get_config!(inner.network.tls.certificate_path); diff --git a/veilid-flutter/example/lib/config.dart b/veilid-flutter/example/lib/config.dart index a80dd74f..1fb61c92 100644 --- a/veilid-flutter/example/lib/config.dart +++ b/veilid-flutter/example/lib/config.dart @@ -84,6 +84,7 @@ Future getDefaultVeilidConfig() async { ), upnp: true, natpmp: true, + detectAddressChanges: true, enableLocalPeerScope: false, restrictedNatRetries: 3, tls: VeilidConfigTLS( diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 1cb38452..18263aa4 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -740,6 +740,7 @@ class VeilidConfigNetwork { VeilidConfigDHT dht; bool upnp; bool natpmp; + bool detectAddressChanges; bool enableLocalPeerScope; int restrictedNatRetries; VeilidConfigTLS tls; @@ -765,6 +766,7 @@ class VeilidConfigNetwork { required this.dht, required this.upnp, required this.natpmp, + required this.detectAddressChanges, required this.enableLocalPeerScope, required this.restrictedNatRetries, required this.tls, @@ -792,6 +794,7 @@ class VeilidConfigNetwork { 'dht': dht.json, 'upnp': upnp, 'natpmp': natpmp, + 'detect_address_changes': detectAddressChanges, 'enable_local_peer_scope': enableLocalPeerScope, 'restricted_nat_retries': restrictedNatRetries, 'tls': tls.json, @@ -822,6 +825,7 @@ class VeilidConfigNetwork { dht = VeilidConfigDHT.fromJson(json['dht']), upnp = json['upnp'], natpmp = json['natpmp'], + detectAddressChanges = json['detect_address_changes'], enableLocalPeerScope = json['enable_local_peer_scope'], restrictedNatRetries = json['restricted_nat_retries'], tls = VeilidConfigTLS.fromJson(json['tls']), diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index fad0abfd..e62242e4 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -98,6 +98,7 @@ core: validate_dial_info_receipt_time_ms: 2000 upnp: false natpmp: false + detect_address_changes: true enable_local_peer_scope: false restricted_nat_retries: 0 tls: @@ -587,6 +588,7 @@ pub struct Network { pub dht: Dht, pub upnp: bool, pub natpmp: bool, + pub detect_address_changes: bool, pub enable_local_peer_scope: bool, pub restricted_nat_retries: u32, pub tls: Tls, @@ -973,6 +975,7 @@ impl Settings { ); set_config_value!(inner.core.network.upnp, value); set_config_value!(inner.core.network.natpmp, value); + set_config_value!(inner.core.network.detect_address_changes, value); set_config_value!(inner.core.network.enable_local_peer_scope, value); set_config_value!(inner.core.network.restricted_nat_retries, value); set_config_value!(inner.core.network.tls.certificate_path, value); @@ -1171,6 +1174,9 @@ impl Settings { )), "network.upnp" => Ok(Box::new(inner.core.network.upnp)), "network.natpmp" => Ok(Box::new(inner.core.network.natpmp)), + "network.detect_address_changes" => { + Ok(Box::new(inner.core.network.detect_address_changes)) + } "network.enable_local_peer_scope" => { Ok(Box::new(inner.core.network.enable_local_peer_scope)) } @@ -1496,6 +1502,7 @@ mod tests { // assert_eq!(s.core.network.upnp, false); assert_eq!(s.core.network.natpmp, false); + assert_eq!(s.core.network.detect_address_changes, true); assert_eq!(s.core.network.enable_local_peer_scope, false); assert_eq!(s.core.network.restricted_nat_retries, 0u32); // diff --git a/veilid-wasm/tests/web.rs b/veilid-wasm/tests/web.rs index caed2a8a..e98612a8 100644 --- a/veilid-wasm/tests/web.rs +++ b/veilid-wasm/tests/web.rs @@ -61,6 +61,7 @@ fn init_callbacks() { case "network.dht.validate_dial_info_receipt_time": return 5000000; case "network.upnp": return false; case "network.natpmp": return false; + case "network.detect_address_changes": return true; case "network.address_filter": return true; case "network.restricted_nat_retries": return 3; case "network.tls.certificate_path": return "";