refactor checkpoint

This commit is contained in:
John Smith 2022-09-03 13:57:25 -04:00
parent 9966d25672
commit e0a5b1bd69
30 changed files with 1274 additions and 838 deletions

View File

@ -3,5 +3,4 @@ core:
network:
dht:
min_peer_count: 1
enable_local_peer_scope: true
bootstrap: []

View File

@ -175,10 +175,6 @@ struct ValueData {
# Operations
##############################
struct OperationStatusQ {
nodeStatus @0 :NodeStatus; # node status update about the statusq sender
}
enum NetworkClass {
inboundCapable @0; # I = Inbound capable without relay, may require signal
outboundOnly @1; # O = Outbound only, inbound relay required except with reverse connect signal
@ -199,7 +195,7 @@ struct DialInfoDetail {
class @1 :DialInfoClass;
}
struct NodeStatus {
struct PublicInternetNodeStatus {
willRoute @0 :Bool;
willTunnel @1 :Bool;
willSignal @2 :Bool;
@ -207,6 +203,18 @@ struct NodeStatus {
willValidateDialInfo @4 :Bool;
}
struct LocalNetworkNodeStatus {
willRelay @0 :Bool;
willValidateDialInfo @1 :Bool;
}
struct NodeStatus {
union {
publicInternet @0 :PublicInternetNodeStatus;
localNetwork @1 :LocalNetworkNodeStatus;
}
}
struct ProtocolTypeSet {
udp @0 :Bool;
tcp @1 :Bool;
@ -239,6 +247,21 @@ struct SenderInfo {
socketAddress @0 :SocketAddress; # socket address was available for peer
}
struct PeerInfo {
nodeId @0 :NodeID; # node id for 'closer peer'
signedNodeInfo @1 :SignedNodeInfo; # signed node info for 'closer peer'
}
struct RoutedOperation {
signatures @0 :List(Signature); # signatures from nodes that have handled the private route
nonce @1 :Nonce; # nonce Xmsg
data @2 :Data; # Operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
}
struct OperationStatusQ {
nodeStatus @0 :NodeStatus; # node status update about the statusq sender
}
struct OperationStatusA {
nodeStatus @0 :NodeStatus; # returned node status
senderInfo @1 :SenderInfo; # info about StatusQ sender from the perspective of the replier
@ -258,21 +281,10 @@ struct OperationFindNodeQ {
nodeId @0 :NodeID; # node id to locate
}
struct PeerInfo {
nodeId @0 :NodeID; # node id for 'closer peer'
signedNodeInfo @1 :SignedNodeInfo; # signed node info for 'closer peer'
}
struct OperationFindNodeA {
peers @0 :List(PeerInfo); # returned 'closer peer' information
}
struct RoutedOperation {
signatures @0 :List(Signature); # signatures from nodes that have handled the private route
nonce @1 :Nonce; # nonce Xmsg
data @2 :Data; # Operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
}
struct OperationRoute {
safetyRoute @0 :SafetyRoute; # Where this should go
operation @1 :RoutedOperation; # The operation to be routed
@ -419,26 +431,25 @@ struct OperationCancelTunnelA {
# Things that want an answer
struct Question {
respondTo :union {
sender @0 :Void; # sender without node info
senderWithInfo @1 :SignedNodeInfo; # some envelope-sender signed node info to be used for reply
privateRoute @2 :PrivateRoute; # embedded private route to be used for reply
sender @0 :Void; # sender
privateRoute @1 :PrivateRoute; # embedded private route to be used for reply
}
detail :union {
# Direct operations
statusQ @3 :OperationStatusQ;
findNodeQ @4 :OperationFindNodeQ;
statusQ @2 :OperationStatusQ;
findNodeQ @3 :OperationFindNodeQ;
# Routable operations
getValueQ @5 :OperationGetValueQ;
setValueQ @6 :OperationSetValueQ;
watchValueQ @7 :OperationWatchValueQ;
supplyBlockQ @8 :OperationSupplyBlockQ;
findBlockQ @9 :OperationFindBlockQ;
getValueQ @4 :OperationGetValueQ;
setValueQ @5 :OperationSetValueQ;
watchValueQ @6 :OperationWatchValueQ;
supplyBlockQ @7 :OperationSupplyBlockQ;
findBlockQ @8 :OperationFindBlockQ;
# Tunnel operations
startTunnelQ @10 :OperationStartTunnelQ;
completeTunnelQ @11 :OperationCompleteTunnelQ;
cancelTunnelQ @12 :OperationCancelTunnelQ;
startTunnelQ @9 :OperationStartTunnelQ;
completeTunnelQ @10 :OperationCompleteTunnelQ;
cancelTunnelQ @11 :OperationCancelTunnelQ;
}
}
@ -480,10 +491,10 @@ struct Answer {
struct Operation {
opId @0 :UInt64; # Random RPC ID. Must be random to foil reply forgery attacks.
senderInfo @1 :SignedNodeInfo; # (optional) SignedNodeInfo for the sender to be cached by the receiver.
kind :union {
question @1 :Question;
statement @2 :Statement;
answer @3 :Answer;
question @2 :Question;
statement @3 :Statement;
answer @4 :Answer;
}
}

View File

@ -552,33 +552,29 @@ impl NetworkManager {
// Run the rolling transfers task
self.unlocked_inner.rolling_transfers_task.tick().await?;
// Process global peer scope ticks
// These only make sense when connected to the actual internet and not just the local network
// Must have at least one outbound protocol enabled, and one global peer scope address family enabled
let global_peer_scope_enabled =
!protocol_config.outbound.is_empty() && !protocol_config.family_global.is_empty();
if global_peer_scope_enabled {
// Run the relay management task
self.unlocked_inner.relay_management_task.tick().await?;
// Run the relay management task
self.unlocked_inner.relay_management_task.tick().await?;
// If routing table has no live entries, then add the bootstrap nodes to it
let live_entry_count = routing_table.get_entry_count(BucketEntryState::Unreliable);
if live_entry_count == 0 {
self.unlocked_inner.bootstrap_task.tick().await?;
}
// If we still don't have enough peers, find nodes until we do
let min_peer_count = {
let c = self.config.get();
c.network.dht.min_peer_count as usize
};
if live_entry_count < min_peer_count {
self.unlocked_inner.peer_minimum_refresh_task.tick().await?;
}
// Ping validate some nodes to groom the table
self.unlocked_inner.ping_validator_task.tick().await?;
// See how many live PublicInternet entries we have
let live_public_internet_entry_count = routing_table.get_entry_count(
RoutingTableSet::only(RoutingTable::PublicInternet),
BucketEntryState::Unreliable,
);
let min_peer_count = {
let c = self.config.get();
c.network.dht.min_peer_count as usize
};
// If none, then add the bootstrap nodes to it
if live_public_internet_entry_count == 0 {
self.unlocked_inner.bootstrap_task.tick().await?;
}
// If we still don't have enough peers, find nodes until we do
else if live_public_internet_entry_count < min_peer_count {
self.unlocked_inner.peer_minimum_refresh_task.tick().await?;
}
// Ping validate some nodes to groom the table
self.unlocked_inner.ping_validator_task.tick().await?;
// Run the routing table tick
routing_table.tick().await?;
@ -605,21 +601,18 @@ impl NetworkManager {
}
// Get our node's capabilities
pub fn generate_node_status(&self) -> NodeStatus {
let peer_info = self
fn generate_public_internet_node_status(&self) -> PublicInternetNodeStatus {
let node_info = self
.routing_table()
.get_own_peer_info(RoutingDomain::PublicInternet);
.get_own_node_info(RoutingDomain::PublicInternet);
let will_route = peer_info.signed_node_info.node_info.can_inbound_relay(); // xxx: eventually this may have more criteria added
let will_tunnel = peer_info.signed_node_info.node_info.can_inbound_relay(); // xxx: we may want to restrict by battery life and network bandwidth at some point
let will_signal = peer_info.signed_node_info.node_info.can_signal();
let will_relay = peer_info.signed_node_info.node_info.can_inbound_relay();
let will_validate_dial_info = peer_info
.signed_node_info
.node_info
.can_validate_dial_info();
let will_route = node_info.can_inbound_relay(); // xxx: eventually this may have more criteria added
let will_tunnel = node_info.can_inbound_relay(); // xxx: we may want to restrict by battery life and network bandwidth at some point
let will_signal = node_info.can_signal();
let will_relay = node_info.can_inbound_relay();
let will_validate_dial_info = node_info.can_validate_dial_info();
NodeStatus {
PublicInternetNodeStatus {
will_route,
will_tunnel,
will_signal,
@ -627,6 +620,30 @@ impl NetworkManager {
will_validate_dial_info,
}
}
fn generate_local_network_node_status(&self) -> LocalNetworkNodeStatus {
let node_info = self
.routing_table()
.get_own_node_info(RoutingDomain::LocalNetwork);
let will_relay = node_info.can_inbound_relay();
let will_validate_dial_info = node_info.can_validate_dial_info();
LocalNetworkNodeStatus {
will_relay,
will_validate_dial_info,
}
}
pub fn generate_node_status(&self, routing_domain: RoutingDomain) -> NodeStatus {
match routing_domain {
RoutingDomain::PublicInternet => {
NodeStatus::PublicInternet(self.generate_public_internet_node_status())
}
RoutingDomain::LocalNetwork => {
NodeStatus::LocalNetwork(self.generate_local_network_node_status())
}
}
}
// Return what protocols we have enabled
pub fn get_protocol_config(&self) -> ProtocolConfig {
@ -650,6 +667,12 @@ impl NetworkManager {
.clone(),
}
}
pub fn get_inbound_node_ref_filter(&self, routing_domain: RoutingDomain) -> NodeRefFilter {
let dif = self.get_inbound_dial_info_filter(routing_domain);
NodeRefFilter::new()
.with_routing_domain(routing_domain)
.with_dial_info_filter(dif)
}
// Return a dial info filter for what we can send out
pub fn get_outbound_dial_info_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
@ -667,6 +690,12 @@ impl NetworkManager {
.clone(),
}
}
pub fn get_outbound_node_ref_filter(&self, routing_domain: RoutingDomain) -> NodeRefFilter {
let dif = self.get_outbound_dial_info_filter(routing_domain);
NodeRefFilter::new()
.with_routing_domain(routing_domain)
.with_dial_info_filter(dif)
}
// Generates a multi-shot/normal receipt
#[instrument(level = "trace", skip(self, extra_data, callback), err)]
@ -949,12 +978,11 @@ impl NetworkManager {
#[instrument(level = "trace", skip(self), ret)]
fn get_contact_method_public(&self, target_node_ref: NodeRef) -> ContactMethod {
// Scope noderef down to protocols we can do outbound
let public_outbound_dif = self.get_outbound_dial_info_filter(RoutingDomain::PublicInternet);
let target_node_ref = target_node_ref.filtered_clone(public_outbound_dif.clone());
let public_outbound_nrf = self.get_outbound_node_ref_filter(RoutingDomain::PublicInternet);
let target_node_ref = target_node_ref.filtered_clone(public_outbound_nrf.clone());
// Get the best match internet dial info if we have it
let opt_target_public_did =
target_node_ref.first_filtered_dial_info_detail(Some(RoutingDomain::PublicInternet));
let opt_target_public_did = target_node_ref.first_filtered_dial_info_detail();
if let Some(target_public_did) = opt_target_public_did {
// Do we need to signal before going inbound?
if !target_public_did.class.requires_signal() {
@ -966,12 +994,9 @@ impl NetworkManager {
// Note that .relay() never returns our own node. We can't relay to ourselves.
if let Some(inbound_relay_nr) = target_node_ref.relay(RoutingDomain::PublicInternet) {
// Scope down to protocols we can do outbound
let inbound_relay_nr = inbound_relay_nr.filtered_clone(public_outbound_dif.clone());
let inbound_relay_nr = inbound_relay_nr.filtered_clone(public_outbound_nrf.clone());
// Can we reach the inbound relay?
if inbound_relay_nr
.first_filtered_dial_info_detail(Some(RoutingDomain::PublicInternet))
.is_some()
{
if inbound_relay_nr.first_filtered_dial_info_detail().is_some() {
// Can we receive anything inbound ever?
let our_network_class = self
.get_network_class(RoutingDomain::PublicInternet)
@ -985,11 +1010,11 @@ impl NetworkManager {
let reverse_dif = self
.get_inbound_dial_info_filter(RoutingDomain::PublicInternet)
.filtered(
target_node_ref
&target_node_ref
.node_info_outbound_filter(RoutingDomain::PublicInternet),
);
if let Some(reverse_did) = routing_table.first_filtered_dial_info_detail(
Some(RoutingDomain::PublicInternet),
RoutingDomainSet::only(RoutingDomain::PublicInternet),
&reverse_dif,
) {
// Ensure we aren't on the same public IP address (no hairpin nat)
@ -1010,16 +1035,16 @@ impl NetworkManager {
// Does the target have a direct udp dialinfo we can reach?
let udp_target_nr = target_node_ref.filtered_clone(
DialInfoFilter::global().with_protocol_type(ProtocolType::UDP),
NodeRefFilter::new().with_protocol_type(ProtocolType::UDP),
);
if let Some(target_udp_dialinfo_detail) = udp_target_nr
.first_filtered_dial_info_detail(Some(RoutingDomain::PublicInternet))
if let Some(target_udp_dialinfo_detail) =
udp_target_nr.first_filtered_dial_info_detail()
{
// Does the self node have a direct udp dialinfo the target can reach?
let inbound_udp_dif = self
.get_inbound_dial_info_filter(RoutingDomain::PublicInternet)
.filtered(
target_node_ref
&target_node_ref
.node_info_outbound_filter(RoutingDomain::PublicInternet),
)
.filtered(
@ -1027,7 +1052,7 @@ impl NetworkManager {
);
if let Some(self_udp_dialinfo_detail) = routing_table
.first_filtered_dial_info_detail(
Some(RoutingDomain::PublicInternet),
RoutingDomainSet::only(RoutingDomain::PublicInternet),
&inbound_udp_dif,
)
{
@ -1056,7 +1081,7 @@ impl NetworkManager {
{
// Can we reach the full relay?
if target_inbound_relay_nr
.first_filtered_dial_info_detail(Some(RoutingDomain::PublicInternet))
.first_filtered_dial_info_detail()
.is_some()
{
return ContactMethod::InboundRelay(target_inbound_relay_nr);
@ -1078,15 +1103,13 @@ impl NetworkManager {
fn get_contact_method_local(&self, target_node_ref: NodeRef) -> ContactMethod {
// Scope noderef down to protocols we can do outbound
let local_outbound_dif = self.get_outbound_dial_info_filter(RoutingDomain::LocalNetwork);
let target_node_ref = target_node_ref.filtered_clone(local_outbound_dif);
let target_node_ref = target_node_ref.filtered_clone(NodeRefFilter::local_outbound_dif);
// Get the best matching local direct dial info if we have it
// ygjghhtiygiukuymyg
if target_node_ref.is_filter_dead() {
return ContactMethod::Unreachable;
}
let opt_target_local_did =
target_node_ref.first_filtered_dial_info_detail(Some(RoutingDomain::LocalNetwork));
let opt_target_local_did = target_node_ref.first_filtered_dial_info_detail();
if let Some(target_local_did) = opt_target_local_did {
return ContactMethod::Direct(target_local_did.dial_info);
}
@ -1096,21 +1119,18 @@ impl NetworkManager {
// Figure out how to reach a node
#[instrument(level = "trace", skip(self), ret)]
pub(crate) fn get_contact_method(&self, target_node_ref: NodeRef) -> ContactMethod {
// Try local first
let out = self.get_contact_method_local(target_node_ref.clone());
if !matches!(out, ContactMethod::Unreachable) {
return out;
}
let routing_domain = match target_node_ref.best_routing_domain() {
Some(rd) => rd,
None => {
log_net!("no routing domain for node {:?}", target_node_ref);
return ContactMethod::Unreachable;
}
};
// Try public next
let out = self.get_contact_method_public(target_node_ref.clone());
if !matches!(out, ContactMethod::Unreachable) {
return out;
match routing_domain {
RoutingDomain::LocalNetwork => self.get_contact_method_local(target_node_ref),
RoutingDomain::PublicInternet => self.get_contact_method_public(target_node_ref),
}
// Otherwise, we can't reach this node
log_net!("unable to reach node {:?}", target_node_ref);
ContactMethod::Unreachable
}
// Send a reverse connection signal and wait for the return receipt over it
@ -1461,6 +1481,18 @@ impl NetworkManager {
return Ok(false);
}
// Get the routing domain for this data
let routing_domain = match self
.routing_table()
.routing_domain_for_address(connection_descriptor.remote().address())
{
Some(rd) => rd,
None => {
log_net!(debug "no routing domain for envelope received from {:?}", connection_descriptor);
return Ok(false);
}
};
// Is this a direct bootstrap request instead of an envelope?
if data[0..4] == *BOOT_MAGIC {
network_result_value_or_log!(debug self.handle_boot_request(connection_descriptor).await? => {});
@ -1588,7 +1620,13 @@ impl NetworkManager {
// xxx: deal with spoofing and flooding here?
// Pass message to RPC system
rpc.enqueue_message(envelope, body, source_noderef, connection_descriptor)?;
rpc.enqueue_message(
envelope,
body,
source_noderef,
connection_descriptor,
routing_domain,
)?;
// Inform caller that we dealt with the envelope locally
Ok(true)
@ -1669,7 +1707,7 @@ impl NetworkManager {
// Determine if a local IP address has changed
// this means we should restart the low level network and and recreate all of our dial info
// Wait until we have received confirmation from N different peers
pub fn report_local_socket_address(
pub fn report_local_network_socket_address(
&self,
_socket_address: SocketAddress,
_connection_descriptor: ConnectionDescriptor,
@ -1681,7 +1719,7 @@ impl NetworkManager {
// Determine if a global IP address has changed
// this means we should recreate our public dial info if it is not static and rediscover it
// Wait until we have received confirmation from N different peers
pub fn report_global_socket_address(
pub fn report_public_internet_socket_address(
&self,
socket_address: SocketAddress, // the socket address as seen by the remote peer
connection_descriptor: ConnectionDescriptor, // the connection descriptor used

View File

@ -603,6 +603,29 @@ impl Network {
// initialize interfaces
self.unlocked_inner.interfaces.refresh().await?;
// build the set of networks we should consider for the 'LocalNetwork' routing domain
let mut local_networks: HashSet<(IpAddr, IpAddr)> = HashSet::new();
self.unlocked_inner
.interfaces
.with_interfaces(|interfaces| {
for (name, intf) in interfaces {
// Skip networks that we should never encounter
if intf.is_loopback() || !intf.is_running() {
return;
}
// Add network to local networks table
for addr in intf.addrs {
let netmask = addr.if_addr().netmask();
let network_ip = ipaddr_apply_netmask(addr.if_addr().ip(), netmask);
local_networks.insert((network_ip, netmask));
}
}
});
let local_networks: Vec<(IpAddr, IpAddr)> = local_networks.into_iter().collect();
self.unlocked_inner
.routing_table
.configure_local_network_routing_domain(local_networks);
// determine if we have ipv4/ipv6 addresses
{
let mut inner = self.inner.lock();

View File

@ -367,13 +367,12 @@ xxx write routing table sieve for routing domain from dialinfo and local network
pub(super) async fn start_ws_listeners(&self) -> EyreResult<()> {
trace!("starting ws listeners");
let routing_table = self.routing_table();
let (listen_address, url, path, enable_local_peer_scope, detect_address_changes) = {
let (listen_address, url, path, detect_address_changes) = {
let c = self.config.get();
(
c.network.protocol.ws.listen_address.clone(),
c.network.protocol.ws.url.clone(),
c.network.protocol.ws.path.clone(),
c.network.enable_local_peer_scope,
c.network.detect_address_changes,
)
};
@ -586,12 +585,11 @@ xxx write routing table sieve for routing domain from dialinfo and local network
trace!("starting tcp listeners");
let routing_table = self.routing_table();
let (listen_address, public_address, enable_local_peer_scope, detect_address_changes) = {
let (listen_address, public_address, detect_address_changes) = {
let c = self.config.get();
(
c.network.protocol.tcp.listen_address.clone(),
c.network.protocol.tcp.public_address.clone(),
c.network.enable_local_peer_scope,
c.network.detect_address_changes,
)
};

View File

@ -197,7 +197,11 @@ impl NetworkManager {
let routing_table = routing_table.clone();
unord.push(
// lets ask bootstrap to find ourselves now
async move { routing_table.reverse_find_node(nr, true).await },
async move {
routing_table
.reverse_find_node(RoutingDomain::PublicInternet, nr, true)
.await
},
);
}
}
@ -299,7 +303,9 @@ impl NetworkManager {
unord.push(async move {
// Need VALID signed peer info, so ask bootstrap to find_node of itself
// which will ensure it has the bootstrap's signed peer info as part of the response
let _ = routing_table.find_target(nr.clone()).await;
let _ = routing_table
.find_target(RoutingDomain::PublicInternet, nr.clone())
.await;
// Ensure we got the signed peer info
if !nr.has_valid_signed_node_info(Some(RoutingDomain::PublicInternet)) {
@ -310,7 +316,9 @@ impl NetworkManager {
// If this node info is invalid, it will time out after being unpingable
} else {
// otherwise this bootstrap is valid, lets ask it to find ourselves now
routing_table.reverse_find_node(nr, true).await
routing_table
.reverse_find_node(RoutingDomain::PublicInternet, nr, true)
.await
}
});
}
@ -324,33 +332,35 @@ impl NetworkManager {
// Ping each node in the routing table if they need to be pinged
// to determine their reliability
#[instrument(level = "trace", skip(self), err)]
pub(super) async fn ping_validator_task_routine(
self,
stop_token: StopToken,
_last_ts: u64,
fn ping_validator_public_internet(
&self,
cur_ts: u64,
unord: &mut FuturesUnordered,
) -> EyreResult<()> {
let rpc = self.rpc_processor();
let routing_table = self.routing_table();
let mut unord = FuturesUnordered::new();
// Get all nodes needing pings in the PublicInternet routing domain
let node_refs = routing_table.get_nodes_needing_ping(RoutingDomain::PublicInternet, cur_ts);
let node_refs = routing_table.get_nodes_needing_ping(cur_ts);
// Look up any NAT mappings we may need to try to preserve with keepalives
let mut mapped_port_info = routing_table.get_mapped_port_info();
let opt_public_internet_relay_nr = routing_table.relay_node(RoutingDomain::PublicInternet);
let opt_public_internet_relay_id = opt_public_internet_relay_nr.map(|nr| nr.node_id());
// let opt_local_network_relay_nr = routing_table.relay_node(RoutingDomain::LocalNetwork);
// let opt_local_network_relay_id = opt_local_network_relay_nr.map(|nr| nr.node_id());
// Public Internet Routing Domain
// Get the PublicInternet relay if we are using one
let opt_relay_nr = routing_table.relay_node(RoutingDomain::PublicInternet);
let opt_relay_id = opt_relay_nr.map(|nr| nr.node_id());
// Get our publicinternet dial info
let dids = routing_table.all_filtered_dial_info_details(
Some(RoutingDomain::PublicInternet),
&DialInfoFilter::global(),
RoutingDomainSet::only(RoutingDomain::PublicInternet),
&DialInfoFilter::all(),
);
// For all nodes needing pings, figure out how many and over what protocols
for nr in node_refs {
let rpc = rpc.clone();
if Some(nr.node_id()) == opt_public_internet_relay_id {
// If this is a relay, let's check for NAT keepalives
let mut did_pings = false;
if Some(nr.node_id()) == opt_relay_id {
// Relay nodes get pinged over all protocols we have inbound dialinfo for
// This is so we can preserve the inbound NAT mappings at our router
for did in &dids {
@ -370,19 +380,84 @@ impl NetworkManager {
};
if needs_ping {
let rpc = rpc.clone();
let dif = did.dial_info.make_filter(true);
let dif = did.dial_info.make_filter();
let nr_filtered = nr.filtered_clone(dif);
log_net!("--> Keepalive ping to {:?}", nr_filtered);
unord.push(async move { rpc.rpc_call_status(nr_filtered).await }.boxed());
unord.push(
async move {
rpc.rpc_call_status(Some(routing_domain), nr_filtered).await
}
.boxed(),
);
did_pings = true;
}
}
} else {
// Just do a single ping with the best protocol for all the other nodes
unord.push(async move { rpc.rpc_call_status(nr).await }.boxed());
}
// Just do a single ping with the best protocol for all the other nodes,
// ensuring that we at least ping a relay with -something- even if we didnt have
// any mapped ports to preserve
if !did_pings {
let rpc = rpc.clone();
unord.push(
async move { rpc.rpc_call_status(Some(routing_domain), nr).await }.boxed(),
);
}
}
// Wait for futures to complete
Ok(())
}
// Ping each node in the LocalNetwork routing domain if they
// need to be pinged to determine their reliability
#[instrument(level = "trace", skip(self), err)]
fn ping_validator_local_network(
&self,
cur_ts: u64,
unord: &mut FuturesUnordered,
) -> EyreResult<()> {
let rpc = self.rpc_processor();
let routing_table = self.routing_table();
// Get all nodes needing pings in the LocalNetwork routing domain
let node_refs = routing_table.get_nodes_needing_ping(RoutingDomain::LocalNetwork, cur_ts);
// Get our LocalNetwork dial info
let dids = routing_table.all_filtered_dial_info_details(
RoutingDomainSet::only(RoutingDomain::LocalNetwork),
&DialInfoFilter::all(),
);
// For all nodes needing pings, figure out how many and over what protocols
for nr in node_refs {
let rpc = rpc.clone();
// Just do a single ping with the best protocol for all the nodes
unord.push(async move { rpc.rpc_call_status(Some(routing_domain), nr).await }.boxed());
}
Ok(())
}
// Ping each node in the routing table if they need to be pinged
// to determine their reliability
#[instrument(level = "trace", skip(self), err)]
pub(super) async fn ping_validator_task_routine(
self,
stop_token: StopToken,
_last_ts: u64,
cur_ts: u64,
) -> EyreResult<()> {
let rpc = self.rpc_processor();
let routing_table = self.routing_table();
let mut unord = FuturesUnordered::new();
// PublicInternet
self.ping_validator_public_internet(cur_ts, &mut unord)?;
// LocalNetwork
self.ping_validator_local_network(cur_ts, &mut unord)?;
// Wait for ping futures to complete in parallel
while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {}
Ok(())
@ -390,24 +465,38 @@ impl NetworkManager {
// Ask our remaining peers to give us more peers before we go
// back to the bootstrap servers to keep us from bothering them too much
// This only adds PublicInternet routing domain peers. The discovery
// mechanism for LocalNetwork suffices for locating all the local network
// peers that are available. This, however, may query other LocalNetwork
// nodes for their PublicInternet peers, which is a very fast way to get
// a new node online.
#[instrument(level = "trace", skip(self), err)]
pub(super) async fn peer_minimum_refresh_task_routine(
self,
stop_token: StopToken,
) -> EyreResult<()> {
let routing_table = self.routing_table();
let cur_ts = intf::get_timestamp();
let mut unord = FuturesOrdered::new();
let min_peer_count = {
let c = self.config.get();
c.network.dht.min_peer_count as usize
};
// get list of all peers we know about, even the unreliable ones, and ask them to find nodes close to our node too
let noderefs = routing_table.get_all_nodes(cur_ts);
// do peer minimum search concurrently
let mut unord = FuturesUnordered::new();
// For the PublicInternet routing domain, get list of all peers we know about
// even the unreliable ones, and ask them to find nodes close to our node too
let noderefs = routing_table.find_fastest_nodes(
min_peer_count,
None,
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
},
);
for nr in noderefs {
log_net!("--- peer minimum search with {:?}", nr);
let routing_table = routing_table.clone();
unord.push(async move { routing_table.reverse_find_node(nr, false).await });
}
// do peer minimum search in order from fastest to slowest
while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {}
Ok(())

View File

@ -39,7 +39,7 @@ pub enum BucketEntryState {
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
struct LastConnectionKey(ProtocolType, AddressType);
struct LastConnectionKey(RoutingDomain, ProtocolType, AddressType);
/// Bucket entry information specific to the LocalNetwork RoutingDomain
#[derive(Debug)]
@ -136,10 +136,18 @@ impl BucketEntryInner {
move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2)
}
pub fn clear_signed_node_info(&mut self, routing_domain: RoutingDomain) {
// Get the correct signed_node_info for the chosen routing domain
let opt_current_sni = match routing_domain {
RoutingDomain::LocalNetwork => &mut self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &mut self.public_internet.signed_node_info,
};
*opt_current_sni = None;
}
// Retuns true if the node info changed
pub fn update_signed_node_info(
&mut self,
routing_domain: RoutingDomain,
signed_node_info: SignedNodeInfo,
allow_invalid_signature: bool,
) {
@ -150,7 +158,7 @@ impl BucketEntryInner {
}
// Get the correct signed_node_info for the chosen routing domain
let opt_current_sni = match routing_domain {
let opt_current_sni = match signed_node_info.routing_domain {
RoutingDomain::LocalNetwork => &mut self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &mut self.public_internet.signed_node_info,
};
@ -160,7 +168,8 @@ impl BucketEntryInner {
// If the timestamp hasn't changed or is less, ignore this update
if signed_node_info.timestamp <= current_sni.timestamp {
// If we received a node update with the same timestamp
// we can make this node live again, but only if our network hasn't changed
// we can make this node live again, but only if our network has recently changed
// which may make nodes that were unreachable now reachable with the same dialinfo
if !self.updated_since_last_network_change
&& signed_node_info.timestamp == current_sni.timestamp
{
@ -185,50 +194,34 @@ impl BucketEntryInner {
self.touch_last_seen(intf::get_timestamp());
}
pub fn has_node_info(&self, opt_routing_domain: Option<RoutingDomain>) -> bool {
if let Some(routing_domain) = opt_routing_domain {
pub fn has_node_info(&self, routing_domain_set: RoutingDomainSet) -> bool {
for routing_domain in routing_domain_set {
// Get the correct signed_node_info for the chosen routing domain
let opt_current_sni = match routing_domain {
RoutingDomain::LocalNetwork => &mut self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &mut self.public_internet.signed_node_info,
};
opt_current_sni.is_some()
} else {
if self.local_network.signed_node_info.is_some() {
true
} else if self.public_internet.signed_node_info.is_some() {
true
} else {
false
if opt_current_sni.is_some() {
return true;
}
}
false
}
pub fn has_valid_signed_node_info(&self, opt_routing_domain: Option<RoutingDomain>) -> bool {
if let Some(routing_domain) = opt_routing_domain {
pub fn has_valid_signed_node_info(&self, routing_domain_set: RoutingDomainSet) -> bool {
for routing_domain in routing_domain_set {
// Get the correct signed_node_info for the chosen routing domain
let opt_current_sni = match routing_domain {
RoutingDomain::LocalNetwork => &mut self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &mut self.public_internet.signed_node_info,
};
if let Some(sni) = opt_current_sni {
sni.is_valid()
} else {
false
}
} else {
if let Some(sni) = self.local_network.signed_node_info {
if sni.is_valid() {
return true;
}
}
if let Some(sni) = self.public_internet.signed_node_info {
if sni.is_valid() {
return true;
}
}
false
}
false
}
pub fn node_info(&self, routing_domain: RoutingDomain) -> Option<NodeInfo> {
@ -250,8 +243,28 @@ impl BucketEntryInner {
})
}
fn descriptor_to_key(last_connection: ConnectionDescriptor) -> LastConnectionKey {
pub fn best_routing_domain(
&self,
routing_domain_set: RoutingDomainSet,
) -> Option<RoutingDomain> {
for routing_domain in routing_domain_set {
let opt_current_sni = match routing_domain {
RoutingDomain::LocalNetwork => &mut self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &mut self.public_internet.signed_node_info,
};
if let Some(sni) = opt_current_sni {
if sni.is_valid() {
return Some(routing_domain);
}
}
}
None
}
fn descriptor_to_key(&self, last_connection: ConnectionDescriptor) -> LastConnectionKey {
let routing_domain = self.routing_domain_for_address(last_connection.remote().address());
LastConnectionKey(
routing_domain,
last_connection.protocol_type(),
last_connection.address_type(),
)
@ -259,7 +272,7 @@ impl BucketEntryInner {
// Stores a connection descriptor in this entry's table of last connections
pub fn set_last_connection(&mut self, last_connection: ConnectionDescriptor, timestamp: u64) {
let key = Self::descriptor_to_key(last_connection);
let key = self.descriptor_to_key(last_connection);
self.last_connections
.insert(key, (last_connection, timestamp));
}
@ -269,18 +282,21 @@ impl BucketEntryInner {
self.last_connections.clear();
}
// Gets the best 'last connection' that matches a set of protocol types and address types
// Gets the best 'last connection' that matches a set of routing domain, protocol types and address types
pub fn last_connection(
&self,
routing_domain_set: RoutingDomainSet,
dial_info_filter: Option<DialInfoFilter>,
) -> Option<(ConnectionDescriptor, u64)> {
// Iterate peer scopes and protocol types and address type in order to ensure we pick the preferred protocols if all else is the same
let dif = dial_info_filter.unwrap_or_default();
for pt in dif.protocol_type_set {
for at in dif.address_type_set {
let key = LastConnectionKey(pt, at);
if let Some(v) = self.last_connections.get(&key) {
return Some(*v);
for rd in routing_domain_set {
for pt in dif.protocol_type_set {
for at in dif.address_type_set {
let key = LastConnectionKey(rd, pt, at);
if let Some(v) = self.last_connections.get(&key) {
return Some(*v);
}
}
}
}
@ -325,7 +341,7 @@ impl BucketEntryInner {
.node_status
.map(|ln| NodeStatus::LocalNetwork(ln)),
RoutingDomain::PublicInternet => self
.local_network
.public_internet
.node_status
.map(|pi| NodeStatus::PublicInternet(pi)),
}

View File

@ -122,7 +122,7 @@ impl RoutingTable {
let entry = v.unwrap();
entry.with(|e| {
// skip nodes on our local network here
if e.has_node_info(Some(RoutingDomain::LocalNetwork)) {
if e.has_node_info(RoutingDomainSet::only(RoutingDomain::LocalNetwork)) {
return false;
}
@ -130,25 +130,22 @@ impl RoutingTable {
let filter = |n: NodeInfo| {
let mut keep = false;
for did in n.dial_info_detail_list {
if did.dial_info.is_global() {
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
for (n, protocol_type) in protocol_types.iter().enumerate() {
if nodes_proto_v4[n] < max_per_type
&& did.dial_info.protocol_type() == *protocol_type
{
nodes_proto_v4[n] += 1;
keep = true;
}
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
for (n, protocol_type) in protocol_types.iter().enumerate() {
if nodes_proto_v4[n] < max_per_type
&& did.dial_info.protocol_type() == *protocol_type
{
nodes_proto_v4[n] += 1;
keep = true;
}
} else if matches!(did.dial_info.address_type(), AddressType::IPV6)
{
for (n, protocol_type) in protocol_types.iter().enumerate() {
if nodes_proto_v6[n] < max_per_type
&& did.dial_info.protocol_type() == *protocol_type
{
nodes_proto_v6[n] += 1;
keep = true;
}
}
} else if matches!(did.dial_info.address_type(), AddressType::IPV6) {
for (n, protocol_type) in protocol_types.iter().enumerate() {
if nodes_proto_v6[n] < max_per_type
&& did.dial_info.protocol_type() == *protocol_type
{
nodes_proto_v6[n] += 1;
keep = true;
}
}
}
@ -168,48 +165,16 @@ impl RoutingTable {
)
}
// Get our own node's peer info (public node info) so we can share it with other nodes
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
PeerInfo::new(
NodeId::new(self.node_id()),
self.get_own_signed_node_info(routing_domain),
)
}
pub fn get_own_signed_node_info(&self, routing_domain: RoutingDomain) -> SignedNodeInfo {
let node_id = NodeId::new(self.node_id());
let secret = self.node_id_secret();
SignedNodeInfo::with_secret(self.get_own_node_info(routing_domain), node_id, &secret)
.unwrap()
}
pub fn get_own_node_info(&self, routing_domain: RoutingDomain) -> NodeInfo {
let netman = self.network_manager();
let relay_node = self.relay_node(routing_domain);
let pc = netman.get_protocol_config();
NodeInfo {
network_class: netman
.get_network_class(routing_domain)
.unwrap_or(NetworkClass::Invalid),
outbound_protocols: pc.outbound,
address_types: pc.family_global,
min_version: MIN_VERSION,
max_version: MAX_VERSION,
dial_info_detail_list: self.dial_info_details(routing_domain),
relay_peer_info: relay_node.and_then(|rn| rn.peer_info(routing_domain).map(Box::new)),
}
}
pub fn filter_has_valid_signed_node_info(
&self,
v: Option<Arc<BucketEntry>>,
own_peer_info_is_valid: bool,
opt_routing_domain: Option<RoutingDomain>,
routing_domain_set: RoutingDomainSet,
) -> bool {
let routing_table = self.clone();
match v {
None => own_peer_info_is_valid,
Some(entry) => entry.with(|e| e.has_valid_signed_node_info(opt_routing_domain)),
Some(entry) => entry.with(|e| e.has_valid_signed_node_info(routing_domain_set)),
}
}
@ -425,7 +390,7 @@ impl RoutingTable {
let mut protocol_to_port =
BTreeMap::<(ProtocolType, AddressType), (LowLevelProtocolType, u16)>::new();
let our_dids = self.all_filtered_dial_info_details(
Some(RoutingDomain::PublicInternet),
RoutingDomainSet::only(RoutingDomain::PublicInternet),
&DialInfoFilter::all(),
);
for did in our_dids {
@ -452,12 +417,12 @@ impl RoutingTable {
// Get all our outbound protocol/address types
let outbound_dif = self
.network_manager()
.get_outbound_dial_info_filter(RoutingDomain::PublicInternet);
.get_outbound_node_ref_filter(RoutingDomain::PublicInternet);
let mapped_port_info = self.get_mapped_port_info();
move |e: &BucketEntryInner| {
// Ensure this node is not on the local network
if e.has_node_info(Some(RoutingDomain::LocalNetwork)) {
if e.has_node_info(RoutingDomainSet::only(RoutingDomain::LocalNetwork)) {
return false;
}
@ -542,11 +507,7 @@ impl RoutingTable {
}
#[instrument(level = "trace", skip(self), ret)]
pub fn register_find_node_answer(
&self,
routing_domain: RoutingDomain,
peers: Vec<PeerInfo>,
) -> Vec<NodeRef> {
pub fn register_find_node_answer(&self, peers: Vec<PeerInfo>) -> Vec<NodeRef> {
let node_id = self.node_id();
// register nodes we'd found
@ -566,7 +527,7 @@ impl RoutingTable {
// register the node if it's new
if let Some(nr) = self.register_node_with_signed_node_info(
routing_domain,
RoutingDomain::PublicInternet,
p.node_id.key,
p.signed_node_info.clone(),
false,
@ -580,7 +541,6 @@ impl RoutingTable {
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn find_node(
&self,
routing_domain: RoutingDomain,
node_ref: NodeRef,
node_id: DHTKey,
) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
@ -589,18 +549,13 @@ impl RoutingTable {
let res = network_result_try!(
rpc_processor
.clone()
.rpc_call_find_node(
Destination::direct(node_ref.clone()).with_routing_domain(routing_domain),
node_id,
None,
rpc_processor.make_respond_to_sender(routing_domain, node_ref.clone()),
)
.rpc_call_find_node(Destination::direct(node_ref), node_id,)
.await?
);
// register nodes we'd found
Ok(NetworkResult::value(
self.register_find_node_answer(routing_domain, res.answer),
self.register_find_node_answer(res.answer),
))
}

View File

@ -3,6 +3,7 @@ mod bucket_entry;
mod debug;
mod find_nodes;
mod node_ref;
mod routing_domains;
mod stats_accounting;
mod tasks;
@ -17,34 +18,31 @@ pub use debug::*;
pub use find_nodes::*;
use hashlink::LruCache;
pub use node_ref::*;
pub use routing_domains::*;
pub use stats_accounting::*;
const RECENT_PEERS_TABLE_SIZE: usize = 64;
//////////////////////////////////////////////////////////////////////////
#[derive(Debug, Default)]
pub struct RoutingDomainDetail {
relay_node: Option<NodeRef>,
dial_info_details: Vec<DialInfoDetail>,
}
#[derive(Debug, Clone, Copy)]
pub struct RecentPeersEntry {
last_connection: ConnectionDescriptor,
}
/// RoutingTable rwlock-internal data
struct RoutingTableInner {
network_manager: NetworkManager,
node_id: DHTKey, // The current node's public DHT key
// The current node's public DHT key
node_id: DHTKey,
node_id_secret: DHTKeySecret, // The current node's DHT key secret
buckets: Vec<Bucket>, // Routing table buckets that hold entries
kick_queue: BTreeSet<usize>, // Buckets to kick on our next kick task
bucket_entry_count: usize, // A fast counter for the number of entries in the table, total
public_internet_routing_domain: RoutingDomainDetail, // The dial info we use on the public internet
local_network_routing_domain: RoutingDomainDetail, // The dial info we use on the local network
public_internet_routing_domain: PublicInternetRoutingDomainDetail, // The public internet
local_network_routing_domain: LocalInternetRoutingDomainDetail, // The dial info we use on the local network
self_latency_stats_accounting: LatencyStatsAccounting, // Interim accounting mechanism for this node's RPC latency to any other node
self_transfer_stats_accounting: TransferStatsAccounting, // Interim accounting mechanism for the total bandwidth to/from this node
@ -80,8 +78,8 @@ impl RoutingTable {
node_id_secret: DHTKeySecret::default(),
buckets: Vec::new(),
kick_queue: BTreeSet::default(),
public_internet_routing_domain: RoutingDomainDetail::default(),
local_network_routing_domain: RoutingDomainDetail::default(),
public_internet_routing_domain: PublicInternetRoutingDomainDetail::default(),
local_network_routing_domain: LocalInternetRoutingDomainDetail::default(),
bucket_entry_count: 0,
self_latency_stats_accounting: LatencyStatsAccounting::new(),
self_transfer_stats_accounting: TransferStatsAccounting::new(),
@ -140,9 +138,21 @@ impl RoutingTable {
self.inner.read().node_id_secret
}
pub fn routing_domain_for_address(&self, address: Address) -> Option<RoutingDomain> {
let inner = self.inner.read();
for rd in RoutingDomain::all() {
let can_contain =
Self::with_routing_domain(&*inner, rd, |rdd| rdd.can_contain_address(address));
if can_contain {
return Some(rd);
}
}
None
}
fn with_routing_domain<F, R>(inner: &RoutingTableInner, domain: RoutingDomain, f: F) -> R
where
F: FnOnce(&RoutingDomainDetail) -> R,
F: FnOnce(&dyn RoutingDomainDetail) -> R,
{
match domain {
RoutingDomain::PublicInternet => f(&inner.public_internet_routing_domain),
@ -156,7 +166,7 @@ impl RoutingTable {
f: F,
) -> R
where
F: FnOnce(&mut RoutingDomainDetail) -> R,
F: FnOnce(&mut dyn RoutingDomainDetail) -> R,
{
match domain {
RoutingDomain::PublicInternet => f(&mut inner.public_internet_routing_domain),
@ -166,79 +176,56 @@ impl RoutingTable {
pub fn relay_node(&self, domain: RoutingDomain) -> Option<NodeRef> {
let inner = self.inner.read();
Self::with_routing_domain(&*inner, domain, |rd| rd.relay_node.clone())
Self::with_routing_domain(&*inner, domain, |rd| rd.relay_node())
}
pub fn set_relay_node(&self, domain: RoutingDomain, opt_relay_node: Option<NodeRef>) {
let inner = self.inner.write();
Self::with_routing_domain(&mut *inner, domain, |rd| rd.relay_node = opt_relay_node);
Self::with_routing_domain_mut(&mut *inner, domain, |rd| rd.set_relay_node(opt_relay_node));
}
pub fn has_dial_info(&self, domain: RoutingDomain) -> bool {
let inner = self.inner.read();
Self::with_routing_domain(&*inner, domain, |rd| !rd.dial_info_details.is_empty())
Self::with_routing_domain(&*inner, domain, |rd| !rd.dial_info_details().is_empty())
}
pub fn dial_info_details(&self, domain: RoutingDomain) -> Vec<DialInfoDetail> {
let inner = self.inner.read();
Self::with_routing_domain(&*inner, domain, |rd| rd.dial_info_details.clone())
Self::with_routing_domain(&*inner, domain, |rd| rd.dial_info_details().clone())
}
pub fn first_filtered_dial_info_detail(
&self,
domain: Option<RoutingDomain>,
routing_domain_set: RoutingDomainSet,
filter: &DialInfoFilter,
) -> Option<DialInfoDetail> {
let inner = self.inner.read();
// Prefer local network first if it isn't filtered out
if domain == None || domain == Some(RoutingDomain::LocalNetwork) {
Self::with_routing_domain(&*inner, RoutingDomain::LocalNetwork, |rd| {
for did in &rd.dial_info_details {
for routing_domain in routing_domain_set {
let did = Self::with_routing_domain(&*inner, routing_domain, |rd| {
for did in rd.dial_info_details() {
if did.matches_filter(filter) {
return Some(did.clone());
}
}
None
})
} else {
None
}
.or_else(|| {
if domain == None || domain == Some(RoutingDomain::PublicInternet) {
Self::with_routing_domain(&*inner, RoutingDomain::PublicInternet, |rd| {
for did in &rd.dial_info_details {
if did.matches_filter(filter) {
return Some(did.clone());
}
}
None
})
} else {
None
});
if did.is_some() {
return did;
}
})
}
None
}
pub fn all_filtered_dial_info_details(
&self,
domain: Option<RoutingDomain>,
routing_domain_set: RoutingDomainSet,
filter: &DialInfoFilter,
) -> Vec<DialInfoDetail> {
let inner = self.inner.read();
let mut ret = Vec::new();
if domain == None || domain == Some(RoutingDomain::LocalNetwork) {
Self::with_routing_domain(&*inner, RoutingDomain::LocalNetwork, |rd| {
for did in &rd.dial_info_details {
if did.matches_filter(filter) {
ret.push(did.clone());
}
}
});
}
if domain == None || domain == Some(RoutingDomain::PublicInternet) {
Self::with_routing_domain(&*inner, RoutingDomain::PublicInternet, |rd| {
for did in &rd.dial_info_details {
for routing_domain in routing_domain_set {
Self::with_routing_domain(&*inner, routing_domain, |rd| {
for did in rd.dial_info_details() {
if did.matches_filter(filter) {
ret.push(did.clone());
}
@ -250,17 +237,13 @@ impl RoutingTable {
}
pub fn ensure_dial_info_is_valid(&self, domain: RoutingDomain, dial_info: &DialInfo) -> bool {
let enable_local_peer_scope = {
let config = self.network_manager().config();
let c = config.get();
c.network.enable_local_peer_scope
};
let address = dial_info.socket_address().address();
let inner = self.inner.read();
let can_contain_address =
Self::with_routing_domain(&*inner, domain, |rd| rd.can_contain_address(address));
if !enable_local_peer_scope
&& matches!(domain, RoutingDomain::PublicInternet)
&& dial_info.is_local()
{
log_rtab!(debug "shouldn't be registering local addresses as public");
if !can_contain_address {
log_rtab!(debug "can not add dial info to this routing domain");
return false;
}
if !dial_info.is_valid() {
@ -281,25 +264,20 @@ impl RoutingTable {
class: DialInfoClass,
) -> EyreResult<()> {
if !self.ensure_dial_info_is_valid(domain, &dial_info) {
return Err(eyre!("dial info is not valid"));
return Err(eyre!("dial info is not valid in this routing domain"));
}
let mut inner = self.inner.write();
Self::with_routing_domain_mut(&mut *inner, domain, |rd| {
rd.dial_info_details.push(DialInfoDetail {
rd.add_dial_info_detail(DialInfoDetail {
dial_info: dial_info.clone(),
class,
});
rd.dial_info_details.sort();
});
let domain_str = match domain {
RoutingDomain::PublicInternet => "Public",
RoutingDomain::LocalNetwork => "Local",
};
info!(
"{} Dial Info: {}",
domain_str,
"{:?} Dial Info: {}",
domain,
NodeDialInfo {
node_id: NodeId::new(inner.node_id),
dial_info
@ -308,19 +286,18 @@ impl RoutingTable {
);
debug!(" Class: {:?}", class);
// Public dial info changed, go through all nodes and reset their 'seen our node info' bit
if matches!(domain, RoutingDomain::PublicInternet) {
Self::reset_all_seen_our_node_info(&mut *inner);
Self::reset_all_updated_since_last_network_change(&mut *inner);
}
Self::reset_all_seen_our_node_info(&mut *inner, domain);
Self::reset_all_updated_since_last_network_change(&mut *inner);
Ok(())
}
fn reset_all_seen_our_node_info(inner: &mut RoutingTableInner) {
fn reset_all_seen_our_node_info(inner: &mut RoutingTableInner, routing_domain: RoutingDomain) {
let cur_ts = intf::get_timestamp();
Self::with_entries(&*inner, cur_ts, BucketEntryState::Dead, |_, v| {
v.with_mut(|e| e.set_seen_our_node_info(false));
v.with_mut(|e| {
e.set_seen_our_node_info(routing_domain, false);
});
Option::<()>::None
});
}
@ -333,20 +310,57 @@ impl RoutingTable {
});
}
pub fn clear_dial_info_details(&self, domain: RoutingDomain) {
trace!("clearing dial info domain: {:?}", domain);
pub fn clear_dial_info_details(&self, routing_domain: RoutingDomain) {
trace!("clearing dial info domain: {:?}", routing_domain);
let mut inner = self.inner.write();
Self::with_routing_domain_mut(&mut *inner, domain, |rd| {
rd.dial_info_details.clear();
Self::with_routing_domain_mut(&mut *inner, routing_domain, |rd| {
rd.clear_dial_info_details();
});
// Public dial info changed, go through all nodes and reset their 'seen our node info' bit
if matches!(domain, RoutingDomain::PublicInternet) {
Self::reset_all_seen_our_node_info(&mut *inner);
Self::reset_all_seen_our_node_info(&mut *inner, routing_domain);
}
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
PeerInfo::new(
NodeId::new(self.node_id()),
self.get_own_signed_node_info(routing_domain),
)
}
pub fn get_own_signed_node_info(&self, routing_domain: RoutingDomain) -> SignedNodeInfo {
let node_id = NodeId::new(self.node_id());
let secret = self.node_id_secret();
SignedNodeInfo::with_secret(self.get_own_node_info(routing_domain), node_id, &secret)
.unwrap()
}
pub fn get_own_node_info(&self, routing_domain: RoutingDomain) -> NodeInfo {
let netman = self.network_manager();
let relay_node = self.relay_node(routing_domain);
let pc = netman.get_protocol_config();
NodeInfo {
network_class: netman
.get_network_class(routing_domain)
.unwrap_or(NetworkClass::Invalid),
outbound_protocols: pc.outbound,
address_types: pc.family_global,
min_version: MIN_VERSION,
max_version: MAX_VERSION,
dial_info_detail_list: self.dial_info_details(routing_domain),
relay_peer_info: relay_node.and_then(|rn| rn.peer_info(routing_domain).map(Box::new)),
}
}
pub fn has_valid_own_node_info(&self, routing_domain: RoutingDomain) -> bool {
let netman = self.network_manager();
let nc = netman
.get_network_class(routing_domain)
.unwrap_or(NetworkClass::Invalid);
!matches!(nc, NetworkClass::Invalid)
}
fn bucket_depth(index: usize) -> usize {
match index {
0 => 256,
@ -398,6 +412,24 @@ impl RoutingTable {
debug!("finished routing table terminate");
}
pub fn configure_local_network_routing_domain(&self, local_networks: Vec<(IpAddr, IpAddr)>) {
let mut inner = self.inner.write();
let changed = inner
.local_network_routing_domain
.set_local_networks(local_networks);
// If the local network topology has changed, nuke the existing local node info and let new local discovery happen
if changed {
let cur_ts = intf::get_timestamp();
Self::with_entries(&*inner, cur_ts, BucketEntryState::Dead, |_rti, e| {
e.with_mut(|e| {
e.clear_signed_node_info(RoutingDomain::LocalNetwork);
});
Option::<()>::None
});
}
}
// Attempt to empty the routing table
// should only be performed when there are no node_refs (detached)
pub fn purge_buckets(&self) {
@ -461,16 +493,28 @@ impl RoutingTable {
.unwrap()
}
pub fn get_entry_count(&self, min_state: BucketEntryState) -> usize {
pub fn get_entry_count(
&self,
routing_domain_set: RoutingDomainSet,
min_state: BucketEntryState,
) -> usize {
let inner = self.inner.read();
Self::get_entry_count_inner(&*inner, min_state)
Self::get_entry_count_inner(&*inner, routing_domain_set, min_state)
}
fn get_entry_count_inner(inner: &RoutingTableInner, min_state: BucketEntryState) -> usize {
fn get_entry_count_inner(
inner: &RoutingTableInner,
routing_domain_set: RoutingDomainSet,
min_state: BucketEntryState,
) -> usize {
let mut count = 0usize;
let cur_ts = intf::get_timestamp();
Self::with_entries(inner, cur_ts, min_state, |_, _| {
count += 1;
Self::with_entries(inner, cur_ts, min_state, |_, e| {
if e.with(|e| e.best_routing_domain(routing_domain_set))
.is_some()
{
count += 1;
}
Option::<()>::None
});
count
@ -505,38 +549,32 @@ impl RoutingTable {
Self::with_entries(&*inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
// Only update nodes that haven't seen our node info yet
if all || !v.with(|e| e.has_seen_our_node_info(routing_domain)) {
node_refs.push(NodeRef::new(
self.clone(),
k,
v,
RoutingDomainSet::only(routing_domain),
None,
));
node_refs.push(NodeRef::new(self.clone(), k, v, None));
}
Option::<()>::None
});
node_refs
}
pub fn get_nodes_needing_ping(&self, cur_ts: u64) -> Vec<NodeRef> {
pub fn get_nodes_needing_ping(
&self,
routing_domain: RoutingDomain,
cur_ts: u64,
) -> Vec<NodeRef> {
let inner = self.inner.read();
// Collect relay nodes
let mut relays: HashSet<DHTKey> = HashSet::new();
for rd in RoutingDomain::all() {
let opt_relay_id =
Self::with_routing_domain(&*inner, RoutingDomain::PublicInternet, |rd| {
rd.relay_node.map(|rn| rn.node_id())
});
if let Some(relay_id) = opt_relay_id {
relays.insert(relay_id);
}
}
let opt_relay_id = Self::with_routing_domain(&*inner, routing_domain, |rd| {
rd.relay_node().map(|rn| rn.node_id())
});
// Collect all entries that are 'needs_ping' and have some node info making them reachable somehow
let mut node_refs = Vec::<NodeRef>::with_capacity(inner.bucket_entry_count);
Self::with_entries(&*inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
if v.with(|e| e.has_node_info(None) && e.needs_ping(&k, cur_ts, relays.contains(&k))) {
if v.with(|e| {
e.has_node_info(RoutingDomainSet::only(routing_domain))
&& e.needs_ping(&k, cur_ts, opt_relay_id == Some(k))
}) {
node_refs.push(NodeRef::new(self.clone(), k, v, None));
}
Option::<()>::None
@ -599,7 +637,7 @@ impl RoutingTable {
// Kick the bucket
inner.kick_queue.insert(idx);
log_rtab!(debug "Routing table now has {} nodes, {} live", cnt, Self::get_entry_count_inner(&mut *inner, BucketEntryState::Unreliable));
log_rtab!(debug "Routing table now has {} nodes, {} live", cnt, Self::get_entry_count_inner(&mut *inner, RoutingDomainSet::all(), BucketEntryState::Unreliable));
nr
}
@ -635,7 +673,6 @@ impl RoutingTable {
// and add the dial info we have for it, since that's pretty common
pub fn register_node_with_signed_node_info(
&self,
routing_domain: RoutingDomain,
node_id: DHTKey,
signed_node_info: SignedNodeInfo,
allow_invalid_signature: bool,
@ -651,9 +688,12 @@ impl RoutingTable {
return None;
}
}
self.create_node_ref(node_id, |e| {
e.update_signed_node_info(routing_domain, signed_node_info, allow_invalid_signature);
e.update_signed_node_info(signed_node_info, allow_invalid_signature);
})
.map(|mut nr| {
nr.set_filter(Some(NodeRefFilter::new().with_routing_domain(signed_node_info.routing_domain)))
nr
})
}

View File

@ -6,11 +6,71 @@ use alloc::fmt;
// We should ping them with some frequency and 30 seconds is typical timeout
const CONNECTIONLESS_TIMEOUT_SECS: u32 = 29;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct NodeRefFilter {
pub routing_domain_set: RoutingDomainSet,
pub dial_info_filter: DialInfoFilter,
}
impl Default for NodeRefFilter {
fn default() -> Self {
self.new()
}
}
impl NodeRefFilter {
pub fn new() -> Self {
Self {
routing_domain_set: RoutingDomainSet::all(),
dial_info_filter: DialInfoFilter::all(),
}
}
pub fn with_routing_domain(mut self, routing_domain: RoutingDomain) -> Self {
self.routing_domain_set = RoutingDomainSet::only(routing_domain);
self
}
pub fn with_routing_domain_set(mut self, routing_domain_set: RoutingDomainSet) -> Self {
self.routing_domain_set = routing_domain_set;
self
}
pub fn with_dial_info_filter(mut self, dial_info_filter: DialInfoFilter) -> Self {
self.dial_info_filter = dial_info_filter;
self
}
pub fn with_protocol_type(mut self, protocol_type: ProtocolType) -> Self {
self.dial_info_filter = self.dial_info_filter.with_protocol_type(protocol_type);
self
}
pub fn with_protocol_type_set(mut self, protocol_set: ProtocolTypeSet) -> Self {
self.dial_info_filter = self.dial_info_filter.with_protocol_type_set(protocol_set);
self
}
pub fn with_address_type(mut self, address_type: AddressType) -> Self {
self.dial_info_filter = self.dial_info_filter.with_address_type(address_type);
self
}
pub fn with_address_type_set(mut self, address_set: AddressTypeSet) -> Self {
self.dial_info_filter = self.dial_info_filter.with_address_type_set(address_set);
self
}
pub fn filtered(mut self, other_filter: &NodeRefFilter) -> Self {
self.routing_domain_set &= other_filter.routing_domain_set;
self.dial_info_filter = self
.dial_info_filter
.filtered(&other_filter.dial_info_filter);
self
}
pub fn is_dead(&self) -> bool {
self.dial_info_filter.is_empty() || self.routing_domain_set.is_empty()
}
}
pub struct NodeRef {
routing_table: RoutingTable,
node_id: DHTKey,
entry: Arc<BucketEntry>,
filter: Option<DialInfoFilter>,
filter: Option<NodeRefFilter>,
#[cfg(feature = "tracking")]
track_id: usize,
}
@ -20,7 +80,7 @@ impl NodeRef {
routing_table: RoutingTable,
node_id: DHTKey,
entry: Arc<BucketEntry>,
filter: Option<DialInfoFilter>,
filter: Option<NodeRefFilter>,
) -> Self {
entry.ref_count.fetch_add(1u32, Ordering::Relaxed);
@ -34,43 +94,7 @@ impl NodeRef {
}
}
pub fn node_id(&self) -> DHTKey {
self.node_id
}
pub fn filter_ref(&self) -> Option<&DialInfoFilter> {
self.filter.as_ref()
}
pub fn take_filter(&mut self) -> Option<DialInfoFilter> {
self.filter.take()
}
pub fn set_filter(&mut self, filter: Option<DialInfoFilter>) {
self.filter = filter
}
pub fn merge_filter(&mut self, filter: DialInfoFilter) {
if let Some(self_filter) = self.filter.take() {
self.filter = Some(self_filter.filtered(&filter));
} else {
self.filter = Some(filter);
}
}
pub fn filtered_clone(&self, filter: DialInfoFilter) -> Self {
let mut out = self.clone();
out.merge_filter(filter);
out
}
pub fn is_filter_dead(&self) -> bool {
if let Some(filter) = &self.filter {
filter.is_dead()
} else {
false
}
}
// Operate on entry accessors
pub(super) fn operate<T, F>(&self, f: F) -> T
where
@ -88,17 +112,67 @@ impl NodeRef {
self.entry.with_mut(|e| f(inner, e))
}
pub fn peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
self.operate(|_rti, e| e.peer_info(self.node_id(), routing_domain))
// Filtering
pub fn filter_ref(&self) -> Option<&NodeRefFilter> {
self.filter.as_ref()
}
pub fn has_valid_signed_node_info(&self, opt_routing_domain: Option<RoutingDomain>) -> bool {
self.operate(|_rti, e| e.has_valid_signed_node_info(opt_routing_domain))
pub fn take_filter(&mut self) -> Option<NodeRefFilter> {
self.filter.take()
}
pub fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| e.has_seen_our_node_info(routing_domain))
pub fn set_filter(&mut self, filter: Option<NodeRefFilter>) {
self.filter = filter
}
pub fn set_seen_our_node_info(&self, routing_domain: RoutingDomain) {
self.operate_mut(|_rti, e| e.set_seen_our_node_info(routing_domain, true));
pub fn merge_filter(&mut self, filter: NodeRefFilter) {
if let Some(self_filter) = self.filter.take() {
self.filter = Some(self_filter.filtered(&filter));
} else {
self.filter = Some(filter);
}
}
pub fn filtered_clone(&self, filter: NodeRefFilter) -> Self {
let mut out = self.clone();
out.merge_filter(filter);
out
}
pub fn is_filter_dead(&self) -> bool {
if let Some(filter) = &self.filter {
filter.is_dead()
} else {
false
}
}
pub fn routing_domain_set(&self) -> RoutingDomainSet {
self.filter
.map(|f| f.routing_domain_set)
.unwrap_or(RoutingDomainSet::all())
}
pub fn best_routing_domain(&self) -> Option<RoutingDomain> {
self.operate(|_rti, e| {
e.best_routing_domain(
self.filter
.map(|f| f.routing_domain_set)
.unwrap_or(RoutingDomainSet::all()),
)
})
}
// Accessors
pub fn routing_table(&self) -> RoutingTable {
self.routing_table.clone()
}
pub fn node_id(&self) -> DHTKey {
self.node_id
}
pub fn has_valid_signed_node_info(&self) -> bool {
self.operate(|_rti, e| e.has_valid_signed_node_info(self.routing_domain_set()))
}
pub fn has_updated_since_last_network_change(&self) -> bool {
self.operate(|_rti, e| e.has_updated_since_last_network_change())
@ -106,24 +180,31 @@ impl NodeRef {
pub fn set_updated_since_last_network_change(&self) {
self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true));
}
pub fn update_node_status(&self, node_status: NodeStatus) {
self.operate_mut(|_rti, e| {
e.update_node_status(node_status);
});
}
pub fn min_max_version(&self) -> Option<(u8, u8)> {
self.operate(|_rti, e| e.min_max_version())
}
pub fn set_min_max_version(&self, min_max_version: (u8, u8)) {
self.operate_mut(|_rti, e| e.set_min_max_version(min_max_version))
}
pub fn state(&self, cur_ts: u64) -> BucketEntryState {
self.operate(|_rti, e| e.state(cur_ts))
}
// Per-RoutingDomain accessors
pub fn peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
self.operate(|_rti, e| e.peer_info(self.node_id(), routing_domain))
}
pub fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| e.has_seen_our_node_info(routing_domain))
}
pub fn set_seen_our_node_info(&self, routing_domain: RoutingDomain) {
self.operate_mut(|_rti, e| e.set_seen_our_node_info(routing_domain, true));
}
pub fn network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class))
}
@ -143,7 +224,6 @@ impl NodeRef {
}
dif
}
pub fn relay(&self, routing_domain: RoutingDomain) -> Option<NodeRef> {
let target_rpi =
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.relay_peer_info))?;
@ -155,83 +235,50 @@ impl NodeRef {
}
// Register relay node and return noderef
self.routing_table
.register_node_with_signed_node_info(
routing_domain,
t.node_id.key,
t.signed_node_info,
false,
)
.map(|mut nr| {
nr.set_filter(self.filter_ref().cloned());
nr
})
})
}
pub fn first_filtered_dial_info_detail(
&self,
routing_domain: Option<RoutingDomain>,
) -> Option<DialInfoDetail> {
self.operate(|_rt, e| {
// Prefer local dial info first unless it is filtered out
if routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork) {
e.node_info(RoutingDomain::LocalNetwork).and_then(|l| {
l.first_filtered_dial_info_detail(|did| {
if let Some(filter) = self.filter.as_ref() {
did.matches_filter(filter)
} else {
true
}
})
})
} else {
None
}
.or_else(|| {
if routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet) {
e.node_info(RoutingDomain::PublicInternet).and_then(|n| {
n.first_filtered_dial_info_detail(|did| {
if let Some(filter) = self.filter.as_ref() {
did.matches_filter(filter)
} else {
true
}
})
})
} else {
None
}
})
self.routing_table.register_node_with_signed_node_info(
t.node_id.key,
t.signed_node_info,
false,
)
})
}
pub fn all_filtered_dial_info_details<F>(
&self,
routing_domain: Option<RoutingDomain>,
) -> Vec<DialInfoDetail> {
let mut out = Vec::new();
// Filtered accessors
pub fn first_filtered_dial_info_detail(&self) -> Option<DialInfoDetail> {
let routing_domain_set = self.routing_domain_set();
self.operate(|_rt, e| {
// Prefer local dial info first unless it is filtered out
if routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork) {
if let Some(ni) = e.node_info(RoutingDomain::LocalNetwork) {
out.append(&mut ni.all_filtered_dial_info_details(|did| {
if let Some(filter) = self.filter.as_ref() {
did.matches_filter(filter)
} else {
true
}
}))
for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) {
let filter = |did: &DialInfoDetail| {
self.filter
.as_ref()
.map(|f| did.matches_filter(f))
.unwrap_or(true)
};
if let Some(did) = ni.first_filtered_dial_info_detail(filter) {
return Some(did);
}
}
}
if routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet) {
if let Some(ni) = e.node_info(RoutingDomain::PublicInternet) {
out.append(&mut ni.all_filtered_dial_info_details(|did| {
if let Some(filter) = self.filter.as_ref() {
did.matches_filter(filter)
} else {
true
}
}))
None
})
}
pub fn all_filtered_dial_info_details<F>(&self) -> Vec<DialInfoDetail> {
let routing_domain_set = self.routing_domain_set();
let mut out = Vec::new();
self.operate(|_rt, e| {
for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) {
let filter = |did: &DialInfoDetail| {
self.filter
.as_ref()
.map(|f| did.matches_filter(f))
.unwrap_or(true)
};
if let Some(did) = ni.first_filtered_dial_info_detail(filter) {
out.push(did);
}
}
}
});
@ -241,8 +288,12 @@ impl NodeRef {
pub async fn last_connection(&self) -> Option<ConnectionDescriptor> {
// Get the last connection and the last time we saw anything with this connection
let (last_connection, last_seen) =
self.operate(|_rti, e| e.last_connection(self.filter.clone()))?;
let (last_connection, last_seen) = self.operate(|_rti, e| {
e.last_connection(
self.filter.routing_domain_set,
self.filter.dial_info_filter.clone(),
)
})?;
// Should we check the connection table?
if last_connection.protocol_type().is_connection_oriented() {

View File

@ -0,0 +1,94 @@
use super::*;
/// General trait for all routing domains
pub trait RoutingDomainDetail {
fn can_contain_address(&self, address: Address) -> bool;
fn relay_node(&self) -> Option<NodeRef>;
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>);
fn dial_info_details(&self) -> &Vec<DialInfoDetail>;
fn clear_dial_info_details(&mut self);
fn add_dial_info_detail(&mut self, did: DialInfoDetail);
}
/// Public Internet routing domain internals
#[derive(Debug, Default)]
pub struct PublicInternetRoutingDomainDetail {
/// An optional node we relay through for this domain
relay_node: Option<NodeRef>,
/// The dial infos on this domain we can be reached by
dial_info_details: Vec<DialInfoDetail>,
}
impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
fn can_contain_address(&self, address: Address) -> bool {
address.is_global()
}
fn relay_node(&self) -> Option<NodeRef> {
self.relay_node.clone()
}
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
self.relay_node = opt_relay_node
.map(|nr| nr.filtered_clone(NodeRefFilter::new().with_routing_domain(PublicInternet)))
}
fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
&self.dial_info_details
}
fn clear_dial_info_details(&mut self) {
self.dial_info_details.clear();
}
fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
self.dial_info_details.push(did);
self.dial_info_details.sort();
}
}
/// Local Network routing domain internals
#[derive(Debug, Default)]
pub struct LocalInternetRoutingDomainDetail {
/// An optional node we relay through for this domain
relay_node: Option<NodeRef>,
/// The dial infos on this domain we can be reached by
dial_info_details: Vec<DialInfoDetail>,
/// The local networks this domain will communicate with
local_networks: Vec<(IpAddr, IpAddr)>,
}
impl LocalInternetRoutingDomainDetail {
pub fn set_local_networks(&mut self, local_networks: Vec<(IpAddr, IpAddr)>) -> bool {
local_networks.sort();
if local_networks == self.local_networks {
return false;
}
self.local_networks = local_networks;
true
}
}
impl RoutingDomainDetail for LocalInternetRoutingDomainDetail {
fn can_contain_address(&self, address: Address) -> bool {
let ip = address.to_ip_addr();
for localnet in self.local_networks {
if ipaddr_in_network(ip, localnet.0, localnet.1) {
return true;
}
}
false
}
fn relay_node(&self) -> Option<NodeRef> {
self.relay_node.clone()
}
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
self.relay_node = opt_relay_node
.map(|nr| nr.filtered_clone(NodeRefFilter::new().with_routing_domain(LocalNetwork)));
}
fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
&self.dial_info_details
}
fn clear_dial_info_details(&mut self) {
self.dial_info_details.clear();
}
fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
self.dial_info_details.push(did);
self.dial_info_details.sort();
}
}

View File

@ -1,9 +1,9 @@
use crate::*;
use rpc_processor::*;
pub fn encode_node_status(
node_status: &NodeStatus,
builder: &mut veilid_capnp::node_status::Builder,
pub fn encode_public_internet_node_status(
public_internet_node_status: &PublicInternetNodeStatus,
builder: &mut veilid_capnp::public_internet_node_status::Builder,
) -> Result<(), RPCError> {
builder.set_will_route(node_status.will_route);
builder.set_will_tunnel(node_status.will_tunnel);
@ -14,10 +14,10 @@ pub fn encode_node_status(
Ok(())
}
pub fn decode_node_status(
reader: &veilid_capnp::node_status::Reader,
) -> Result<NodeStatus, RPCError> {
Ok(NodeStatus {
pub fn decode_public_internet_node_status(
reader: &veilid_capnp::public_internet_node_status::Reader,
) -> Result<PublicInternetNodeStatus, RPCError> {
Ok(PublicInternetNodeStatus {
will_route: reader.reborrow().get_will_route(),
will_tunnel: reader.reborrow().get_will_tunnel(),
will_signal: reader.reborrow().get_will_signal(),
@ -25,3 +25,62 @@ pub fn decode_node_status(
will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(),
})
}
pub fn encode_local_network_node_status(
local_network_node_status: &LocalNetworkNodeStatus,
builder: &mut veilid_capnp::local_network_node_status::Builder,
) -> Result<(), RPCError> {
builder.set_will_relay(node_status.will_relay);
builder.set_will_validate_dial_info(node_status.will_validate_dial_info);
Ok(())
}
pub fn decode_local_network_node_status(
reader: &veilid_capnp::local_network_node_status::Reader,
) -> Result<LocalNetworkNodeStatus, RPCError> {
Ok(NodeStatus {
will_relay: reader.reborrow().get_will_relay(),
will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(),
})
}
pub fn encode_node_status(
node_status: &NodeStatus,
builder: &mut veilid_capnp::node_status::Builder,
) -> Result<(), RPCError> {
match node_status {
NodeStatus::PublicInternetNodeStatus(ns) => {
let mut pi_builder = builder.reborrow().init_public_internet();
encode_public_internet_node_status(&ns, &mut pi_builder)
}
NodeStatus::LocalNetworkNodeStatus(ns) => {
let mut ln_builder = builder.reborrow().init_local_network();
encode_local_network_node_status(&ns, &mut ln_builder)
}
}
Ok(())
}
pub fn decode_node_status(
reader: &veilid_capnp::node_status::Reader,
) -> Result<NodeStatus, RPCError> {
Ok(
match reader
.which()
.map_err(RPCError::map_internal("invalid node status"))?
{
veilid_capnp::node_status::PublicInternet(pi) => {
let r = r.map_err(RPCError::protocol)?;
let pins = decode_public_internet_node_status(&r)?;
NodeStatus::PublicInternet(pins)
}
veilid_capnp::node_status::LocalNetwork(ln) => {
let r = ln.map_err(RPCError::protocol)?;
let lnns = decode_local_network_node_status(&r)?;
NodeStatus::LocalNetwork(lnns)
}
},
)
}

View File

@ -58,26 +58,34 @@ impl RPCOperationKind {
#[derive(Debug, Clone)]
pub struct RPCOperation {
op_id: u64,
sender_info: Option<SignedNodeInfo>,
kind: RPCOperationKind,
}
impl RPCOperation {
pub fn new_question(question: RPCQuestion) -> Self {
pub fn new_question(question: RPCQuestion, sender_info: Option<SignedNodeInfo>) -> Self {
Self {
op_id: intf::get_random_u64(),
sender_info,
kind: RPCOperationKind::Question(question),
}
}
pub fn new_statement(statement: RPCStatement) -> Self {
pub fn new_statement(statement: RPCStatement, sender_info: Option<SignedNodeInfo>) -> Self {
Self {
op_id: intf::get_random_u64(),
sender_info,
kind: RPCOperationKind::Statement(statement),
}
}
pub fn new_answer(request: &RPCOperation, answer: RPCAnswer) -> Self {
pub fn new_answer(
request: &RPCOperation,
answer: RPCAnswer,
sender_info: Option<SignedNodeInfo>,
) -> Self {
Self {
op_id: request.op_id,
sender_info,
kind: RPCOperationKind::Answer(answer),
}
}
@ -86,6 +94,10 @@ impl RPCOperation {
self.op_id
}
pub fn sender_info(&self) -> Option<&SignedNodeInfo> {
self.sender_info.as_ref()
}
pub fn kind(&self) -> &RPCOperationKind {
&self.kind
}
@ -100,16 +112,32 @@ impl RPCOperation {
) -> Result<Self, RPCError> {
let op_id = operation_reader.get_op_id();
let sender_info = if operation_reader.has_sender_info() {
let sni_reader = operation_reader.get_sender_info();
let sni = decode_signed_node_info(&sni_reader, sender_node_id, true)?;
Some(sni)
} else {
None
};
let kind_reader = operation_reader.get_kind();
let kind = RPCOperationKind::decode(&kind_reader, sender_node_id)?;
Ok(RPCOperation { op_id, kind })
Ok(RPCOperation {
op_id,
sender_info,
kind,
})
}
pub fn encode(&self, builder: &mut veilid_capnp::operation::Builder) -> Result<(), RPCError> {
builder.set_op_id(self.op_id);
let mut k_builder = builder.reborrow().init_kind();
self.kind.encode(&mut k_builder)?;
if let Some(sender_info) = self.sender_info {
let si_builder = builder.reborrow().init_sender_info();
encode_signed_node_info(&self.sender_info, &mut si_builder)?;
}
Ok(())
}
}

View File

@ -18,12 +18,6 @@ impl RPCQuestion {
pub fn detail(&self) -> &RPCQuestionDetail {
&self.detail
}
// pub fn into_detail(self) -> RPCQuestionDetail {
// self.detail
// }
// pub fn into_respond_to_detail(self) -> (RespondTo, RPCQuestionDetail) {
// (self.respond_to, self.detail)
// }
pub fn desc(&self) -> &'static str {
self.detail.desc()
}
@ -32,7 +26,7 @@ impl RPCQuestion {
sender_node_id: &DHTKey,
) -> Result<RPCQuestion, RPCError> {
let rt_reader = reader.get_respond_to();
let respond_to = RespondTo::decode(&rt_reader, sender_node_id)?;
let respond_to = RespondTo::decode(&rt_reader)?;
let d_reader = reader.get_detail();
let detail = RPCQuestionDetail::decode(&d_reader)?;
Ok(RPCQuestion { respond_to, detail })

View File

@ -3,7 +3,7 @@ use rpc_processor::*;
#[derive(Debug, Clone)]
pub enum RespondTo {
Sender(Option<SignedNodeInfo>),
Sender,
PrivateRoute(PrivateRoute),
}
@ -13,11 +13,7 @@ impl RespondTo {
builder: &mut veilid_capnp::question::respond_to::Builder,
) -> Result<(), RPCError> {
match self {
Self::Sender(Some(sni)) => {
let mut sni_builder = builder.reborrow().init_sender_with_info();
encode_signed_node_info(sni, &mut sni_builder)?;
}
Self::Sender(None) => {
Self::Sender => {
builder.reborrow().set_sender(());
}
Self::PrivateRoute(pr) => {
@ -28,17 +24,9 @@ impl RespondTo {
Ok(())
}
pub fn decode(
reader: &veilid_capnp::question::respond_to::Reader,
sender_node_id: &DHTKey,
) -> Result<Self, RPCError> {
pub fn decode(reader: &veilid_capnp::question::respond_to::Reader) -> Result<Self, RPCError> {
let respond_to = match reader.which().map_err(RPCError::protocol)? {
veilid_capnp::question::respond_to::Sender(()) => RespondTo::Sender(None),
veilid_capnp::question::respond_to::SenderWithInfo(sender_ni_reader) => {
let sender_ni_reader = sender_ni_reader.map_err(RPCError::protocol)?;
let sni = decode_signed_node_info(&sender_ni_reader, sender_node_id, true)?;
RespondTo::Sender(Some(sni))
}
veilid_capnp::question::respond_to::Sender(()) => RespondTo::Sender,
veilid_capnp::question::respond_to::PrivateRoute(pr_reader) => {
let pr_reader = pr_reader.map_err(RPCError::protocol)?;
let pr = decode_private_route(&pr_reader)?;

View File

@ -0,0 +1,135 @@
use super::*;
/// Where to send an RPC message
#[derive(Debug, Clone)]
pub enum Destination {
/// Send to node directly
Direct {
/// The node to send to
target: NodeRef,
/// An optional safety route specification to send from for sender privacy
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
},
/// Send to node for relay purposes
Relay {
/// The relay to send to
relay: NodeRef,
/// The final destination the relay should send to
target: DHTKey,
/// An optional safety route specification to send from for sender privacy
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
},
/// Send to private route (privateroute)
PrivateRoute {
/// A private route to send to
private_route: PrivateRoute,
/// An optional safety route specification to send from for sender privacy
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
},
}
impl Destination {
pub fn direct(target: NodeRef) -> Self {
Self::Direct {
target,
safety_route_spec: None,
}
}
pub fn relay(relay: NodeRef, target: DHTKey) -> Self {
Self::Relay {
relay,
target,
safety_route_spec: None,
}
}
pub fn private_route(private_route: PrivateRoute) -> Self {
Self::PrivateRoute {
private_route,
safety_route_spec: None,
}
}
pub fn safety_route_spec(&self) -> Option<Arc<SafetyRouteSpec>> {
match self {
Destination::Direct {
target,
safety_route_spec,
} => safety_route_spec.clone(),
Destination::Relay {
relay,
target,
safety_route_spec,
} => safety_route_spec.clone(),
Destination::PrivateRoute {
private_route,
safety_route_spec,
} => safety_route_spec.clone(),
}
}
pub fn with_safety_route_spec(self, safety_route_spec: Arc<SafetyRouteSpec>) -> Self {
match self {
Destination::Direct {
target,
safety_route_spec: _,
} => Self::Direct {
target,
safety_route_spec: Some(safety_route_spec),
},
Destination::Relay {
relay,
target,
safety_route_spec: _,
} => Self::Relay {
relay,
target,
safety_route_spec: Some(safety_route_spec),
},
Destination::PrivateRoute {
private_route,
safety_route_spec: _,
} => Self::PrivateRoute {
private_route,
safety_route_spec: Some(safety_route_spec),
},
}
}
}
impl fmt::Display for Destination {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Destination::Direct {
target,
routing_domain,
safety_route_spec,
} => {
let sr = safety_route_spec
.map(|_sr| "+SR".to_owned())
.unwrap_or_default();
write!(f, "{:?}{}", target, sr)
}
Destination::Relay {
relay,
target,
safety_route_spec,
} => {
let sr = safety_route_spec
.map(|_sr| "+SR".to_owned())
.unwrap_or_default();
write!(f, "{:?}@{:?}{}", target.encode(), relay, sr)
}
Destination::PrivateRoute {
private_route,
safety_route_spec,
} => {
let sr = safety_route_spec
.map(|_sr| "+SR".to_owned())
.unwrap_or_default();
write!(f, "{}{}", private_route, sr)
}
}
}
}

View File

@ -1,4 +1,5 @@
mod coders;
mod destination;
mod private_route;
mod rpc_cancel_tunnel;
mod rpc_complete_tunnel;
@ -18,6 +19,7 @@ mod rpc_validate_dial_info;
mod rpc_value_changed;
mod rpc_watch_value;
pub use destination::*;
pub use private_route::*;
pub use rpc_error::*;
@ -36,208 +38,6 @@ use stop_token::future::FutureExt;
type OperationId = u64;
/// Where to send an RPC message
#[derive(Debug, Clone)]
pub enum Destination {
/// Send to node directly
Direct {
/// The node to send to
target: NodeRef,
/// An optional routing domain to require
routing_domain: Option<RoutingDomain>,
/// An optional safety route specification to send from for sender privacy
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
},
/// Send to node for relay purposes
Relay {
/// The relay to send to
relay: NodeRef,
/// The final destination the relay should send to
target: DHTKey,
/// An optional routing domain to require
routing_domain: Option<RoutingDomain>,
/// An optional safety route specification to send from for sender privacy
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
},
/// Send to private route (privateroute)
PrivateRoute {
/// A private route to send to
private_route: PrivateRoute,
/// An optional safety route specification to send from for sender privacy
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
},
}
impl Destination {
pub fn direct(target: NodeRef) -> Self {
Self::Direct {
target,
routing_domain: None,
safety_route_spec: None,
}
}
pub fn relay(relay: NodeRef, target: DHTKey) -> Self {
Self::Relay {
relay,
target,
routing_domain: None,
safety_route_spec: None,
}
}
pub fn private_route(private_route: PrivateRoute) -> Self {
Self::PrivateRoute {
private_route,
safety_route_spec: None,
}
}
pub fn
pub fn routing_domain(&self) -> Option<RoutingDomain> {
match self {
Destination::Direct {
target,
routing_domain,
safety_route_spec,
} => *routing_domain,
Destination::Relay {
relay,
target,
routing_domain,
safety_route_spec,
} => *routing_domain,
Destination::PrivateRoute {
private_route,
safety_route_spec,
} => Some(RoutingDomain::PublicInternet),
}
}
pub fn safety_route_spec(&self) -> Option<Arc<SafetyRouteSpec>> {
match self {
Destination::Direct {
target,
routing_domain,
safety_route_spec,
} => safety_route_spec.clone(),
Destination::Relay {
relay,
target,
routing_domain,
safety_route_spec,
} => safety_route_spec.clone(),
Destination::PrivateRoute {
private_route,
safety_route_spec,
} => safety_route_spec.clone(),
}
}
pub fn with_routing_domain(self, routing_domain: RoutingDomain) -> Self {
match self {
Destination::Direct {
target,
routing_domain: _,
safety_route_spec,
} => Self::Direct {
target,
routing_domain: Some(routing_domain),
safety_route_spec,
},
Destination::Relay {
relay,
target,
routing_domain: _,
safety_route_spec,
} => Self::Relay {
relay,
target,
routing_domain: Some(routing_domain),
safety_route_spec,
},
Destination::PrivateRoute {
private_route: _,
safety_route_spec: _,
} => panic!("Private route is only valid in PublicInternet routing domain"),
}
}
pub fn with_safety_route_spec(self, safety_route_spec: Arc<SafetyRouteSpec>) -> Self {
match self {
Destination::Direct {
target,
routing_domain,
safety_route_spec: _,
} => Self::Direct {
target,
routing_domain,
safety_route_spec: Some(safety_route_spec),
},
Destination::Relay {
relay,
target,
routing_domain,
safety_route_spec: _,
} => Self::Relay {
relay,
target,
routing_domain,
safety_route_spec: Some(safety_route_spec),
},
Destination::PrivateRoute {
private_route,
safety_route_spec: _,
} => Self::PrivateRoute {
private_route,
safety_route_spec: Some(safety_route_spec),
},
}
}
}
impl fmt::Display for Destination {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Destination::Direct {
target,
routing_domain,
safety_route_spec,
} => {
let rd = routing_domain
.map(|rd| format!("#{:?}", rd))
.unwrap_or_default();
let sr = safety_route_spec
.map(|_sr| "+SR".to_owned())
.unwrap_or_default();
write!(f, "{:?}{}{}", target, rd, sr)
}
Destination::Relay {
relay,
target,
routing_domain,
safety_route_spec,
} => {
let rd = routing_domain
.map(|rd| format!("#{:?}", rd))
.unwrap_or_default();
let sr = safety_route_spec
.map(|_sr| "+SR".to_owned())
.unwrap_or_default();
write!(f, "{:?}@{:?}{}{}", target.encode(), relay, rd, sr)
}
Destination::PrivateRoute {
private_route,
safety_route_spec,
} => {
let sr = safety_route_spec
.map(|_sr| "+SR".to_owned())
.unwrap_or_default();
write!(f, "{}{}", private_route, sr)
}
}
}
}
/// The decoded header of an RPC message
#[derive(Debug, Clone)]
struct RPCMessageHeader {
@ -251,6 +51,8 @@ struct RPCMessageHeader {
peer_noderef: NodeRef,
/// The connection from the peer sent the message (not the original sender)
connection_descriptor: ConnectionDescriptor,
/// The routing domain the message was sent through
routing_domain: RoutingDomain,
}
#[derive(Debug)]
@ -352,7 +154,6 @@ pub struct RPCProcessorInner {
pub struct RPCProcessor {
crypto: Crypto,
config: VeilidConfig,
enable_local_peer_scope: bool,
inner: Arc<Mutex<RPCProcessorInner>>,
}
@ -375,11 +176,6 @@ impl RPCProcessor {
Self {
crypto: network_manager.crypto(),
config: network_manager.config(),
enable_local_peer_scope: network_manager
.config()
.get()
.network
.enable_local_peer_scope,
inner: Arc::new(Mutex::new(Self::new_inner(network_manager))),
}
}
@ -402,12 +198,8 @@ impl RPCProcessor {
//////////////////////////////////////////////////////////////////////
fn filter_peer_scope(&self, node_info: &NodeInfo) -> bool {
// if local peer scope is enabled, then don't reject any peer info
if self.enable_local_peer_scope {
return true;
}
/// Determine if a NodeInfo can be placed into the specified routing domain
fn filter_node_info(&self, routing_domain: RoutingDomain, node_info: &NodeInfo) -> bool {
// reject attempts to include non-public addresses in results
for did in &node_info.dial_info_detail_list {
if !did.dial_info.is_global() {
@ -584,31 +376,6 @@ impl RPCProcessor {
out
}
/// Gets a 'RespondTo::Sender' that contains either our dial info,
/// or None if the peer has seen our dial info before or our node info is not yet valid
/// because of an unknown network class
pub fn make_respond_to_sender(
&self,
routing_domain: RoutingDomain,
peer: NodeRef,
) -> RespondTo {
if peer.has_seen_our_node_info(routing_domain)
|| matches!(
self.network_manager()
.get_network_class(routing_domain)
.unwrap_or(NetworkClass::Invalid),
NetworkClass::Invalid
)
{
RespondTo::Sender(None)
} else {
let our_sni = self
.routing_table()
.get_own_signed_node_info(routing_domain);
RespondTo::Sender(Some(our_sni))
}
}
/// Produce a byte buffer that represents the wire encoding of the entire
/// unencrypted envelope body for a RPC message. This incorporates
/// wrapping a private and/or safety route if they are specified.
@ -617,7 +384,7 @@ impl RPCProcessor {
&self,
dest: Destination,
operation: &RPCOperation,
) -> Result<RenderedOperation, RPCError> { xxx continue propagating safetyroutespec
) -> Result<RenderedOperation, RPCError> {
let out_node_id; // Envelope Node Id
let mut out_node_ref: Option<NodeRef> = None; // Node to send envelope to
let out_hop_count: usize; // Total safety + private route hop count
@ -634,12 +401,28 @@ impl RPCProcessor {
// To where are we sending the request
match dest {
Destination::Direct(ref node_ref) | Destination::Relay(ref node_ref, _) => {
Destination::Direct {
target: node_ref,
routing_domain,
safety_route_spec,
}
| Destination::Relay {
relay: node_ref,
target: _,
routing_domain,
safety_route_spec,
} => {
// Send to a node without a private route
// --------------------------------------
// Get the actual destination node id accounting for relays
let (node_ref, node_id) = if let Destination::Relay(_, dht_key) = dest {
let (node_ref, node_id) = if let Destination::Relay {
relay: _,
target: dht_key,
routing_domain: _,
safety_route_spec: _,
} = dest
{
(node_ref.clone(), dht_key.clone())
} else {
let node_id = node_ref.node_id();
@ -676,7 +459,10 @@ impl RPCProcessor {
}
};
}
Destination::PrivateRoute(private_route) => {
Destination::PrivateRoute {
private_route,
safety_route_spec,
} => {
// Send to private route
// ---------------------
// Reply with 'route' operation
@ -723,12 +509,22 @@ impl RPCProcessor {
}
// Issue a question over the network, possibly using an anonymized route
#[instrument(level = "debug", skip(self, question, safety_route_spec), err)]
#[instrument(level = "debug", skip(self, question), err)]
async fn question(
&self,
dest: Destination,
question: RPCQuestion,
) -> Result<NetworkResult<WaitableReply>, RPCError> {
// Get sender info if we should send that
let opt_sender_info = if dest.safety_route_spec().is_none() && matches!(question.respond_to(), RespondTo::Sender) {
// Sender is not private, send sender info if needed
// Get the noderef of the eventual destination or first route hop
if let Some(target_nr) = self.routing_table().lookup_node_ref(dest.get_target_id()) {
if target_nr.has_seen_our_node_info(R)
}
}
// Wrap question in operation
let operation = RPCOperation::new_question(question);
let op_id = operation.op_id();
@ -951,6 +747,10 @@ impl RPCProcessor {
&self,
encoded_msg: RPCMessageEncoded,
) -> Result<(), RPCError> {
// Get the routing domain
let routing_domain = encoded_msg.header.routing_domain;
// Decode the operation
let sender_node_id = encoded_msg.header.envelope.get_sender_id();
@ -971,12 +771,13 @@ impl RPCProcessor {
match q.respond_to() {
RespondTo::Sender(Some(sender_ni)) => {
// Sender NodeInfo was specified, update our routing table with it
if !self.filter_peer_scope(&sender_ni.node_info) {
if !self.filter_node_info(&sender_ni.node_info) {
return Err(RPCError::invalid_format(
"respond_to_sender_signed_node_info has invalid peer scope",
));
}
opt_sender_nr = self.routing_table().register_node_with_signed_node_info(
routing_domain,
sender_node_id,
sender_ni.clone(),
false,
@ -1168,6 +969,7 @@ impl RPCProcessor {
body: Vec<u8>,
peer_noderef: NodeRef,
connection_descriptor: ConnectionDescriptor,
routing_domain: RoutingDomain,
) -> EyreResult<()> {
let msg = RPCMessageEncoded {
header: RPCMessageHeader {
@ -1176,6 +978,7 @@ impl RPCProcessor {
body_len: body.len() as u64,
peer_noderef,
connection_descriptor,
routing_domain,
},
data: RPCMessageData { contents: body },
};

View File

@ -4,7 +4,7 @@ impl RPCProcessor {
//////////////////////////////////////////////////////////////////////
fn compile_safety_route(
&self,
safety_route_spec: &SafetyRouteSpec,
safety_route_spec: Arc<SafetyRouteSpec>,
private_route: PrivateRoute,
) -> Result<SafetyRoute, RPCError> {
// Ensure the total hop count isn't too long for our config
@ -111,15 +111,15 @@ impl RPCProcessor {
// Wrap an operation inside a route
pub(super) fn wrap_with_route(
&self,
safety_route_spec: Option<&SafetyRouteSpec>,
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
private_route: PrivateRoute,
message_data: Vec<u8>,
) -> Result<Vec<u8>, RPCError> {
// Encrypt routed operation
// Xmsg + ENC(Xmsg, DH(PKapr, SKbsr))
let nonce = Crypto::get_random_nonce();
let stub_safety_route_spec = SafetyRouteSpec::new();
let safety_route_spec = safety_route_spec.unwrap_or(&stub_safety_route_spec);
let safety_route_spec =
safety_route_spec.unwrap_or_else(|| Arc::new(SafetyRouteSpec::new()));
let dh_secret = self
.crypto
.cached_dh(&private_route.public_key, &safety_route_spec.secret_key)

View File

@ -8,15 +8,11 @@ impl RPCProcessor {
self,
dest: Destination,
key: DHTKey,
safety_route: Option<&SafetyRouteSpec>,
respond_to: RespondTo,
) -> Result<NetworkResult<Answer<Vec<PeerInfo>>>, RPCError> {
let find_node_q = RPCOperationFindNodeQ { node_id: key };
let question = RPCQuestion::new(respond_to, RPCQuestionDetail::FindNodeQ(find_node_q));
let find_node_q = RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ { node_id: key });
// Send the find_node request
let waitable_reply =
network_result_try!(self.question(dest, question, safety_route).await?);
let waitable_reply = network_result_try!(self.question(dest, find_node_q).await?);
// Wait for reply
let (msg, latency) = match self.wait_for_reply(waitable_reply).await? {
@ -35,7 +31,7 @@ impl RPCProcessor {
// Verify peers are in the correct peer scope
for peer_info in &find_node_a.peers {
if !self.filter_peer_scope(&peer_info.signed_node_info.node_info) {
if !self.filter_node_info(&peer_info.signed_node_info.node_info) {
return Err(RPCError::invalid_format(
"find_node response has invalid peer scope",
));

View File

@ -2,20 +2,20 @@ use super::*;
impl RPCProcessor {
// Sends a our node info to another node
// Can be sent via all methods including relays and routes
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn rpc_call_node_info_update(
self,
target: NodeRef,
routing_domain: RoutingDomain,
) -> Result<NetworkResult<()>, RPCError> {
// Get the signed node info for the desired routing domain to send update with
let signed_node_info = self
.routing_table()
.get_own_signed_node_info(routing_domain);
let node_info_update = RPCOperationNodeInfoUpdate { signed_node_info };
let statement = RPCStatement::new(RPCStatementDetail::NodeInfoUpdate(node_info_update));
// Send the node_info_update request
// Send the node_info_update request to the specific routing domain requested
network_result_try!(
self.statement(
Destination::direct(target).with_routing_domain(routing_domain),
@ -41,7 +41,7 @@ impl RPCProcessor {
};
// Update our routing table with signed node info
if !self.filter_peer_scope(&node_info_update.signed_node_info.node_info) {
if !self.filter_node_info(&node_info_update.signed_node_info.node_info) {
log_rpc!(debug
"node_info_update has invalid peer scope from {}", sender_node_id
);

View File

@ -8,14 +8,21 @@ impl RPCProcessor {
self,
peer: NodeRef,
) -> Result<NetworkResult<Answer<SenderInfo>>, RPCError> {
let node_status = self.network_manager().generate_node_status();
let routing_domain = match peer.best_routing_domain() {
Some(rd) => rd,
None => {
return Ok(NetworkResult::no_connection_other(
"no routing domain for peer",
))
}
};
let node_status = self.network_manager().generate_node_status(routing_domain);
let status_q = RPCOperationStatusQ { node_status };
let respond_to = self.make_respond_to_sender(peer.clone());
let question = RPCQuestion::new(respond_to, RPCQuestionDetail::StatusQ(status_q));
let question = RPCQuestion::new(RespondTo::Sender, RPCQuestionDetail::StatusQ(status_q));
// Send the info request
let waitable_reply = network_result_try!(
self.question(Destination::Direct(peer.clone()), question, None)
self.question(Destination::direct(peer.clone()), question)
.await?
);
@ -37,26 +44,48 @@ impl RPCProcessor {
_ => return Err(RPCError::invalid_format("not an answer")),
};
// Ensure the returned node status is the kind for the routing domain we asked for
match routing_domain {
RoutingDomain::PublicInternet => {
if !matches!(status_a.node_status, NodeStatus::PublicInternet(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match PublicInternet routing domain",
));
}
}
RoutingDomain::LocalNetwork => {
if !matches!(status_a.node_status, NodeStatus::LocalNetwork(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match LocalNetwork routing domain",
));
}
}
}
// Update latest node status in routing table
peer.update_node_status(status_a.node_status.clone());
peer.update_node_status(status_a.node_status);
// Report sender_info IP addresses to network manager
// Don't need to validate these addresses for the current routing domain
// the address itself is irrelevant, and the remote node can lie anyway
if let Some(socket_address) = status_a.sender_info.socket_address {
match send_data_kind {
SendDataKind::Direct(connection_descriptor) => {
match connection_descriptor.peer_scope() {
PeerScope::Global => self.network_manager().report_global_socket_address(
SendDataKind::Direct(connection_descriptor) => match routing_domain {
RoutingDomain::PublicInternet => self
.network_manager()
.report_public_internet_socket_address(
socket_address,
connection_descriptor,
peer,
),
PeerScope::Local => self.network_manager().report_local_socket_address(
RoutingDomain::LocalNetwork => {
self.network_manager().report_local_network_socket_address(
socket_address,
connection_descriptor,
peer,
),
)
}
}
},
SendDataKind::Indirect => {
// Do nothing in this case, as the socket address returned here would be for any node other than ours
}
@ -75,6 +104,7 @@ impl RPCProcessor {
#[instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id, res), err)]
pub(crate) async fn process_status_q(&self, msg: RPCMessage) -> Result<(), RPCError> {
let connection_descriptor = msg.header.connection_descriptor;
let routing_domain = msg.header.routing_domain;
// Get the question
let status_q = match msg.operation.kind() {
@ -85,6 +115,24 @@ impl RPCProcessor {
_ => panic!("not a question"),
};
// Ensure the node status from the question is the kind for the routing domain we received the request in
match routing_domain {
RoutingDomain::PublicInternet => {
if !matches!(status_a.node_status, NodeStatus::PublicInternet(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match PublicInternet routing domain",
));
}
}
RoutingDomain::LocalNetwork => {
if !matches!(status_a.node_status, NodeStatus::LocalNetwork(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match LocalNetwork routing domain",
));
}
}
}
// update node status for the requesting node to our routing table
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
// Update latest node status in routing table for the statusq sender
@ -92,7 +140,7 @@ impl RPCProcessor {
}
// Make status answer
let node_status = self.network_manager().generate_node_status();
let node_status = self.network_manager().generate_node_status(routing_domain);
// Get the peer address in the returned sender info
let sender_info = SenderInfo {
@ -106,11 +154,7 @@ impl RPCProcessor {
// Send status answer
let res = self
.answer(
msg,
RPCAnswer::new(RPCAnswerDetail::StatusA(status_a)),
None,
)
.answer(msg, RPCAnswer::new(RPCAnswerDetail::StatusA(status_a)))
.await?;
tracing::Span::current().record("res", &tracing::field::display(res));
Ok(())

View File

@ -224,7 +224,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.upnp" => Ok(Box::new(false)),
"network.natpmp" => Ok(Box::new(false)),
"network.detect_address_changes" => Ok(Box::new(true)),
"network.enable_local_peer_scope" => Ok(Box::new(false)),
"network.restricted_nat_retries" => Ok(Box::new(3u32)),
"network.tls.certificate_path" => Ok(Box::new(get_certfile_path())),
"network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())),
@ -354,7 +353,6 @@ pub async fn test_config() {
assert_eq!(inner.network.upnp, false);
assert_eq!(inner.network.natpmp, false);
assert_eq!(inner.network.detect_address_changes, true);
assert_eq!(inner.network.enable_local_peer_scope, false);
assert_eq!(inner.network.restricted_nat_retries, 3u32);
assert_eq!(inner.network.tls.certificate_path, get_certfile_path());
assert_eq!(inner.network.tls.private_key_path, get_keyfile_path());

View File

@ -45,6 +45,16 @@ fn get_address_type(text: &str) -> Option<AddressType> {
None
}
}
fn get_routing_domain(text: &str) -> Option<RoutingDomain> {
let lctext = text.to_ascii_lowercase();
if "publicinternet".starts_with(&lctext) {
Some(RoutingDomain::PublicInternet)
} else if "localnetwork".starts_with(&lctext) {
Some(RoutingDomain::LocalNetwork)
} else {
None
}
}
fn get_debug_argument<T, G: FnOnce(&str) -> Option<T>>(
value: &str,
@ -331,27 +341,40 @@ impl VeilidAPI {
None => return Ok("Node id not found in routing table".to_owned()),
};
if args.len() >= 2 {
let pt =
get_debug_argument_at(&args, 1, "debug_ping", "protocol_type", get_protocol_type)?;
nr.merge_filter(DialInfoFilter::all().with_protocol_type(pt));
if args.len() >= 3 {
let at = get_debug_argument_at(
&args,
2,
"debug_ping",
"address_type",
get_address_type,
)?;
let mut ai = 1;
let mut routing_domain = None;
while ai < args.len() {
if let Ok(pt) =
get_debug_argument_at(&args, ai, "debug_ping", "protocol_type", get_protocol_type)
{
nr.merge_filter(DialInfoFilter::all().with_protocol_type(pt));
} else if let Ok(at) =
get_debug_argument_at(&args, ai, "debug_ping", "address_type", get_address_type)
{
nr.merge_filter(DialInfoFilter::all().with_address_type(at));
} else if let Ok(rd) = get_debug_argument_at(
&args,
ai,
"debug_ping",
"routing_domain",
get_routing_domain,
) {
if routing_domain.is_none() {
routing_domain = Some(rd);
} else {
return Ok("Multiple routing domains specified".to_owned());
}
} else {
return Ok(format!("Invalid argument specified: {}", args[ai]));
}
ai += 1;
}
let rpc = self.network_manager()?.rpc_processor();
// Dump routing table entry
let out = match rpc
.rpc_call_status(nr)
.rpc_call_status(routing_domain, nr)
.await
.map_err(VeilidAPIError::internal)?
{
@ -383,7 +406,7 @@ impl VeilidAPI {
attach
detach
restart network
ping <node_id> [protocol_type [address_type]]
ping <node_id> [protocol_type][address_type][routing_domain]
contact <node_id> [protocol_type [address_type]]
"#
.to_owned())

View File

@ -460,9 +460,31 @@ pub struct NodeInfo {
}
impl NodeInfo {
pub fn is_valid(&self) -> bool {
!matches!(self.network_class, NetworkClass::Invalid)
pub fn is_valid_in_routing_domain(
&self,
routing_table: RoutingTable,
routing_domain: RoutingDomain,
) -> bool {
// Should not be passing around nodeinfo with an invalid network class
if matches!(self.network_class, NetworkClass::Invalid) {
return false;
}
// Ensure all of the dial info works in this routing domain
for did in self.dial_info_detail_list {
if !routing_table.ensure_dial_info_is_valid(routing_domain, &did.dial_info) {
return false;
}
}
// Ensure the relay is also valid in this routing domain if it is provided
if let Some(relay_peer_info) = self.relay_peer_info {
let relay_ni = &relay_peer_info.signed_node_info.node_info;
if !relay_ni.is_valid_in_routing_domain(routing_table, routing_domain) {
return false;
}
}
true
}
pub fn first_filtered_dial_info_detail<F>(&self, filter: F) -> Option<DialInfoDetail>
where
F: Fn(&DialInfoDetail) -> bool,
@ -783,7 +805,7 @@ impl FromStr for SocketAddress {
//////////////////////////////////////////////////////////////////
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct DialInfoFilter {
pub protocol_type_set: ProtocolTypeSet,
pub address_type_set: AddressTypeSet,
@ -1096,6 +1118,14 @@ impl DialInfo {
pub fn address_type(&self) -> AddressType {
self.socket_address().address_type()
}
pub fn address(&self) -> Address {
match self {
Self::UDP(di) => di.socket_address.address,
Self::TCP(di) => di.socket_address.address,
Self::WS(di) => di.socket_address.address,
Self::WSS(di) => di.socket_address.address,
}
}
pub fn socket_address(&self) -> SocketAddress {
match self {
Self::UDP(di) => di.socket_address,

View File

@ -137,7 +137,6 @@ pub struct VeilidConfigNetwork {
pub upnp: bool,
pub natpmp: bool,
pub detect_address_changes: bool,
pub enable_local_peer_scope: bool,
pub restricted_nat_retries: u32,
pub tls: VeilidConfigTLS,
pub application: VeilidConfigApplication,
@ -356,7 +355,6 @@ impl VeilidConfig {
get_config!(inner.network.upnp);
get_config!(inner.network.natpmp);
get_config!(inner.network.detect_address_changes);
get_config!(inner.network.enable_local_peer_scope);
get_config!(inner.network.restricted_nat_retries);
get_config!(inner.network.tls.certificate_path);
get_config!(inner.network.tls.private_key_path);

View File

@ -215,3 +215,48 @@ pub fn ip_to_ipblock(ip6_prefix_size: usize, addr: IpAddr) -> IpAddr {
}
}
}
pub fn ipaddr_apply_netmask(addr: IpAddr, netmask: IpAddr) -> IpAddr {
match addr {
IpAddr::V4(v4) => {
let v4mask = match netmask {
IpAddr::V4(v4mask) => v4mask,
IpAddr::V6(_) => {
panic!("netmask doesn't match ipv4 address");
}
};
let v4 = v4.octets();
let v4mask = v4mask.octets();
IpAddr::V4(Ipv4Addr::new(
v4[0] & v4mask[0],
v4[1] & v4mask[1],
v4[2] & v4mask[2],
v4[3] & v4mask[3],
))
}
IpAddr::V6(v6) => {
let v6mask = match netmask {
IpAddr::V4(_) => {
panic!("netmask doesn't match ipv6 address");
}
IpAddr::V6(v6mask) => v6mask,
};
let v6 = v6.segments();
let v6mask = v6mask.segments();
IpAddr::V6(Ipv6Addr::new(
v6[0] & v6mask[0],
v6[1] & v6mask[1],
v6[2] & v6mask[2],
v6[3] & v6mask[3],
v6[4] & v6mask[4],
v6[5] & v6mask[5],
v6[6] & v6mask[6],
v6[7] & v6mask[7],
))
}
}
}
pub fn ipaddr_in_network(addr: IpAddr, netaddr: IpAddr, netmask: IpAddr) -> bool {
ipaddr_apply_netmask(netaddr, netmask) == ipaddr_apply_netmask(addr, netmask)
}

View File

@ -85,7 +85,6 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
upnp: true,
natpmp: true,
detectAddressChanges: true,
enableLocalPeerScope: false,
restrictedNatRetries: 0,
tls: VeilidConfigTLS(
certificatePath: "",

View File

@ -741,7 +741,6 @@ class VeilidConfigNetwork {
bool upnp;
bool natpmp;
bool detectAddressChanges;
bool enableLocalPeerScope;
int restrictedNatRetries;
VeilidConfigTLS tls;
VeilidConfigApplication application;
@ -767,7 +766,6 @@ class VeilidConfigNetwork {
required this.upnp,
required this.natpmp,
required this.detectAddressChanges,
required this.enableLocalPeerScope,
required this.restrictedNatRetries,
required this.tls,
required this.application,
@ -795,7 +793,6 @@ class VeilidConfigNetwork {
'upnp': upnp,
'natpmp': natpmp,
'detect_address_changes': detectAddressChanges,
'enable_local_peer_scope': enableLocalPeerScope,
'restricted_nat_retries': restrictedNatRetries,
'tls': tls.json,
'application': application.json,
@ -826,7 +823,6 @@ class VeilidConfigNetwork {
upnp = json['upnp'],
natpmp = json['natpmp'],
detectAddressChanges = json['detect_address_changes'],
enableLocalPeerScope = json['enable_local_peer_scope'],
restrictedNatRetries = json['restricted_nat_retries'],
tls = VeilidConfigTLS.fromJson(json['tls']),
application = VeilidConfigApplication.fromJson(json['application']),

View File

@ -130,11 +130,7 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.value_name("BOOTSTRAP_NODE_LIST")
.help("Specify a list of bootstrap node dialinfos to use"),
)
.arg(
Arg::new("local")
.long("local")
.help("Enable local peer scope")
);
;
#[cfg(debug_assertions)]
let matches = matches.arg(
@ -218,9 +214,6 @@ pub fn process_command_line() -> EyreResult<(Settings, ArgMatches)> {
if matches.is_present("attach") {
settingsrw.auto_attach = !matches!(matches.value_of("attach"), Some("true"));
}
if matches.is_present("local") {
settingsrw.core.network.enable_local_peer_scope = true;
}
if matches.occurrences_of("delete-protected-store") != 0 {
settingsrw.core.protected_store.delete = true;
}

View File

@ -99,7 +99,6 @@ core:
upnp: true
natpmp: false
detect_address_changes: true
enable_local_peer_scope: false
restricted_nat_retries: 0
tls:
certificate_path: '%CERTIFICATE_PATH%'
@ -589,7 +588,6 @@ pub struct Network {
pub upnp: bool,
pub natpmp: bool,
pub detect_address_changes: bool,
pub enable_local_peer_scope: bool,
pub restricted_nat_retries: u32,
pub tls: Tls,
pub application: Application,
@ -976,7 +974,6 @@ impl Settings {
set_config_value!(inner.core.network.upnp, value);
set_config_value!(inner.core.network.natpmp, value);
set_config_value!(inner.core.network.detect_address_changes, value);
set_config_value!(inner.core.network.enable_local_peer_scope, value);
set_config_value!(inner.core.network.restricted_nat_retries, value);
set_config_value!(inner.core.network.tls.certificate_path, value);
set_config_value!(inner.core.network.tls.private_key_path, value);
@ -1177,9 +1174,6 @@ impl Settings {
"network.detect_address_changes" => {
Ok(Box::new(inner.core.network.detect_address_changes))
}
"network.enable_local_peer_scope" => {
Ok(Box::new(inner.core.network.enable_local_peer_scope))
}
"network.restricted_nat_retries" => {
Ok(Box::new(inner.core.network.restricted_nat_retries))
}
@ -1503,7 +1497,6 @@ mod tests {
assert_eq!(s.core.network.upnp, true);
assert_eq!(s.core.network.natpmp, false);
assert_eq!(s.core.network.detect_address_changes, true);
assert_eq!(s.core.network.enable_local_peer_scope, false);
assert_eq!(s.core.network.restricted_nat_retries, 0u32);
//
assert_eq!(