diff --git a/veilid-core/src/network_manager/types/network_class.rs b/veilid-core/src/network_manager/types/network_class.rs index e1389333..0570b9d2 100644 --- a/veilid-core/src/network_manager/types/network_class.rs +++ b/veilid-core/src/network_manager/types/network_class.rs @@ -15,11 +15,6 @@ impl Default for NetworkClass { } impl NetworkClass { - // Must an inbound relay be kept available? - // In the case of InboundCapable, it is left up to the class of each DialInfo to determine if an inbound relay is required - pub fn inbound_wants_relay(&self) -> bool { - matches!(self, Self::OutboundOnly | Self::WebApp) - } // Should an outbound relay be kept available? pub fn outbound_wants_relay(&self) -> bool { matches!(self, Self::WebApp) diff --git a/veilid-core/src/routing_table/routing_domains.rs b/veilid-core/src/routing_table/routing_domains.rs index b318327b..f03dc669 100644 --- a/veilid-core/src/routing_table/routing_domains.rs +++ b/veilid-core/src/routing_table/routing_domains.rs @@ -319,17 +319,27 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { } fn get_contact_method( &self, - _rti: &RoutingTableInner, + rti: &RoutingTableInner, peer_a: &PeerInfo, peer_b: &PeerInfo, dial_info_filter: DialInfoFilter, sequencing: Sequencing, dif_sort: Option>, ) -> ContactMethod { + let ip6_prefix_size = rti + .unlocked_inner + .config + .get() + .network + .max_connections_per_ip6_prefix_size as usize; + // Get the nodeinfos for convenience let node_a = peer_a.signed_node_info().node_info(); let node_b = peer_b.signed_node_info().node_info(); + // Check to see if these nodes are on the same network + let same_ipblock = node_a.node_is_on_same_ipblock(node_b, ip6_prefix_size); + // Get the node ids that would be used between these peers let cck = common_crypto_kinds(&peer_a.node_ids().kinds(), &peer_b.node_ids().kinds()); let Some(best_ck) = cck.first().copied() else { @@ -341,13 +351,20 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { let node_b_id = peer_b.node_ids().get(best_ck).unwrap(); // Get the best match dial info for node B if we have it - if let Some(target_did) = first_filtered_dial_info_detail_between_nodes( - node_a, - node_b, - &dial_info_filter, - sequencing, - dif_sort.clone(), - ) { + // Don't try direct inbound at all if the two nodes are on the same ipblock to avoid hairpin NAT issues + // as well avoiding direct traffic between same-network nodes. This would be done in the LocalNetwork RoutingDomain. + if let Some(target_did) = (!same_ipblock) + .then(|| { + first_filtered_dial_info_detail_between_nodes( + node_a, + node_b, + &dial_info_filter, + sequencing, + dif_sort.clone(), + ) + }) + .flatten() + { // Do we need to signal before going inbound? if !target_did.class.requires_signal() { // Go direct without signaling @@ -449,7 +466,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { } } } - // If the node B has no direct dial info, it needs to have an inbound relay + // If the node B has no direct dial info or is on the same ipblock, it needs to have an inbound relay else if let Some(node_b_relay) = peer_b.signed_node_info().relay_info() { // Note that relay_peer_info could be node_a, in which case a connection already exists // and we only get here if the connection had dropped, in which case node_b is unreachable until @@ -481,13 +498,19 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { ///////// Reverse connection // Get the best match dial info for an reverse inbound connection from node B to node A - if let Some(reverse_did) = first_filtered_dial_info_detail_between_nodes( - node_b, - node_a, - &dial_info_filter, - sequencing, - dif_sort.clone(), - ) { + // unless both nodes are on the same ipblock + if let Some(reverse_did) = (!same_ipblock) + .then(|| { + first_filtered_dial_info_detail_between_nodes( + node_b, + node_a, + &dial_info_filter, + sequencing, + dif_sort.clone(), + ) + }) + .flatten() + { // Can we receive a direct reverse connection? if !reverse_did.class.requires_signal() { return ContactMethod::SignalReverse(node_b_relay_id, node_b_id); diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index e188a390..fc3cd5d4 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -146,7 +146,11 @@ impl RoutingTable { // Get all our outbound protocol/address types let outbound_dif = self.get_outbound_dial_info_filter(RoutingDomain::PublicInternet); let mapped_port_info = self.get_low_level_port_info(); - + let own_node_info = self + .get_own_peer_info(RoutingDomain::PublicInternet) + .signed_node_info() + .node_info() + .clone(); let ip6_prefix_size = self .unlocked_inner .config @@ -154,15 +158,6 @@ impl RoutingTable { .network .max_connections_per_ip6_prefix_size as usize; - let our_ip_blocks = self - .get_own_peer_info(RoutingDomain::PublicInternet) - .signed_node_info() - .node_info() - .dial_info_detail_list() - .iter() - .map(|did| ip_to_ipblock(ip6_prefix_size, did.dial_info.to_socket_addr().ip())) - .collect::>(); - move |e: &BucketEntryInner| { // Ensure this node is not on the local network and is on the public internet if e.has_node_info(RoutingDomain::LocalNetwork.into()) { @@ -215,11 +210,8 @@ impl RoutingTable { } // Exclude any nodes that have our same network block - for did in dids { - let ipblock = ip_to_ipblock(ip6_prefix_size, did.dial_info.to_socket_addr().ip()); - if our_ip_blocks.contains(&ipblock) { - return false; - } + if own_node_info.node_is_on_same_ipblock(node_info, ip6_prefix_size) { + return false; } true diff --git a/veilid-core/src/routing_table/types/node_info.rs b/veilid-core/src/routing_table/types/node_info.rs index ce8b8dfd..5c9849b4 100644 --- a/veilid-core/src/routing_table/types/node_info.rs +++ b/veilid-core/src/routing_table/types/node_info.rs @@ -175,4 +175,21 @@ impl NodeInfo { } true } + + /// Does this appear on the same network within the routing domain + pub fn node_is_on_same_ipblock(&self, node_b: &NodeInfo, ip6_prefix_size: usize) -> bool { + let our_ip_blocks = self + .dial_info_detail_list() + .iter() + .map(|did| ip_to_ipblock(ip6_prefix_size, did.dial_info.to_socket_addr().ip())) + .collect::>(); + + for did in node_b.dial_info_detail_list() { + let ipblock = ip_to_ipblock(ip6_prefix_size, did.dial_info.to_socket_addr().ip()); + if our_ip_blocks.contains(&ipblock) { + return true; + } + } + false + } }