This commit is contained in:
John Smith 2022-08-05 14:48:02 -04:00
parent 82dce24224
commit 9e506d23df
3 changed files with 117 additions and 66 deletions

View File

@ -107,28 +107,50 @@ impl DiscoveryContext {
address_type: AddressType, address_type: AddressType,
ignore_node: Option<DHTKey>, ignore_node: Option<DHTKey>,
) -> Option<(SocketAddress, NodeRef)> { ) -> Option<(SocketAddress, NodeRef)> {
let filter = DialInfoFilter::global()
.with_protocol_type(protocol_type)
.with_address_type(address_type);
let node_count = { let node_count = {
let config = self.routing_table.network_manager().config(); let config = self.routing_table.network_manager().config();
let c = config.get(); let c = config.get();
c.network.dht.max_find_node_count as usize c.network.dht.max_find_node_count as usize
}; };
// Build an filter that matches our protocol and address type
// and excludes relays so we can get an accurate external address
let dial_info_filter = DialInfoFilter::global()
.with_protocol_type(protocol_type)
.with_address_type(address_type);
let inbound_dial_info_entry_filter =
RoutingTable::make_inbound_dial_info_entry_filter(dial_info_filter.clone());
let disallow_relays_filter = move |e: &BucketEntryInner| {
if let Some(n) = e.node_info() {
n.relay_peer_info.is_none()
} else {
false
}
};
let filter =
RoutingTable::combine_filters(inbound_dial_info_entry_filter, disallow_relays_filter);
// Find public nodes matching this filter
let peers = self let peers = self
.routing_table .routing_table
.find_fast_public_nodes_filtered(node_count, &filter); .find_fast_public_nodes_filtered(node_count, filter);
if peers.is_empty() { if peers.is_empty() {
log_net!("no peers of type '{:?}'", filter); log_net!(
"no external address detection peers of type {:?}:{:?}",
protocol_type,
address_type
);
return None; return None;
} }
for peer in peers {
// For each peer, if it's not our ignore-node, ask them for our public address, filtering on desired dial info
for mut peer in peers {
if let Some(ignore_node) = ignore_node { if let Some(ignore_node) = ignore_node {
if peer.node_id() == ignore_node { if peer.node_id() == ignore_node {
continue; continue;
} }
} }
peer.set_filter(Some(dial_info_filter.clone()));
if let Some(sa) = self.request_public_address(peer.clone()).await { if let Some(sa) = self.request_public_address(peer.clone()).await {
return Some((sa, peer)); return Some((sa, peer));
} }
@ -249,7 +271,10 @@ impl DiscoveryContext {
inner.external_1_address = Some(external_1); inner.external_1_address = Some(external_1);
inner.node_1 = Some(node_1); inner.node_1 = Some(node_1);
log_net!(debug "external_1_dial_info: {:?}\nexternal_1_address: {:?}\nnode_1: {:?}", inner.external_1_dial_info, inner.external_1_address, inner.node_1); info!(
"external_1_dial_info: {:?}\nexternal_1_address: {:?}\nnode_1: {:?}",
inner.external_1_dial_info, inner.external_1_address, inner.node_1
);
true true
} }
@ -339,6 +364,11 @@ impl DiscoveryContext {
Some(v) => v, Some(v) => v,
}; };
info!(
"external_2_address: {:?}\nnode_2: {:?}",
external_2_address, node_2
);
// If we have two different external addresses, then this is a symmetric NAT // If we have two different external addresses, then this is a symmetric NAT
if external_2_address != external_1_address { if external_2_address != external_1_address {
// Symmetric NAT is outbound only, no public dial info will work // Symmetric NAT is outbound only, no public dial info will work

View File

@ -5,15 +5,64 @@ use crate::xx::*;
use crate::*; use crate::*;
impl RoutingTable { impl RoutingTable {
// Retrieve the fastest nodes in the routing table with a particular kind of protocol and address type // Makes a filter that finds nodes with a matching inbound dialinfo
// Returns noderefs are are scoped to that address type only pub fn make_inbound_dial_info_entry_filter(
pub fn find_fast_public_nodes_filtered( dial_info_filter: DialInfoFilter,
) -> impl FnMut(&BucketEntryInner) -> bool {
// does it have matching public dial info?
move |e| {
e.node_info()
.map(|n| {
n.first_filtered_dial_info_detail(|did| did.matches_filter(&dial_info_filter))
.is_some()
})
.unwrap_or(false)
}
}
// Makes a filter that finds nodes capable of dialing a particular outbound dialinfo
pub fn make_outbound_dial_info_entry_filter(
dial_info: DialInfo,
) -> impl FnMut(&BucketEntryInner) -> bool {
// does the node's outbound capabilities match the dialinfo?
move |e| {
e.node_info()
.map(|n| {
let mut dif = DialInfoFilter::all();
dif = dif.with_protocol_type_set(n.outbound_protocols);
dif = dif.with_address_type_set(n.address_types);
dial_info.matches_filter(&dif)
})
.unwrap_or(false)
}
}
// Make a filter that wraps another filter
pub fn combine_filters<F, G>(mut f1: F, mut f2: G) -> impl FnMut(&BucketEntryInner) -> bool
where
F: FnMut(&BucketEntryInner) -> bool,
G: FnMut(&BucketEntryInner) -> bool,
{
move |e| {
if !f1(e) {
return false;
}
if !f2(e) {
return false;
}
true
}
}
// Retrieve the fastest nodes in the routing table matching an entry filter
pub fn find_fast_public_nodes_filtered<F>(
&self, &self,
node_count: usize, node_count: usize,
dial_info_filter: &DialInfoFilter, mut entry_filter: F,
) -> Vec<NodeRef> { ) -> Vec<NodeRef>
let dial_info_filter1 = dial_info_filter.clone(); where
F: FnMut(&BucketEntryInner) -> bool,
{
self.find_fastest_nodes( self.find_fastest_nodes(
// count // count
node_count, node_count,
@ -26,25 +75,13 @@ impl RoutingTable {
return false; return false;
} }
// does it have matching public dial info? // skip nodes that dont match entry filter
e.node_info() entry_filter(e)
.map(|n| {
n.first_filtered_dial_info_detail(|did| {
did.matches_filter(&dial_info_filter1)
})
.is_some()
})
.unwrap_or(false)
}) })
}), }),
// transform // transform
|k: DHTKey, v: Option<Arc<BucketEntry>>| { |k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new( NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
self.clone(),
k,
v.unwrap().clone(),
Some(dial_info_filter.clone()),
)
}, },
) )
} }

View File

@ -80,57 +80,41 @@ impl RPCProcessor {
// Use the address type though, to ensure we reach an ipv6 capable node if this is // Use the address type though, to ensure we reach an ipv6 capable node if this is
// an ipv6 address // an ipv6 address
let routing_table = self.routing_table(); let routing_table = self.routing_table();
let filter = DialInfoFilter::global().with_address_type(dial_info.address_type());
let sender_id = msg.header.envelope.get_sender_id(); let sender_id = msg.header.envelope.get_sender_id();
let node_count = { let node_count = {
let c = self.config.get(); let c = self.config.get();
c.network.dht.max_find_node_count as usize c.network.dht.max_find_node_count as usize
}; };
let peers = routing_table.find_fast_public_nodes_filtered(node_count, &filter);
// Filter on nodes that can validate dial info, and can reach a specific dial info
let outbound_dial_info_entry_filter =
RoutingTable::make_outbound_dial_info_entry_filter(dial_info.clone());
let will_validate_dial_info_filter = |e: &BucketEntryInner| {
if let Some(status) = &e.peer_stats().status {
status.will_validate_dial_info
} else {
true
}
};
let filter = RoutingTable::combine_filters(
outbound_dial_info_entry_filter,
will_validate_dial_info_filter,
);
// Find nodes matching filter to redirect this to
let peers = routing_table.find_fast_public_nodes_filtered(node_count, filter);
if peers.is_empty() { if peers.is_empty() {
return Err(RPCError::internal(format!( return Err(RPCError::internal(format!(
"no peers matching filter '{:?}'", "no peers able to reach dialinfo '{:?}'",
filter dial_info
))); )));
} }
for mut peer in peers { for peer in peers {
// Ensure the peer is not the one asking for the validation // Ensure the peer is not the one asking for the validation
if peer.node_id() == sender_id { if peer.node_id() == sender_id {
continue; continue;
} }
// Release the filter on the peer because we don't need to send the redirect with the filter
// we just wanted to make sure we only selected nodes that were capable of
// using the correct protocol for the dial info being validated
peer.set_filter(None);
// Ensure the peer's status is known and that it is capable of
// making outbound connections for the dial info we want to verify
// and if this peer can validate dial info
let can_contact_dial_info = peer.operate(|e: &BucketEntryInner| {
if let Some(ni) = e.node_info() {
ni.outbound_protocols.contains(dial_info.protocol_type())
&& ni.can_validate_dial_info()
} else {
false
}
});
if !can_contact_dial_info {
continue;
}
// See if this peer will validate dial info
let will_validate_dial_info = peer.operate(|e: &BucketEntryInner| {
if let Some(status) = &e.peer_stats().status {
status.will_validate_dial_info
} else {
true
}
});
if !will_validate_dial_info {
continue;
}
// Make a copy of the request, without the redirect flag // Make a copy of the request, without the redirect flag
let validate_dial_info = RPCOperationValidateDialInfo { let validate_dial_info = RPCOperationValidateDialInfo {
dial_info: dial_info.clone(), dial_info: dial_info.clone(),