From 8f9b9b58d5043d3c901c51ba2138ea37727d92a3 Mon Sep 17 00:00:00 2001 From: John Smith Date: Wed, 15 Feb 2023 18:18:08 -0500 Subject: [PATCH] more refactor --- veilid-core/proto/veilid.capnp | 2 +- veilid-core/src/crypto/mod.rs | 13 +- veilid-core/src/crypto/types.rs | 28 +++- veilid-core/src/network_manager/mod.rs | 4 +- veilid-core/src/routing_table/bucket_entry.rs | 9 ++ veilid-core/src/routing_table/node_ref.rs | 8 ++ veilid-core/src/routing_table/privacy.rs | 12 -- .../src/routing_table/route_spec_store.rs | 122 ++++++++++-------- .../src/routing_table/routing_table_inner.rs | 14 +- .../src/routing_table/tasks/ping_validator.rs | 63 ++++----- .../src/rpc_processor/coders/node_info.rs | 30 +++++ .../src/rpc_processor/coders/peer_info.rs | 6 +- .../coders/signed_direct_node_info.rs | 2 +- .../rpc_processor/coders/signed_node_info.rs | 2 +- .../coders/signed_relayed_node_info.rs | 17 ++- veilid-core/src/rpc_processor/destination.rs | 24 ++-- veilid-core/src/veilid_api/api.rs | 16 ++- veilid-core/src/veilid_api/types.rs | 26 +++- 18 files changed, 257 insertions(+), 141 deletions(-) diff --git a/veilid-core/proto/veilid.capnp b/veilid-core/proto/veilid.capnp index 1f45cc57..a09da6f9 100644 --- a/veilid-core/proto/veilid.capnp +++ b/veilid-core/proto/veilid.capnp @@ -134,7 +134,7 @@ struct RouteHopData @0x8ce231f9d1b7adf2 { struct RouteHop @0xf8f672d75cce0c3b { node :union { - nodeId @0 :TypedKey; # node id only for established routes + nodeId @0 :PublicKey; # node id key only for established routes (kind is the same as the pr or sr it is part of) peerInfo @1 :PeerInfo; # full peer info for this hop to establish the route } nextHop @2 :RouteHopData; # optional: If this the end of a private route, this field will not exist diff --git a/veilid-core/src/crypto/mod.rs b/veilid-core/src/crypto/mod.rs index 1b025ee4..e7ae9ecc 100644 --- a/veilid-core/src/crypto/mod.rs +++ b/veilid-core/src/crypto/mod.rs @@ -36,9 +36,14 @@ pub fn best_crypto_kind() -> CryptoKind { VALID_CRYPTO_KINDS[0] } -/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one +// Version number of envelope format pub type EnvelopeVersion = u8; + +/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [0u8]; +/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid +pub const MAX_ENVELOPE_VERSIONS: usize = 3; +/// Return the best envelope version we support pub fn best_envelope_version() -> EnvelopeVersion { VALID_ENVELOPE_VERSIONS[0] } @@ -218,17 +223,19 @@ impl Crypto { node_ids: &[TypedKey], data: &[u8], typed_signatures: &[TypedSignature], - ) -> Result<(), VeilidAPIError> { + ) -> Result, VeilidAPIError> { + let mut out = Vec::with_capacity(node_ids.len()); for sig in typed_signatures { for nid in node_ids { if nid.kind == sig.kind { if let Some(vcrypto) = self.get(sig.kind) { vcrypto.verify(&nid.key, data, &sig.signature)?; + out.push(nid.kind); } } } } - Ok(()) + Ok(out) } /// Signature set generation diff --git a/veilid-core/src/crypto/types.rs b/veilid-core/src/crypto/types.rs index 8548e171..a6334ee6 100644 --- a/veilid-core/src/crypto/types.rs +++ b/veilid-core/src/crypto/types.rs @@ -27,6 +27,17 @@ pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering { } } +/// Intersection of crypto kind vectors +pub fn common_crypto_kinds(a: &[CryptoKind], b: &[CryptoKind]) -> Vec { + let mut out = Vec::new(); + for ack in a { + if b.contains(ack) { + out.push(*ack); + } + } + out +} + #[derive( Clone, Copy, @@ -42,7 +53,7 @@ pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering { RkyvSerialize, RkyvDeserialize, )] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] pub struct KeyPair { pub key: PublicKey, pub secret: SecretKey, @@ -67,7 +78,7 @@ impl KeyPair { RkyvSerialize, RkyvDeserialize, )] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] pub struct TypedKey { pub kind: CryptoKind, pub key: PublicKey, @@ -126,7 +137,7 @@ impl FromStr for TypedKey { RkyvSerialize, RkyvDeserialize, )] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] pub struct TypedKeySet { items: Vec, } @@ -178,6 +189,11 @@ impl TypedKeySet { self.items.remove(idx); } } + pub fn remove_all(&self, kinds: &[CryptoKind]) { + for k in kinds { + self.remove(*k); + } + } pub fn best(&self) -> Option { self.items.first().copied() } @@ -255,7 +271,7 @@ impl FromStr for TypedKeySet { RkyvSerialize, RkyvDeserialize, )] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] pub struct TypedKeyPair { pub kind: CryptoKind, pub key: PublicKey, @@ -329,7 +345,7 @@ impl FromStr for TypedKeyPair { RkyvSerialize, RkyvDeserialize, )] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] pub struct TypedSignature { pub kind: CryptoKind, pub signature: Signature, @@ -399,7 +415,7 @@ impl FromStr for TypedSignature { RkyvSerialize, RkyvDeserialize, )] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] pub struct TypedKeySignature { pub kind: CryptoKind, pub key: PublicKey, diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index 8562d16e..a8bdb7c2 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -893,7 +893,7 @@ impl NetworkManager { // We expect the inbound noderef to be the same as the target noderef // if they aren't the same, we should error on this and figure out what then hell is up - if target_nr.node_id() != inbound_nr.node_id() { + if !target_nr.same_entry(&inbound_nr) { bail!("unexpected noderef mismatch on reverse connect"); } @@ -998,7 +998,7 @@ impl NetworkManager { // We expect the inbound noderef to be the same as the target noderef // if they aren't the same, we should error on this and figure out what then hell is up - if target_nr.node_id() != inbound_nr.node_id() { + if !target_nr.same_entry(&inbound_nr) { bail!( "unexpected noderef mismatch on hole punch {}, expected {}", inbound_nr, diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index c0df7e4d..35a3c503 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -133,6 +133,15 @@ impl BucketEntryInner { self.node_ids.best().unwrap() } + // Crypto kinds + pub fn crypto_kinds(&self) -> Vec { + self.node_ids.kinds() + } + pub fn common_crypto_kinds(&self, other: &[CryptoKind]) -> Vec { + common_crypto_kinds(&self.node_ids.kinds(), other) + } + + // Less is faster pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering { // Lower latency to the front diff --git a/veilid-core/src/routing_table/node_ref.rs b/veilid-core/src/routing_table/node_ref.rs index e2a7d6bd..45dbff36 100644 --- a/veilid-core/src/routing_table/node_ref.rs +++ b/veilid-core/src/routing_table/node_ref.rs @@ -20,6 +20,14 @@ pub trait NodeRefBase: Sized { fn common(&self) -> &NodeRefBaseCommon; fn common_mut(&mut self) -> &mut NodeRefBaseCommon; + // Comparators + fn same_entry(&self, other: &T) -> bool { + Arc::ptr_eq(&self.common().entry, &other.common().entry) + } + fn same_bucket_entry(&self, entry: &Arc) -> bool { + Arc::ptr_eq(&self.common().entry, entry) + } + // Implementation-specific operators fn operate(&self, f: F) -> T where diff --git a/veilid-core/src/routing_table/privacy.rs b/veilid-core/src/routing_table/privacy.rs index 8652866b..75a9ea3d 100644 --- a/veilid-core/src/routing_table/privacy.rs +++ b/veilid-core/src/routing_table/privacy.rs @@ -20,18 +20,6 @@ pub enum RouteNode { /// Route node with full contact method information to ensure the peer is reachable PeerInfo(PeerInfo), } -impl fmt::Display for RouteNode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - match self { - RouteNode::NodeId(x) => x.key.encode(), - RouteNode::PeerInfo(pi) => pi.node_id.key.encode(), - } - ) - } -} /// An unencrypted private/safety route hop #[derive(Clone, Debug)] diff --git a/veilid-core/src/routing_table/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store.rs index 988b8c87..ab81c0b2 100644 --- a/veilid-core/src/routing_table/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store.rs @@ -221,7 +221,7 @@ impl RouteSpecDetail { #[archive_attr(repr(C, align(8)), derive(CheckBytes))] pub struct RouteSpecStoreContent { /// All of the routes we have allocated so far - details: HashMap, + details: HashMap, } /// What remote private routes have seen @@ -250,19 +250,19 @@ impl RemotePrivateRouteInfo { #[derive(Debug)] pub struct RouteSpecStoreCache { /// How many times nodes have been used - used_nodes: HashMap, + used_nodes: HashMap, /// How many times nodes have been used at the terminal point of a route - used_end_nodes: HashMap, + used_end_nodes: HashMap, /// Route spec hop cache, used to quickly disqualify routes hop_cache: HashSet>, /// Has a remote private route responded to a question and when - remote_private_route_cache: LruCache, + remote_private_route_cache: LruCache, /// Compiled route cache compiled_route_cache: LruCache, /// List of dead allocated routes - dead_routes: Vec, + dead_routes: Vec, /// List of dead remote routes - dead_remote_routes: Vec, + dead_remote_routes: Vec, } impl Default for RouteSpecStoreCache { @@ -577,15 +577,15 @@ impl RouteSpecStore { fn detail<'a>( inner: &'a RouteSpecStoreInner, - public_key: &PublicKey, + route_spec_key: &TypedKey, ) -> Option<&'a RouteSpecDetail> { - inner.content.details.get(public_key) + inner.content.details.get(route_spec_key) } fn detail_mut<'a>( inner: &'a mut RouteSpecStoreInner, - public_key: &PublicKey, + route_spec_key: &TypedKey, ) -> Option<&'a mut RouteSpecDetail> { - inner.content.details.get_mut(public_key) + inner.content.details.get_mut(route_spec_key) } /// Purge the route spec store @@ -605,12 +605,13 @@ impl RouteSpecStore { #[instrument(level = "trace", skip(self), ret, err)] pub fn allocate_route( &self, + crypto_kinds: &[CryptoKind], stability: Stability, sequencing: Sequencing, hop_count: usize, directions: DirectionSet, - avoid_node_ids: &[PublicKey], - ) -> EyreResult> { + avoid_nodes: &[TypedKey], + ) -> EyreResult> { let inner = &mut *self.inner.lock(); let routing_table = self.unlocked_inner.routing_table.clone(); let rti = &mut *routing_table.inner.write(); @@ -618,11 +619,12 @@ impl RouteSpecStore { self.allocate_route_inner( inner, rti, + crypto_kinds, stability, sequencing, hop_count, directions, - avoid_node_ids, + avoid_nodes, ) } @@ -631,12 +633,13 @@ impl RouteSpecStore { &self, inner: &mut RouteSpecStoreInner, rti: &RoutingTableInner, + crypto_kinds: &[CryptoKind], stability: Stability, sequencing: Sequencing, hop_count: usize, directions: DirectionSet, - avoid_node_ids: &[PublicKey], - ) -> EyreResult> { + avoid_nodes: &[TypedKey], + ) -> EyreResult> { use core::cmp::Ordering; if hop_count < 1 { @@ -651,52 +654,60 @@ impl RouteSpecStore { bail!("Can't allocate route until we have our own peer info"); }; - // Get relay node id if we have one - let opt_relay_id = rti - .relay_node(RoutingDomain::PublicInternet) - .map(|nr| nr.node_id()); + // Get relay node if we have one + let opt_own_relay_nr = rti.relay_node(RoutingDomain::PublicInternet).map(|nr| nr.locked(rti)); // Get list of all nodes, and sort them for selection let cur_ts = get_aligned_timestamp(); let filter = Box::new( - move |rti: &RoutingTableInner, k: PublicKey, v: Option>| -> bool { + move |rti: &RoutingTableInner, entry: Option>| -> bool { // Exclude our own node from routes - if v.is_none() { + if entry.is_none() { return false; } - let v = v.unwrap(); + let entry = entry.unwrap(); // Exclude our relay if we have one - if let Some(own_relay_id) = opt_relay_id { - if k == own_relay_id { + if let Some(own_relay_nr) = opt_own_relay_nr { + if own_relay_nr.same_bucket_entry(&entry) { return false; } } - // Exclude nodes we have specifically chosen to avoid - if avoid_node_ids.contains(&k) { - return false; - } - // Process node info exclusions - let keep = v.with(rti, |_rti, e| { + let keep = entry.with(rti, |_rti, e| { + + // Exclude nodes that don't have our requested crypto kinds + let common_ck = e.common_crypto_kinds(crypto_kinds); + if common_ck.len() != crypto_kinds.len() { + return false; + } + + // Exclude nodes we have specifically chosen to avoid + if e.node_ids().contains_any(avoid_nodes) { + return false; + } + // Exclude nodes on our local network if e.node_info(RoutingDomain::LocalNetwork).is_some() { return false; } + // Exclude nodes that have no publicinternet signednodeinfo let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) else { return false; }; + // Relay check - if let Some(relay_id) = sni.relay_id() { + let relay_ids = sni.relay_ids(); + if relay_ids.len() != 0 { // Exclude nodes whose relays we have chosen to avoid - if avoid_node_ids.contains(&relay_id.key) { + if relay_ids.contains_any(avoid_nodes) { return false; } // Exclude nodes whose relay is our own relay if we have one - if let Some(own_relay_id) = opt_relay_id { - if own_relay_id == relay_id.key { + if let Some(own_relay_nr) = opt_own_relay_nr { + if relay_ids.contains_any(&own_relay_nr.node_ids()) { return false; } } @@ -708,7 +719,7 @@ impl RouteSpecStore { } // Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route - v.with(rti, move |_rti, e| { + entry.with(rti, |_rti, e| { let node_info_ok = if let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) { sni.has_sequencing_matched_dial_info(sequencing) @@ -728,9 +739,14 @@ impl RouteSpecStore { ) as RoutingTableEntryFilter; let filters = VecDeque::from([filter]); let compare = |rti: &RoutingTableInner, - v1: &(PublicKey, Option>), - v2: &(PublicKey, Option>)| + entry1: &Option>, + entry2: &Option>| -> Ordering { + + + // xxx also sort my most overlapping crypto kinds + + // deprioritize nodes that we have already used as end points let e1_used_end = inner .cache @@ -1031,7 +1047,7 @@ impl RouteSpecStore { } #[instrument(level = "trace", skip(self), ret, err)] - async fn test_allocated_route(&self, key: &PublicKey) -> EyreResult { + async fn test_allocated_route(&self, key: &TypedKey) -> EyreResult { // Make loopback route to test with let dest = { let private_route = self.assemble_private_route(key, None)?; @@ -1074,7 +1090,7 @@ impl RouteSpecStore { } #[instrument(level = "trace", skip(self), ret, err)] - async fn test_remote_route(&self, key: &PublicKey) -> EyreResult { + async fn test_remote_route(&self, key: &TypedKey) -> EyreResult { // Make private route test let dest = { // Get the route to test @@ -1114,7 +1130,7 @@ impl RouteSpecStore { /// Test an allocated route for continuity #[instrument(level = "trace", skip(self), ret, err)] - pub async fn test_route(&self, key: &PublicKey) -> EyreResult { + pub async fn test_route(&self, key: &[TypedKey]) -> EyreResult { let is_remote = { let inner = &mut *self.inner.lock(); let cur_ts = get_aligned_timestamp(); @@ -1574,8 +1590,8 @@ impl RouteSpecStore { rti: &RoutingTableInner, safety_spec: &SafetySpec, direction: DirectionSet, - avoid_node_ids: &[PublicKey], - ) -> EyreResult> { + avoid_nodes: &[TypedKey], + ) -> EyreResult> { // Ensure the total hop count isn't too long for our config let max_route_hop_count = self.unlocked_inner.max_route_hop_count; if safety_spec.hop_count == 0 { @@ -1634,8 +1650,8 @@ impl RouteSpecStore { pub fn get_private_route_for_safety_spec( &self, safety_spec: &SafetySpec, - avoid_node_ids: &[PublicKey], - ) -> EyreResult> { + avoid_nodes: &[TypedKey], + ) -> EyreResult> { let inner = &mut *self.inner.lock(); let routing_table = self.unlocked_inner.routing_table.clone(); let rti = &*routing_table.inner.read(); @@ -1645,7 +1661,7 @@ impl RouteSpecStore { rti, safety_spec, Direction::Inbound.into(), - avoid_node_ids, + avoid_nodes, )?) } @@ -1742,7 +1758,7 @@ impl RouteSpecStore { /// Import a remote private route for compilation #[instrument(level = "trace", skip(self, blob), ret, err)] - pub fn import_remote_private_route(&self, blob: Vec) -> EyreResult { + pub fn import_remote_private_route(&self, blob: Vec) -> EyreResult { // decode the pr blob let private_route = RouteSpecStore::blob_to_private_route(blob)?; @@ -1767,7 +1783,7 @@ impl RouteSpecStore { /// Release a remote private route that is no longer in use #[instrument(level = "trace", skip(self), ret)] - fn release_remote_private_route(&self, key: &PublicKey) -> bool { + fn release_remote_private_route(&self, key: &TypedKey) -> bool { let inner = &mut *self.inner.lock(); if inner.cache.remote_private_route_cache.remove(key).is_some() { // Mark it as dead for the update @@ -1779,7 +1795,7 @@ impl RouteSpecStore { } /// Retrieve an imported remote private route by its public key - pub fn get_remote_private_route(&self, key: &PublicKey) -> Option { + pub fn get_remote_private_route(&self, key: &TypedKey) -> Option { let inner = &mut *self.inner.lock(); let cur_ts = get_aligned_timestamp(); Self::with_get_remote_private_route(inner, cur_ts, key, |r| { @@ -1788,7 +1804,7 @@ impl RouteSpecStore { } /// Retrieve an imported remote private route by its public key but don't 'touch' it - pub fn peek_remote_private_route(&self, key: &PublicKey) -> Option { + pub fn peek_remote_private_route(&self, key: &TypedKey) -> Option { let inner = &mut *self.inner.lock(); let cur_ts = get_aligned_timestamp(); Self::with_peek_remote_private_route(inner, cur_ts, key, |r| { @@ -1849,7 +1865,7 @@ impl RouteSpecStore { fn with_get_remote_private_route( inner: &mut RouteSpecStoreInner, cur_ts: Timestamp, - key: &PublicKey, + remote_private_route: &TypedKey, f: F, ) -> Option where @@ -1869,7 +1885,7 @@ impl RouteSpecStore { fn with_peek_remote_private_route( inner: &mut RouteSpecStoreInner, cur_ts: Timestamp, - key: &PublicKey, + remote_private_route: &TypedKey, f: F, ) -> Option where @@ -1891,7 +1907,7 @@ impl RouteSpecStore { /// Check to see if this remote (not ours) private route has seen our current node info yet /// This happens when you communicate with a private route without a safety route - pub fn has_remote_private_route_seen_our_node_info(&self, key: &PublicKey) -> bool { + pub fn has_remote_private_route_seen_our_node_info(&self, remote_private_route: &TypedKey) -> bool { let our_node_info_ts = { let rti = &*self.unlocked_inner.routing_table.inner.read(); let Some(ts) = rti.get_own_node_info_ts(RoutingDomain::PublicInternet) else { @@ -1903,7 +1919,7 @@ impl RouteSpecStore { let opt_rpr_node_info_ts = { let inner = &mut *self.inner.lock(); let cur_ts = get_aligned_timestamp(); - Self::with_peek_remote_private_route(inner, cur_ts, key, |rpr| { + Self::with_peek_remote_private_route(inner, cur_ts, remote_private_route, |rpr| { rpr.last_seen_our_node_info_ts }) }; diff --git a/veilid-core/src/routing_table/routing_table_inner.rs b/veilid-core/src/routing_table/routing_table_inner.rs index 69c64f55..377f1e67 100644 --- a/veilid-core/src/routing_table/routing_table_inner.rs +++ b/veilid-core/src/routing_table/routing_table_inner.rs @@ -360,7 +360,7 @@ impl RoutingTableInner { self.all_entries.remove_expired(); log_rtab!(debug - "Routing table buckets purge complete. Routing table now has {} nodes", + "Routing table buckets purge complete. Routing table now has {} nodes", self.bucket_entry_count() ); } @@ -383,8 +383,8 @@ impl RoutingTableInner { self.all_entries.remove_expired(); log_rtab!(debug - "Routing table last_connections purge complete. Routing table now has {} nodes", - self.bucket_entry_count() + "Routing table last_connections purge complete. Routing table now has {} nodes", + self.bucket_entry_count() ); } @@ -506,9 +506,9 @@ impl RoutingTableInner { } pub fn get_all_nodes(&self, outer_self: RoutingTable, cur_ts: Timestamp) -> Vec { - let mut node_refs = Vec::::with_capacity(self.bucket_entry_count); - self.with_entries(cur_ts, BucketEntryState::Unreliable, |_rti, k, v| { - node_refs.push(NodeRef::new(outer_self.clone(), k, v, None)); + let mut node_refs = Vec::::with_capacity(self.bucket_entry_count()); + self.with_entries(cur_ts, BucketEntryState::Unreliable, |_rti, entry| { + node_refs.push(NodeRef::new(outer_self.clone(), entry, None)); Option::<()>::None }); node_refs @@ -525,7 +525,7 @@ impl RoutingTableInner { ) -> Option where F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner), - { + {xxx continue here // Ensure someone isn't trying register this node itself if self.unlocked_inner.matches_own_node_id(node_ids) { log_rtab!(debug "can't register own node"); diff --git a/veilid-core/src/routing_table/tasks/ping_validator.rs b/veilid-core/src/routing_table/tasks/ping_validator.rs index f94f603c..7ba146c1 100644 --- a/veilid-core/src/routing_table/tasks/ping_validator.rs +++ b/veilid-core/src/routing_table/tasks/ping_validator.rs @@ -25,7 +25,6 @@ impl RoutingTable { // Get the PublicInternet relay if we are using one let opt_relay_nr = self.relay_node(RoutingDomain::PublicInternet); - let opt_relay_id = opt_relay_nr.map(|nr| nr.node_id()); // Get our publicinternet dial info let dids = self.all_filtered_dial_info_details( @@ -35,38 +34,42 @@ impl RoutingTable { // For all nodes needing pings, figure out how many and over what protocols for nr in node_refs { - // If this is a relay, let's check for NAT keepalives + // If this is our 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 { - // Do we need to do this ping? - // Check if we have already pinged over this low-level-protocol/address-type/port combo - // We want to ensure we do the bare minimum required here - let pt = did.dial_info.protocol_type(); - let at = did.dial_info.address_type(); - let needs_ping = if let Some((llpt, port)) = - mapped_port_info.protocol_to_port.get(&(pt, at)) - { - mapped_port_info - .low_level_protocol_ports - .remove(&(*llpt, at, *port)) - } else { - false - }; - if needs_ping { - let rpc = rpc.clone(); - let dif = did.dial_info.make_filter(); - let nr_filtered = - nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif)); - log_net!("--> Keepalive ping to {:?}", nr_filtered); - unord.push( - async move { rpc.rpc_call_status(Destination::direct(nr_filtered)).await } + if let Some(relay_nr) = opt_relay_nr { + if nr.same_entry(&relay_nr) { + // 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 { + // Do we need to do this ping? + // Check if we have already pinged over this low-level-protocol/address-type/port combo + // We want to ensure we do the bare minimum required here + let pt = did.dial_info.protocol_type(); + let at = did.dial_info.address_type(); + let needs_ping = if let Some((llpt, port)) = + mapped_port_info.protocol_to_port.get(&(pt, at)) + { + mapped_port_info + .low_level_protocol_ports + .remove(&(*llpt, at, *port)) + } else { + false + }; + if needs_ping { + let rpc = rpc.clone(); + let dif = did.dial_info.make_filter(); + let nr_filtered = + nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif)); + log_net!("--> Keepalive ping to {:?}", nr_filtered); + unord.push( + async move { + rpc.rpc_call_status(Destination::direct(nr_filtered)).await + } .instrument(Span::current()) .boxed(), - ); - did_pings = true; + ); + did_pings = true; + } } } } diff --git a/veilid-core/src/rpc_processor/coders/node_info.rs b/veilid-core/src/rpc_processor/coders/node_info.rs index 80893219..6d2d87c6 100644 --- a/veilid-core/src/rpc_processor/coders/node_info.rs +++ b/veilid-core/src/rpc_processor/coders/node_info.rs @@ -79,6 +79,21 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result MAX_ENVELOPE_VERSIONS { + return Err(RPCError::protocol("too many envelope versions")); + } + if envelope_support.len() == 0 { + return Err(RPCError::protocol("no envelope versions")); + } + let crypto_support: Vec = reader .reborrow() .get_crypto_support() @@ -87,6 +102,21 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result MAX_CRYPTO_KINDS { + return Err(RPCError::protocol("too many crypto kinds")); + } + if crypto_support.len() == 0 { + return Err(RPCError::protocol("no crypto kinds")); + } + let didl_reader = reader .reborrow() .get_dial_info_detail_list() diff --git a/veilid-core/src/rpc_processor/coders/peer_info.rs b/veilid-core/src/rpc_processor/coders/peer_info.rs index f7ddabe6..28e51ae4 100644 --- a/veilid-core/src/rpc_processor/coders/peer_info.rs +++ b/veilid-core/src/rpc_processor/coders/peer_info.rs @@ -43,8 +43,10 @@ pub fn decode_peer_info( for nid_reader in nids_reader.iter() { node_ids.add(decode_typed_key(&nid_reader)?); } - let signed_node_info = decode_signed_node_info(&sni_reader, crypto, &node_ids)?; - + let signed_node_info = decode_signed_node_info(&sni_reader, crypto, &mut node_ids)?; + if node_ids.len() == 0 { + return Err(RPCError::protocol("no verified node ids")); + } Ok(PeerInfo { node_ids, signed_node_info, diff --git a/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs index dfbb8a12..189e507b 100644 --- a/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs +++ b/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs @@ -35,7 +35,7 @@ pub fn encode_signed_direct_node_info( pub fn decode_signed_direct_node_info( reader: &veilid_capnp::signed_direct_node_info::Reader, crypto: Crypto, - node_ids: &[TypedKey], + node_ids: &mut TypedKeySet, ) -> Result { let ni_reader = reader .reborrow() diff --git a/veilid-core/src/rpc_processor/coders/signed_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_node_info.rs index 37996c26..aeede197 100644 --- a/veilid-core/src/rpc_processor/coders/signed_node_info.rs +++ b/veilid-core/src/rpc_processor/coders/signed_node_info.rs @@ -21,7 +21,7 @@ pub fn encode_signed_node_info( pub fn decode_signed_node_info( reader: &veilid_capnp::signed_node_info::Reader, crypto: Crypto, - node_ids: &[TypedKey], + node_ids: &mut TypedKeySet, ) -> Result { match reader .which() diff --git a/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs index b14163e0..f56265ef 100644 --- a/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs +++ b/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs @@ -55,7 +55,7 @@ pub fn encode_signed_relayed_node_info( pub fn decode_signed_relayed_node_info( reader: &veilid_capnp::signed_relayed_node_info::Reader, crypto: Crypto, - node_ids: &[TypedKey], + node_ids: &mut TypedKeySet, ) -> Result { let ni_reader = reader .reborrow() @@ -81,7 +81,20 @@ pub fn decode_signed_relayed_node_info( .reborrow() .get_relay_info() .map_err(RPCError::protocol)?; - let relay_info = decode_signed_direct_node_info(&ri_reader, crypto, &relay_ids)?; + let relay_info = decode_signed_direct_node_info(&ri_reader, crypto, &mut relay_ids)?; + + // Ensure the relay info for the node has a superset of the crypto kinds of the node it is relaying + if common_crypto_kinds( + &node_info.crypto_support, + &relay_info.node_info.crypto_support, + ) + .len() + != node_info.crypto_support.len() + { + return Err(RPCError::protocol( + "relay should have superset of node crypto kinds", + )); + } let timestamp = reader.reborrow().get_timestamp().into(); diff --git a/veilid-core/src/rpc_processor/destination.rs b/veilid-core/src/rpc_processor/destination.rs index 482df992..9a692fef 100644 --- a/veilid-core/src/rpc_processor/destination.rs +++ b/veilid-core/src/rpc_processor/destination.rs @@ -15,7 +15,7 @@ pub enum Destination { /// The relay to send to relay: NodeRef, /// The final destination the relay should send to - target: TypedKey, + target: NodeRef, /// Require safety route or not safety_selection: SafetySelection, }, @@ -36,7 +36,7 @@ impl Destination { safety_selection: SafetySelection::Unsafe(sequencing), } } - pub fn relay(relay: NodeRef, target: TypedKey) -> Self { + pub fn relay(relay: NodeRef, target: NodeRef) -> Self { let sequencing = relay.sequencing(); Self::Relay { relay, @@ -124,7 +124,7 @@ impl fmt::Display for Destination { "" }; - write!(f, "{}@{}{}", target.encode(), relay, sr) + write!(f, "{}@{}{}", target, relay, sr) } Destination::PrivateRoute { private_route, @@ -163,7 +163,7 @@ impl RPCProcessor { SafetySelection::Safe(safety_spec) => { // Sent directly but with a safety route, respond to private route let Some(pr_key) = rss - .get_private_route_for_safety_spec(safety_spec, &[target.node_id()]) + .get_private_route_for_safety_spec(safety_spec, &target.node_ids()) .map_err(RPCError::internal)? else { return Ok(NetworkResult::no_connection_other("no private route for response at this time")); }; @@ -187,11 +187,13 @@ impl RPCProcessor { } SafetySelection::Safe(safety_spec) => { // Sent via a relay but with a safety route, respond to private route + let mut avoid_nodes = relay.node_ids(); + avoid_nodes.add_all(&target.node_ids()); let Some(pr_key) = rss - .get_private_route_for_safety_spec(safety_spec, &[relay.node_id(), *target]) - .map_err(RPCError::internal)? else { - return Ok(NetworkResult::no_connection_other("no private route for response at this time")); - }; + .get_private_route_for_safety_spec(safety_spec, &avoid_nodes) + .map_err(RPCError::internal)? else { + return Ok(NetworkResult::no_connection_other("no private route for response at this time")); + }; // Get the assembled route for response let private_route = rss @@ -209,6 +211,8 @@ impl RPCProcessor { return Err(RPCError::internal("destination private route must have first hop")); }; + let crypto_kind = private_route.public_key.kind; + match safety_selection { SafetySelection::Unsafe(_) => { // Sent to a private route with no safety route, use a stub safety route for the response @@ -221,7 +225,7 @@ impl RPCProcessor { if !routing_table.has_valid_own_node_info(RoutingDomain::PublicInternet) { return Ok(NetworkResult::no_connection_other("Own node info must be valid to use private route")); } - RouteNode::NodeId(NodeId::new(routing_table.node_id())) + RouteNode::NodeId(routing_table.node_id(crypto_kind)) } false => { let Some(own_peer_info) = @@ -233,7 +237,7 @@ impl RPCProcessor { }; Ok(NetworkResult::value(RespondTo::PrivateRoute( - PrivateRoute::new_stub(routing_table.node_id(), route_node), + PrivateRoute::new_stub(routing_table.node_id(crypto_kind), route_node), ))) } SafetySelection::Safe(safety_spec) => { diff --git a/veilid-core/src/veilid_api/api.rs b/veilid-core/src/veilid_api/api.rs index 35fa5541..ef76f6e1 100644 --- a/veilid-core/src/veilid_api/api.rs +++ b/veilid-core/src/veilid_api/api.rs @@ -167,17 +167,22 @@ impl VeilidAPI { // Private route allocation #[instrument(level = "debug", skip(self))] - pub async fn new_private_route(&self) -> Result<(TypedKey, Vec), VeilidAPIError> { - self.new_custom_private_route(Stability::default(), Sequencing::default()) - .await + pub async fn new_private_route(&self) -> Result<(TypedKeySet, Vec), VeilidAPIError> { + self.new_custom_private_route( + &VALID_CRYPTO_KINDS, + Stability::default(), + Sequencing::default(), + ) + .await } #[instrument(level = "debug", skip(self))] pub async fn new_custom_private_route( &self, + crypto_kinds: &[CryptoKind], stability: Stability, sequencing: Sequencing, - ) -> Result<(TypedKey, Vec), VeilidAPIError> { + ) -> Result<(TypedKeySet, Vec), VeilidAPIError> { let default_route_hop_count: usize = { let config = self.config()?; let c = config.get(); @@ -187,6 +192,7 @@ impl VeilidAPI { let rss = self.routing_table()?.route_spec_store(); let r = rss .allocate_route( + &crypto_kinds, stability, sequencing, default_route_hop_count, @@ -194,7 +200,7 @@ impl VeilidAPI { &[], ) .map_err(VeilidAPIError::internal)?; - let Some(pr_pubkey) = r else { + let Some(pr_keys) = r else { apibail_generic!("unable to allocate route"); }; if !rss diff --git a/veilid-core/src/veilid_api/types.rs b/veilid-core/src/veilid_api/types.rs index c248ee39..5e79231c 100644 --- a/veilid-core/src/veilid_api/types.rs +++ b/veilid-core/src/veilid_api/types.rs @@ -35,7 +35,7 @@ pub type ValueSchema = FourCC; RkyvSerialize, RkyvDeserialize, )] -#[archive_attr(repr(C), derive(CheckBytes, PartialOrd, Ord, PartialEq, Eq))] +#[archive_attr(repr(C), derive(CheckBytes, PartialOrd, Ord, PartialEq, Eq, Hash))] pub struct FourCC(pub [u8; 4]); impl From<[u8; 4]> for FourCC { @@ -513,7 +513,7 @@ impl SafetySelection { #[archive_attr(repr(C), derive(CheckBytes))] pub struct SafetySpec { /// preferred safety route if it still exists - pub preferred_route: Option, + pub preferred_route: Option, /// must be greater than 0 pub hop_count: usize, /// prefer reliability over speed @@ -1853,7 +1853,7 @@ pub struct SignedDirectNodeInfo { impl SignedDirectNodeInfo { pub fn new( crypto: Crypto, - node_ids: &[TypedKey], + node_ids: &mut TypedKeySet, node_info: NodeInfo, timestamp: Timestamp, typed_signatures: Vec, @@ -1861,7 +1861,13 @@ impl SignedDirectNodeInfo { let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; // Verify the signatures that we can - crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; + let valid_crypto_kinds = + crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; + node_ids.remove_all(&valid_crypto_kinds); + if node_ids.len() == 0 { + apibail_generic!("no valid node ids in direct node info"); + } + Ok(Self { node_info, timestamp, @@ -1933,7 +1939,7 @@ pub struct SignedRelayedNodeInfo { impl SignedRelayedNodeInfo { pub fn new( crypto: Crypto, - node_ids: &[TypedKey], + node_ids: &mut TypedKeySet, node_info: NodeInfo, relay_ids: TypedKeySet, relay_info: SignedDirectNodeInfo, @@ -1942,7 +1948,14 @@ impl SignedRelayedNodeInfo { ) -> Result { let node_info_bytes = Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?; - crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; + let valid_crypto_kinds = + crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; + + node_ids.remove_all(&valid_crypto_kinds); + if node_ids.len() == 0 { + apibail_generic!("no valid node ids in relayed node info"); + } + Ok(Self { node_info, relay_ids, @@ -2113,6 +2126,7 @@ pub struct PeerInfo { impl PeerInfo { pub fn new(node_ids: TypedKeySet, signed_node_info: SignedNodeInfo) -> Self { + assert!(node_ids.len() > 0 && node_ids.len() <= MAX_CRYPTO_KINDS); Self { node_ids, signed_node_info,