Relay debugging

This commit is contained in:
Christien Rioux 2025-02-16 03:03:05 +00:00
parent df573368c3
commit 7ba1c0e3a7
33 changed files with 513 additions and 342 deletions

View File

@ -299,10 +299,14 @@ impl AddressCheck {
flow: Flow, // the flow used flow: Flow, // the flow used
reporting_ipblock: IpAddr, // the IP block this report came from reporting_ipblock: IpAddr, // the IP block this report came from
) -> bool { ) -> bool {
let acckey = // Don't do this if we aren't ever going to use it
AddressCheckCacheKey(routing_domain, flow.protocol_type(), flow.address_type()); if !self.config.detect_address_changes {
return false;
}
// Add the currently seen socket address into the consistency table // Add the currently seen socket address into the consistency table
let acckey =
AddressCheckCacheKey(routing_domain, flow.protocol_type(), flow.address_type());
let cache = self let cache = self
.address_consistency_table .address_consistency_table
.entry(acckey) .entry(acckey)

View File

@ -10,9 +10,9 @@ impl NetworkManager {
let bootstrap_nodes = routing_table.find_bootstrap_nodes_filtered(2); let bootstrap_nodes = routing_table.find_bootstrap_nodes_filtered(2);
// Serialize out peer info // Serialize out peer info
let bootstrap_peerinfo: Vec<PeerInfo> = bootstrap_nodes let bootstrap_peerinfo: Vec<Arc<PeerInfo>> = bootstrap_nodes
.iter() .iter()
.filter_map(|nr| nr.make_peer_info(RoutingDomain::PublicInternet)) .filter_map(|nr| nr.get_peer_info(RoutingDomain::PublicInternet))
.collect(); .collect();
let json_bytes = serialize_json(bootstrap_peerinfo).as_bytes().to_vec(); let json_bytes = serialize_json(bootstrap_peerinfo).as_bytes().to_vec();

View File

@ -107,6 +107,7 @@ struct NodeContactMethodCacheKey {
target_node_info_ts: Timestamp, target_node_info_ts: Timestamp,
target_node_ref_filter: NodeRefFilter, target_node_ref_filter: NodeRefFilter,
target_node_ref_sequencing: Sequencing, target_node_ref_sequencing: Sequencing,
dial_info_failures_map: BTreeMap<DialInfo, Timestamp>,
} }
enum SendDataToExistingFlowResult { enum SendDataToExistingFlowResult {
@ -1179,19 +1180,6 @@ impl NetworkManager {
self.net().restart_network(); 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 // Report peer info changes
fn peer_info_change_event_handler(&self, evt: Arc<PeerInfoChangeEvent>) { fn peer_info_change_event_handler(&self, evt: Arc<PeerInfoChangeEvent>) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();

View File

@ -414,38 +414,49 @@ impl DiscoveryContext {
&self, &self,
unord: &mut FuturesUnordered<SendPinBoxFuture<Option<DetectionResult>>>, unord: &mut FuturesUnordered<SendPinBoxFuture<Option<DetectionResult>>>,
) { ) {
let external_1 = self.inner.lock().external_info.first().cloned().unwrap(); let external_infos = self.inner.lock().external_info.clone();
// Have all the external validator nodes check us
for external_info in external_infos {
let this = self.clone(); let this = self.clone();
let do_no_nat_fut: SendPinBoxFuture<Option<DetectionResult>> = Box::pin(async move { let do_no_nat_fut: SendPinBoxFuture<Option<DetectionResult>> = Box::pin(async move {
// Do a validate_dial_info on the external address from a redirected node // Do a validate_dial_info on the external address from a redirected node
if this if this
.validate_dial_info(external_1.node.clone(), external_1.dial_info.clone(), true) .validate_dial_info(
external_info.node.clone(),
external_info.dial_info.clone(),
true,
)
.await .await
{ {
// Add public dial info with Direct dialinfo class // Add public dial info with Direct dialinfo class
Some(DetectionResult { Some(DetectionResult {
config: this.config, config: this.config,
ddi: DetectedDialInfo::Detected(DialInfoDetail { ddi: DetectedDialInfo::Detected(DialInfoDetail {
dial_info: external_1.dial_info.clone(), dial_info: external_info.dial_info.clone(),
class: DialInfoClass::Direct, class: DialInfoClass::Direct,
}), }),
external_address_types: AddressTypeSet::only(external_1.address.address_type()), external_address_types: AddressTypeSet::only(
external_info.address.address_type(),
),
}) })
} else { } else {
// Add public dial info with Blocked dialinfo class // Add public dial info with Blocked dialinfo class
Some(DetectionResult { Some(DetectionResult {
config: this.config, config: this.config,
ddi: DetectedDialInfo::Detected(DialInfoDetail { ddi: DetectedDialInfo::Detected(DialInfoDetail {
dial_info: external_1.dial_info.clone(), dial_info: external_info.dial_info.clone(),
class: DialInfoClass::Blocked, class: DialInfoClass::Blocked,
}), }),
external_address_types: AddressTypeSet::only(external_1.address.address_type()), external_address_types: AddressTypeSet::only(
external_info.address.address_type(),
),
}) })
} }
}); });
unord.push(do_no_nat_fut); unord.push(do_no_nat_fut);
} }
}
// If we know we are behind NAT check what kind // If we know we are behind NAT check what kind
#[instrument(level = "trace", skip(self), ret)] #[instrument(level = "trace", skip(self), ret)]
@ -725,7 +736,7 @@ impl DiscoveryContext {
// NAT Detection // 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 let local_address_in_external_info = self
.inner .inner
.lock() .lock()

View File

@ -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 UPDATE_NETWORK_CLASS_TASK_TICK_PERIOD_SECS: u32 = 1;
pub const NETWORK_INTERFACES_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; 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 /// 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 /// such as dhcp release or change of address or interfaces being added or removed
network_needs_restart: bool, 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<RoutingDomain, usize>,
/// 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 for all the low level network background tasks
join_handles: Vec<MustJoinHandle<()>>, join_handles: Vec<MustJoinHandle<()>>,
/// stop source for shutting down the low level network background tasks /// stop source for shutting down the low level network background tasks
stop_source: Option<StopSource>, stop_source: Option<StopSource>,
/// set if we need to calculate our public dial info again /// set if we need to calculate our public dial info again
needs_public_dial_info_check: bool, 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 /// the punishment closure to enax
public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>, public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
/// Actual bound addresses per protocol /// Actual bound addresses per protocol
@ -151,8 +155,9 @@ impl Network {
fn new_inner() -> NetworkInner { fn new_inner() -> NetworkInner {
NetworkInner { NetworkInner {
network_needs_restart: false, network_needs_restart: false,
dial_info_failure_count: BTreeMap::new(),
next_outbound_only_dial_info_check: Timestamp::default(),
needs_public_dial_info_check: false, needs_public_dial_info_check: false,
network_already_cleared: false,
public_dial_info_check_punishment: None, public_dial_info_check_punishment: None,
join_handles: Vec::new(), join_handles: Vec::new(),
stop_source: None, stop_source: None,
@ -312,11 +317,42 @@ impl Network {
dial_info: DialInfo, dial_info: DialInfo,
fut: F, fut: F,
) -> EyreResult<NetworkResult<T>> { ) -> EyreResult<NetworkResult<T>> {
let opt_routing_domain = self
.routing_table()
.routing_domain_for_address(dial_info.address());
let network_result = fut.await?; let network_result = fut.await?;
if matches!(network_result, NetworkResult::NoConnection(_)) { if matches!(network_result, NetworkResult::NoConnection(_)) {
// Dial info failure
self.network_manager() self.network_manager()
.address_filter() .address_filter()
.set_dial_info_failed(dial_info); .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) Ok(network_result)
} }
@ -793,6 +829,20 @@ impl Network {
editor_local_network.publish(); 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) 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( pub fn set_needs_public_dial_info_check(
&self, &self,
punishment: Option<Box<dyn FnOnce() + Send + 'static>>, punishment: Option<Box<dyn FnOnce() + Send + 'static>>,

View File

@ -14,12 +14,10 @@ pub struct ProtocolConfig {
pub(super) struct NetworkState { pub(super) struct NetworkState {
/// the calculated protocol configuration for inbound/outbound protocols /// the calculated protocol configuration for inbound/outbound protocols
pub protocol_config: ProtocolConfig, pub protocol_config: ProtocolConfig,
/// does our network have ipv4 on any network? /// does our network have ipv4 on any interface?
pub enable_ipv4: bool, pub enable_ipv4: bool,
/// does our network have ipv6 on the global internet? /// does our network have ipv6 on any interface?
pub enable_ipv6_global: bool, pub enable_ipv6: bool,
/// does our network have ipv6 on the local network?
pub enable_ipv6_local: bool,
/// The list of stable interface addresses we have last seen /// The list of stable interface addresses we have last seen
pub stable_interface_addresses: Vec<IpAddr>, pub stable_interface_addresses: Vec<IpAddr>,
/// The local networks (network+mask) most recently seen /// The local networks (network+mask) most recently seen
@ -84,21 +82,15 @@ impl Network {
// determine if we have ipv4/ipv6 addresses // determine if we have ipv4/ipv6 addresses
let mut enable_ipv4 = false; let mut enable_ipv4 = false;
let mut enable_ipv6_global = false; let mut enable_ipv6 = false;
let mut enable_ipv6_local = false;
let stable_interface_addresses = self.make_stable_interface_addresses(); 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() { if addr.is_ipv4() {
enable_ipv4 = true; enable_ipv4 = true;
} else if addr.is_ipv6() { } else if addr.is_ipv6() {
let address = Address::from_ip_addr(addr); enable_ipv6 = true;
if address.is_global() {
enable_ipv6_global = true;
} else if address.is_local() {
enable_ipv6_local = true;
}
} }
} }
@ -141,10 +133,8 @@ impl Network {
family_global.insert(AddressType::IPV4); family_global.insert(AddressType::IPV4);
family_local.insert(AddressType::IPV4); family_local.insert(AddressType::IPV4);
} }
if enable_ipv6_global { if enable_ipv6 {
family_global.insert(AddressType::IPV6); family_global.insert(AddressType::IPV6);
}
if enable_ipv6_local {
family_local.insert(AddressType::IPV6); family_local.insert(AddressType::IPV6);
} }
@ -178,8 +168,7 @@ impl Network {
Ok(NetworkState { Ok(NetworkState {
protocol_config, protocol_config,
enable_ipv4, enable_ipv4,
enable_ipv6_global, enable_ipv6,
enable_ipv6_local,
stable_interface_addresses, stable_interface_addresses,
local_networks, local_networks,
}) })

View File

@ -350,6 +350,14 @@ impl Network {
.or_default(); .or_default();
bapp.push(addr); 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( Self::set_preferred_local_address(
&mut inner, &mut inner,
PeerAddress::new(SocketAddress::from_socket_addr(addr), protocol_type), PeerAddress::new(SocketAddress::from_socket_addr(addr), protocol_type),

View File

@ -158,6 +158,14 @@ impl Network {
.or_default(); .or_default();
bapp.push(addr); 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( Self::set_preferred_local_address(
&mut inner, &mut inner,
PeerAddress::new( PeerAddress::new(

View File

@ -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)] #[instrument(level = "trace", target = "net", name = "Network::tick", skip_all, err)]
pub async fn tick(&self) -> EyreResult<()> { pub async fn tick(&self) -> EyreResult<()> {
let Ok(_guard) = self.startup_lock.enter() else { let Ok(_guard) = self.startup_lock.enter() else {
@ -62,38 +98,10 @@ impl Network {
// 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?;
// Check our public dial info to see if it has changed if self.needs_update_network_class_tick() {
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?; self.update_network_class_task.tick().await?;
} }
} }
}
// If we need to tick upnp, do it // If we need to tick upnp, do it
if upnp { if upnp {

View File

@ -9,7 +9,8 @@ impl Network {
_t: Timestamp, _t: Timestamp,
) -> EyreResult<()> { ) -> EyreResult<()> {
// Network lock ensures only one task operating on the low level network state // 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() { let _guard = match self.network_task_lock.try_lock() {
Ok(v) => v, Ok(v) => v,
Err(_) => { Err(_) => {
@ -68,7 +69,6 @@ impl Network {
// If any of the new addresses were PublicInternet addresses, re-run public dial info check // If any of the new addresses were PublicInternet addresses, re-run public dial info check
if public_internet_changed { if public_internet_changed {
// inner.network_needs_restart = true;
self.set_needs_public_dial_info_check(None); self.set_needs_public_dial_info_check(None);
} }

View File

@ -14,14 +14,9 @@ impl Network {
t: Timestamp, t: Timestamp,
) -> EyreResult<()> { ) -> EyreResult<()> {
// Network lock ensures only one task operating on the low level network state // 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. This a blocking lock so we can ensure this runs
let _guard = match self.network_task_lock.try_lock() { // as soon as network_interfaces_task is finished
Ok(v) => v, let _guard = self.network_task_lock.lock().await;
Err(_) => {
// If we can't get the lock right now, then
return Ok(());
}
};
// Do the public dial info check // Do the public dial info check
let finished = self.do_public_dial_info_check(stop_token, l, t).await?; 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 // Done with public dial info check
if finished { if finished {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
// Note that we did the check successfully
inner.needs_public_dial_info_check = false; 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(()) Ok(())
@ -94,7 +95,7 @@ impl Network {
) -> EyreResult<bool> { ) -> EyreResult<bool> {
// Figure out if we can optimize TCP/WS checking since they are often on the same port // Figure out if we can optimize TCP/WS checking since they are often on the same port
let (protocol_config, inbound_protocol_map) = { let (protocol_config, inbound_protocol_map) = {
let mut inner = self.inner.lock(); let inner = self.inner.lock();
let Some(protocol_config) = inner let Some(protocol_config) = inner
.network_state .network_state
.as_ref() .as_ref()
@ -103,9 +104,6 @@ impl Network {
bail!("should not be doing public dial info check before we have an initial network state"); 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 = let mut inbound_protocol_map =
HashMap::<(AddressType, LowLevelProtocolType, u16), Vec<ProtocolType>>::new(); HashMap::<(AddressType, LowLevelProtocolType, u16), Vec<ProtocolType>>::new();
for at in protocol_config.family_global { for at in protocol_config.family_global {
@ -171,6 +169,7 @@ impl Network {
// Wait for all discovery futures to complete and apply discoverycontexts // Wait for all discovery futures to complete and apply discoverycontexts
let mut external_address_types = AddressTypeSet::new(); let mut external_address_types = AddressTypeSet::new();
let mut detection_results = HashMap::<DiscoveryContextConfig, DetectionResult>::new();
loop { loop {
match unord match unord
.next() .next()
@ -185,8 +184,34 @@ impl Network {
// Add the external address kinds to the set we've seen // Add the external address kinds to the set we've seen
external_address_types |= dr.external_address_types; external_address_types |= dr.external_address_types;
// Import the dialinfo // Get best detection result for each discovery context config
self.update_with_detection_result(&mut editor, &inbound_protocol_map, dr); 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)) => { Ok(Some(None)) => {
// Found no dial info for this protocol/address combination // 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 // See if we have any discovery contexts that did not complete for a
// particular protocol type if its external address type was supported. // particular protocol type if its external address type was supported.
let mut success = true; let mut success = true;

View File

@ -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)] #[instrument(level = "trace", target = "net", skip_all, err)]
pub fn get_node_contact_method( pub fn get_node_contact_method(
&self, &self,
@ -430,48 +427,76 @@ impl NetworkManager {
let peer_a = routing_table.get_current_peer_info(routing_domain); let peer_a = routing_table.get_current_peer_info(routing_domain);
let own_node_info_ts = peer_a.signed_node_info().timestamp(); 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 // Peer B is the target node, get the whole peer info now
let target_node_info_ts = match target_node_ref.operate(|_rti, e| { let Some(peer_b) = target_node_ref.get_peer_info(routing_domain) else {
e.signed_node_info(routing_domain) log_net!("no node info for node {:?}", target_node_ref);
.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); 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::<DialInfo, Timestamp>::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 // Get cache key
let mut ncm_key = NodeContactMethodCacheKey { let ncm_key = NodeContactMethodCacheKey {
node_ids: target_node_ref.node_ids(), node_ids: target_node_ref.node_ids(),
own_node_info_ts, 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_filter: target_node_ref.filter(),
target_node_ref_sequencing: target_node_ref.sequencing(), 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) { if let Some(ncm) = self.inner.lock().node_contact_method_cache.get(&ncm_key) {
return Ok(ncm.clone()); return Ok(ncm.clone());
} }
// Peer B is the target node, get the whole peer info now // Calculate the node contact method
let peer_b = match target_node_ref.make_peer_info(routing_domain) { let routing_table = self.routing_table();
Some(pi) => Arc::new(pi), let ncm = Self::get_node_contact_method_uncached(
None => { &routing_table,
log_net!("no node info for node {:?}", target_node_ref); routing_domain,
return Ok(NodeContactMethod::Unreachable); target_node_ref,
} peer_a,
}; peer_b,
// Update the key's timestamp to ensure we avoid any race conditions &ncm_key,
ncm_key.target_node_info_ts = peer_b.signed_node_info().timestamp(); )?;
// 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<PeerInfo>,
peer_b: Arc<PeerInfo>,
ncm_key: &NodeContactMethodCacheKey,
) -> EyreResult<NodeContactMethod> {
// Dial info filter comes from the target node ref but must be filtered by this node's outbound capabilities // 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( 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_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()), .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 // Deprioritize dial info that have recently failed
let address_filter = self.address_filter(); let dif_sort: Option<DialInfoDetailSort> = if ncm_key.dial_info_failures_map.is_empty() {
let mut dial_info_failures_map = BTreeMap::<DialInfo, Timestamp>::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<Arc<DialInfoDetailSort>> = if dial_info_failures_map.is_empty() {
None None
} else { } else {
Some(Arc::new(move |a: &DialInfoDetail, b: &DialInfoDetail| { Some(Box::new(|a: &DialInfoDetail, b: &DialInfoDetail| {
let ats = dial_info_failures_map let ats = ncm_key
.dial_info_failures_map
.get(&a.dial_info) .get(&a.dial_info)
.copied() .copied()
.unwrap_or_default(); .unwrap_or_default();
let bts = dial_info_failures_map let bts = ncm_key
.dial_info_failures_map
.get(&b.dial_info) .get(&b.dial_info)
.copied() .copied()
.unwrap_or_default(); .unwrap_or_default();
@ -521,7 +537,7 @@ impl NetworkManager {
peer_b.clone(), peer_b.clone(),
dial_info_filter, dial_info_filter,
sequencing, sequencing,
dif_sort, dif_sort.as_ref(),
); );
// Translate the raw contact method to a referenced contact method // Translate the raw contact method to a referenced contact method
@ -549,7 +565,7 @@ impl NetworkManager {
let tighten = peer_a let tighten = peer_a
.signed_node_info() .signed_node_info()
.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) did.matches_filter(&dial_info_filter)
}) })
.iter() .iter()
@ -634,11 +650,6 @@ impl NetworkManager {
} }
}; };
// Cache this
self.inner
.lock()
.node_contact_method_cache
.insert(ncm_key, ncm.clone());
Ok(ncm) Ok(ncm)
} }
@ -806,10 +817,13 @@ impl NetworkManager {
// punch should come through and create a real 'last connection' for us if this succeeds // punch should come through and create a real 'last connection' for us if this succeeds
network_result_try!( network_result_try!(
self.net() 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? .await?
); );
// Add small delay to encourage packets to be delivered in order
sleep(HOLE_PUNCH_DELAY_MS).await;
// Issue the signal // Issue the signal
let rpc = self.rpc_processor(); let rpc = self.rpc_processor();
network_result_try!(rpc network_result_try!(rpc
@ -823,6 +837,13 @@ impl NetworkManager {
.await .await
.wrap_err("failed to send signal")?); .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 // Wait for the return receipt
let inbound_nr = match eventual_value let inbound_nr = match eventual_value
.timeout_at(stop_token) .timeout_at(stop_token)

View File

@ -72,7 +72,7 @@ impl Address {
pub fn canonical(&self) -> Address { pub fn canonical(&self) -> Address {
match self { match self {
Address::IPV4(v4) => Address::IPV4(*v4), 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), Some(v4) => Address::IPV4(v4),
None => Address::IPV6(*v6), None => Address::IPV6(*v6),
}, },

View File

@ -38,7 +38,7 @@ impl DialInfoFilter {
self.address_type_set = address_set; self.address_type_set = address_set;
self 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.protocol_type_set &= other_dif.protocol_type_set;
self.address_type_set &= other_dif.address_type_set; self.address_type_set &= other_dif.address_type_set;
self self
@ -54,7 +54,7 @@ impl DialInfoFilter {
Sequencing::EnsureOrdered => ( Sequencing::EnsureOrdered => (
true, true,
self.filtered( self.filtered(
&DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()), DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()),
), ),
), ),
} }

View File

@ -336,6 +336,18 @@ impl Network {
.await .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<NetworkResult<UniqueFlow>> {
return Ok(NetworkResult::ServiceUnavailable(
"unimplemented for this platform".to_owned(),
));
}
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
pub async fn startup_internal(&self) -> EyreResult<StartupDisposition> { pub async fn startup_internal(&self) -> EyreResult<StartupDisposition> {

View File

@ -182,6 +182,9 @@ pub(crate) struct BucketEntryInner {
local_network: BucketEntryLocalNetwork, local_network: BucketEntryLocalNetwork,
/// Statistics gathered for the peer /// Statistics gathered for the peer
peer_stats: PeerStats, peer_stats: PeerStats,
/// The peer info cache for speedy access to fully encapsulated per-routing-domain peer info
#[serde(skip)]
peer_info_cache: Mutex<BTreeMap<RoutingDomain, Option<Arc<PeerInfo>>>>,
/// The accounting for the latency statistics /// The accounting for the latency statistics
#[serde(skip)] #[serde(skip)]
latency_stats_accounting: LatencyStatsAccounting, latency_stats_accounting: LatencyStatsAccounting,
@ -277,7 +280,7 @@ impl BucketEntryInner {
/// Add a node id for a particular crypto kind. /// 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(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 /// Results Err() if this operation would add more crypto kinds than we support
pub fn add_node_id(&mut self, node_id: TypedKey) -> EyreResult<Option<TypedKey>> { pub fn add_node_id(&mut self, node_id: TypedKey) -> EyreResult<Option<TypedKey>> {
let total_node_id_count = self.validated_node_ids.len() + self.unsupported_node_ids.len(); 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 { if old_node_id == node_id {
return Ok(None); 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); 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)); return Ok(Some(old_node_id));
} }
// Check to ensure we aren't adding more crypto kinds than we support // 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"); bail!("too many crypto kinds for this node");
} }
node_ids.add(node_id); 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) Ok(None)
} }
@ -314,7 +327,14 @@ impl BucketEntryInner {
&mut self.unsupported_node_ids &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 { pub fn best_node_id(&self) -> TypedKey {
@ -413,9 +433,6 @@ impl BucketEntryInner {
move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2) 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( pub fn update_signed_node_info(
&mut self, &mut self,
routing_domain: RoutingDomain, routing_domain: RoutingDomain,
@ -429,7 +446,10 @@ impl BucketEntryInner {
// See if we have an existing signed_node_info to update or not // See if we have an existing signed_node_info to update or not
let mut node_info_changed = false; let mut node_info_changed = false;
let mut had_previous_node_info = false;
if let Some(current_sni) = opt_current_sni { if let Some(current_sni) = opt_current_sni {
had_previous_node_info = true;
// Always allow overwriting unsigned node (bootstrap) // Always allow overwriting unsigned node (bootstrap)
if current_sni.has_any_signature() { if current_sni.has_any_signature() {
// If the timestamp hasn't changed or is less, ignore this update // If the timestamp hasn't changed or is less, ignore this update
@ -480,6 +500,12 @@ impl BucketEntryInner {
self.clear_last_flows_except_latest(); 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 node_info_changed
} }
@ -535,16 +561,29 @@ impl BucketEntryInner {
opt_current_sni.as_ref().map(|s| s.as_ref()) opt_current_sni.as_ref().map(|s| s.as_ref())
} }
pub fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> { pub fn get_peer_info(&self, routing_domain: RoutingDomain) -> Option<Arc<PeerInfo>> {
// 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 { let opt_current_sni = match routing_domain {
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info, RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info, RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
}; };
// Peer info includes all node ids, even unvalidated ones // Peer info includes all node ids, even unvalidated ones
let node_ids = self.node_ids(); let node_ids = self.node_ids();
opt_current_sni let opt_pi = opt_current_sni
.as_ref() .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( pub fn best_routing_domain(
@ -1138,6 +1177,7 @@ impl BucketEntry {
transfer: TransferStatsDownUp::default(), transfer: TransferStatsDownUp::default(),
state: StateStats::default(), state: StateStats::default(),
}, },
peer_info_cache: Mutex::new(BTreeMap::new()),
latency_stats_accounting: LatencyStatsAccounting::new(), latency_stats_accounting: LatencyStatsAccounting::new(),
transfer_stats_accounting: TransferStatsAccounting::new(), transfer_stats_accounting: TransferStatsAccounting::new(),
state_stats_accounting: Mutex::new(StateStatsAccounting::new()), state_stats_accounting: Mutex::new(StateStatsAccounting::new()),

View File

@ -133,9 +133,9 @@ impl RoutingTable {
filters, filters,
// transform // transform
|rti, entry| { |rti, entry| {
entry.unwrap().with(rti, |_rti, e| { entry
Arc::new(e.make_peer_info(routing_domain).unwrap()) .unwrap()
}) .with(rti, |_rti, e| e.get_peer_info(routing_domain).unwrap())
}, },
) { ) {
Ok(v) => v, Ok(v) => v,

View File

@ -552,7 +552,7 @@ impl RoutingTable {
peer_b: Arc<PeerInfo>, peer_b: Arc<PeerInfo>,
dial_info_filter: DialInfoFilter, dial_info_filter: DialInfoFilter,
sequencing: Sequencing, sequencing: Sequencing,
dif_sort: Option<Arc<DialInfoDetailSort>>, dif_sort: Option<&DialInfoDetailSort>,
) -> ContactMethod { ) -> ContactMethod {
self.inner.read().get_contact_method( self.inner.read().get_contact_method(
routing_domain, routing_domain,
@ -586,7 +586,7 @@ impl RoutingTable {
/// Return the domain's currently registered network class /// Return the domain's currently registered network class
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), expect(dead_code))] #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), expect(dead_code))]
pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> { pub fn get_network_class(&self, routing_domain: RoutingDomain) -> NetworkClass {
self.inner.read().get_network_class(routing_domain) self.inner.read().get_network_class(routing_domain)
} }
@ -796,7 +796,7 @@ impl RoutingTable {
e.with(rti, |_rti, e| { e.with(rti, |_rti, e| {
if let Some(ni) = e.node_info(routing_domain) { if let Some(ni) = e.node_info(routing_domain) {
if ni 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) did.matches_filter(&dial_info_filter)
}) })
.is_some() .is_some()

View File

@ -63,7 +63,7 @@ impl FilteredNodeRef {
} }
pub fn merge_filter(&mut self, filter: NodeRefFilter) { 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) { pub fn set_sequencing(&mut self, sequencing: Sequencing) {

View File

@ -49,11 +49,11 @@ impl NodeRefFilter {
self.dial_info_filter = self.dial_info_filter.with_address_type_set(address_set); self.dial_info_filter = self.dial_info_filter.with_address_type_set(address_set);
self 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.routing_domain_set &= other_filter.routing_domain_set;
self.dial_info_filter = self self.dial_info_filter = self
.dial_info_filter .dial_info_filter
.filtered(&other_filter.dial_info_filter); .filtered(other_filter.dial_info_filter);
self self
} }
#[expect(dead_code)] #[expect(dead_code)]

View File

@ -70,8 +70,8 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait
self.operate(|_rti, e| e.peer_stats().clone()) self.operate(|_rti, e| e.peer_stats().clone())
} }
fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> { fn get_peer_info(&self, routing_domain: RoutingDomain) -> Option<Arc<PeerInfo>> {
self.operate(|_rti, e| e.make_peer_info(routing_domain)) self.operate(|_rti, e| e.get_peer_info(routing_domain))
} }
fn node_info(&self, routing_domain: RoutingDomain) -> Option<NodeInfo> { fn node_info(&self, routing_domain: RoutingDomain) -> Option<NodeInfo> {
self.operate(|_rti, e| e.node_info(routing_domain).cloned()) self.operate(|_rti, e| e.node_info(routing_domain).cloned())
@ -150,7 +150,7 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait
self.operate(|_rt, e| { self.operate(|_rt, e| {
for routing_domain in routing_domain_set { for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) { 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); return Some(did);
} }
} }
@ -177,7 +177,7 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait
self.operate(|_rt, e| { self.operate(|_rt, e| {
for routing_domain in routing_domain_set { for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) { 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); out.append(&mut dids);
} }
} }

View File

@ -493,11 +493,9 @@ impl RouteSpecStore {
let nodes_pi: Vec<Arc<PeerInfo>> = nodes let nodes_pi: Vec<Arc<PeerInfo>> = nodes
.iter() .iter()
.map(|nr| { .map(|nr| {
Arc::new(
nr.locked(rti) nr.locked(rti)
.make_peer_info(RoutingDomain::PublicInternet) .get_peer_info(RoutingDomain::PublicInternet)
.unwrap(), .unwrap()
)
}) })
.collect(); .collect();
@ -1260,14 +1258,14 @@ impl RouteSpecStore {
let pi = rti let pi = rti
.with_node_entry(node_id, |entry| { .with_node_entry(node_id, |entry| {
entry.with(rti, |_rti, e| { entry.with(rti, |_rti, e| {
e.make_peer_info(RoutingDomain::PublicInternet) e.get_peer_info(RoutingDomain::PublicInternet)
}) })
}) })
.flatten(); .flatten();
if pi.is_none() { if pi.is_none() {
apibail_internal!("peer info should exist for route but doesn't"); 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), next_hop: Some(route_hop_data),
}; };
@ -1504,14 +1502,14 @@ impl RouteSpecStore {
let pi = rti let pi = rti
.with_node_entry(node_id, |entry| { .with_node_entry(node_id, |entry| {
entry.with(rti, |_rti, e| { entry.with(rti, |_rti, e| {
e.make_peer_info(RoutingDomain::PublicInternet) e.get_peer_info(RoutingDomain::PublicInternet)
}) })
}) })
.flatten(); .flatten();
if pi.is_none() { if pi.is_none() {
apibail_internal!("peer info should exist for route but doesn't"); 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), next_hop: Some(route_hop_data),
} }

View File

@ -219,7 +219,7 @@ impl RoutingTableInner {
peer_b: Arc<PeerInfo>, peer_b: Arc<PeerInfo>,
dial_info_filter: DialInfoFilter, dial_info_filter: DialInfoFilter,
sequencing: Sequencing, sequencing: Sequencing,
dif_sort: Option<Arc<DialInfoDetailSort>>, dif_sort: Option<&DialInfoDetailSort>,
) -> ContactMethod { ) -> ContactMethod {
self.with_routing_domain(routing_domain, |rdd| { self.with_routing_domain(routing_domain, |rdd| {
rdd.get_contact_method(self, peer_a, peer_b, dial_info_filter, sequencing, dif_sort) 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 /// Return the domain's currently registered network class
pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> { pub fn get_network_class(&self, routing_domain: RoutingDomain) -> NetworkClass {
self.with_routing_domain(routing_domain, |rdd| rdd.network_class()) 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 // New node id, get the old peer info if we don't have it yet
if old_peer_infos.is_empty() { if old_peer_infos.is_empty() {
for rd in RoutingDomainSet::all() { 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); old_peer_infos.push(old_peer_info);
} }
} }
@ -667,7 +667,7 @@ impl RoutingTableInner {
if !old_peer_infos.is_empty() { if !old_peer_infos.is_empty() {
let mut new_peer_infos = vec![]; let mut new_peer_infos = vec![];
for rd in RoutingDomainSet::all() { 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); new_peer_infos.push(new_peer_info);
} }
} }
@ -895,13 +895,13 @@ impl RoutingTableInner {
let mut updated = false; let mut updated = false;
let mut old_peer_info = None; let mut old_peer_info = None;
let nr = self.create_node_ref(&node_ids, |_rti, e| { 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); updated = e.update_signed_node_info(routing_domain, &signed_node_info);
})?; })?;
// Process any new or updated PeerInfo // Process any new or updated PeerInfo
if old_peer_info.is_none() || updated { 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); 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 /// 2. nodes are removed that don't have any peer info
fn on_entry_peer_info_updated( fn on_entry_peer_info_updated(
&mut self, &mut self,
old_peer_info: Option<PeerInfo>, old_peer_info: Option<Arc<PeerInfo>>,
new_peer_info: Option<PeerInfo>, new_peer_info: Option<Arc<PeerInfo>>,
) { ) {
let (routing_domain, node_ids) = match (old_peer_info.as_ref(), new_peer_info.as_ref()) { let (routing_domain, node_ids) = match (old_peer_info.as_ref(), new_peer_info.as_ref()) {
(None, None) => { (None, None) => {
@ -1114,9 +1114,7 @@ impl RoutingTableInner {
) -> Arc<PeerInfo> { ) -> Arc<PeerInfo> {
match entry { match entry {
None => own_peer_info.clone(), None => own_peer_info.clone(),
Some(entry) => { Some(entry) => entry.with_inner(|e| e.get_peer_info(routing_domain).unwrap()),
entry.with_inner(|e| Arc::new(e.make_peer_info(routing_domain).unwrap()))
}
} }
} }

View File

@ -55,7 +55,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
RoutingDomain::LocalNetwork RoutingDomain::LocalNetwork
} }
fn network_class(&self) -> Option<NetworkClass> { fn network_class(&self) -> NetworkClass {
self.common.network_class() self.common.network_class()
} }
fn outbound_protocols(&self) -> ProtocolTypeSet { fn outbound_protocols(&self) -> ProtocolTypeSet {
@ -67,11 +67,14 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
fn address_types(&self) -> AddressTypeSet { fn address_types(&self) -> AddressTypeSet {
self.common.address_types() self.common.address_types()
} }
fn compatible_address_types(&self) -> AddressTypeSet {
AddressType::IPV4 | AddressType::IPV6
}
fn capabilities(&self) -> Vec<Capability> { fn capabilities(&self) -> Vec<Capability> {
self.common.capabilities() self.common.capabilities()
} }
fn requires_relay(&self) -> Option<RelayKind> { fn requires_relay(&self) -> Option<RelayKind> {
self.common.requires_relay() self.common.requires_relay(self.compatible_address_types())
} }
fn relay_node(&self) -> Option<FilteredNodeRef> { fn relay_node(&self) -> Option<FilteredNodeRef> {
self.common.relay_node() self.common.relay_node()
@ -182,7 +185,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
peer_b: Arc<PeerInfo>, peer_b: Arc<PeerInfo>,
dial_info_filter: DialInfoFilter, dial_info_filter: DialInfoFilter,
sequencing: Sequencing, sequencing: Sequencing,
dif_sort: Option<Arc<DialInfoDetailSort>>, dif_sort: Option<&DialInfoDetailSort>,
) -> ContactMethod { ) -> ContactMethod {
// Get the nodeinfos for convenience // Get the nodeinfos for convenience
let node_a = peer_a.signed_node_info().node_info(); 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( if let Some(target_did) = first_filtered_dial_info_detail_between_nodes(
node_a, node_a,
node_b, node_b,
&dial_info_filter, dial_info_filter,
sequencing, sequencing,
dif_sort, dif_sort,
) { ) {

View File

@ -13,10 +13,11 @@ pub trait RoutingDomainDetail {
// Common accessors // Common accessors
#[expect(dead_code)] #[expect(dead_code)]
fn routing_domain(&self) -> RoutingDomain; fn routing_domain(&self) -> RoutingDomain;
fn network_class(&self) -> Option<NetworkClass>; fn network_class(&self) -> NetworkClass;
fn outbound_protocols(&self) -> ProtocolTypeSet; fn outbound_protocols(&self) -> ProtocolTypeSet;
fn inbound_protocols(&self) -> ProtocolTypeSet; fn inbound_protocols(&self) -> ProtocolTypeSet;
fn address_types(&self) -> AddressTypeSet; fn address_types(&self) -> AddressTypeSet;
fn compatible_address_types(&self) -> AddressTypeSet;
fn capabilities(&self) -> Vec<Capability>; fn capabilities(&self) -> Vec<Capability>;
fn requires_relay(&self) -> Option<RelayKind>; fn requires_relay(&self) -> Option<RelayKind>;
fn relay_node(&self) -> Option<FilteredNodeRef>; fn relay_node(&self) -> Option<FilteredNodeRef>;
@ -47,7 +48,7 @@ pub trait RoutingDomainDetail {
peer_b: Arc<PeerInfo>, peer_b: Arc<PeerInfo>,
dial_info_filter: DialInfoFilter, dial_info_filter: DialInfoFilter,
sequencing: Sequencing, sequencing: Sequencing,
dif_sort: Option<Arc<DialInfoDetailSort>>, dif_sort: Option<&DialInfoDetailSort>,
) -> ContactMethod; ) -> ContactMethod;
// Set last relay keepalive time // Set last relay keepalive time
@ -63,13 +64,13 @@ trait RoutingDomainDetailCommonAccessors: RoutingDomainDetail {
fn first_filtered_dial_info_detail_between_nodes( fn first_filtered_dial_info_detail_between_nodes(
from_node: &NodeInfo, from_node: &NodeInfo,
to_node: &NodeInfo, to_node: &NodeInfo,
dial_info_filter: &DialInfoFilter, dial_info_filter: DialInfoFilter,
sequencing: Sequencing, sequencing: Sequencing,
dif_sort: Option<Arc<DialInfoDetailSort>>, dif_sort: Option<&DialInfoDetailSort>,
) -> Option<DialInfoDetail> { ) -> Option<DialInfoDetail> {
// Consider outbound capabilities // Consider outbound capabilities
let dial_info_filter = (*dial_info_filter).filtered( let dial_info_filter = dial_info_filter.filtered(
&DialInfoFilter::all() DialInfoFilter::all()
.with_address_type_set(from_node.address_types()) .with_address_type_set(from_node.address_types())
.with_protocol_type_set(from_node.outbound_protocols()), .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 // based on an external preference table, for example the one kept by
// AddressFilter to deprioritize dialinfo that have recently failed to connect // AddressFilter to deprioritize dialinfo that have recently failed to connect
let (ordered, dial_info_filter) = dial_info_filter.apply_sequencing(sequencing); let (ordered, dial_info_filter) = dial_info_filter.apply_sequencing(sequencing);
let sort: Option<Box<DialInfoDetailSort>> = if ordered { let sort: Option<DialInfoDetailSort> = if ordered {
if let Some(dif_sort) = dif_sort { 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); let mut ord = dif_sort(a, b);
if ord == core::cmp::Ordering::Equal { if ord == core::cmp::Ordering::Equal {
ord = DialInfoDetail::ordered_sequencing_sort(a, b); ord = DialInfoDetail::ordered_sequencing_sort(a, b);
@ -89,12 +90,12 @@ fn first_filtered_dial_info_detail_between_nodes(
ord ord
})) }))
} else { } else {
Some(Box::new(move |a, b| { Some(Box::new(|a, b| {
DialInfoDetail::ordered_sequencing_sort(a, b) DialInfoDetail::ordered_sequencing_sort(a, b)
})) }))
} }
} else if let Some(dif_sort) = dif_sort { } 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 { } else {
None 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 // Get the best match dial info for node B if we have it
let direct_filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter); 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)] #[derive(Debug)]
@ -141,22 +142,18 @@ impl RoutingDomainDetailCommon {
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// Accessors // Accessors
pub fn network_class(&self) -> Option<NetworkClass> { pub fn network_class(&self) -> NetworkClass {
cfg_if! { cfg_if! {
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
Some(NetworkClass::WebApp) NetworkClass::WebApp
} else { } else {
if self.address_types.is_empty() { if self.address_types.is_empty() {
None NetworkClass::Invalid
} }
else if self.dial_info_details.is_empty() { else if self.dial_info_details.is_empty() {
if self.relay_node.is_none() { NetworkClass::OutboundOnly
None
} else { } else {
Some(NetworkClass::OutboundOnly) NetworkClass::InboundCapable
}
} else {
Some(NetworkClass::InboundCapable)
} }
} }
} }
@ -178,23 +175,30 @@ impl RoutingDomainDetailCommon {
self.capabilities.clone() self.capabilities.clone()
} }
pub fn requires_relay(&self) -> Option<RelayKind> { pub fn requires_relay(&self, compatible_address_types: AddressTypeSet) -> Option<RelayKind> {
match self.network_class()? { match self.network_class() {
NetworkClass::InboundCapable => { NetworkClass::InboundCapable => {
let mut all_inbound_set: HashSet<(ProtocolType, AddressType)> = HashSet::new(); let mut all_inbound_set: HashSet<(ProtocolType, AddressType)> = HashSet::new();
let mut address_types = AddressTypeSet::empty();
for p in self.inbound_protocols { for p in self.inbound_protocols {
for a in self.address_types { for a in self.address_types {
all_inbound_set.insert((p, a)); all_inbound_set.insert((p, a));
} }
} }
for did in &self.dial_info_details { for did in &self.dial_info_details {
// Request an inbound relay if any of our dialinfo require one
if did.class.requires_relay() { if did.class.requires_relay() {
return Some(RelayKind::Inbound); return Some(RelayKind::Inbound);
} }
let ib = (did.dial_info.protocol_type(), did.dial_info.address_type()); let ib = (did.dial_info.protocol_type(), did.dial_info.address_type());
all_inbound_set.remove(&ib); 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); return Some(RelayKind::Inbound);
} }
} }
@ -316,7 +320,7 @@ impl RoutingDomainDetailCommon {
let routing_table = rti.routing_table(); let routing_table = rti.routing_table();
let node_info = NodeInfo::new( let node_info = NodeInfo::new(
self.network_class().unwrap_or(NetworkClass::Invalid), self.network_class(),
self.outbound_protocols, self.outbound_protocols,
self.address_types, self.address_types,
VALID_ENVELOPE_VERSIONS.to_vec(), VALID_ENVELOPE_VERSIONS.to_vec(),
@ -326,9 +330,10 @@ impl RoutingDomainDetailCommon {
); );
let relay_info = if let Some(rn) = &self.relay_node { 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 { 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 { match relay_sni {
SignedNodeInfo::Direct(d) => Some((relay_ids, d)), SignedNodeInfo::Direct(d) => Some((relay_ids, d)),
SignedNodeInfo::Relayed(_) => { SignedNodeInfo::Relayed(_) => {

View File

@ -36,7 +36,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
RoutingDomain::PublicInternet RoutingDomain::PublicInternet
} }
fn network_class(&self) -> Option<NetworkClass> { fn network_class(&self) -> NetworkClass {
self.common.network_class() self.common.network_class()
} }
fn outbound_protocols(&self) -> ProtocolTypeSet { fn outbound_protocols(&self) -> ProtocolTypeSet {
@ -48,11 +48,14 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
fn address_types(&self) -> AddressTypeSet { fn address_types(&self) -> AddressTypeSet {
self.common.address_types() self.common.address_types()
} }
fn compatible_address_types(&self) -> AddressTypeSet {
AddressType::IPV4 | AddressType::IPV6
}
fn capabilities(&self) -> Vec<Capability> { fn capabilities(&self) -> Vec<Capability> {
self.common.capabilities() self.common.capabilities()
} }
fn requires_relay(&self) -> Option<RelayKind> { fn requires_relay(&self) -> Option<RelayKind> {
self.common.requires_relay() self.common.requires_relay(self.compatible_address_types())
} }
fn relay_node(&self) -> Option<FilteredNodeRef> { fn relay_node(&self) -> Option<FilteredNodeRef> {
self.common.relay_node() self.common.relay_node()
@ -160,7 +163,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
peer_b: Arc<PeerInfo>, peer_b: Arc<PeerInfo>,
dial_info_filter: DialInfoFilter, dial_info_filter: DialInfoFilter,
sequencing: Sequencing, sequencing: Sequencing,
dif_sort: Option<Arc<DialInfoDetailSort>>, dif_sort: Option<&DialInfoDetailSort>,
) -> ContactMethod { ) -> ContactMethod {
let ip6_prefix_size = rti let ip6_prefix_size = rti
.config() .config()
@ -191,9 +194,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
first_filtered_dial_info_detail_between_nodes( first_filtered_dial_info_detail_between_nodes(
node_a, node_a,
node_b, node_b,
&dial_info_filter, dial_info_filter,
sequencing, sequencing,
dif_sort.clone(), dif_sort,
) )
}) })
.flatten() .flatten()
@ -228,9 +231,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
if first_filtered_dial_info_detail_between_nodes( if first_filtered_dial_info_detail_between_nodes(
node_a, node_a,
node_b_relay, node_b_relay,
&dial_info_filter, dial_info_filter,
sequencing, sequencing,
dif_sort.clone(), dif_sort,
) )
.is_some() .is_some()
{ {
@ -242,9 +245,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
if let Some(reverse_did) = first_filtered_dial_info_detail_between_nodes( if let Some(reverse_did) = first_filtered_dial_info_detail_between_nodes(
node_b, node_b,
node_a, node_a,
&dial_info_filter, dial_info_filter,
sequencing, sequencing,
dif_sort.clone(), dif_sort,
) { ) {
// Ensure we aren't on the same public IP address (no hairpin nat) // 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() { 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? // Does node B have a direct udp dialinfo node A can reach?
let udp_dial_info_filter = dial_info_filter 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( if let Some(target_udp_did) = first_filtered_dial_info_detail_between_nodes(
node_a, node_a,
node_b, node_b,
&udp_dial_info_filter, udp_dial_info_filter,
sequencing, sequencing,
dif_sort.clone(), dif_sort,
) { ) {
// Does node A have a direct udp dialinfo that node B can reach? // Does node A have a direct udp dialinfo that node B can reach?
if let Some(reverse_udp_did) = if let Some(reverse_udp_did) =
first_filtered_dial_info_detail_between_nodes( first_filtered_dial_info_detail_between_nodes(
node_b, node_b,
node_a, node_a,
&udp_dial_info_filter, udp_dial_info_filter,
sequencing, sequencing,
dif_sort.clone(), dif_sort,
) )
{ {
// Ensure we aren't on the same public IP address (no hairpin nat) // 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( if first_filtered_dial_info_detail_between_nodes(
node_a, node_a,
node_b_relay, node_b_relay,
&dial_info_filter, dial_info_filter,
sequencing, sequencing,
dif_sort.clone(), dif_sort,
) )
.is_some() .is_some()
{ {
@ -337,9 +340,9 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
first_filtered_dial_info_detail_between_nodes( first_filtered_dial_info_detail_between_nodes(
node_b, node_b,
node_a, node_a,
&dial_info_filter, dial_info_filter,
sequencing, sequencing,
dif_sort.clone(), dif_sort,
) )
}) })
.flatten() .flatten()

View File

@ -330,12 +330,12 @@ impl RoutingTable {
peers: Vec<Arc<PeerInfo>>, peers: Vec<Arc<PeerInfo>>,
stop_token: StopToken, stop_token: StopToken,
) -> EyreResult<()> { ) -> EyreResult<()> {
log_rtab!(debug " bootstrapped peers: {:?}", &peers); log_rtab!(debug " bootstrap peers: {:?}", &peers);
// Get crypto kinds to bootstrap // Get crypto kinds to bootstrap
let crypto_kinds = self.get_bootstrap_crypto_kinds(); 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 // Run all bootstrap operations concurrently
let mut unord = FuturesUnordered::<SendPinBoxFuture<()>>::new(); let mut unord = FuturesUnordered::<SendPinBoxFuture<()>>::new();

View File

@ -6,14 +6,8 @@ impl RoutingTable {
fn public_internet_wants_relay(&self) -> Option<RelayKind> { fn public_internet_wants_relay(&self) -> Option<RelayKind> {
let own_peer_info = self.get_current_peer_info(RoutingDomain::PublicInternet); let own_peer_info = self.get_current_peer_info(RoutingDomain::PublicInternet);
let own_node_info = own_peer_info.signed_node_info().node_info(); 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 we need a relay, always request one
if matches!(network_class, NetworkClass::Invalid) {
return None;
}
// If we -need- a relay always request one
let requires_relay = self let requires_relay = self
.inner .inner
.read() .read()
@ -22,16 +16,6 @@ impl RoutingTable {
return Some(rk); 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 // 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 // in case we need to navigate hairpin NAT to our own network
let mut inbound_addresses = HashSet::<SocketAddr>::new(); let mut inbound_addresses = HashSet::<SocketAddr>::new();
@ -200,7 +184,7 @@ impl RoutingTable {
// Disqualify nodes that don't cover all our inbound ports for tcp and udp // 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 // 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 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) did.matches_filter(&outbound_dif)
}); });
for did in &dids { for did in &dids {

View File

@ -19,7 +19,8 @@ impl MatchesDialInfoFilter for DialInfoDetail {
} }
} }
pub type DialInfoDetailSort = dyn Fn(&DialInfoDetail, &DialInfoDetail) -> core::cmp::Ordering; pub type DialInfoDetailSort<'a> =
Box<dyn Fn(&DialInfoDetail, &DialInfoDetail) -> core::cmp::Ordering + 'a>;
impl DialInfoDetail { impl DialInfoDetail {
pub fn ordered_sequencing_sort(a: &DialInfoDetail, b: &DialInfoDetail) -> core::cmp::Ordering { pub fn ordered_sequencing_sort(a: &DialInfoDetail, b: &DialInfoDetail) -> core::cmp::Ordering {
@ -29,7 +30,5 @@ impl DialInfoDetail {
} }
a.class.cmp(&b.class) a.class.cmp(&b.class)
} }
pub const NO_SORT: std::option::Option< pub const NO_SORT: std::option::Option<&DialInfoDetailSort<'static>> = None::<_>;
for<'r, 's> fn(&'r DialInfoDetail, &'s DialInfoDetail) -> std::cmp::Ordering,
> = None::<fn(&DialInfoDetail, &DialInfoDetail) -> core::cmp::Ordering>;
} }

View File

@ -85,10 +85,10 @@ impl NodeInfo {
&self.dial_info_detail_list &self.dial_info_detail_list
} }
pub fn first_filtered_dial_info_detail<S, F>( pub fn first_filtered_dial_info_detail<'a, S, F>(
&self, &self,
sort: Option<S>, sort: Option<&'a S>,
filter: F, filter: &'a F,
) -> Option<DialInfoDetail> ) -> Option<DialInfoDetail>
where where
S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering,
@ -114,8 +114,8 @@ impl NodeInfo {
pub fn filtered_dial_info_details<S, F>( pub fn filtered_dial_info_details<S, F>(
&self, &self,
sort: Option<S>, sort: Option<&S>,
filter: F, filter: &F,
) -> Vec<DialInfoDetail> ) -> Vec<DialInfoDetail>
where where
S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering,

View File

@ -738,7 +738,7 @@ impl RPCProcessor {
// No private route was specified for the request // No private route was specified for the request
// but we are using a safety route, so we must create an empty private route // but we are using a safety route, so we must create an empty private route
// Destination relay is ignored for safety routed operations // 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 => { None => {
return Ok(NetworkResult::no_connection_other( return Ok(NetworkResult::no_connection_other(
"No peer info for stub private route", "No peer info for stub private route",
@ -748,7 +748,7 @@ impl RPCProcessor {
}; };
let private_route = PrivateRoute::new_stub( let private_route = PrivateRoute::new_stub(
destination_node_ref.best_node_id(), destination_node_ref.best_node_id(),
RouteNode::PeerInfo(Arc::new(peer_info)), RouteNode::PeerInfo(peer_info),
); );
// Wrap with safety route // Wrap with safety route

View File

@ -13,10 +13,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.11.0" version: "2.12.0"
async_tools: async_tools:
dependency: transitive dependency: transitive
description: description:
@ -29,10 +29,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
change_case: change_case:
dependency: transitive dependency: transitive
description: description:
@ -45,10 +45,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.4.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -61,18 +61,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.2"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.0" version: "1.19.1"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -101,10 +101,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.3.2"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@ -117,10 +117,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: file name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.0" version: "7.0.1"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -195,18 +195,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.7" version: "10.0.8"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.8" version: "3.0.9"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
@ -243,10 +243,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16+1" version: "0.12.17"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
@ -259,18 +259,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.15.0" version: "1.16.0"
path: path:
dependency: "direct main" dependency: "direct main"
description: description:
name: path name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.0" version: "1.9.1"
path_provider: path_provider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -323,10 +323,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.5" version: "3.1.6"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -339,10 +339,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: process name: process
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "5.0.3"
quiver: quiver:
dependency: transitive dependency: transitive
description: description:
@ -360,34 +360,34 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.10.1"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.12.0" version: "1.12.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.4"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.4.1"
sync_http: sync_http:
dependency: transitive dependency: transitive
description: description:
@ -416,18 +416,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "1.2.2"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.3" version: "0.7.4"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -462,10 +462,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.3.0" version: "14.3.1"
webdriver: webdriver:
dependency: transitive dependency: transitive
description: description:
@ -499,5 +499,5 @@ packages:
source: hosted source: hosted
version: "0.0.6" version: "0.0.6"
sdks: sdks:
dart: ">=3.5.0 <4.0.0" dart: ">=3.7.0-0 <4.0.0"
flutter: ">=3.24.0" flutter: ">=3.24.0"

View File

@ -177,8 +177,8 @@ core:
public_watch_limit: 32 public_watch_limit: 32
member_watch_limit: 8 member_watch_limit: 8
max_watch_expiration_ms: 600000 max_watch_expiration_ms: 600000
upnp: true upnp: false
detect_address_changes: true detect_address_changes: false
restricted_nat_retries: 0 restricted_nat_retries: 0
tls: tls:
certificate_path: '%CERTIFICATE_PATH%' 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.member_watch_limit, 8u32);
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!(!s.core.network.detect_address_changes);
assert_eq!(s.core.network.restricted_nat_retries, 0u32); assert_eq!(s.core.network.restricted_nat_retries, 0u32);
// //
assert_eq!( assert_eq!(