diff --git a/veilid-core/src/network_manager/address_check.rs b/veilid-core/src/network_manager/address_check.rs index 6b9981e3..2187a914 100644 --- a/veilid-core/src/network_manager/address_check.rs +++ b/veilid-core/src/network_manager/address_check.rs @@ -299,10 +299,14 @@ impl AddressCheck { flow: Flow, // the flow used reporting_ipblock: IpAddr, // the IP block this report came from ) -> bool { - let acckey = - AddressCheckCacheKey(routing_domain, flow.protocol_type(), flow.address_type()); + // Don't do this if we aren't ever going to use it + if !self.config.detect_address_changes { + return false; + } // Add the currently seen socket address into the consistency table + let acckey = + AddressCheckCacheKey(routing_domain, flow.protocol_type(), flow.address_type()); let cache = self .address_consistency_table .entry(acckey) diff --git a/veilid-core/src/network_manager/direct_boot.rs b/veilid-core/src/network_manager/direct_boot.rs index a095970f..d1c22e05 100644 --- a/veilid-core/src/network_manager/direct_boot.rs +++ b/veilid-core/src/network_manager/direct_boot.rs @@ -10,9 +10,9 @@ impl NetworkManager { let bootstrap_nodes = routing_table.find_bootstrap_nodes_filtered(2); // Serialize out peer info - let bootstrap_peerinfo: Vec = bootstrap_nodes + let bootstrap_peerinfo: Vec> = bootstrap_nodes .iter() - .filter_map(|nr| nr.make_peer_info(RoutingDomain::PublicInternet)) + .filter_map(|nr| nr.get_peer_info(RoutingDomain::PublicInternet)) .collect(); let json_bytes = serialize_json(bootstrap_peerinfo).as_bytes().to_vec(); diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index aea181e8..e50c3d94 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -107,6 +107,7 @@ struct NodeContactMethodCacheKey { target_node_info_ts: Timestamp, target_node_ref_filter: NodeRefFilter, target_node_ref_sequencing: Sequencing, + dial_info_failures_map: BTreeMap, } enum SendDataToExistingFlowResult { @@ -1179,19 +1180,6 @@ impl NetworkManager { self.net().restart_network(); } - /// If some other subsystem believes our dial info is no longer valid, this will trigger - /// a re-check of the dial info and network class - pub fn set_needs_dial_info_check(&self, routing_domain: RoutingDomain) { - match routing_domain { - RoutingDomain::LocalNetwork => { - // nothing here yet - } - RoutingDomain::PublicInternet => { - self.net().set_needs_public_dial_info_check(None); - } - } - } - // Report peer info changes fn peer_info_change_event_handler(&self, evt: Arc) { let mut inner = self.inner.lock(); diff --git a/veilid-core/src/network_manager/native/discovery_context.rs b/veilid-core/src/network_manager/native/discovery_context.rs index e6d99a96..cc93879b 100644 --- a/veilid-core/src/network_manager/native/discovery_context.rs +++ b/veilid-core/src/network_manager/native/discovery_context.rs @@ -414,37 +414,48 @@ impl DiscoveryContext { &self, unord: &mut FuturesUnordered>>, ) { - let external_1 = self.inner.lock().external_info.first().cloned().unwrap(); + let external_infos = self.inner.lock().external_info.clone(); - let this = self.clone(); - let do_no_nat_fut: SendPinBoxFuture> = Box::pin(async move { - // Do a validate_dial_info on the external address from a redirected node - if this - .validate_dial_info(external_1.node.clone(), external_1.dial_info.clone(), true) - .await - { - // Add public dial info with Direct dialinfo class - Some(DetectionResult { - config: this.config, - ddi: DetectedDialInfo::Detected(DialInfoDetail { - dial_info: external_1.dial_info.clone(), - class: DialInfoClass::Direct, - }), - external_address_types: AddressTypeSet::only(external_1.address.address_type()), - }) - } else { - // Add public dial info with Blocked dialinfo class - Some(DetectionResult { - config: this.config, - ddi: DetectedDialInfo::Detected(DialInfoDetail { - dial_info: external_1.dial_info.clone(), - class: DialInfoClass::Blocked, - }), - external_address_types: AddressTypeSet::only(external_1.address.address_type()), - }) - } - }); - unord.push(do_no_nat_fut); + // Have all the external validator nodes check us + for external_info in external_infos { + let this = self.clone(); + let do_no_nat_fut: SendPinBoxFuture> = Box::pin(async move { + // Do a validate_dial_info on the external address from a redirected node + if this + .validate_dial_info( + external_info.node.clone(), + external_info.dial_info.clone(), + true, + ) + .await + { + // Add public dial info with Direct dialinfo class + Some(DetectionResult { + config: this.config, + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: external_info.dial_info.clone(), + class: DialInfoClass::Direct, + }), + external_address_types: AddressTypeSet::only( + external_info.address.address_type(), + ), + }) + } else { + // Add public dial info with Blocked dialinfo class + Some(DetectionResult { + config: this.config, + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: external_info.dial_info.clone(), + class: DialInfoClass::Blocked, + }), + external_address_types: AddressTypeSet::only( + external_info.address.address_type(), + ), + }) + } + }); + unord.push(do_no_nat_fut); + } } // If we know we are behind NAT check what kind @@ -725,7 +736,7 @@ impl DiscoveryContext { // NAT Detection /////////// - // If our local interface list contains external_1 then there is no NAT in place + // If our local interface list contains any of the external addresses then there is no NAT in place let local_address_in_external_info = self .inner .lock() diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index e4c67592..9bcd37c2 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -31,6 +31,8 @@ use std::path::{Path, PathBuf}; ///////////////////////////////////////////////////////////////// +pub const MAX_DIAL_INFO_FAILURE_COUNT: usize = 100; +pub const UPDATE_OUTBOUND_ONLY_NETWORK_CLASS_PERIOD_SECS: u32 = 10; pub const UPDATE_NETWORK_CLASS_TASK_TICK_PERIOD_SECS: u32 = 1; pub const NETWORK_INTERFACES_TASK_TICK_PERIOD_SECS: u32 = 1; pub const UPNP_TASK_TICK_PERIOD_SECS: u32 = 1; @@ -82,15 +84,17 @@ struct NetworkInner { /// set if the network needs to be restarted due to a low level configuration change /// such as dhcp release or change of address or interfaces being added or removed network_needs_restart: bool, - + /// the number of consecutive dial info failures per routing domain, + /// which may indicate the network is down for that domain + dial_info_failure_count: BTreeMap, + /// the next time we are allowed to check for better dialinfo when we are OutboundOnly + next_outbound_only_dial_info_check: Timestamp, /// join handles for all the low level network background tasks join_handles: Vec>, /// stop source for shutting down the low level network background tasks stop_source: Option, /// set if we need to calculate our public dial info again needs_public_dial_info_check: bool, - /// set if we have yet to clear the network during public dial info checking - network_already_cleared: bool, /// the punishment closure to enax public_dial_info_check_punishment: Option>, /// Actual bound addresses per protocol @@ -151,8 +155,9 @@ impl Network { fn new_inner() -> NetworkInner { NetworkInner { network_needs_restart: false, + dial_info_failure_count: BTreeMap::new(), + next_outbound_only_dial_info_check: Timestamp::default(), needs_public_dial_info_check: false, - network_already_cleared: false, public_dial_info_check_punishment: None, join_handles: Vec::new(), stop_source: None, @@ -312,11 +317,42 @@ impl Network { dial_info: DialInfo, fut: F, ) -> EyreResult> { + let opt_routing_domain = self + .routing_table() + .routing_domain_for_address(dial_info.address()); + let network_result = fut.await?; if matches!(network_result, NetworkResult::NoConnection(_)) { + // Dial info failure self.network_manager() .address_filter() .set_dial_info_failed(dial_info); + + // Increment consecutive failure count for this routing domain + if let Some(rd) = opt_routing_domain { + let dial_info_failure_count = { + let mut inner = self.inner.lock(); + *inner + .dial_info_failure_count + .entry(rd) + .and_modify(|x| *x += 1) + .or_insert(1) + }; + + if dial_info_failure_count == MAX_DIAL_INFO_FAILURE_COUNT { + log_net!(debug "Node may be offline. Exceeded maximum dial info failure count for {:?}", rd); + // todo: what operations should we perform here? + // self.set_needs_dial_info_check(rd); + } + } + } else { + // Dial info success + + // Clear failure count for this routing domain + if let Some(rd) = opt_routing_domain { + let mut inner = self.inner.lock(); + inner.dial_info_failure_count.remove(&rd); + } } Ok(network_result) } @@ -793,6 +829,20 @@ impl Network { editor_local_network.publish(); } + if self.config().with(|c| c.network.detect_address_changes) { + // Say we need to detect the public dialinfo + self.set_needs_public_dial_info_check(None); + } else { + // Warn if we have no public dialinfo, because we're not going to magically find some + // with detect address changes turned off + let pi = routing_table.get_current_peer_info(RoutingDomain::PublicInternet); + if !pi.signed_node_info().has_any_dial_info() { + warn!( + "This node has no valid public dial info.\nConfigure this node with a static public IP address and correct firewall rules." + ); + } + } + Ok(StartupDisposition::Success) } @@ -918,6 +968,17 @@ impl Network { } ////////////////////////////////////////// + // pub fn set_needs_dial_info_check(&self, routing_domain: RoutingDomain) { + // match routing_domain { + // RoutingDomain::LocalNetwork => { + // // nothing here yet + // } + // RoutingDomain::PublicInternet => { + // self.set_needs_public_dial_info_check(None); + // } + // } + // } + pub fn set_needs_public_dial_info_check( &self, punishment: Option>, diff --git a/veilid-core/src/network_manager/native/network_state.rs b/veilid-core/src/network_manager/native/network_state.rs index 0b8a7ca0..f5f99242 100644 --- a/veilid-core/src/network_manager/native/network_state.rs +++ b/veilid-core/src/network_manager/native/network_state.rs @@ -14,12 +14,10 @@ pub struct ProtocolConfig { pub(super) struct NetworkState { /// the calculated protocol configuration for inbound/outbound protocols pub protocol_config: ProtocolConfig, - /// does our network have ipv4 on any network? + /// does our network have ipv4 on any interface? pub enable_ipv4: bool, - /// does our network have ipv6 on the global internet? - pub enable_ipv6_global: bool, - /// does our network have ipv6 on the local network? - pub enable_ipv6_local: bool, + /// does our network have ipv6 on any interface? + pub enable_ipv6: bool, /// The list of stable interface addresses we have last seen pub stable_interface_addresses: Vec, /// The local networks (network+mask) most recently seen @@ -84,21 +82,15 @@ impl Network { // determine if we have ipv4/ipv6 addresses let mut enable_ipv4 = false; - let mut enable_ipv6_global = false; - let mut enable_ipv6_local = false; + let mut enable_ipv6 = false; let stable_interface_addresses = self.make_stable_interface_addresses(); - for addr in stable_interface_addresses.iter().copied() { + for addr in stable_interface_addresses.iter() { if addr.is_ipv4() { enable_ipv4 = true; } else if addr.is_ipv6() { - let address = Address::from_ip_addr(addr); - if address.is_global() { - enable_ipv6_global = true; - } else if address.is_local() { - enable_ipv6_local = true; - } + enable_ipv6 = true; } } @@ -141,10 +133,8 @@ impl Network { family_global.insert(AddressType::IPV4); family_local.insert(AddressType::IPV4); } - if enable_ipv6_global { + if enable_ipv6 { family_global.insert(AddressType::IPV6); - } - if enable_ipv6_local { family_local.insert(AddressType::IPV6); } @@ -178,8 +168,7 @@ impl Network { Ok(NetworkState { protocol_config, enable_ipv4, - enable_ipv6_global, - enable_ipv6_local, + enable_ipv6, stable_interface_addresses, local_networks, }) diff --git a/veilid-core/src/network_manager/native/network_tcp.rs b/veilid-core/src/network_manager/native/network_tcp.rs index c14e5e51..7d817d4e 100644 --- a/veilid-core/src/network_manager/native/network_tcp.rs +++ b/veilid-core/src/network_manager/native/network_tcp.rs @@ -350,6 +350,14 @@ impl Network { .or_default(); bapp.push(addr); + log_net!( + debug + "set_preferred_local_address: {:?} {:?} -> {:?}", + protocol_type, + addr, + PeerAddress::new(SocketAddress::from_socket_addr(addr), protocol_type) + ); + Self::set_preferred_local_address( &mut inner, PeerAddress::new(SocketAddress::from_socket_addr(addr), protocol_type), diff --git a/veilid-core/src/network_manager/native/network_udp.rs b/veilid-core/src/network_manager/native/network_udp.rs index ad4c18fa..2266d213 100644 --- a/veilid-core/src/network_manager/native/network_udp.rs +++ b/veilid-core/src/network_manager/native/network_udp.rs @@ -158,6 +158,14 @@ impl Network { .or_default(); bapp.push(addr); + log_net!( + debug + "set_preferred_local_address: {:?} {:?} -> {:?}", + ProtocolType::UDP, + addr, + PeerAddress::new(SocketAddress::from_socket_addr(addr), ProtocolType::UDP) + ); + Self::set_preferred_local_address( &mut inner, PeerAddress::new( diff --git a/veilid-core/src/network_manager/native/tasks/mod.rs b/veilid-core/src/network_manager/native/tasks/mod.rs index ab7caa44..b76f37dd 100644 --- a/veilid-core/src/network_manager/native/tasks/mod.rs +++ b/veilid-core/src/network_manager/native/tasks/mod.rs @@ -39,6 +39,42 @@ impl Network { } } + // Determine if we need to check for public dialinfo + fn needs_update_network_class_tick(&self) -> bool { + let public_internet_network_class = self + .routing_table() + .get_network_class(RoutingDomain::PublicInternet); + let needs_public_dial_info_check = self.needs_public_dial_info_check(); + + if needs_public_dial_info_check + || public_internet_network_class == NetworkClass::Invalid + || (public_internet_network_class == NetworkClass::OutboundOnly + && self.inner.lock().next_outbound_only_dial_info_check <= Timestamp::now()) + { + let routing_table = self.routing_table(); + let rth = routing_table.get_routing_table_health(); + + // We want at least two live entries per crypto kind before we start doing this (bootstrap) + let mut has_at_least_two = true; + for ck in VALID_CRYPTO_KINDS { + if rth + .live_entry_counts + .get(&(RoutingDomain::PublicInternet, ck)) + .copied() + .unwrap_or_default() + < 2 + { + has_at_least_two = false; + break; + } + } + + has_at_least_two + } else { + false + } + } + #[instrument(level = "trace", target = "net", name = "Network::tick", skip_all, err)] pub async fn tick(&self) -> EyreResult<()> { let Ok(_guard) = self.startup_lock.enter() else { @@ -62,36 +98,8 @@ impl Network { // Check our network interfaces to see if they have changed self.network_interfaces_task.tick().await?; - // Check our public dial info to see if it has changed - let public_internet_network_class = self - .routing_table() - .get_network_class(RoutingDomain::PublicInternet) - .unwrap_or(NetworkClass::Invalid); - let needs_public_dial_info_check = self.needs_public_dial_info_check(); - if public_internet_network_class == NetworkClass::Invalid - || needs_public_dial_info_check - { - let routing_table = self.routing_table(); - let rth = routing_table.get_routing_table_health(); - - // We want at least two live entries per crypto kind before we start doing this (bootstrap) - let mut has_at_least_two = true; - for ck in VALID_CRYPTO_KINDS { - if rth - .live_entry_counts - .get(&(RoutingDomain::PublicInternet, ck)) - .copied() - .unwrap_or_default() - < 2 - { - has_at_least_two = false; - break; - } - } - - if has_at_least_two { - self.update_network_class_task.tick().await?; - } + if self.needs_update_network_class_tick() { + self.update_network_class_task.tick().await?; } } diff --git a/veilid-core/src/network_manager/native/tasks/network_interfaces_task.rs b/veilid-core/src/network_manager/native/tasks/network_interfaces_task.rs index 79f57b5d..fe88d0ba 100644 --- a/veilid-core/src/network_manager/native/tasks/network_interfaces_task.rs +++ b/veilid-core/src/network_manager/native/tasks/network_interfaces_task.rs @@ -9,7 +9,8 @@ impl Network { _t: Timestamp, ) -> EyreResult<()> { // Network lock ensures only one task operating on the low level network state - // can happen at the same time. + // can happen at the same time. Try lock is here to give preference to other longer + // running processes like update_network_class_task. let _guard = match self.network_task_lock.try_lock() { Ok(v) => v, Err(_) => { @@ -68,7 +69,6 @@ impl Network { // If any of the new addresses were PublicInternet addresses, re-run public dial info check if public_internet_changed { - // inner.network_needs_restart = true; self.set_needs_public_dial_info_check(None); } diff --git a/veilid-core/src/network_manager/native/tasks/update_network_class_task.rs b/veilid-core/src/network_manager/native/tasks/update_network_class_task.rs index d27e63e8..43a82410 100644 --- a/veilid-core/src/network_manager/native/tasks/update_network_class_task.rs +++ b/veilid-core/src/network_manager/native/tasks/update_network_class_task.rs @@ -14,14 +14,9 @@ impl Network { t: Timestamp, ) -> EyreResult<()> { // Network lock ensures only one task operating on the low level network state - // can happen at the same time. - let _guard = match self.network_task_lock.try_lock() { - Ok(v) => v, - Err(_) => { - // If we can't get the lock right now, then - return Ok(()); - } - }; + // can happen at the same time. This a blocking lock so we can ensure this runs + // as soon as network_interfaces_task is finished + let _guard = self.network_task_lock.lock().await; // Do the public dial info check let finished = self.do_public_dial_info_check(stop_token, l, t).await?; @@ -29,7 +24,13 @@ impl Network { // Done with public dial info check if finished { let mut inner = self.inner.lock(); + + // Note that we did the check successfully inner.needs_public_dial_info_check = false; + + // Don't try to re-do OutboundOnly dialinfo for another 10 seconds + inner.next_outbound_only_dial_info_check = Timestamp::now() + + TimestampDuration::new_secs(UPDATE_OUTBOUND_ONLY_NETWORK_CLASS_PERIOD_SECS) } Ok(()) @@ -94,7 +95,7 @@ impl Network { ) -> EyreResult { // Figure out if we can optimize TCP/WS checking since they are often on the same port let (protocol_config, inbound_protocol_map) = { - let mut inner = self.inner.lock(); + let inner = self.inner.lock(); let Some(protocol_config) = inner .network_state .as_ref() @@ -103,9 +104,6 @@ impl Network { bail!("should not be doing public dial info check before we have an initial network state"); }; - // Allow network to be cleared if external addresses change - inner.network_already_cleared = false; - let mut inbound_protocol_map = HashMap::<(AddressType, LowLevelProtocolType, u16), Vec>::new(); for at in protocol_config.family_global { @@ -171,6 +169,7 @@ impl Network { // Wait for all discovery futures to complete and apply discoverycontexts let mut external_address_types = AddressTypeSet::new(); + let mut detection_results = HashMap::::new(); loop { match unord .next() @@ -185,8 +184,34 @@ impl Network { // Add the external address kinds to the set we've seen external_address_types |= dr.external_address_types; - // Import the dialinfo - self.update_with_detection_result(&mut editor, &inbound_protocol_map, dr); + // Get best detection result for each discovery context config + if let Some(cur_dr) = detection_results.get_mut(&dr.config) { + let ddi = &mut cur_dr.ddi; + // Upgrade existing dialinfo + match ddi { + DetectedDialInfo::SymmetricNAT => { + // Whatever we got is better than or equal to symmetric + *ddi = dr.ddi; + } + DetectedDialInfo::Detected(cur_did) => match dr.ddi { + DetectedDialInfo::SymmetricNAT => { + // Nothing is worse than this + } + DetectedDialInfo::Detected(did) => { + // Pick the best dial info class we detected + // because some nodes could be degenerate and if any node can validate a + // better dial info class we should go with it and leave the + // degenerate nodes in the dust to fade into obscurity + if did.class < cur_did.class { + cur_did.class = did.class; + } + } + }, + } + cur_dr.external_address_types |= dr.external_address_types; + } else { + detection_results.insert(dr.config, dr); + } } Ok(Some(None)) => { // Found no dial info for this protocol/address combination @@ -202,6 +227,12 @@ impl Network { } } + // Apply best effort coalesced detection results + for (_, dr) in detection_results { + // Import the dialinfo + self.update_with_detection_result(&mut editor, &inbound_protocol_map, dr); + } + // See if we have any discovery contexts that did not complete for a // particular protocol type if its external address type was supported. let mut success = true; diff --git a/veilid-core/src/network_manager/send_data.rs b/veilid-core/src/network_manager/send_data.rs index 32d9fbe3..dc1b2e5e 100644 --- a/veilid-core/src/network_manager/send_data.rs +++ b/veilid-core/src/network_manager/send_data.rs @@ -397,9 +397,6 @@ impl NetworkManager { })) } - /// Figure out how to reach a node from our own node over the best routing domain and reference the nodes we want to access - /// Uses NodeRefs to ensure nodes are referenced, this is not a part of 'RoutingTable' because RoutingTable is not - /// allowed to use NodeRefs due to recursive locking #[instrument(level = "trace", target = "net", skip_all, err)] pub fn get_node_contact_method( &self, @@ -430,48 +427,76 @@ impl NetworkManager { let peer_a = routing_table.get_current_peer_info(routing_domain); let own_node_info_ts = peer_a.signed_node_info().timestamp(); - // Peer B is the target node, get just the timestamp for the cache check - let target_node_info_ts = match target_node_ref.operate(|_rti, e| { - e.signed_node_info(routing_domain) - .map(|sni| sni.timestamp()) - }) { - Some(ts) => ts, - None => { - log_net!( - "no node info for node {:?} in {:?}", - target_node_ref, - routing_domain - ); - return Ok(NodeContactMethod::Unreachable); + // Peer B is the target node, get the whole peer info now + let Some(peer_b) = target_node_ref.get_peer_info(routing_domain) else { + log_net!("no node info for node {:?}", target_node_ref); + return Ok(NodeContactMethod::Unreachable); + }; + + // Calculate the dial info failures map + let address_filter = self.address_filter(); + let dial_info_failures_map = { + let mut dial_info_failures_map = BTreeMap::::new(); + for did in peer_b + .signed_node_info() + .node_info() + .filtered_dial_info_details(DialInfoDetail::NO_SORT, &|_| true) + { + if let Some(ts) = address_filter.get_dial_info_failed_ts(&did.dial_info) { + dial_info_failures_map.insert(did.dial_info, ts); + } } + dial_info_failures_map }; // Get cache key - let mut ncm_key = NodeContactMethodCacheKey { + let ncm_key = NodeContactMethodCacheKey { node_ids: target_node_ref.node_ids(), own_node_info_ts, - target_node_info_ts, + target_node_info_ts: peer_b.signed_node_info().timestamp(), target_node_ref_filter: target_node_ref.filter(), target_node_ref_sequencing: target_node_ref.sequencing(), + dial_info_failures_map, }; if let Some(ncm) = self.inner.lock().node_contact_method_cache.get(&ncm_key) { return Ok(ncm.clone()); } - // Peer B is the target node, get the whole peer info now - let peer_b = match target_node_ref.make_peer_info(routing_domain) { - Some(pi) => Arc::new(pi), - None => { - log_net!("no node info for node {:?}", target_node_ref); - return Ok(NodeContactMethod::Unreachable); - } - }; - // Update the key's timestamp to ensure we avoid any race conditions - ncm_key.target_node_info_ts = peer_b.signed_node_info().timestamp(); + // Calculate the node contact method + let routing_table = self.routing_table(); + let ncm = Self::get_node_contact_method_uncached( + &routing_table, + routing_domain, + target_node_ref, + peer_a, + peer_b, + &ncm_key, + )?; + // Cache this + self.inner + .lock() + .node_contact_method_cache + .insert(ncm_key, ncm.clone()); + + Ok(ncm) + } + + /// Figure out how to reach a node from our own node over the best routing domain and reference the nodes we want to access + /// Uses NodeRefs to ensure nodes are referenced, this is not a part of 'RoutingTable' because RoutingTable is not + /// allowed to use NodeRefs due to recursive locking + #[instrument(level = "trace", target = "net", skip_all, err)] + fn get_node_contact_method_uncached( + routing_table: &RoutingTable, + routing_domain: RoutingDomain, + target_node_ref: FilteredNodeRef, + peer_a: Arc, + peer_b: Arc, + ncm_key: &NodeContactMethodCacheKey, + ) -> EyreResult { // Dial info filter comes from the target node ref but must be filtered by this node's outbound capabilities let dial_info_filter = target_node_ref.dial_info_filter().filtered( - &DialInfoFilter::all() + DialInfoFilter::all() .with_address_type_set(peer_a.signed_node_info().node_info().address_types()) .with_protocol_type_set(peer_a.signed_node_info().node_info().outbound_protocols()), ); @@ -487,26 +512,17 @@ impl NetworkManager { // } // Deprioritize dial info that have recently failed - let address_filter = self.address_filter(); - let mut dial_info_failures_map = BTreeMap::::new(); - for did in peer_b - .signed_node_info() - .node_info() - .filtered_dial_info_details(DialInfoDetail::NO_SORT, |_| true) - { - if let Some(ts) = address_filter.get_dial_info_failed_ts(&did.dial_info) { - dial_info_failures_map.insert(did.dial_info, ts); - } - } - let dif_sort: Option> = if dial_info_failures_map.is_empty() { + let dif_sort: Option = if ncm_key.dial_info_failures_map.is_empty() { None } else { - Some(Arc::new(move |a: &DialInfoDetail, b: &DialInfoDetail| { - let ats = dial_info_failures_map + Some(Box::new(|a: &DialInfoDetail, b: &DialInfoDetail| { + let ats = ncm_key + .dial_info_failures_map .get(&a.dial_info) .copied() .unwrap_or_default(); - let bts = dial_info_failures_map + let bts = ncm_key + .dial_info_failures_map .get(&b.dial_info) .copied() .unwrap_or_default(); @@ -521,7 +537,7 @@ impl NetworkManager { peer_b.clone(), dial_info_filter, sequencing, - dif_sort, + dif_sort.as_ref(), ); // Translate the raw contact method to a referenced contact method @@ -549,7 +565,7 @@ impl NetworkManager { let tighten = peer_a .signed_node_info() .node_info() - .filtered_dial_info_details(DialInfoDetail::NO_SORT, |did| { + .filtered_dial_info_details(DialInfoDetail::NO_SORT, &|did| { did.matches_filter(&dial_info_filter) }) .iter() @@ -634,11 +650,6 @@ impl NetworkManager { } }; - // Cache this - self.inner - .lock() - .node_contact_method_cache - .insert(ncm_key, ncm.clone()); Ok(ncm) } @@ -806,10 +817,13 @@ impl NetworkManager { // punch should come through and create a real 'last connection' for us if this succeeds network_result_try!( self.net() - .send_hole_punch(hole_punch_did.dial_info.clone()) + .send_data_to_dial_info(hole_punch_did.dial_info.clone(), Vec::new()) .await? ); + // Add small delay to encourage packets to be delivered in order + sleep(HOLE_PUNCH_DELAY_MS).await; + // Issue the signal let rpc = self.rpc_processor(); network_result_try!(rpc @@ -823,6 +837,13 @@ impl NetworkManager { .await .wrap_err("failed to send signal")?); + // Another hole punch after the signal for UDP redundancy + network_result_try!( + self.net() + .send_data_to_dial_info(hole_punch_did.dial_info, Vec::new()) + .await? + ); + // Wait for the return receipt let inbound_nr = match eventual_value .timeout_at(stop_token) diff --git a/veilid-core/src/network_manager/types/address.rs b/veilid-core/src/network_manager/types/address.rs index 0091f08e..e6f113be 100644 --- a/veilid-core/src/network_manager/types/address.rs +++ b/veilid-core/src/network_manager/types/address.rs @@ -72,7 +72,7 @@ impl Address { pub fn canonical(&self) -> Address { match self { Address::IPV4(v4) => Address::IPV4(*v4), - Address::IPV6(v6) => match v6.to_ipv4() { + Address::IPV6(v6) => match v6.to_ipv4_mapped() { Some(v4) => Address::IPV4(v4), None => Address::IPV6(*v6), }, diff --git a/veilid-core/src/network_manager/types/dial_info_filter.rs b/veilid-core/src/network_manager/types/dial_info_filter.rs index e3621950..3a312511 100644 --- a/veilid-core/src/network_manager/types/dial_info_filter.rs +++ b/veilid-core/src/network_manager/types/dial_info_filter.rs @@ -38,7 +38,7 @@ impl DialInfoFilter { self.address_type_set = address_set; self } - pub fn filtered(mut self, other_dif: &DialInfoFilter) -> Self { + pub fn filtered(mut self, other_dif: DialInfoFilter) -> Self { self.protocol_type_set &= other_dif.protocol_type_set; self.address_type_set &= other_dif.address_type_set; self @@ -54,7 +54,7 @@ impl DialInfoFilter { Sequencing::EnsureOrdered => ( true, self.filtered( - &DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()), + DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()), ), ), } diff --git a/veilid-core/src/network_manager/wasm/mod.rs b/veilid-core/src/network_manager/wasm/mod.rs index 3780ce60..2fd6ab41 100644 --- a/veilid-core/src/network_manager/wasm/mod.rs +++ b/veilid-core/src/network_manager/wasm/mod.rs @@ -336,6 +336,18 @@ impl Network { .await } + // Send hole punch attempt to a specific dialinfo. May not be appropriate for all protocols. + // Returns a flow for the connection used to send the data + #[instrument(level = "trace", target = "net", err, skip(self))] + pub async fn send_hole_punch( + &self, + dial_info: DialInfo, + ) -> EyreResult> { + return Ok(NetworkResult::ServiceUnavailable( + "unimplemented for this platform".to_owned(), + )); + } + ///////////////////////////////////////////////////////////////// pub async fn startup_internal(&self) -> EyreResult { diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index a3c8ef32..4c02acb3 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -182,6 +182,9 @@ pub(crate) struct BucketEntryInner { local_network: BucketEntryLocalNetwork, /// Statistics gathered for the peer peer_stats: PeerStats, + /// The peer info cache for speedy access to fully encapsulated per-routing-domain peer info + #[serde(skip)] + peer_info_cache: Mutex>>>, /// The accounting for the latency statistics #[serde(skip)] latency_stats_accounting: LatencyStatsAccounting, @@ -277,7 +280,7 @@ impl BucketEntryInner { /// Add a node id for a particular crypto kind. /// Returns Ok(Some(node)) any previous existing node id associated with that crypto kind - /// Returns Ok(None) if no previous existing node id was associated with that crypto kind + /// Returns Ok(None) if no previous existing node id was associated with that crypto kind, or one existed but nothing changed. /// Results Err() if this operation would add more crypto kinds than we support pub fn add_node_id(&mut self, node_id: TypedKey) -> EyreResult> { let total_node_id_count = self.validated_node_ids.len() + self.unsupported_node_ids.len(); @@ -292,8 +295,13 @@ impl BucketEntryInner { if old_node_id == node_id { return Ok(None); } - // Won't change number of crypto kinds + // Won't change number of crypto kinds, but the node id changed node_ids.add(node_id); + + // Also clear the peerinfo cache since the node ids changed + let mut pi_cache = self.peer_info_cache.lock(); + pi_cache.clear(); + return Ok(Some(old_node_id)); } // Check to ensure we aren't adding more crypto kinds than we support @@ -301,6 +309,11 @@ impl BucketEntryInner { bail!("too many crypto kinds for this node"); } node_ids.add(node_id); + + // Also clear the peerinfo cache since the node ids changed + let mut pi_cache = self.peer_info_cache.lock(); + pi_cache.clear(); + Ok(None) } @@ -314,7 +327,14 @@ impl BucketEntryInner { &mut self.unsupported_node_ids }; - node_ids.remove(crypto_kind) + let opt_dead_id = node_ids.remove(crypto_kind); + if opt_dead_id.is_some() { + // Also clear the peerinfo cache since the node ids changed + let mut pi_cache = self.peer_info_cache.lock(); + pi_cache.clear(); + } + + opt_dead_id } pub fn best_node_id(&self) -> TypedKey { @@ -413,9 +433,6 @@ impl BucketEntryInner { move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2) } - // xxx: if we ever implement a 'remove_signed_node_info' to take nodes out of a routing domain - // then we need to call 'on_entry_node_info_updated' with that removal. as of right now - // this never happens, because we only have one routing domain implemented. pub fn update_signed_node_info( &mut self, routing_domain: RoutingDomain, @@ -429,7 +446,10 @@ impl BucketEntryInner { // See if we have an existing signed_node_info to update or not let mut node_info_changed = false; + let mut had_previous_node_info = false; if let Some(current_sni) = opt_current_sni { + had_previous_node_info = true; + // Always allow overwriting unsigned node (bootstrap) if current_sni.has_any_signature() { // If the timestamp hasn't changed or is less, ignore this update @@ -480,6 +500,12 @@ impl BucketEntryInner { self.clear_last_flows_except_latest(); } + // Clear the peerinfo cache since the node info changed or was added + if node_info_changed || !had_previous_node_info { + let mut pi_cache = self.peer_info_cache.lock(); + pi_cache.remove(&routing_domain); + } + node_info_changed } @@ -535,16 +561,29 @@ impl BucketEntryInner { opt_current_sni.as_ref().map(|s| s.as_ref()) } - pub fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option { + pub fn get_peer_info(&self, routing_domain: RoutingDomain) -> Option> { + // Return cached peer info if we have it + let mut pi_cache = self.peer_info_cache.lock(); + if let Some(opt_pi) = pi_cache.get(&routing_domain).cloned() { + return opt_pi; + } + + // Create a new peerinfo let opt_current_sni = match routing_domain { RoutingDomain::LocalNetwork => &self.local_network.signed_node_info, RoutingDomain::PublicInternet => &self.public_internet.signed_node_info, }; // Peer info includes all node ids, even unvalidated ones let node_ids = self.node_ids(); - opt_current_sni + let opt_pi = opt_current_sni .as_ref() - .map(|s| PeerInfo::new(routing_domain, node_ids, *s.clone())) + .map(|s| Arc::new(PeerInfo::new(routing_domain, node_ids, *s.clone()))); + + // Cache the peerinfo + pi_cache.insert(routing_domain, opt_pi.clone()); + + // Return the peerinfo + opt_pi } pub fn best_routing_domain( @@ -1138,6 +1177,7 @@ impl BucketEntry { transfer: TransferStatsDownUp::default(), state: StateStats::default(), }, + peer_info_cache: Mutex::new(BTreeMap::new()), latency_stats_accounting: LatencyStatsAccounting::new(), transfer_stats_accounting: TransferStatsAccounting::new(), state_stats_accounting: Mutex::new(StateStatsAccounting::new()), diff --git a/veilid-core/src/routing_table/find_peers.rs b/veilid-core/src/routing_table/find_peers.rs index 71b3b073..48bfa05c 100644 --- a/veilid-core/src/routing_table/find_peers.rs +++ b/veilid-core/src/routing_table/find_peers.rs @@ -133,9 +133,9 @@ impl RoutingTable { filters, // transform |rti, entry| { - entry.unwrap().with(rti, |_rti, e| { - Arc::new(e.make_peer_info(routing_domain).unwrap()) - }) + entry + .unwrap() + .with(rti, |_rti, e| e.get_peer_info(routing_domain).unwrap()) }, ) { Ok(v) => v, @@ -187,8 +187,8 @@ impl RoutingTable { if d_far < d_near { let warning = format!( r#"peer: {} -near (key): {} -far (self): {} +near (key): {} +far (self): {} d_near: {} d_far: {} cmp: {:?}"#, diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 758a7403..0cb17dba 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -552,7 +552,7 @@ impl RoutingTable { peer_b: Arc, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option>, + dif_sort: Option<&DialInfoDetailSort>, ) -> ContactMethod { self.inner.read().get_contact_method( routing_domain, @@ -586,7 +586,7 @@ impl RoutingTable { /// Return the domain's currently registered network class #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), expect(dead_code))] - pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option { + pub fn get_network_class(&self, routing_domain: RoutingDomain) -> NetworkClass { self.inner.read().get_network_class(routing_domain) } @@ -796,7 +796,7 @@ impl RoutingTable { e.with(rti, |_rti, e| { if let Some(ni) = e.node_info(routing_domain) { if ni - .first_filtered_dial_info_detail(DialInfoDetail::NO_SORT, |did| { + .first_filtered_dial_info_detail(DialInfoDetail::NO_SORT, &|did| { did.matches_filter(&dial_info_filter) }) .is_some() diff --git a/veilid-core/src/routing_table/node_ref/filtered_node_ref.rs b/veilid-core/src/routing_table/node_ref/filtered_node_ref.rs index a6c21e07..353d4405 100644 --- a/veilid-core/src/routing_table/node_ref/filtered_node_ref.rs +++ b/veilid-core/src/routing_table/node_ref/filtered_node_ref.rs @@ -63,7 +63,7 @@ impl FilteredNodeRef { } pub fn merge_filter(&mut self, filter: NodeRefFilter) { - self.filter = self.filter.filtered(&filter); + self.filter = self.filter.filtered(filter); } pub fn set_sequencing(&mut self, sequencing: Sequencing) { diff --git a/veilid-core/src/routing_table/node_ref/node_ref_filter.rs b/veilid-core/src/routing_table/node_ref/node_ref_filter.rs index 94ced1ae..322a048a 100644 --- a/veilid-core/src/routing_table/node_ref/node_ref_filter.rs +++ b/veilid-core/src/routing_table/node_ref/node_ref_filter.rs @@ -49,11 +49,11 @@ impl NodeRefFilter { self.dial_info_filter = self.dial_info_filter.with_address_type_set(address_set); self } - pub fn filtered(mut self, other_filter: &NodeRefFilter) -> Self { + pub fn filtered(mut self, other_filter: NodeRefFilter) -> Self { self.routing_domain_set &= other_filter.routing_domain_set; self.dial_info_filter = self .dial_info_filter - .filtered(&other_filter.dial_info_filter); + .filtered(other_filter.dial_info_filter); self } #[expect(dead_code)] diff --git a/veilid-core/src/routing_table/node_ref/traits.rs b/veilid-core/src/routing_table/node_ref/traits.rs index e18d54e4..22fceac9 100644 --- a/veilid-core/src/routing_table/node_ref/traits.rs +++ b/veilid-core/src/routing_table/node_ref/traits.rs @@ -70,8 +70,8 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait self.operate(|_rti, e| e.peer_stats().clone()) } - fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option { - self.operate(|_rti, e| e.make_peer_info(routing_domain)) + fn get_peer_info(&self, routing_domain: RoutingDomain) -> Option> { + self.operate(|_rti, e| e.get_peer_info(routing_domain)) } fn node_info(&self, routing_domain: RoutingDomain) -> Option { self.operate(|_rti, e| e.node_info(routing_domain).cloned()) @@ -150,7 +150,7 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait self.operate(|_rt, e| { for routing_domain in routing_domain_set { if let Some(ni) = e.node_info(routing_domain) { - if let Some(did) = ni.first_filtered_dial_info_detail(sort, filter) { + if let Some(did) = ni.first_filtered_dial_info_detail(sort.as_ref(), &filter) { return Some(did); } } @@ -177,7 +177,7 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait self.operate(|_rt, e| { for routing_domain in routing_domain_set { if let Some(ni) = e.node_info(routing_domain) { - let mut dids = ni.filtered_dial_info_details(sort, filter); + let mut dids = ni.filtered_dial_info_details(sort.as_ref(), &filter); out.append(&mut dids); } } diff --git a/veilid-core/src/routing_table/route_spec_store/mod.rs b/veilid-core/src/routing_table/route_spec_store/mod.rs index 568c1964..1b5d00b7 100644 --- a/veilid-core/src/routing_table/route_spec_store/mod.rs +++ b/veilid-core/src/routing_table/route_spec_store/mod.rs @@ -493,11 +493,9 @@ impl RouteSpecStore { let nodes_pi: Vec> = nodes .iter() .map(|nr| { - Arc::new( - nr.locked(rti) - .make_peer_info(RoutingDomain::PublicInternet) - .unwrap(), - ) + nr.locked(rti) + .get_peer_info(RoutingDomain::PublicInternet) + .unwrap() }) .collect(); @@ -1260,14 +1258,14 @@ impl RouteSpecStore { let pi = rti .with_node_entry(node_id, |entry| { entry.with(rti, |_rti, e| { - e.make_peer_info(RoutingDomain::PublicInternet) + e.get_peer_info(RoutingDomain::PublicInternet) }) }) .flatten(); if pi.is_none() { apibail_internal!("peer info should exist for route but doesn't"); } - RouteNode::PeerInfo(Arc::new(pi.unwrap())) + RouteNode::PeerInfo(pi.unwrap()) }, next_hop: Some(route_hop_data), }; @@ -1504,14 +1502,14 @@ impl RouteSpecStore { let pi = rti .with_node_entry(node_id, |entry| { entry.with(rti, |_rti, e| { - e.make_peer_info(RoutingDomain::PublicInternet) + e.get_peer_info(RoutingDomain::PublicInternet) }) }) .flatten(); if pi.is_none() { apibail_internal!("peer info should exist for route but doesn't"); } - RouteNode::PeerInfo(Arc::new(pi.unwrap())) + RouteNode::PeerInfo(pi.unwrap()) }, next_hop: Some(route_hop_data), } diff --git a/veilid-core/src/routing_table/routing_table_inner/mod.rs b/veilid-core/src/routing_table/routing_table_inner/mod.rs index c9898ed3..a7ca5982 100644 --- a/veilid-core/src/routing_table/routing_table_inner/mod.rs +++ b/veilid-core/src/routing_table/routing_table_inner/mod.rs @@ -219,7 +219,7 @@ impl RoutingTableInner { peer_b: Arc, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option>, + dif_sort: Option<&DialInfoDetailSort>, ) -> ContactMethod { self.with_routing_domain(routing_domain, |rdd| { rdd.get_contact_method(self, peer_a, peer_b, dial_info_filter, sequencing, dif_sort) @@ -256,7 +256,7 @@ impl RoutingTableInner { } /// Return the domain's currently registered network class - pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option { + pub fn get_network_class(&self, routing_domain: RoutingDomain) -> NetworkClass { self.with_routing_domain(routing_domain, |rdd| rdd.network_class()) } @@ -624,7 +624,7 @@ impl RoutingTableInner { // New node id, get the old peer info if we don't have it yet if old_peer_infos.is_empty() { for rd in RoutingDomainSet::all() { - if let Some(old_peer_info) = e.make_peer_info(rd) { + if let Some(old_peer_info) = e.get_peer_info(rd) { old_peer_infos.push(old_peer_info); } } @@ -667,7 +667,7 @@ impl RoutingTableInner { if !old_peer_infos.is_empty() { let mut new_peer_infos = vec![]; for rd in RoutingDomainSet::all() { - if let Some(new_peer_info) = e.make_peer_info(rd) { + if let Some(new_peer_info) = e.get_peer_info(rd) { new_peer_infos.push(new_peer_info); } } @@ -895,13 +895,13 @@ impl RoutingTableInner { let mut updated = false; let mut old_peer_info = None; let nr = self.create_node_ref(&node_ids, |_rti, e| { - old_peer_info = e.make_peer_info(routing_domain); + old_peer_info = e.get_peer_info(routing_domain); updated = e.update_signed_node_info(routing_domain, &signed_node_info); })?; // Process any new or updated PeerInfo if old_peer_info.is_none() || updated { - let new_peer_info = nr.locked(self).make_peer_info(routing_domain); + let new_peer_info = nr.locked(self).get_peer_info(routing_domain); self.on_entry_peer_info_updated(old_peer_info, new_peer_info); } @@ -940,8 +940,8 @@ impl RoutingTableInner { /// 2. nodes are removed that don't have any peer info fn on_entry_peer_info_updated( &mut self, - old_peer_info: Option, - new_peer_info: Option, + old_peer_info: Option>, + new_peer_info: Option>, ) { let (routing_domain, node_ids) = match (old_peer_info.as_ref(), new_peer_info.as_ref()) { (None, None) => { @@ -1114,9 +1114,7 @@ impl RoutingTableInner { ) -> Arc { match entry { None => own_peer_info.clone(), - Some(entry) => { - entry.with_inner(|e| Arc::new(e.make_peer_info(routing_domain).unwrap())) - } + Some(entry) => entry.with_inner(|e| e.get_peer_info(routing_domain).unwrap()), } } diff --git a/veilid-core/src/routing_table/routing_table_inner/routing_domains/local_network/mod.rs b/veilid-core/src/routing_table/routing_table_inner/routing_domains/local_network/mod.rs index 98305320..1726c42e 100644 --- a/veilid-core/src/routing_table/routing_table_inner/routing_domains/local_network/mod.rs +++ b/veilid-core/src/routing_table/routing_table_inner/routing_domains/local_network/mod.rs @@ -55,7 +55,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { RoutingDomain::LocalNetwork } - fn network_class(&self) -> Option { + fn network_class(&self) -> NetworkClass { self.common.network_class() } fn outbound_protocols(&self) -> ProtocolTypeSet { @@ -67,11 +67,14 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { fn address_types(&self) -> AddressTypeSet { self.common.address_types() } + fn compatible_address_types(&self) -> AddressTypeSet { + AddressType::IPV4 | AddressType::IPV6 + } fn capabilities(&self) -> Vec { self.common.capabilities() } fn requires_relay(&self) -> Option { - self.common.requires_relay() + self.common.requires_relay(self.compatible_address_types()) } fn relay_node(&self) -> Option { self.common.relay_node() @@ -182,7 +185,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { peer_b: Arc, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option>, + dif_sort: Option<&DialInfoDetailSort>, ) -> ContactMethod { // Get the nodeinfos for convenience let node_a = peer_a.signed_node_info().node_info(); @@ -198,7 +201,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { if let Some(target_did) = first_filtered_dial_info_detail_between_nodes( node_a, node_b, - &dial_info_filter, + dial_info_filter, sequencing, dif_sort, ) { diff --git a/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs b/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs index f5050003..4e46e160 100644 --- a/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs +++ b/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs @@ -13,10 +13,11 @@ pub trait RoutingDomainDetail { // Common accessors #[expect(dead_code)] fn routing_domain(&self) -> RoutingDomain; - fn network_class(&self) -> Option; + fn network_class(&self) -> NetworkClass; fn outbound_protocols(&self) -> ProtocolTypeSet; fn inbound_protocols(&self) -> ProtocolTypeSet; fn address_types(&self) -> AddressTypeSet; + fn compatible_address_types(&self) -> AddressTypeSet; fn capabilities(&self) -> Vec; fn requires_relay(&self) -> Option; fn relay_node(&self) -> Option; @@ -47,7 +48,7 @@ pub trait RoutingDomainDetail { peer_b: Arc, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option>, + dif_sort: Option<&DialInfoDetailSort>, ) -> ContactMethod; // Set last relay keepalive time @@ -63,13 +64,13 @@ trait RoutingDomainDetailCommonAccessors: RoutingDomainDetail { fn first_filtered_dial_info_detail_between_nodes( from_node: &NodeInfo, to_node: &NodeInfo, - dial_info_filter: &DialInfoFilter, + dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option>, + dif_sort: Option<&DialInfoDetailSort>, ) -> Option { // Consider outbound capabilities - let dial_info_filter = (*dial_info_filter).filtered( - &DialInfoFilter::all() + let dial_info_filter = dial_info_filter.filtered( + DialInfoFilter::all() .with_address_type_set(from_node.address_types()) .with_protocol_type_set(from_node.outbound_protocols()), ); @@ -79,9 +80,9 @@ fn first_filtered_dial_info_detail_between_nodes( // based on an external preference table, for example the one kept by // AddressFilter to deprioritize dialinfo that have recently failed to connect let (ordered, dial_info_filter) = dial_info_filter.apply_sequencing(sequencing); - let sort: Option> = if ordered { + let sort: Option = if ordered { if let Some(dif_sort) = dif_sort { - Some(Box::new(move |a, b| { + Some(Box::new(|a, b| { let mut ord = dif_sort(a, b); if ord == core::cmp::Ordering::Equal { ord = DialInfoDetail::ordered_sequencing_sort(a, b); @@ -89,12 +90,12 @@ fn first_filtered_dial_info_detail_between_nodes( ord })) } else { - Some(Box::new(move |a, b| { + Some(Box::new(|a, b| { DialInfoDetail::ordered_sequencing_sort(a, b) })) } } else if let Some(dif_sort) = dif_sort { - Some(Box::new(move |a, b| dif_sort(a, b))) + Some(Box::new(|a, b| dif_sort(a, b))) } else { None }; @@ -106,7 +107,7 @@ fn first_filtered_dial_info_detail_between_nodes( // Get the best match dial info for node B if we have it let direct_filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter); - to_node.first_filtered_dial_info_detail(sort, direct_filter) + to_node.first_filtered_dial_info_detail(sort.as_ref(), &direct_filter) } #[derive(Debug)] @@ -141,22 +142,18 @@ impl RoutingDomainDetailCommon { /////////////////////////////////////////////////////////////////////// // Accessors - pub fn network_class(&self) -> Option { + pub fn network_class(&self) -> NetworkClass { cfg_if! { if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { - Some(NetworkClass::WebApp) + NetworkClass::WebApp } else { if self.address_types.is_empty() { - None + NetworkClass::Invalid } else if self.dial_info_details.is_empty() { - if self.relay_node.is_none() { - None - } else { - Some(NetworkClass::OutboundOnly) - } + NetworkClass::OutboundOnly } else { - Some(NetworkClass::InboundCapable) + NetworkClass::InboundCapable } } } @@ -178,23 +175,30 @@ impl RoutingDomainDetailCommon { self.capabilities.clone() } - pub fn requires_relay(&self) -> Option { - match self.network_class()? { + pub fn requires_relay(&self, compatible_address_types: AddressTypeSet) -> Option { + match self.network_class() { NetworkClass::InboundCapable => { let mut all_inbound_set: HashSet<(ProtocolType, AddressType)> = HashSet::new(); + let mut address_types = AddressTypeSet::empty(); for p in self.inbound_protocols { for a in self.address_types { all_inbound_set.insert((p, a)); } } for did in &self.dial_info_details { + // Request an inbound relay if any of our dialinfo require one if did.class.requires_relay() { return Some(RelayKind::Inbound); } let ib = (did.dial_info.protocol_type(), did.dial_info.address_type()); all_inbound_set.remove(&ib); + address_types |= did.dial_info.address_type(); } - if !all_inbound_set.is_empty() { + + // Request an inbound relay if any of our inbound protocols do not have dialinfo for all address types + // we want to support, or if this routing domain doesn't support the full range of compatible address types + // for the routing domain + if !all_inbound_set.is_empty() || address_types != compatible_address_types { return Some(RelayKind::Inbound); } } @@ -316,7 +320,7 @@ impl RoutingDomainDetailCommon { let routing_table = rti.routing_table(); let node_info = NodeInfo::new( - self.network_class().unwrap_or(NetworkClass::Invalid), + self.network_class(), self.outbound_protocols, self.address_types, VALID_ENVELOPE_VERSIONS.to_vec(), @@ -326,9 +330,10 @@ impl RoutingDomainDetailCommon { ); let relay_info = if let Some(rn) = &self.relay_node { - let opt_relay_pi = rn.locked(rti).make_peer_info(self.routing_domain); + let opt_relay_pi = rn.locked(rti).get_peer_info(self.routing_domain); if let Some(relay_pi) = opt_relay_pi { - let (_routing_domain, relay_ids, relay_sni) = relay_pi.destructure(); + let (_routing_domain, relay_ids, relay_sni) = + relay_pi.as_ref().clone().destructure(); match relay_sni { SignedNodeInfo::Direct(d) => Some((relay_ids, d)), SignedNodeInfo::Relayed(_) => { diff --git a/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs b/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs index 261f845c..09c64fc5 100644 --- a/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs +++ b/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs @@ -36,7 +36,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { RoutingDomain::PublicInternet } - fn network_class(&self) -> Option { + fn network_class(&self) -> NetworkClass { self.common.network_class() } fn outbound_protocols(&self) -> ProtocolTypeSet { @@ -48,11 +48,14 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { fn address_types(&self) -> AddressTypeSet { self.common.address_types() } + fn compatible_address_types(&self) -> AddressTypeSet { + AddressType::IPV4 | AddressType::IPV6 + } fn capabilities(&self) -> Vec { self.common.capabilities() } fn requires_relay(&self) -> Option { - self.common.requires_relay() + self.common.requires_relay(self.compatible_address_types()) } fn relay_node(&self) -> Option { self.common.relay_node() @@ -160,7 +163,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { peer_b: Arc, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option>, + dif_sort: Option<&DialInfoDetailSort>, ) -> ContactMethod { let ip6_prefix_size = rti .config() @@ -191,9 +194,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { first_filtered_dial_info_detail_between_nodes( node_a, node_b, - &dial_info_filter, + dial_info_filter, sequencing, - dif_sort.clone(), + dif_sort, ) }) .flatten() @@ -228,9 +231,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { if first_filtered_dial_info_detail_between_nodes( node_a, node_b_relay, - &dial_info_filter, + dial_info_filter, sequencing, - dif_sort.clone(), + dif_sort, ) .is_some() { @@ -242,9 +245,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { if let Some(reverse_did) = first_filtered_dial_info_detail_between_nodes( node_b, node_a, - &dial_info_filter, + dial_info_filter, sequencing, - dif_sort.clone(), + dif_sort, ) { // Ensure we aren't on the same public IP address (no hairpin nat) if reverse_did.dial_info.ip_addr() != target_did.dial_info.ip_addr() { @@ -262,22 +265,22 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { // Does node B have a direct udp dialinfo node A can reach? let udp_dial_info_filter = dial_info_filter - .filtered(&DialInfoFilter::all().with_protocol_type(ProtocolType::UDP)); + .filtered(DialInfoFilter::all().with_protocol_type(ProtocolType::UDP)); if let Some(target_udp_did) = first_filtered_dial_info_detail_between_nodes( node_a, node_b, - &udp_dial_info_filter, + udp_dial_info_filter, sequencing, - dif_sort.clone(), + dif_sort, ) { // Does node A have a direct udp dialinfo that node B can reach? if let Some(reverse_udp_did) = first_filtered_dial_info_detail_between_nodes( node_b, node_a, - &udp_dial_info_filter, + udp_dial_info_filter, sequencing, - dif_sort.clone(), + dif_sort, ) { // Ensure we aren't on the same public IP address (no hairpin nat) @@ -322,9 +325,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { if first_filtered_dial_info_detail_between_nodes( node_a, node_b_relay, - &dial_info_filter, + dial_info_filter, sequencing, - dif_sort.clone(), + dif_sort, ) .is_some() { @@ -337,9 +340,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { first_filtered_dial_info_detail_between_nodes( node_b, node_a, - &dial_info_filter, + dial_info_filter, sequencing, - dif_sort.clone(), + dif_sort, ) }) .flatten() diff --git a/veilid-core/src/routing_table/tasks/bootstrap.rs b/veilid-core/src/routing_table/tasks/bootstrap.rs index c5f30b2f..fe58cff3 100644 --- a/veilid-core/src/routing_table/tasks/bootstrap.rs +++ b/veilid-core/src/routing_table/tasks/bootstrap.rs @@ -330,12 +330,12 @@ impl RoutingTable { peers: Vec>, stop_token: StopToken, ) -> EyreResult<()> { - log_rtab!(debug " bootstrapped peers: {:?}", &peers); + log_rtab!(debug " bootstrap peers: {:?}", &peers); // Get crypto kinds to bootstrap let crypto_kinds = self.get_bootstrap_crypto_kinds(); - log_rtab!(debug " bootstrapped crypto kinds: {:?}", &crypto_kinds); + log_rtab!(debug " bootstrap crypto kinds: {:?}", &crypto_kinds); // Run all bootstrap operations concurrently let mut unord = FuturesUnordered::>::new(); diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index 9d35d911..9dcfb733 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -6,14 +6,8 @@ impl RoutingTable { fn public_internet_wants_relay(&self) -> Option { let own_peer_info = self.get_current_peer_info(RoutingDomain::PublicInternet); let own_node_info = own_peer_info.signed_node_info().node_info(); - let network_class = own_node_info.network_class(); - // Never give a relay to something with an invalid network class - if matches!(network_class, NetworkClass::Invalid) { - return None; - } - - // If we -need- a relay always request one + // If we need a relay, always request one let requires_relay = self .inner .read() @@ -22,16 +16,6 @@ impl RoutingTable { return Some(rk); } - // If we don't always need a relay, but we don't have support for - // all the address types then we should request one anyway - let mut address_types = AddressTypeSet::empty(); - for did in own_node_info.dial_info_detail_list() { - address_types |= did.dial_info.address_type(); - } - if address_types != AddressTypeSet::all() { - return Some(RelayKind::Inbound); - } - // If we are behind some NAT, then we should get ourselves a relay just // in case we need to navigate hairpin NAT to our own network let mut inbound_addresses = HashSet::::new(); @@ -200,7 +184,7 @@ impl RoutingTable { // Disqualify nodes that don't cover all our inbound ports for tcp and udp // as we need to be able to use the relay for keepalives for all nat mappings let mut low_level_protocol_ports = mapped_port_info.low_level_protocol_ports.clone(); - let dids = node_info.filtered_dial_info_details(DialInfoDetail::NO_SORT, |did| { + let dids = node_info.filtered_dial_info_details(DialInfoDetail::NO_SORT, &|did| { did.matches_filter(&outbound_dif) }); for did in &dids { diff --git a/veilid-core/src/routing_table/types/dial_info_detail.rs b/veilid-core/src/routing_table/types/dial_info_detail.rs index b16e685b..821e713b 100644 --- a/veilid-core/src/routing_table/types/dial_info_detail.rs +++ b/veilid-core/src/routing_table/types/dial_info_detail.rs @@ -19,7 +19,8 @@ impl MatchesDialInfoFilter for DialInfoDetail { } } -pub type DialInfoDetailSort = dyn Fn(&DialInfoDetail, &DialInfoDetail) -> core::cmp::Ordering; +pub type DialInfoDetailSort<'a> = + Box core::cmp::Ordering + 'a>; impl DialInfoDetail { pub fn ordered_sequencing_sort(a: &DialInfoDetail, b: &DialInfoDetail) -> core::cmp::Ordering { @@ -29,7 +30,5 @@ impl DialInfoDetail { } a.class.cmp(&b.class) } - pub const NO_SORT: std::option::Option< - for<'r, 's> fn(&'r DialInfoDetail, &'s DialInfoDetail) -> std::cmp::Ordering, - > = None:: core::cmp::Ordering>; + pub const NO_SORT: std::option::Option<&DialInfoDetailSort<'static>> = None::<_>; } diff --git a/veilid-core/src/routing_table/types/node_info.rs b/veilid-core/src/routing_table/types/node_info.rs index 8640cad1..842ef87e 100644 --- a/veilid-core/src/routing_table/types/node_info.rs +++ b/veilid-core/src/routing_table/types/node_info.rs @@ -85,10 +85,10 @@ impl NodeInfo { &self.dial_info_detail_list } - pub fn first_filtered_dial_info_detail( + pub fn first_filtered_dial_info_detail<'a, S, F>( &self, - sort: Option, - filter: F, + sort: Option<&'a S>, + filter: &'a F, ) -> Option where S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, @@ -114,8 +114,8 @@ impl NodeInfo { pub fn filtered_dial_info_details( &self, - sort: Option, - filter: F, + sort: Option<&S>, + filter: &F, ) -> Vec where S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index 9a21d4de..8e6f3e1f 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -738,7 +738,7 @@ impl RPCProcessor { // No private route was specified for the request // but we are using a safety route, so we must create an empty private route // Destination relay is ignored for safety routed operations - let peer_info = match destination_node_ref.make_peer_info(routing_domain) { + let peer_info = match destination_node_ref.get_peer_info(routing_domain) { None => { return Ok(NetworkResult::no_connection_other( "No peer info for stub private route", @@ -748,7 +748,7 @@ impl RPCProcessor { }; let private_route = PrivateRoute::new_stub( destination_node_ref.best_node_id(), - RouteNode::PeerInfo(Arc::new(peer_info)), + RouteNode::PeerInfo(peer_info), ); // Wrap with safety route diff --git a/veilid-flutter/example/pubspec.lock b/veilid-flutter/example/pubspec.lock index 89460f85..1b92be16 100644 --- a/veilid-flutter/example/pubspec.lock +++ b/veilid-flutter/example/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" async_tools: dependency: transitive description: @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" change_case: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" charcode: dependency: transitive description: @@ -61,18 +61,18 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" convert: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" ffi: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" fixnum: dependency: transitive description: @@ -195,18 +195,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -243,10 +243,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -259,18 +259,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" path: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_provider: dependency: "direct main" description: @@ -323,10 +323,10 @@ packages: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -339,10 +339,10 @@ packages: dependency: transitive description: name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" + sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.3" quiver: dependency: transitive description: @@ -360,34 +360,34 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" sync_http: dependency: transitive description: @@ -416,18 +416,18 @@ packages: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" typed_data: dependency: transitive description: @@ -462,10 +462,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.3.1" webdriver: dependency: transitive description: @@ -499,5 +499,5 @@ packages: source: hosted version: "0.0.6" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.24.0" diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index 27089473..1fedb3e5 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -177,8 +177,8 @@ core: public_watch_limit: 32 member_watch_limit: 8 max_watch_expiration_ms: 600000 - upnp: true - detect_address_changes: true + upnp: false + detect_address_changes: false restricted_nat_retries: 0 tls: certificate_path: '%CERTIFICATE_PATH%' @@ -1851,8 +1851,8 @@ mod tests { assert_eq!(s.core.network.dht.member_watch_limit, 8u32); assert_eq!(s.core.network.dht.max_watch_expiration_ms, 600_000u32); // - assert!(s.core.network.upnp); - assert!(s.core.network.detect_address_changes); + assert!(!s.core.network.upnp); + assert!(!s.core.network.detect_address_changes); assert_eq!(s.core.network.restricted_nat_retries, 0u32); // assert_eq!(