From dfb4eefd926be5b05bcc41b0147ef321b1a90900 Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 3 Jul 2023 15:10:28 -0400 Subject: [PATCH 1/6] switch out capabilities --- doc/config/sample.config | 1 + doc/config/veilid-server-config.md | 1 + veilid-core/proto/veilid.capnp | 14 ++-- veilid-core/src/crypto/none/mod.rs | 2 +- veilid-core/src/crypto/vld0/mod.rs | 2 +- veilid-core/src/network_manager/mod.rs | 53 ++++++++++----- veilid-core/src/network_manager/native/mod.rs | 16 ++--- .../route_spec_store/route_spec_store.rs | 2 +- .../routing_table/tasks/relay_management.rs | 2 +- .../src/routing_table/types/node_status.rs | 47 ++++--------- .../src/rpc_processor/coders/node_info.rs | 18 +++-- .../src/rpc_processor/coders/node_status.rs | 66 ++++++++++++++----- .../rpc_processor/rpc_validate_dial_info.rs | 2 +- .../src/tests/common/test_veilid_config.rs | 16 +---- veilid-core/src/veilid_api/tests/fixtures.rs | 8 +-- veilid-core/src/veilid_config.rs | 16 +---- veilid-flutter/lib/default_config.dart | 10 +-- veilid-flutter/lib/veilid_config.dart | 33 ++-------- veilid-python/veilid/config.py | 10 +-- veilid-python/veilid/types.py | 32 +++++---- veilid-server/src/settings.rs | 18 +++-- veilid-wasm/tests/web.rs | 8 +-- 22 files changed, 172 insertions(+), 205 deletions(-) diff --git a/doc/config/sample.config b/doc/config/sample.config index b59cd7c2..c2af31a9 100644 --- a/doc/config/sample.config +++ b/doc/config/sample.config @@ -50,6 +50,7 @@ core: reverse_connection_receipt_time_ms: 5000 hole_punch_receipt_time_ms: 5000 network_key_password: null + disable_capabilites: [] routing_table: node_id: null node_id_secret: null diff --git a/doc/config/veilid-server-config.md b/doc/config/veilid-server-config.md index 116d2dec..5c81b6b3 100644 --- a/doc/config/veilid-server-config.md +++ b/doc/config/veilid-server-config.md @@ -189,6 +189,7 @@ network: reverse_connection_receipt_time_ms: 5000 hole_punch_receipt_time_ms: 5000 network_key_password: null + disable_capabilites: [] node_id: null node_id_secret: null bootstrap: ['bootstrap.veilid.net'] diff --git a/veilid-core/proto/veilid.capnp b/veilid-core/proto/veilid.capnp index 3d813450..b7aa5db4 100644 --- a/veilid-core/proto/veilid.capnp +++ b/veilid-core/proto/veilid.capnp @@ -34,6 +34,7 @@ using TunnelID = UInt64; # Id for tunnels using CryptoKind = UInt32; # FOURCC code for cryptography type using ValueSeqNum = UInt32; # sequence numbers for values using Subkey = UInt32; # subkey index for dht +using Capability = UInt32; # FOURCC code for capability struct TypedKey @0xe2d567a9f1e61b29 { kind @0 :CryptoKind; @@ -190,22 +191,17 @@ struct DialInfoDetail @0x96423aa1d67b74d8 { } struct PublicInternetNodeStatus @0x9c9d7f1f12eb088f { - willRoute @0 :Bool; - willTunnel @1 :Bool; - willSignal @2 :Bool; - willRelay @3 :Bool; - willValidateDialInfo @4 :Bool; + capabilities @0 :List(Capability); # List of Capability FOURCC codes that this node is advertising it is capable of in the publicinternet routing domain } struct LocalNetworkNodeStatus @0x957f5bfed2d0b5a5 { - willRelay @0 :Bool; - willValidateDialInfo @1 :Bool; + capabilities @0 :List(Capability); # List of Capability FOURCC codes that this node is advertising it is capable of in the localnetwork routing domain } struct NodeStatus @0xd36b9e7a3bf3330d { union { - publicInternet @0 :PublicInternetNodeStatus; - localNetwork @1 :LocalNetworkNodeStatus; + publicInternet @0 :PublicInternetNodeStatus; + localNetwork @1 :LocalNetworkNodeStatus; } } diff --git a/veilid-core/src/crypto/none/mod.rs b/veilid-core/src/crypto/none/mod.rs index b81056b6..48b80106 100644 --- a/veilid-core/src/crypto/none/mod.rs +++ b/veilid-core/src/crypto/none/mod.rs @@ -4,7 +4,7 @@ use data_encoding::BASE64URL_NOPAD; use digest::Digest; use rand::RngCore; const AEAD_OVERHEAD: usize = PUBLIC_KEY_LENGTH; -pub const CRYPTO_KIND_NONE: CryptoKind = FourCC([b'N', b'O', b'N', b'E']); +pub const CRYPTO_KIND_NONE: CryptoKind = FourCC(*b"NONE"); pub fn none_generate_keypair() -> KeyPair { let mut csprng = VeilidRng {}; diff --git a/veilid-core/src/crypto/vld0/mod.rs b/veilid-core/src/crypto/vld0/mod.rs index 680e7468..e13ec49d 100644 --- a/veilid-core/src/crypto/vld0/mod.rs +++ b/veilid-core/src/crypto/vld0/mod.rs @@ -15,7 +15,7 @@ use ed25519_dalek as ed; use x25519_dalek as xd; const AEAD_OVERHEAD: usize = 16; -pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC([b'V', b'L', b'D', b'0']); +pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC(*b"VLD0"); fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> VeilidAPIResult { let bytes = key.to_bytes(); diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index 3be13686..bc9277cb 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -495,31 +495,42 @@ impl NetworkManager { /// Get our node's capabilities in the PublicInternet routing domain fn generate_public_internet_node_status(&self) -> PublicInternetNodeStatus { + let Some(own_peer_info) = self .routing_table() .get_own_peer_info(RoutingDomain::PublicInternet) else { - return PublicInternetNodeStatus { - will_route: false, - will_tunnel: false, - will_signal: false, - will_relay: false, - will_validate_dial_info: false, - }; + return PublicInternetNodeStatus::default(); }; let own_node_info = own_peer_info.signed_node_info().node_info(); + let config = self.config(); + let c = config.get(); + let will_route = own_node_info.can_inbound_relay(); // xxx: eventually this may have more criteria added let will_tunnel = own_node_info.can_inbound_relay(); // xxx: we may want to restrict by battery life and network bandwidth at some point let will_signal = own_node_info.can_signal(); let will_relay = own_node_info.can_inbound_relay(); let will_validate_dial_info = own_node_info.can_validate_dial_info(); + let mut capabilities = Vec::new(); + if will_route && !c.capabilities.disable.contains(&CAP_WILL_ROUTE) { + capabilities.push(CAP_WILL_ROUTE); + } + if will_tunnel && !c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { + capabilities.push(CAP_WILL_TUNNEL); + } + if will_signal && !c.capabilities.disable.contains(&CAP_WILL_SIGNAL) { + capabilities.push(CAP_WILL_SIGNAL); + } + if will_relay && !c.capabilities.disable.contains(&CAP_WILL_RELAY){ + capabilities.push(CAP_WILL_RELAY); + } + if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) { + capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO); + } + PublicInternetNodeStatus { - will_route, - will_tunnel, - will_signal, - will_relay, - will_validate_dial_info, + capabilities } } /// Get our node's capabilities in the LocalNetwork routing domain @@ -527,20 +538,26 @@ impl NetworkManager { let Some(own_peer_info) = self .routing_table() .get_own_peer_info(RoutingDomain::LocalNetwork) else { - return LocalNetworkNodeStatus { - will_relay: false, - will_validate_dial_info: false, - }; + return LocalNetworkNodeStatus::default(); }; let own_node_info = own_peer_info.signed_node_info().node_info(); + + let config = self.config(); + let c = config.get(); let will_relay = own_node_info.can_inbound_relay(); let will_validate_dial_info = own_node_info.can_validate_dial_info(); + let mut capabilities = Vec::new(); + if will_relay && !c.capabilities.disable.contains(&CAP_WILL_RELAY) { + capabilities.push(CAP_WILL_RELAY); + } + if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) { + capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO); + } LocalNetworkNodeStatus { - will_relay, - will_validate_dial_info, + capabilities } } diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index c773b944..2a719507 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -686,30 +686,30 @@ impl Network { let c = self.config.get(); let mut inbound = ProtocolTypeSet::new(); - if c.network.protocol.udp.enabled && c.capabilities.protocol_udp { + if c.network.protocol.udp.enabled { inbound.insert(ProtocolType::UDP); } - if c.network.protocol.tcp.listen && c.capabilities.protocol_accept_tcp { + if c.network.protocol.tcp.listen { inbound.insert(ProtocolType::TCP); } - if c.network.protocol.ws.listen && c.capabilities.protocol_accept_ws { + if c.network.protocol.ws.listen { inbound.insert(ProtocolType::WS); } - if c.network.protocol.wss.listen && c.capabilities.protocol_accept_wss { + if c.network.protocol.wss.listen { inbound.insert(ProtocolType::WSS); } let mut outbound = ProtocolTypeSet::new(); - if c.network.protocol.udp.enabled && c.capabilities.protocol_udp { + if c.network.protocol.udp.enabled { outbound.insert(ProtocolType::UDP); } - if c.network.protocol.tcp.connect && c.capabilities.protocol_connect_tcp { + if c.network.protocol.tcp.connect { outbound.insert(ProtocolType::TCP); } - if c.network.protocol.ws.connect && c.capabilities.protocol_connect_ws { + if c.network.protocol.ws.connect { outbound.insert(ProtocolType::WS); } - if c.network.protocol.wss.connect && c.capabilities.protocol_connect_wss { + if c.network.protocol.wss.connect { outbound.insert(ProtocolType::WSS); } diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs index 83167860..61830d88 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs @@ -265,7 +265,7 @@ impl RouteSpecStore { }; let node_status_ok = if let Some(ns) = e.node_status(RoutingDomain::PublicInternet) { - ns.will_route() + ns.has_capability(CAP_WILL_ROUTE) } else { false }; diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index 37d5ec5f..dfb2c268 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -148,7 +148,7 @@ impl RoutingTable { // Ensure we have the node's status if let Some(node_status) = e.node_status(routing_domain) { // Ensure the node will relay - if node_status.will_relay() { + if node_status.has_capability(CAP_WILL_RELAY) { // Compare against previous candidate if let Some(best_inbound_relay) = best_inbound_relay.as_mut() { // Less is faster diff --git a/veilid-core/src/routing_table/types/node_status.rs b/veilid-core/src/routing_table/types/node_status.rs index 11c388d0..2ab2b5b4 100644 --- a/veilid-core/src/routing_table/types/node_status.rs +++ b/veilid-core/src/routing_table/types/node_status.rs @@ -3,17 +3,21 @@ use super::*; /// RoutingDomain-specific status for each node /// is returned by the StatusA call +pub type Capability = FourCC; +pub const CAP_WILL_ROUTE: Capability = FourCC(*b"ROUT"); +pub const CAP_WILL_TUNNEL: Capability = FourCC(*b"TUNL"); +pub const CAP_WILL_SIGNAL: Capability = FourCC(*b"SGNL"); +pub const CAP_WILL_RELAY: Capability = FourCC(*b"RLAY"); +pub const CAP_WILL_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL"); +pub const MAX_CAPABILITIES: usize = 64; + /// PublicInternet RoutingDomain Status #[derive( Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, )] #[archive_attr(repr(C), derive(CheckBytes))] pub struct PublicInternetNodeStatus { - pub will_route: bool, - pub will_tunnel: bool, - pub will_signal: bool, - pub will_relay: bool, - pub will_validate_dial_info: bool, + pub capabilities: Vec, } #[derive( @@ -21,8 +25,7 @@ pub struct PublicInternetNodeStatus { )] #[archive_attr(repr(C), derive(CheckBytes))] pub struct LocalNetworkNodeStatus { - pub will_relay: bool, - pub will_validate_dial_info: bool, + pub capabilities: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] @@ -33,34 +36,10 @@ pub enum NodeStatus { } impl NodeStatus { - pub fn will_route(&self) -> bool { + pub fn has_capability(&self, cap: Capability) -> bool { match self { - NodeStatus::PublicInternet(pi) => pi.will_route, - NodeStatus::LocalNetwork(_) => false, - } - } - pub fn will_tunnel(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_tunnel, - NodeStatus::LocalNetwork(_) => false, - } - } - pub fn will_signal(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_signal, - NodeStatus::LocalNetwork(_) => false, - } - } - pub fn will_relay(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_relay, - NodeStatus::LocalNetwork(ln) => ln.will_relay, - } - } - pub fn will_validate_dial_info(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_validate_dial_info, - NodeStatus::LocalNetwork(ln) => ln.will_validate_dial_info, + NodeStatus::PublicInternet(pi) => pi.capabilities.contains(&cap), + NodeStatus::LocalNetwork(ln) => ln.capabilities.contains(&cap), } } } diff --git a/veilid-core/src/rpc_processor/coders/node_info.rs b/veilid-core/src/rpc_processor/coders/node_info.rs index 874a7e6b..9e89da68 100644 --- a/veilid-core/src/rpc_processor/coders/node_info.rs +++ b/veilid-core/src/rpc_processor/coders/node_info.rs @@ -71,13 +71,11 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result Result = reader + let cs_reader = reader .reborrow() .get_crypto_support() - .map_err(RPCError::protocol)? + .map_err(RPCError::protocol)?; + + if cs_reader.len() as usize > MAX_CRYPTO_KINDS { + return Err(RPCError::protocol("too many crypto kinds")); + } + + let crypto_support: Vec = cs_reader .as_slice() .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) .unwrap_or_default(); diff --git a/veilid-core/src/rpc_processor/coders/node_status.rs b/veilid-core/src/rpc_processor/coders/node_status.rs index ed72123b..7e8a2fd5 100644 --- a/veilid-core/src/rpc_processor/coders/node_status.rs +++ b/veilid-core/src/rpc_processor/coders/node_status.rs @@ -4,44 +4,74 @@ 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(public_internet_node_status.will_route); - builder.set_will_tunnel(public_internet_node_status.will_tunnel); - builder.set_will_signal(public_internet_node_status.will_signal); - builder.set_will_relay(public_internet_node_status.will_relay); - builder.set_will_validate_dial_info(public_internet_node_status.will_validate_dial_info); + let mut cap_builder = builder + .reborrow() + .init_capabilities(public_internet_node_status.capabilities.len() as u32); + if let Some(s) = cap_builder.as_slice() { + let capvec: Vec = public_internet_node_status + .capabilities + .iter() + .map(|x| u32::from_be_bytes(x.0)) + .collect(); + s.clone_from_slice(&capvec); + } Ok(()) } pub fn decode_public_internet_node_status( reader: &veilid_capnp::public_internet_node_status::Reader, ) -> Result { - Ok(PublicInternetNodeStatus { - will_route: reader.reborrow().get_will_route(), - will_tunnel: reader.reborrow().get_will_tunnel(), - will_signal: reader.reborrow().get_will_signal(), - will_relay: reader.reborrow().get_will_relay(), - will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(), - }) + let cap_reader = reader + .reborrow() + .get_capabilities() + .map_err(RPCError::protocol)?; + if cap_reader.len() as usize > MAX_CAPABILITIES { + return Err(RPCError::protocol("too many capabilities")); + } + let capabilities = cap_reader + .as_slice() + .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) + .unwrap_or_default(); + + Ok(PublicInternetNodeStatus { capabilities }) } 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(local_network_node_status.will_relay); - builder.set_will_validate_dial_info(local_network_node_status.will_validate_dial_info); + let mut cap_builder = builder + .reborrow() + .init_capabilities(local_network_node_status.capabilities.len() as u32); + if let Some(s) = cap_builder.as_slice() { + let capvec: Vec = local_network_node_status + .capabilities + .iter() + .map(|x| u32::from_be_bytes(x.0)) + .collect(); + s.clone_from_slice(&capvec); + } Ok(()) } pub fn decode_local_network_node_status( reader: &veilid_capnp::local_network_node_status::Reader, ) -> Result { - Ok(LocalNetworkNodeStatus { - will_relay: reader.reborrow().get_will_relay(), - will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(), - }) + let cap_reader = reader + .reborrow() + .get_capabilities() + .map_err(RPCError::protocol)?; + if cap_reader.len() as usize > MAX_CAPABILITIES { + return Err(RPCError::protocol("too many capabilities")); + } + let capabilities = cap_reader + .as_slice() + .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) + .unwrap_or_default(); + + Ok(LocalNetworkNodeStatus { capabilities }) } pub fn encode_node_status( diff --git a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs index 2ff38e4e..06906639 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -105,7 +105,7 @@ impl RPCProcessor { let entry = v.unwrap(); entry.with(rti, move |_rti, e| { if let Some(status) = &e.node_status(routing_domain) { - status.will_validate_dial_info() + status.has_capability(CAP_WILL_VALIDATE_DIAL_INFO) } else { true } diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index 1df141f9..9ba054ac 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -168,13 +168,7 @@ fn config_callback(key: String) -> ConfigCallbackReturn { match key.as_str() { "program_name" => Ok(Box::new(String::from("VeilidCoreTests"))), "namespace" => Ok(Box::new(String::from(""))), - "capabilities.protocol_udp" => Ok(Box::new(true)), - "capabilities.protocol_connect_tcp" => Ok(Box::new(true)), - "capabilities.protocol_accept_tcp" => Ok(Box::new(true)), - "capabilities.protocol_connect_ws" => Ok(Box::new(true)), - "capabilities.protocol_accept_ws" => Ok(Box::new(true)), - "capabilities.protocol_connect_wss" => Ok(Box::new(true)), - "capabilities.protocol_accept_wss" => Ok(Box::new(true)), + "capabilities.disable" => Ok(Box::new(Vec::::new())), "table_store.directory" => Ok(Box::new(get_table_store_path())), "table_store.delete" => Ok(Box::new(true)), "block_store.directory" => Ok(Box::new(get_block_store_path())), @@ -299,13 +293,7 @@ pub async fn test_config() { let inner = vc.get(); assert_eq!(inner.program_name, String::from("VeilidCoreTests")); assert_eq!(inner.namespace, String::from("")); - assert_eq!(inner.capabilities.protocol_udp, true); - assert_eq!(inner.capabilities.protocol_connect_tcp, true); - assert_eq!(inner.capabilities.protocol_accept_tcp, true); - assert_eq!(inner.capabilities.protocol_connect_ws, true); - assert_eq!(inner.capabilities.protocol_accept_ws, true); - assert_eq!(inner.capabilities.protocol_connect_wss, true); - assert_eq!(inner.capabilities.protocol_accept_wss, true); + assert_eq!(inner.capabilities.disable, Vec::::new()); assert_eq!(inner.table_store.directory, get_table_store_path()); assert_eq!(inner.table_store.delete, true); assert_eq!(inner.block_store.directory, get_block_store_path()); diff --git a/veilid-core/src/veilid_api/tests/fixtures.rs b/veilid-core/src/veilid_api/tests/fixtures.rs index b2791f68..cb81285d 100644 --- a/veilid-core/src/veilid_api/tests/fixtures.rs +++ b/veilid-core/src/veilid_api/tests/fixtures.rs @@ -76,13 +76,7 @@ pub fn fix_veilidconfiginner() -> VeilidConfigInner { program_name: "Bob".to_string(), namespace: "Internets".to_string(), capabilities: VeilidConfigCapabilities { - protocol_udp: false, - protocol_connect_tcp: true, - protocol_accept_tcp: false, - protocol_connect_ws: true, - protocol_accept_ws: false, - protocol_connect_wss: true, - protocol_accept_wss: false, + disable: Vec::new(), }, protected_store: VeilidConfigProtectedStore { allow_insecure_fallback: true, diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 6934bcca..3d51a0bd 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -468,13 +468,7 @@ pub struct VeilidConfigProtectedStore { JsonSchema, )] pub struct VeilidConfigCapabilities { - pub protocol_udp: bool, - pub protocol_connect_tcp: bool, - pub protocol_accept_tcp: bool, - pub protocol_connect_ws: bool, - pub protocol_accept_ws: bool, - pub protocol_connect_wss: bool, - pub protocol_accept_wss: bool, + pub disable: Vec, } #[derive( @@ -670,13 +664,7 @@ impl VeilidConfig { get_config!(inner.program_name); get_config!(inner.namespace); - get_config!(inner.capabilities.protocol_udp); - get_config!(inner.capabilities.protocol_connect_tcp); - get_config!(inner.capabilities.protocol_accept_tcp); - get_config!(inner.capabilities.protocol_connect_ws); - get_config!(inner.capabilities.protocol_accept_ws); - get_config!(inner.capabilities.protocol_connect_wss); - get_config!(inner.capabilities.protocol_accept_wss); + get_config!(inner.capabilities.disable); get_config!(inner.table_store.directory); get_config!(inner.table_store.delete); get_config!(inner.block_store.directory); diff --git a/veilid-flutter/lib/default_config.dart b/veilid-flutter/lib/default_config.dart index c323b70f..b0ae51f2 100644 --- a/veilid-flutter/lib/default_config.dart +++ b/veilid-flutter/lib/default_config.dart @@ -52,15 +52,7 @@ Future getDefaultVeilidConfig(String programName) async { return VeilidConfig( programName: programName, namespace: "", - capabilities: VeilidConfigCapabilities( - protocolUDP: !kIsWeb, - protocolConnectTCP: !kIsWeb, - protocolAcceptTCP: !kIsWeb, - protocolConnectWS: true, - protocolAcceptWS: !kIsWeb, - protocolConnectWSS: true, - protocolAcceptWSS: false, - ), + capabilities: VeilidConfigCapabilities(disable: []), protectedStore: VeilidConfigProtectedStore( allowInsecureFallback: false, alwaysUseInsecureStorage: false, diff --git a/veilid-flutter/lib/veilid_config.dart b/veilid-flutter/lib/veilid_config.dart index 62fa7e8f..c44e0659 100644 --- a/veilid-flutter/lib/veilid_config.dart +++ b/veilid-flutter/lib/veilid_config.dart @@ -867,44 +867,19 @@ class VeilidConfigProtectedStore { //////////// class VeilidConfigCapabilities { - bool protocolUDP; - bool protocolConnectTCP; - bool protocolAcceptTCP; - bool protocolConnectWS; - bool protocolAcceptWS; - bool protocolConnectWSS; - bool protocolAcceptWSS; + List disable; VeilidConfigCapabilities({ - required this.protocolUDP, - required this.protocolConnectTCP, - required this.protocolAcceptTCP, - required this.protocolConnectWS, - required this.protocolAcceptWS, - required this.protocolConnectWSS, - required this.protocolAcceptWSS, + required this.disable, }); Map toJson() { return { - 'protocol_udp': protocolUDP, - 'protocol_connect_tcp': protocolConnectTCP, - 'protocol_accept_tcp': protocolAcceptTCP, - 'protocol_connect_ws': protocolConnectWS, - 'protocol_accept_ws': protocolAcceptWS, - 'protocol_connect_wss': protocolConnectWSS, - 'protocol_accept_wss': protocolAcceptWSS, + 'disable': disable, }; } - VeilidConfigCapabilities.fromJson(dynamic json) - : protocolUDP = json['protocol_udp'], - protocolConnectTCP = json['protocol_connect_tcp'], - protocolAcceptTCP = json['protocol_accept_tcp'], - protocolConnectWS = json['protocol_connect_ws'], - protocolAcceptWS = json['protocol_accept_ws'], - protocolConnectWSS = json['protocol_connect_wss'], - protocolAcceptWSS = json['protocol_accept_wss']; + VeilidConfigCapabilities.fromJson(dynamic json) : disable = json['disable']; } //////////// diff --git a/veilid-python/veilid/config.py b/veilid-python/veilid/config.py index 9533a64b..255a091f 100644 --- a/veilid-python/veilid/config.py +++ b/veilid-python/veilid/config.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, fields from enum import StrEnum from typing import Optional, Self -from .types import TypedKey, TypedSecret +from .types import TypedKey, TypedSecret, Capability class VeilidConfigLogLevel(StrEnum): @@ -41,13 +41,7 @@ class ConfigBase: @dataclass class VeilidConfigCapabilities(ConfigBase): - protocol_udp: bool - protocol_connect_tcp: bool - protocol_accept_tcp: bool - protocol_connect_ws: bool - protocol_accept_ws: bool - protocol_connect_wss: bool - protocol_accept_wss: bool + disable: list[Capability] @dataclass diff --git a/veilid-python/veilid/types.py b/veilid-python/veilid/types.py index 4f1ea344..40a739aa 100644 --- a/veilid-python/veilid/types.py +++ b/veilid-python/veilid/types.py @@ -52,6 +52,14 @@ class CryptoKind(StrEnum): CRYPTO_KIND_VLD0 = "VLD0" +class Capability(StrEnum): + CAP_WILL_ROUTE = "ROUT" + CAP_WILL_TUNNEL = "TUNL" + CAP_WILL_SIGNAL = "SGNL" + CAP_WILL_RELAY = "RLAY" + CAP_WILL_VALIDATE_DIAL_INFO = "DIAL" + + class Stability(StrEnum): LOW_LATENCY = "LowLatency" RELIABLE = "Reliable" @@ -67,6 +75,7 @@ class DHTSchemaKind(StrEnum): DFLT = "DFLT" SMPL = "SMPL" + class SafetySelectionKind(StrEnum): UNSAFE = "Unsafe" SAFE = "Safe" @@ -235,11 +244,10 @@ class VeilidVersion: if self._patch < other._patch: return True return False - + def __eq__(self, other): return isinstance(other, VeilidVersion) and self.data == other.data and self.seq == other.seq and self.writer == other.writer - @property def major(self): return self._major @@ -308,7 +316,8 @@ class DHTSchema: if DHTSchemaKind(j["kind"]) == DHTSchemaKind.SMPL: return cls.smpl( j["o_cnt"], - [DHTSchemaSMPLMember.from_json(member) for member in j["members"]], + [DHTSchemaSMPLMember.from_json(member) + for member in j["members"]], ) raise Exception("Unknown DHTSchema kind", j["kind"]) @@ -339,7 +348,8 @@ class DHTRecordDescriptor: return cls( TypedKey(j["key"]), PublicKey(j["owner"]), - None if j["owner_secret"] is None else SecretKey(j["owner_secret"]), + None if j["owner_secret"] is None else SecretKey( + j["owner_secret"]), DHTSchema.from_json(j["schema"]), ) @@ -404,14 +414,15 @@ class SafetySpec: @classmethod def from_json(cls, j: dict) -> Self: - return cls(RouteId(j["preferred_route"]) if "preferred_route" in j else None, - j["hop_count"], - Stability(j["stability"]), - Sequencing(j["sequencing"])) + return cls(RouteId(j["preferred_route"]) if "preferred_route" in j else None, + j["hop_count"], + Stability(j["stability"]), + Sequencing(j["sequencing"])) def to_json(self) -> dict: return self.__dict__ + class SafetySelection: kind: SafetySelectionKind @@ -438,9 +449,8 @@ class SafetySelection: def to_json(self) -> dict: if self.kind == SafetySelectionKind.UNSAFE: - return {"Unsafe": self.sequencing } + return {"Unsafe": self.sequencing} elif self.kind == SafetySelectionKind.SAFE: - return {"Safe": self.safety_spec.to_json() } + return {"Safe": self.safety_spec.to_json()} else: raise Exception("Invalid SafetySelection") - diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index 10743c6a..827e87ac 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -46,6 +46,8 @@ logging: testing: subnode_index: 0 core: + capabilities: + disable: [] protected_store: allow_insecure_fallback: true always_use_insecure_storage: true @@ -70,6 +72,7 @@ core: reverse_connection_receipt_time_ms: 5000 hole_punch_receipt_time_ms: 5000 network_key_password: null + disable_capabilites: [] routing_table: node_id: null node_id_secret: null @@ -622,8 +625,14 @@ pub struct ProtectedStore { pub new_device_encryption_key_password: Option, } +#[derive(Debug, Deserialize, Serialize)] +pub struct Capabilities { + pub disable: Vec, +} + #[derive(Debug, Deserialize, Serialize)] pub struct Core { + pub capabilities: Capabilities, pub protected_store: ProtectedStore, pub table_store: TableStore, pub block_store: BlockStore, @@ -962,6 +971,7 @@ impl Settings { set_config_value!(inner.logging.otlp.grpc_endpoint, value); set_config_value!(inner.logging.console.enabled, value); set_config_value!(inner.testing.subnode_index, value); + set_config_value!(inner.core.capabilities.disable, value); set_config_value!(inner.core.protected_store.allow_insecure_fallback, value); set_config_value!( inner.core.protected_store.always_use_insecure_storage, @@ -1093,13 +1103,7 @@ impl Settings { } else { format!("subnode{}", inner.testing.subnode_index) })), - "capabilities.protocol_udp" => Ok(Box::new(true)), - "capabilities.protocol_connect_tcp" => Ok(Box::new(true)), - "capabilities.protocol_accept_tcp" => Ok(Box::new(true)), - "capabilities.protocol_connect_ws" => Ok(Box::new(true)), - "capabilities.protocol_accept_ws" => Ok(Box::new(true)), - "capabilities.protocol_connect_wss" => Ok(Box::new(true)), - "capabilities.protocol_accept_wss" => Ok(Box::new(true)), + "capabilities.disable" => Ok(Box::new(Vec::::new())), "protected_store.allow_insecure_fallback" => { Ok(Box::new(inner.core.protected_store.allow_insecure_fallback)) } diff --git a/veilid-wasm/tests/web.rs b/veilid-wasm/tests/web.rs index 5cfbfd26..8022f2bb 100644 --- a/veilid-wasm/tests/web.rs +++ b/veilid-wasm/tests/web.rs @@ -33,13 +33,7 @@ fn init_callbacks() { window.configCallback = (configKey) => { switch(configKey) { case "namespace": return ""; - case "capabilities.protocol_udp": return false; - case "capabilities.protocol_connect_tcp": return false; - case "capabilities.protocol_accept_tcp": return false; - case "capabilities.protocol_connect_ws": return true; - case "capabilities.protocol_accept_ws": return false; - case "capabilities.protocol_connect_wss": return true; - case "capabilities.protocol_accept_wss": return false; + case "capabilities.disable": return []; case "tablestore.directory": return ""; case "network.routing_table.node_id": return []; case "network.routing_table.node_id_secret": return []; From ffc54f482e91714ca3cb30d4d20b47140ab7deef Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 3 Jul 2023 15:56:13 -0400 Subject: [PATCH 2/6] more capabilities --- veilid-core/src/network_manager/mod.rs | 12 ++++++++++++ veilid-core/src/routing_table/types/node_status.rs | 2 ++ veilid-core/src/rpc_processor/rpc_app_call.rs | 8 ++++++++ veilid-core/src/rpc_processor/rpc_app_message.rs | 8 ++++++++ veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs | 10 ++++++++++ .../src/rpc_processor/rpc_complete_tunnel.rs | 9 +++++++++ veilid-core/src/rpc_processor/rpc_find_block.rs | 7 +++++++ veilid-core/src/rpc_processor/rpc_get_value.rs | 7 +++++++ veilid-core/src/rpc_processor/rpc_route.rs | 13 +++++++++++++ veilid-core/src/rpc_processor/rpc_set_value.rs | 7 +++++++ veilid-core/src/rpc_processor/rpc_signal.rs | 8 ++++++++ veilid-core/src/rpc_processor/rpc_start_tunnel.rs | 9 +++++++++ veilid-core/src/rpc_processor/rpc_supply_block.rs | 9 +++++++++ .../src/rpc_processor/rpc_validate_dial_info.rs | 13 +++++++++++++ veilid-core/src/rpc_processor/rpc_value_changed.rs | 9 +++++++++ veilid-core/src/rpc_processor/rpc_watch_value.rs | 10 ++++++++++ veilid-python/veilid/types.py | 2 ++ 17 files changed, 143 insertions(+) diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index bc9277cb..b0c77c8d 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -528,6 +528,12 @@ impl NetworkManager { if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) { capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO); } + if !c.capabilities.disable.contains(&CAP_WILL_DHT) { + capabilities.push(CAP_WILL_DHT); + } + if !c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { + capabilities.push(CAP_WILL_APPMESSAGE); + } PublicInternetNodeStatus { capabilities @@ -556,6 +562,12 @@ impl NetworkManager { if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) { capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO); } + if !c.capabilities.disable.contains(&CAP_WILL_DHT) { + capabilities.push(CAP_WILL_DHT); + } + if !c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { + capabilities.push(CAP_WILL_APPMESSAGE); + } LocalNetworkNodeStatus { capabilities } diff --git a/veilid-core/src/routing_table/types/node_status.rs b/veilid-core/src/routing_table/types/node_status.rs index 2ab2b5b4..c8abe079 100644 --- a/veilid-core/src/routing_table/types/node_status.rs +++ b/veilid-core/src/routing_table/types/node_status.rs @@ -9,6 +9,8 @@ pub const CAP_WILL_TUNNEL: Capability = FourCC(*b"TUNL"); pub const CAP_WILL_SIGNAL: Capability = FourCC(*b"SGNL"); pub const CAP_WILL_RELAY: Capability = FourCC(*b"RLAY"); pub const CAP_WILL_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL"); +pub const CAP_WILL_DHT: Capability = FourCC(*b"DHTV"); +pub const CAP_WILL_APPMESSAGE: Capability = FourCC(*b"APPM"); pub const MAX_CAPABILITIES: usize = 64; /// PublicInternet RoutingDomain Status diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index cf0318de..00d1840e 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -53,6 +53,14 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { + return Ok(NetworkResult::service_unavailable("appcall is disabled")); + } + } + // Get the question let (op_id, _, _, kind) = msg.operation.clone().destructure(); let app_call_q = match kind { diff --git a/veilid-core/src/rpc_processor/rpc_app_message.rs b/veilid-core/src/rpc_processor/rpc_app_message.rs index ba49c701..e7b4daaf 100644 --- a/veilid-core/src/rpc_processor/rpc_app_message.rs +++ b/veilid-core/src/rpc_processor/rpc_app_message.rs @@ -24,6 +24,14 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { + return Ok(NetworkResult::service_unavailable("appmessage is disabled")); + } + } + // Get the statement let (_, _, _, kind) = msg.operation.destructure(); let app_message = match kind { diff --git a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs index 4698234a..0450ef38 100644 --- a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs @@ -6,6 +6,16 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { + return Ok(NetworkResult::service_unavailable( + "cancel tunnel is disabled", + )); + } + } + Err(RPCError::unimplemented("process_cancel_tunnel_q")) } } diff --git a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs index 307e659f..deb40a6d 100644 --- a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs @@ -6,6 +6,15 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { + return Ok(NetworkResult::service_unavailable( + "complete tunnel is disabled", + )); + } + } Err(RPCError::unimplemented("process_complete_tunnel_q")) } } diff --git a/veilid-core/src/rpc_processor/rpc_find_block.rs b/veilid-core/src/rpc_processor/rpc_find_block.rs index 3bfe81ea..6d92a6d8 100644 --- a/veilid-core/src/rpc_processor/rpc_find_block.rs +++ b/veilid-core/src/rpc_processor/rpc_find_block.rs @@ -6,6 +6,13 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_BLOCKSTORE) { + return Ok(NetworkResult::service_unavailable("find block is disabled")); + } + } Err(RPCError::unimplemented("process_find_block_q")) } } diff --git a/veilid-core/src/rpc_processor/rpc_get_value.rs b/veilid-core/src/rpc_processor/rpc_get_value.rs index 8f419f80..de3f923f 100644 --- a/veilid-core/src/rpc_processor/rpc_get_value.rs +++ b/veilid-core/src/rpc_processor/rpc_get_value.rs @@ -163,6 +163,13 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_DHT) { + return Ok(NetworkResult::service_unavailable("get value is disabled")); + } + } // Ensure this never came over a private route, safety route is okay though match &msg.header.detail { RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {} diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index 7e45bccd..0274e5aa 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -365,6 +365,19 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities + .disable + .contains(&CAP_WILL_ROUTE) + { + return Ok(NetworkResult::service_unavailable( + "route is disabled", + )); + } + } + // Get header detail, must be direct and not inside a route itself let detail = match msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, diff --git a/veilid-core/src/rpc_processor/rpc_set_value.rs b/veilid-core/src/rpc_processor/rpc_set_value.rs index 05bc3a23..43fd4a6b 100644 --- a/veilid-core/src/rpc_processor/rpc_set_value.rs +++ b/veilid-core/src/rpc_processor/rpc_set_value.rs @@ -175,6 +175,13 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_DHT) { + return Ok(NetworkResult::service_unavailable("set value is disabled")); + } + } // Ensure this never came over a private route, safety route is okay though match &msg.header.detail { RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {} diff --git a/veilid-core/src/rpc_processor/rpc_signal.rs b/veilid-core/src/rpc_processor/rpc_signal.rs index 224a9bac..4d7d33f0 100644 --- a/veilid-core/src/rpc_processor/rpc_signal.rs +++ b/veilid-core/src/rpc_processor/rpc_signal.rs @@ -37,6 +37,14 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_SIGNAL) { + return Ok(NetworkResult::service_unavailable("signal is disabled")); + } + } + // Can't allow anything other than direct packets here, as handling reverse connections // or anything like via signals over private routes would deanonymize the route match &msg.header.detail { diff --git a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs index ce9bce42..241e102c 100644 --- a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs @@ -6,6 +6,15 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { + return Ok(NetworkResult::service_unavailable( + "start tunnel is disabled", + )); + } + } Err(RPCError::unimplemented("process_start_tunnel_q")) } } diff --git a/veilid-core/src/rpc_processor/rpc_supply_block.rs b/veilid-core/src/rpc_processor/rpc_supply_block.rs index 5b27a512..a716b6b8 100644 --- a/veilid-core/src/rpc_processor/rpc_supply_block.rs +++ b/veilid-core/src/rpc_processor/rpc_supply_block.rs @@ -6,6 +6,15 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_BLOCKSTORE) { + return Ok(NetworkResult::service_unavailable( + "supply block is disabled", + )); + } + } Err(RPCError::unimplemented("process_supply_block_q")) } } diff --git a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs index 06906639..991b2e2c 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -58,6 +58,19 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities + .disable + .contains(&CAP_WILL_VALIDATE_DIAL_INFO) + { + return Ok(NetworkResult::service_unavailable( + "validate dial info is disabled", + )); + } + } + let detail = match msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, RPCMessageHeaderDetail::SafetyRouted(_) | RPCMessageHeaderDetail::PrivateRouted(_) => { diff --git a/veilid-core/src/rpc_processor/rpc_value_changed.rs b/veilid-core/src/rpc_processor/rpc_value_changed.rs index 621569e3..72f599e5 100644 --- a/veilid-core/src/rpc_processor/rpc_value_changed.rs +++ b/veilid-core/src/rpc_processor/rpc_value_changed.rs @@ -6,6 +6,15 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_DHT) { + return Ok(NetworkResult::service_unavailable( + "value changed is disabled", + )); + } + } Err(RPCError::unimplemented("process_value_changed")) } } diff --git a/veilid-core/src/rpc_processor/rpc_watch_value.rs b/veilid-core/src/rpc_processor/rpc_watch_value.rs index 0f4d24d1..5640bc3b 100644 --- a/veilid-core/src/rpc_processor/rpc_watch_value.rs +++ b/veilid-core/src/rpc_processor/rpc_watch_value.rs @@ -6,6 +6,16 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { + // Ignore if disabled + { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_DHT) { + return Ok(NetworkResult::service_unavailable( + "watch value is disabled", + )); + } + } + Err(RPCError::unimplemented("process_watch_value_q")) } } diff --git a/veilid-python/veilid/types.py b/veilid-python/veilid/types.py index 40a739aa..7c5a8bb1 100644 --- a/veilid-python/veilid/types.py +++ b/veilid-python/veilid/types.py @@ -58,6 +58,8 @@ class Capability(StrEnum): CAP_WILL_SIGNAL = "SGNL" CAP_WILL_RELAY = "RLAY" CAP_WILL_VALIDATE_DIAL_INFO = "DIAL" + CAP_WILL_DHT = "DHTV" + CAP_WILL_APPMESSAGE = "APPM" class Stability(StrEnum): From 6de2ccb1f9d0fc483a9fa1af1697d871930b8ea1 Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 3 Jul 2023 16:42:49 -0400 Subject: [PATCH 3/6] more punish fixes --- .../src/network_manager/address_filter.rs | 2 ++ .../src/network_manager/native/network_tcp.rs | 2 +- .../src/network_manager/network_connection.rs | 16 +++++++++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/veilid-core/src/network_manager/address_filter.rs b/veilid-core/src/network_manager/address_filter.rs index 9818c671..326a41b0 100644 --- a/veilid-core/src/network_manager/address_filter.rs +++ b/veilid-core/src/network_manager/address_filter.rs @@ -115,6 +115,7 @@ impl AddressFilter { } } for key in dead_keys { + log_net!(debug ">>> FORGIVING: {}", key); inner.punishments_by_ip4.remove(&key); } } @@ -130,6 +131,7 @@ impl AddressFilter { } } for key in dead_keys { + log_net!(debug ">>> FORGIVING: {}", key); inner.punishments_by_ip6_prefix.remove(&key); } } diff --git a/veilid-core/src/network_manager/native/network_tcp.rs b/veilid-core/src/network_manager/native/network_tcp.rs index 925ebb31..3c9434bf 100644 --- a/veilid-core/src/network_manager/native/network_tcp.rs +++ b/veilid-core/src/network_manager/native/network_tcp.rs @@ -118,8 +118,8 @@ impl Network { return; } }; - let address_filter = self.network_manager().address_filter(); // Check to see if it is punished + let address_filter = self.network_manager().address_filter(); if address_filter.is_punished(peer_addr.ip()) { return; } diff --git a/veilid-core/src/network_manager/network_connection.rs b/veilid-core/src/network_manager/network_connection.rs index 4f532838..f657037b 100644 --- a/veilid-core/src/network_manager/network_connection.rs +++ b/veilid-core/src/network_manager/network_connection.rs @@ -240,6 +240,7 @@ impl NetworkConnection { ); let network_manager = connection_manager.network_manager(); + let address_filter = network_manager.address_filter(); let mut unord = FuturesUnordered::new(); let mut need_receiver = true; let mut need_sender = true; @@ -301,11 +302,20 @@ impl NetworkConnection { .then(|res| async { match res { Ok(v) => { - if v.is_no_connection() { - let peer_address = protocol_connection.descriptor().remote(); - log_net!(debug "Connection closed from: {} ({})", peer_address.socket_address().to_socket_addr(), peer_address.protocol_type()); + let peer_address = protocol_connection.descriptor().remote(); + + // Check to see if it is punished + if address_filter.is_punished(peer_address.to_socket_addr().ip()) { return RecvLoopAction::Finish; } + + // Check for connection close + if v.is_no_connection() { + log_net!(debug "Connection closed from: {} ({})", peer_address.to_socket_addr(), peer_address.protocol_type()); + return RecvLoopAction::Finish; + } + + // Log other network results let mut message = network_result_value_or_log!(v => [ format!(": protocol_connection={:?}", protocol_connection) ] { return RecvLoopAction::Finish; }); From e674eaf4969faef8a767846baaca8bab755900c2 Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 3 Jul 2023 18:01:02 -0400 Subject: [PATCH 4/6] capability work --- .../tasks/private_route_management.rs | 22 ++++++++++++++----- veilid-server/src/settings.rs | 9 +++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/veilid-core/src/routing_table/tasks/private_route_management.rs b/veilid-core/src/routing_table/tasks/private_route_management.rs index a5fe37d8..1abdd51b 100644 --- a/veilid-core/src/routing_table/tasks/private_route_management.rs +++ b/veilid-core/src/routing_table/tasks/private_route_management.rs @@ -7,6 +7,14 @@ use stop_token::future::FutureExt as StopFutureExt; const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2; impl RoutingTable { + fn get_background_safety_route_count(&self) -> usize { + let c = self.config.get(); + if c.capabilities.disable.contains(&CAP_WILL_ROUTE) { + 0 + } else { + BACKGROUND_SAFETY_ROUTE_COUNT + } + } /// Fastest routes sort fn route_sort_latency_fn(a: &(RouteId, u64), b: &(RouteId, u64)) -> cmp::Ordering { let mut al = a.1; @@ -69,13 +77,14 @@ impl RoutingTable { unpublished_routes.sort_by(Self::route_sort_latency_fn); // Save up to N unpublished routes and test them - for x in 0..(usize::min(BACKGROUND_SAFETY_ROUTE_COUNT, unpublished_routes.len())) { + let background_safety_route_count = self.get_background_safety_route_count(); + for x in 0..(usize::min(background_safety_route_count, unpublished_routes.len())) { must_test_routes.push(unpublished_routes[x].0); } // Kill off all but N unpublished routes rather than testing them - if unpublished_routes.len() > BACKGROUND_SAFETY_ROUTE_COUNT { - for x in &unpublished_routes[BACKGROUND_SAFETY_ROUTE_COUNT..] { + if unpublished_routes.len() > background_safety_route_count { + for x in &unpublished_routes[background_safety_route_count..] { expired_routes.push(x.0); } } @@ -192,8 +201,11 @@ impl RoutingTable { } Option::<()>::None }); - if local_unpublished_route_count < BACKGROUND_SAFETY_ROUTE_COUNT { - let routes_to_allocate = BACKGROUND_SAFETY_ROUTE_COUNT - local_unpublished_route_count; + + let background_safety_route_count = self.get_background_safety_route_count(); + + if local_unpublished_route_count < background_safety_route_count { + let routes_to_allocate = background_safety_route_count - local_unpublished_route_count; // Newly allocated routes let mut newly_allocated_routes = Vec::new(); diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index 827e87ac..1d7c0b3d 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -1103,7 +1103,14 @@ impl Settings { } else { format!("subnode{}", inner.testing.subnode_index) })), - "capabilities.disable" => Ok(Box::new(Vec::::new())), + "capabilities.disable" => { + let mut caps = Vec::::new(); + for c in &inner.core.capabilities.disable { + let cap = FourCC::from_str(c.as_str()).map_err(VeilidAPIError::generic)?; + caps.push(cap); + } + Ok(Box::new(caps)) + } "protected_store.allow_insecure_fallback" => { Ok(Box::new(inner.core.protected_store.allow_insecure_fallback)) } From 8f721c428bc559c20a6f5ecb31d8884a38719232 Mon Sep 17 00:00:00 2001 From: John Smith Date: Tue, 4 Jul 2023 00:24:55 -0400 Subject: [PATCH 5/6] more capability work --- veilid-core/proto/veilid.capnp | 18 +- veilid-core/src/network_manager/mod.rs | 299 ++++++++---------- veilid-core/src/network_manager/native/mod.rs | 18 ++ .../tests/test_signed_node_info.rs | 2 + veilid-core/src/routing_table/bucket_entry.rs | 20 +- veilid-core/src/routing_table/node_ref.rs | 4 +- .../route_spec_store/route_spec_store.rs | 17 +- .../routing_table/routing_domain_editor.rs | 14 +- .../src/routing_table/routing_domains.rs | 8 + .../src/routing_table/tasks/bootstrap.rs | 1 + .../routing_table/tasks/relay_management.rs | 4 +- .../tests/test_serialize_routing_table.rs | 1 + .../src/routing_table/types/node_info.rs | 95 ++++++ .../src/routing_table/types/node_status.rs | 46 +-- .../src/rpc_processor/coders/node_info.rs | 25 ++ .../src/rpc_processor/coders/node_status.rs | 109 +------ veilid-core/src/rpc_processor/mod.rs | 7 + veilid-core/src/rpc_processor/rpc_app_call.rs | 10 +- .../src/rpc_processor/rpc_app_message.rs | 10 +- .../src/rpc_processor/rpc_cancel_tunnel.rs | 15 +- .../src/rpc_processor/rpc_complete_tunnel.rs | 15 +- .../src/rpc_processor/rpc_find_block.rs | 13 +- .../src/rpc_processor/rpc_get_value.rs | 20 +- veilid-core/src/rpc_processor/rpc_route.rs | 15 +- .../src/rpc_processor/rpc_set_value.rs | 10 +- veilid-core/src/rpc_processor/rpc_signal.rs | 10 +- .../src/rpc_processor/rpc_start_tunnel.rs | 15 +- veilid-core/src/rpc_processor/rpc_status.rs | 38 +-- .../src/rpc_processor/rpc_supply_block.rs | 15 +- .../rpc_processor/rpc_validate_dial_info.rs | 34 +- .../src/rpc_processor/rpc_value_changed.rs | 10 +- .../src/rpc_processor/rpc_watch_value.rs | 11 +- veilid-python/veilid/types.py | 1 + 33 files changed, 456 insertions(+), 474 deletions(-) diff --git a/veilid-core/proto/veilid.capnp b/veilid-core/proto/veilid.capnp index b7aa5db4..9aef1568 100644 --- a/veilid-core/proto/veilid.capnp +++ b/veilid-core/proto/veilid.capnp @@ -190,19 +190,8 @@ struct DialInfoDetail @0x96423aa1d67b74d8 { class @1 :DialInfoClass; } -struct PublicInternetNodeStatus @0x9c9d7f1f12eb088f { - capabilities @0 :List(Capability); # List of Capability FOURCC codes that this node is advertising it is capable of in the publicinternet routing domain -} - -struct LocalNetworkNodeStatus @0x957f5bfed2d0b5a5 { - capabilities @0 :List(Capability); # List of Capability FOURCC codes that this node is advertising it is capable of in the localnetwork routing domain -} - struct NodeStatus @0xd36b9e7a3bf3330d { - union { - publicInternet @0 :PublicInternetNodeStatus; - localNetwork @1 :LocalNetworkNodeStatus; - } + # Reserved for non-nodeinfo status } struct ProtocolTypeSet @0x82f12f55a1b73326 { @@ -227,7 +216,8 @@ struct NodeInfo @0xe125d847e3f9f419 { addressTypes @2 :AddressTypeSet; # address types supported envelopeSupport @3 :List(UInt8); # supported rpc envelope/receipt versions cryptoSupport @4 :List(CryptoKind); # cryptography systems supported - dialInfoDetailList @5 :List(DialInfoDetail); # inbound dial info details for this node + capabilities @5 :List(Capability); # capabilities supported by the node + dialInfoDetailList @6 :List(DialInfoDetail); # inbound dial info details for this node } struct SignedDirectNodeInfo @0xe0e7ea3e893a3dd7 { @@ -283,7 +273,7 @@ struct OperationReturnReceipt @0xeb0fb5b5a9160eeb { } struct OperationFindNodeQ @0xfdef788fe9623bcd { - nodeId @0 :TypedKey; # node id to locate + nodeId @0 :TypedKey; # node id to locate } struct OperationFindNodeA @0xa84cf2fb40c77089 { diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index b0c77c8d..18cacbf6 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -5,31 +5,31 @@ mod native; #[cfg(target_arch = "wasm32")] mod wasm; -mod direct_boot; -mod send_data; -mod connection_handle; mod address_filter; +mod connection_handle; mod connection_manager; mod connection_table; +mod direct_boot; mod network_connection; +mod send_data; +mod stats; mod tasks; mod types; -mod stats; pub mod tests; //////////////////////////////////////////////////////////////////////////////////////// pub use connection_manager::*; -pub use network_connection::*; -pub use types::*; -pub use send_data::*; pub use direct_boot::*; +pub use network_connection::*; +pub use send_data::*; pub use stats::*; +pub use types::*; //////////////////////////////////////////////////////////////////////////////////////// -use connection_handle::*; use address_filter::*; +use connection_handle::*; use crypto::*; use futures_util::stream::FuturesUnordered; use hashlink::LruCache; @@ -47,13 +47,16 @@ use wasm::*; pub const MAX_MESSAGE_SIZE: usize = MAX_ENVELOPE_SIZE; pub const IPADDR_TABLE_SIZE: usize = 1024; -pub const IPADDR_MAX_INACTIVE_DURATION_US: TimestampDuration = TimestampDuration::new(300_000_000u64); // 5 minutes +pub const IPADDR_MAX_INACTIVE_DURATION_US: TimestampDuration = + TimestampDuration::new(300_000_000u64); // 5 minutes pub const NODE_CONTACT_METHOD_CACHE_SIZE: usize = 1024; pub const PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT: usize = 3; pub const PUBLIC_ADDRESS_CHECK_CACHE_SIZE: usize = 8; pub const PUBLIC_ADDRESS_CHECK_TASK_INTERVAL_SECS: u32 = 60; -pub const PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US: TimestampDuration = TimestampDuration::new(300_000_000u64); // 5 minutes -pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: TimestampDuration = TimestampDuration::new(3600_000_000u64); // 60 minutes +pub const PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US: TimestampDuration = + TimestampDuration::new(300_000_000u64); // 5 minutes +pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: TimestampDuration = + TimestampDuration::new(3600_000_000u64); // 60 minutes pub const ADDRESS_FILTER_TASK_INTERVAL_SECS: u32 = 60; pub const BOOT_MAGIC: &[u8; 4] = b"BOOT"; @@ -75,7 +78,6 @@ struct NetworkComponents { receipt_manager: ReceiptManager, } - #[derive(Debug)] struct ClientWhitelistEntry { last_seen_ts: Timestamp, @@ -134,7 +136,7 @@ struct NetworkManagerUnlockedInner { storage_manager: StorageManager, protected_store: ProtectedStore, table_store: TableStore, - #[cfg(feature="unstable-blockstore")] + #[cfg(feature = "unstable-blockstore")] block_store: BlockStore, crypto: Crypto, address_filter: AddressFilter, @@ -171,8 +173,7 @@ impl NetworkManager { storage_manager: StorageManager, protected_store: ProtectedStore, table_store: TableStore, - #[cfg(feature="unstable-blockstore")] - block_store: BlockStore, + #[cfg(feature = "unstable-blockstore")] block_store: BlockStore, crypto: Crypto, network_key: Option, ) -> NetworkManagerUnlockedInner { @@ -181,7 +182,7 @@ impl NetworkManager { storage_manager, protected_store, table_store, - #[cfg(feature="unstable-blockstore")] + #[cfg(feature = "unstable-blockstore")] block_store, crypto, address_filter: AddressFilter::new(config), @@ -200,18 +201,20 @@ impl NetworkManager { storage_manager: StorageManager, protected_store: ProtectedStore, table_store: TableStore, - #[cfg(feature="unstable-blockstore")] - block_store: BlockStore, + #[cfg(feature = "unstable-blockstore")] block_store: BlockStore, crypto: Crypto, ) -> Self { - // Make the network key let network_key = { let c = config.get(); let network_key_password = if let Some(nkp) = c.network.network_key_password.clone() { Some(nkp) } else { - if c.network.routing_table.bootstrap.contains(&"bootstrap.veilid.net".to_owned()) { + if c.network + .routing_table + .bootstrap + .contains(&"bootstrap.veilid.net".to_owned()) + { None } else { Some(c.network.routing_table.bootstrap.join(",")) @@ -224,7 +227,13 @@ impl NetworkManager { let bcs = crypto.best(); // Yes the use of the salt this way is generally bad, but this just needs to be hashed - Some(bcs.derive_shared_secret(network_key_password.as_bytes(), network_key_password.as_bytes()).expect("failed to derive network key")) + Some( + bcs.derive_shared_secret( + network_key_password.as_bytes(), + network_key_password.as_bytes(), + ) + .expect("failed to derive network key"), + ) } else { None } @@ -242,7 +251,7 @@ impl NetworkManager { storage_manager, protected_store, table_store, - #[cfg(feature="unstable-blockstore")] + #[cfg(feature = "unstable-blockstore")] block_store, crypto, network_key, @@ -271,7 +280,7 @@ impl NetworkManager { pub fn table_store(&self) -> TableStore { self.unlocked_inner.table_store.clone() } - #[cfg(feature="unstable-blockstore")] + #[cfg(feature = "unstable-blockstore")] pub fn block_store(&self) -> BlockStore { self.unlocked_inner.block_store.clone() } @@ -443,7 +452,7 @@ impl NetworkManager { pub fn update_client_whitelist(&self, client: TypedKey) { let mut inner = self.inner.lock(); - match inner.client_whitelist.entry(client, |_k,_v| { + match inner.client_whitelist.entry(client, |_k, _v| { // do nothing on LRU evict }) { hashlink::lru_cache::Entry::Occupied(mut entry) => { @@ -461,7 +470,7 @@ impl NetworkManager { pub fn check_client_whitelist(&self, client: TypedKey) -> bool { let mut inner = self.inner.lock(); - match inner.client_whitelist.entry(client, |_k,_v| { + match inner.client_whitelist.entry(client, |_k, _v| { // do nothing on LRU evict }) { hashlink::lru_cache::Entry::Occupied(mut entry) => { @@ -475,7 +484,8 @@ impl NetworkManager { pub fn purge_client_whitelist(&self) { let timeout_ms = self.with_config(|c| c.network.client_whitelist_timeout_ms); let mut inner = self.inner.lock(); - let cutoff_timestamp = get_aligned_timestamp() - TimestampDuration::new((timeout_ms as u64) * 1000u64); + let cutoff_timestamp = + get_aligned_timestamp() - TimestampDuration::new((timeout_ms as u64) * 1000u64); // Remove clients from the whitelist that haven't been since since our whitelist timeout while inner .client_whitelist @@ -493,95 +503,8 @@ impl NetworkManager { net.needs_restart() } - /// Get our node's capabilities in the PublicInternet routing domain - fn generate_public_internet_node_status(&self) -> PublicInternetNodeStatus { - - let Some(own_peer_info) = self - .routing_table() - .get_own_peer_info(RoutingDomain::PublicInternet) else { - return PublicInternetNodeStatus::default(); - }; - let own_node_info = own_peer_info.signed_node_info().node_info(); - - let config = self.config(); - let c = config.get(); - - let will_route = own_node_info.can_inbound_relay(); // xxx: eventually this may have more criteria added - let will_tunnel = own_node_info.can_inbound_relay(); // xxx: we may want to restrict by battery life and network bandwidth at some point - let will_signal = own_node_info.can_signal(); - let will_relay = own_node_info.can_inbound_relay(); - let will_validate_dial_info = own_node_info.can_validate_dial_info(); - - let mut capabilities = Vec::new(); - if will_route && !c.capabilities.disable.contains(&CAP_WILL_ROUTE) { - capabilities.push(CAP_WILL_ROUTE); - } - if will_tunnel && !c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { - capabilities.push(CAP_WILL_TUNNEL); - } - if will_signal && !c.capabilities.disable.contains(&CAP_WILL_SIGNAL) { - capabilities.push(CAP_WILL_SIGNAL); - } - if will_relay && !c.capabilities.disable.contains(&CAP_WILL_RELAY){ - capabilities.push(CAP_WILL_RELAY); - } - if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) { - capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO); - } - if !c.capabilities.disable.contains(&CAP_WILL_DHT) { - capabilities.push(CAP_WILL_DHT); - } - if !c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { - capabilities.push(CAP_WILL_APPMESSAGE); - } - - PublicInternetNodeStatus { - capabilities - } - } - /// Get our node's capabilities in the LocalNetwork routing domain - fn generate_local_network_node_status(&self) -> LocalNetworkNodeStatus { - let Some(own_peer_info) = self - .routing_table() - .get_own_peer_info(RoutingDomain::LocalNetwork) else { - return LocalNetworkNodeStatus::default(); - }; - - let own_node_info = own_peer_info.signed_node_info().node_info(); - - let config = self.config(); - let c = config.get(); - - let will_relay = own_node_info.can_inbound_relay(); - let will_validate_dial_info = own_node_info.can_validate_dial_info(); - - let mut capabilities = Vec::new(); - if will_relay && !c.capabilities.disable.contains(&CAP_WILL_RELAY) { - capabilities.push(CAP_WILL_RELAY); - } - if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) { - capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO); - } - if !c.capabilities.disable.contains(&CAP_WILL_DHT) { - capabilities.push(CAP_WILL_DHT); - } - if !c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { - capabilities.push(CAP_WILL_APPMESSAGE); - } - LocalNetworkNodeStatus { - capabilities - } - } - - 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()) - } - } + pub fn generate_node_status(&self, _routing_domain: RoutingDomain) -> NodeStatus { + NodeStatus {} } /// Generates a multi-shot/normal receipt @@ -598,12 +521,18 @@ impl NetworkManager { // Generate receipt and serialized form to return let vcrypto = self.crypto().best(); - + let nonce = vcrypto.random_nonce(); let node_id = routing_table.node_id(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); - - let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; + + let receipt = Receipt::try_new( + best_envelope_version(), + node_id.kind, + nonce, + node_id.value, + extra_data, + )?; let out = receipt .to_signed_data(self.crypto(), &node_id_secret) .wrap_err("failed to generate signed receipt")?; @@ -627,12 +556,18 @@ impl NetworkManager { // Generate receipt and serialized form to return let vcrypto = self.crypto().best(); - + let nonce = vcrypto.random_nonce(); let node_id = routing_table.node_id(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); - - let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; + + let receipt = Receipt::try_new( + best_envelope_version(), + node_id.kind, + nonce, + node_id.value, + extra_data, + )?; let out = receipt .to_signed_data(self.crypto(), &node_id_secret) .wrap_err("failed to generate signed receipt")?; @@ -744,9 +679,10 @@ impl NetworkManager { ) { Ok(nr) => nr, Err(e) => { - return Ok(NetworkResult::invalid_message( - format!("unable to register reverse connect peerinfo: {}", e) - )); + return Ok(NetworkResult::invalid_message(format!( + "unable to register reverse connect peerinfo: {}", + e + ))); } }; @@ -767,9 +703,10 @@ impl NetworkManager { ) { Ok(nr) => nr, Err(e) => { - return Ok(NetworkResult::invalid_message( - format!("unable to register hole punch connect peerinfo: {}", e) - )); + return Ok(NetworkResult::invalid_message(format!( + "unable to register hole punch connect peerinfo: {}", + e + ))); } }; @@ -813,7 +750,10 @@ impl NetworkManager { } /// Builds an envelope for sending over the network - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, body), err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self, body), err) + )] fn build_envelope>( &self, dest_node_id: TypedKey, @@ -834,9 +774,21 @@ impl NetworkManager { let nonce = vcrypto.random_nonce(); // Encode envelope - let envelope = Envelope::new(version, node_id.kind, ts, nonce, node_id.value, dest_node_id.value); + let envelope = Envelope::new( + version, + node_id.kind, + ts, + nonce, + node_id.value, + dest_node_id.value, + ); envelope - .to_encrypted_data(self.crypto(), body.as_ref(), &node_id_secret, &self.unlocked_inner.network_key) + .to_encrypted_data( + self.crypto(), + body.as_ref(), + &node_id_secret, + &self.unlocked_inner.network_key, + ) .wrap_err("envelope failed to encode") } @@ -844,18 +796,20 @@ impl NetworkManager { /// node_ref is the direct destination to which the envelope will be sent /// If 'destination_node_ref' is specified, it can be different than the node_ref being sent to /// which will cause the envelope to be relayed - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, body), ret, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self, body), ret, err) + )] pub async fn send_envelope>( &self, node_ref: NodeRef, destination_node_ref: Option, body: B, ) -> EyreResult> { - let destination_node_ref = destination_node_ref.as_ref().unwrap_or(&node_ref).clone(); - + if !node_ref.same_entry(&destination_node_ref) { - log_net!( + log_net!( "sending envelope to {:?} via {:?}", destination_node_ref, node_ref @@ -915,7 +869,7 @@ impl NetworkManager { data: &mut [u8], connection_descriptor: ConnectionDescriptor, ) -> EyreResult { - #[cfg(feature="verbose-tracing")] + #[cfg(feature = "verbose-tracing")] let root = span!( parent: None, Level::TRACE, @@ -923,7 +877,7 @@ impl NetworkManager { "data.len" = data.len(), "descriptor" = ?connection_descriptor ); - #[cfg(feature="verbose-tracing")] + #[cfg(feature = "verbose-tracing")] let _root_enter = root.enter(); log_net!( @@ -934,10 +888,7 @@ impl NetworkManager { let remote_addr = connection_descriptor.remote_address().to_ip_addr(); // Network accounting - self.stats_packet_rcvd( - remote_addr, - ByteCount::new(data.len() as u64), - ); + self.stats_packet_rcvd(remote_addr, ByteCount::new(data.len() as u64)); // If this is a zero length packet, just drop it, because these are used for hole punching // and possibly other low-level network connectivity tasks and will never require @@ -978,20 +929,30 @@ impl NetworkManager { } // Decode envelope header (may fail signature validation) - let envelope = match Envelope::from_signed_data(self.crypto(), data, &self.unlocked_inner.network_key) { - Ok(v) => v, - Err(e) => { - log_net!(debug "envelope failed to decode: {}", e); - self.address_filter().punish(remote_addr); - return Ok(false); - } - }; + let envelope = + match Envelope::from_signed_data(self.crypto(), data, &self.unlocked_inner.network_key) + { + Ok(v) => v, + Err(e) => { + log_net!(debug "envelope failed to decode: {}", e); + self.address_filter().punish(remote_addr); + return Ok(false); + } + }; // Get timestamp range let (tsbehind, tsahead) = self.with_config(|c| { ( - c.network.rpc.max_timestamp_behind_ms.map(ms_to_us).map(TimestampDuration::new), - c.network.rpc.max_timestamp_ahead_ms.map(ms_to_us).map(TimestampDuration::new), + c.network + .rpc + .max_timestamp_behind_ms + .map(ms_to_us) + .map(TimestampDuration::new), + c.network + .rpc + .max_timestamp_ahead_ms + .map(ms_to_us) + .map(TimestampDuration::new), ) }); @@ -1034,7 +995,10 @@ impl NetworkManager { let some_relay_nr = if self.check_client_whitelist(sender_id) { // Full relay allowed, do a full resolve_node - match rpc.resolve_node(recipient_id, SafetySelection::Unsafe(Sequencing::default())).await { + match rpc + .resolve_node(recipient_id, SafetySelection::Unsafe(Sequencing::default())) + .await + { Ok(v) => v, Err(e) => { log_net!(debug "failed to resolve recipient node for relay, dropping outbound relayed packet: {}" ,e); @@ -1059,7 +1023,7 @@ impl NetworkManager { }; if let Some(relay_nr) = some_relay_nr { - // Force sequencing if this came in sequenced. + // Force sequencing if this came in sequenced. // The sender did the prefer/ensure calculation when it did get_contact_method, // so we don't need to do it here. let relay_nr = if connection_descriptor.remote().protocol_type().is_ordered() { @@ -1078,7 +1042,7 @@ impl NetworkManager { Ok(v) => v, Err(e) => { log_net!(debug "failed to forward envelope: {}" ,e); - return Ok(false); + return Ok(false); } } => [ format!(": relay_nr={}, data.len={}", relay_nr, data.len()) ] { return Ok(false); @@ -1093,15 +1057,19 @@ impl NetworkManager { let node_id_secret = routing_table.node_id_secret_key(envelope.get_crypto_kind()); // Decrypt the envelope body - let body = match envelope - .decrypt_body(self.crypto(), data, &node_id_secret, &self.unlocked_inner.network_key) { - Ok(v) => v, - Err(e) => { - log_net!(debug "failed to decrypt envelope body: {}",e); - self.address_filter().punish(remote_addr); - return Ok(false); - } - }; + let body = match envelope.decrypt_body( + self.crypto(), + data, + &node_id_secret, + &self.unlocked_inner.network_key, + ) { + Ok(v) => v, + Err(e) => { + log_net!(debug "failed to decrypt envelope body: {}",e); + self.address_filter().punish(remote_addr); + return Ok(false); + } + }; // Cache the envelope information in the routing table let source_noderef = match routing_table.register_node_with_existing_connection( @@ -1195,7 +1163,7 @@ impl NetworkManager { if pait.contains_key(&ipblock) { return; } - pacc.insert(ipblock, socket_address, |_k,_v| { + pacc.insert(ipblock, socket_address, |_k, _v| { // do nothing on LRU evict }); @@ -1253,8 +1221,8 @@ impl NetworkManager { .public_address_inconsistencies_table .entry(key) .or_insert_with(|| HashMap::new()); - let exp_ts = - get_aligned_timestamp() + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US; + let exp_ts = get_aligned_timestamp() + + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US; for i in inconsistencies { pait.insert(i, exp_ts); } @@ -1325,5 +1293,4 @@ impl NetworkManager { } } } - } diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index 2a719507..a4cf7854 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -773,16 +773,34 @@ impl Network { // set up the routing table's network config // if we have static public dialinfo, upgrade our network class + let public_internet_capabilities = { + let c = self.config.get(); + PUBLIC_INTERNET_CAPABILITIES + .iter() + .copied() + .filter(|cap| !c.capabilities.disable.contains(cap)) + .collect::>() + }; + let local_network_capabilities = { + let c = self.config.get(); + LOCAL_NETWORK_CAPABILITIES + .iter() + .copied() + .filter(|cap| !c.capabilities.disable.contains(cap)) + .collect::>() + }; editor_public_internet.setup_network( protocol_config.outbound, protocol_config.inbound, protocol_config.family_global, + public_internet_capabilities, ); editor_local_network.setup_network( protocol_config.outbound, protocol_config.inbound, protocol_config.family_local, + local_network_capabilities, ); let detect_address_changes = { let c = self.config.get(); diff --git a/veilid-core/src/network_manager/tests/test_signed_node_info.rs b/veilid-core/src/network_manager/tests/test_signed_node_info.rs index e554b54a..1bcc2bb0 100644 --- a/veilid-core/src/network_manager/tests/test_signed_node_info.rs +++ b/veilid-core/src/network_manager/tests/test_signed_node_info.rs @@ -20,6 +20,7 @@ pub async fn test_signed_node_info() { AddressTypeSet::all(), VALID_ENVELOPE_VERSIONS.to_vec(), VALID_CRYPTO_KINDS.to_vec(), + PUBLIC_INTERNET_CAPABILITIES.to_vec(), vec![DialInfoDetail { class: DialInfoClass::Mapped, dial_info: DialInfo::udp(SocketAddress::default()), @@ -75,6 +76,7 @@ pub async fn test_signed_node_info() { AddressTypeSet::all(), VALID_ENVELOPE_VERSIONS.to_vec(), VALID_CRYPTO_KINDS.to_vec(), + PUBLIC_INTERNET_CAPABILITIES.to_vec(), vec![DialInfoDetail { class: DialInfoClass::Blocked, dial_info: DialInfo::udp(SocketAddress::default()), diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index fedf0d16..a4d197d2 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -51,7 +51,7 @@ pub struct BucketEntryPublicInternet { /// The last node info timestamp of ours that this entry has seen last_seen_our_node_info_ts: Timestamp, /// Last known node status - node_status: Option, + node_status: Option, } /// Bucket entry information specific to the LocalNetwork RoutingDomain @@ -63,7 +63,7 @@ pub struct BucketEntryLocalNetwork { /// The last node info timestamp of ours that this entry has seen last_seen_our_node_info_ts: Timestamp, /// Last known node status - node_status: Option, + node_status: Option, } /// The data associated with each bucket entry @@ -502,13 +502,13 @@ impl BucketEntryInner { &self.peer_stats } - pub fn update_node_status(&mut self, status: NodeStatus) { - match status { - NodeStatus::LocalNetwork(ln) => { - self.local_network.node_status = Some(ln); + pub fn update_node_status(&mut self, routing_domain: RoutingDomain, status: NodeStatus) { + match routing_domain { + RoutingDomain::LocalNetwork => { + self.local_network.node_status = Some(status); } - NodeStatus::PublicInternet(pi) => { - self.public_internet.node_status = Some(pi); + RoutingDomain::PublicInternet => { + self.public_internet.node_status = Some(status); } } } @@ -518,12 +518,12 @@ impl BucketEntryInner { .local_network .node_status .as_ref() - .map(|ln| NodeStatus::LocalNetwork(ln.clone())), + .map(|ns| ns.clone()), RoutingDomain::PublicInternet => self .public_internet .node_status .as_ref() - .map(|pi| NodeStatus::PublicInternet(pi.clone())), + .map(|ns| ns.clone()), } } diff --git a/veilid-core/src/routing_table/node_ref.rs b/veilid-core/src/routing_table/node_ref.rs index 826cd662..a10dfecf 100644 --- a/veilid-core/src/routing_table/node_ref.rs +++ b/veilid-core/src/routing_table/node_ref.rs @@ -118,9 +118,9 @@ pub trait NodeRefBase: Sized { fn set_updated_since_last_network_change(&self) { self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true)); } - fn update_node_status(&self, node_status: NodeStatus) { + fn update_node_status(&self, routing_domain: RoutingDomain, node_status: NodeStatus) { self.operate_mut(|_rti, e| { - e.update_node_status(node_status); + e.update_node_status(routing_domain, node_status); }); } fn envelope_support(&self) -> Vec { diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs index 61830d88..0ebeb351 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs @@ -257,20 +257,9 @@ impl RouteSpecStore { // Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route entry.with_inner(|e| { - let node_info_ok = - if let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) { - sni.has_sequencing_matched_dial_info(sequencing) - } else { - false - }; - let node_status_ok = - if let Some(ns) = e.node_status(RoutingDomain::PublicInternet) { - ns.has_capability(CAP_WILL_ROUTE) - } else { - false - }; - - node_info_ok && node_status_ok + e.signed_node_info(RoutingDomain::PublicInternet).map(|sni| + sni.has_sequencing_matched_dial_info(sequencing) && sni.node_info().can_route() + ).unwrap_or(false) }) }, ) as RoutingTableEntryFilter; diff --git a/veilid-core/src/routing_table/routing_domain_editor.rs b/veilid-core/src/routing_table/routing_domain_editor.rs index d1694ec8..01f862ff 100644 --- a/veilid-core/src/routing_table/routing_domain_editor.rs +++ b/veilid-core/src/routing_table/routing_domain_editor.rs @@ -13,6 +13,7 @@ enum RoutingDomainChange { outbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet, address_types: AddressTypeSet, + capabilities: Vec, }, SetNetworkClass { network_class: Option, @@ -79,11 +80,13 @@ impl RoutingDomainEditor { outbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet, address_types: AddressTypeSet, + capabilities: Vec, ) { self.changes.push(RoutingDomainChange::SetupNetwork { outbound_protocols, inbound_protocols, address_types, + capabilities, }) } @@ -142,27 +145,32 @@ impl RoutingDomainEditor { outbound_protocols, inbound_protocols, address_types, + capabilities, } => { let old_outbound_protocols = detail.common().outbound_protocols(); let old_inbound_protocols = detail.common().inbound_protocols(); let old_address_types = detail.common().address_types(); + let old_capabilities = detail.common().capabilities(); let this_changed = old_outbound_protocols != outbound_protocols || old_inbound_protocols != inbound_protocols - || old_address_types != address_types; + || old_address_types != address_types + || old_capabilities != capabilities; debug!( - "[{:?}] setup network: {:?} {:?} {:?}", + "[{:?}] setup network: {:?} {:?} {:?} {:?}", self.routing_domain, outbound_protocols, inbound_protocols, - address_types + address_types, + capabilities ); detail.common_mut().setup_network( outbound_protocols, inbound_protocols, address_types, + capabilities, ); if this_changed { changed = true; diff --git a/veilid-core/src/routing_table/routing_domains.rs b/veilid-core/src/routing_table/routing_domains.rs index e9f96a1b..b1d0b97a 100644 --- a/veilid-core/src/routing_table/routing_domains.rs +++ b/veilid-core/src/routing_table/routing_domains.rs @@ -27,6 +27,7 @@ pub struct RoutingDomainDetailCommon { inbound_protocols: ProtocolTypeSet, address_types: AddressTypeSet, relay_node: Option, + capabilities: Vec, dial_info_details: Vec, // caches cached_peer_info: Mutex>, @@ -41,6 +42,7 @@ impl RoutingDomainDetailCommon { inbound_protocols: Default::default(), address_types: Default::default(), relay_node: Default::default(), + capabilities: Default::default(), dial_info_details: Default::default(), cached_peer_info: Mutex::new(Default::default()), } @@ -52,10 +54,12 @@ impl RoutingDomainDetailCommon { outbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet, address_types: AddressTypeSet, + capabilities: Vec, ) { self.outbound_protocols = outbound_protocols; self.inbound_protocols = inbound_protocols; self.address_types = address_types; + self.capabilities = capabilities; self.clear_cache(); } @@ -75,6 +79,9 @@ impl RoutingDomainDetailCommon { pub fn address_types(&self) -> AddressTypeSet { self.address_types } + pub fn capabilities(&self) -> Vec { + self.capabilities.clone() + } pub fn relay_node(&self) -> Option { self.relay_node.clone() } @@ -108,6 +115,7 @@ impl RoutingDomainDetailCommon { self.address_types, VALID_ENVELOPE_VERSIONS.to_vec(), VALID_CRYPTO_KINDS.to_vec(), + self.capabilities.clone(), self.dial_info_details.clone() ); diff --git a/veilid-core/src/routing_table/tasks/bootstrap.rs b/veilid-core/src/routing_table/tasks/bootstrap.rs index 39db5b0f..1913e5be 100644 --- a/veilid-core/src/routing_table/tasks/bootstrap.rs +++ b/veilid-core/src/routing_table/tasks/bootstrap.rs @@ -344,6 +344,7 @@ impl RoutingTable { AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable bsrec.envelope_support, // Envelope support is as specified in the bootstrap list crypto_support, // Crypto support is derived from list of node ids + vec![], // Bootstrap needs no capabilities bsrec.dial_info_details, // Dial info is as specified in the bootstrap list ))); diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index dfb2c268..543fdffd 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -146,9 +146,9 @@ impl RoutingTable { let entry2 = entry.clone(); entry.with(rti, |rti, e| { // Ensure we have the node's status - if let Some(node_status) = e.node_status(routing_domain) { + if let Some(node_info) = e.node_info(routing_domain) { // Ensure the node will relay - if node_status.has_capability(CAP_WILL_RELAY) { + if node_info.can_inbound_relay() { // Compare against previous candidate if let Some(best_inbound_relay) = best_inbound_relay.as_mut() { // Less is faster diff --git a/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs b/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs index 7a79b1ac..89811612 100644 --- a/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs +++ b/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs @@ -100,6 +100,7 @@ pub async fn test_round_trip_peerinfo() { AddressTypeSet::new(), vec![0], vec![CRYPTO_KIND_VLD0], + PUBLIC_INTERNET_CAPABILITIES.to_vec(), vec![], ), Timestamp::new(0), diff --git a/veilid-core/src/routing_table/types/node_info.rs b/veilid-core/src/routing_table/types/node_info.rs index e67f4711..067d2fed 100644 --- a/veilid-core/src/routing_table/types/node_info.rs +++ b/veilid-core/src/routing_table/types/node_info.rs @@ -1,5 +1,54 @@ use super::*; +pub type Capability = FourCC; +pub const CAP_WILL_ROUTE: Capability = FourCC(*b"ROUT"); +#[cfg(feature = "unstable-tunnels")] +pub const CAP_WILL_TUNNEL: Capability = FourCC(*b"TUNL"); +pub const CAP_WILL_SIGNAL: Capability = FourCC(*b"SGNL"); +pub const CAP_WILL_RELAY: Capability = FourCC(*b"RLAY"); +pub const CAP_WILL_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL"); +pub const CAP_WILL_DHT: Capability = FourCC(*b"DHTV"); +pub const CAP_WILL_APPMESSAGE: Capability = FourCC(*b"APPM"); +#[cfg(feature = "unstable-blockstore")] +pub const CAP_WILL_BLOCKSTORE: Capability = FourCC(*b"BLOC"); + +cfg_if! { + if #[cfg(all(feature = "unstable-blockstore", feature="unstable-tunnels"))] { + const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 8; + } else if #[cfg(any(feature = "unstable-blockstore", feature="unstable-tunnels"))] { + const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 7; + } else { + const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 6; + } +} +pub const PUBLIC_INTERNET_CAPABILITIES: [Capability; PUBLIC_INTERNET_CAPABILITIES_LEN] = [ + CAP_WILL_ROUTE, + #[cfg(feature = "unstable-tunnels")] + CAP_WILL_TUNNEL, + CAP_WILL_SIGNAL, + CAP_WILL_RELAY, + CAP_WILL_VALIDATE_DIAL_INFO, + CAP_WILL_DHT, + CAP_WILL_APPMESSAGE, + #[cfg(feature = "unstable-blockstore")] + CAP_WILL_BLOCKSTORE, +]; + +#[cfg(feature = "unstable-blockstore")] +const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 4; +#[cfg(not(feature = "unstable-blockstore"))] +const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 3; + +pub const LOCAL_NETWORK_CAPABILITIES: [Capability; LOCAL_NETWORK_CAPABILITIES_LEN] = [ + CAP_WILL_RELAY, + CAP_WILL_DHT, + CAP_WILL_APPMESSAGE, + #[cfg(feature = "unstable-blockstore")] + CAP_WILL_BLOCKSTORE, +]; + +pub const MAX_CAPABILITIES: usize = 64; + #[derive( Clone, Default, @@ -21,6 +70,7 @@ pub struct NodeInfo { address_types: AddressTypeSet, envelope_support: Vec, crypto_support: Vec, + capabilities: Vec, dial_info_detail_list: Vec, } @@ -31,6 +81,7 @@ impl NodeInfo { address_types: AddressTypeSet, envelope_support: Vec, crypto_support: Vec, + capabilities: Vec, dial_info_detail_list: Vec, ) -> Self { Self { @@ -39,6 +90,7 @@ impl NodeInfo { address_types, envelope_support, crypto_support, + capabilities, dial_info_detail_list, } } @@ -58,6 +110,9 @@ impl NodeInfo { pub fn crypto_support(&self) -> &[CryptoKind] { &self.crypto_support } + pub fn capabilities(&self) -> &[Capability] { + &self.capabilities + } pub fn dial_info_detail_list(&self) -> &[DialInfoDetail] { &self.dial_info_detail_list } @@ -144,8 +199,17 @@ impl NodeInfo { false } + fn has_capability(&self, cap: Capability) -> bool { + self.capabilities.contains(&cap) + } + /// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself. pub fn can_signal(&self) -> bool { + // Has capability? + if !self.has_capability(CAP_WILL_SIGNAL) { + return false; + } + // Must be inbound capable if !matches!(self.network_class, NetworkClass::InboundCapable) { return false; @@ -161,13 +225,44 @@ impl NodeInfo { /// Can this node relay be an inbound relay? pub fn can_inbound_relay(&self) -> bool { + // Has capability? + if !self.has_capability(CAP_WILL_RELAY) { + return false; + } + // For now this is the same self.can_signal() } /// Is this node capable of validating dial info pub fn can_validate_dial_info(&self) -> bool { + // Has capability? + if !self.has_capability(CAP_WILL_VALIDATE_DIAL_INFO) { + return false; + } // For now this is the same self.can_signal() } + /// Is this node capable of private routing + pub fn can_route(&self) -> bool { + self.has_capability(CAP_WILL_ROUTE) + } + /// Is this node capable of dht operations + pub fn can_dht(&self) -> bool { + self.has_capability(CAP_WILL_DHT) + } + /// Is this node capable of app_message and app_call + pub fn can_appmessage(&self) -> bool { + self.has_capability(CAP_WILL_APPMESSAGE) + } + /// Is this node capable of tunneling + #[cfg(feature = "unstable-tunnels")] + pub fn can_tunnel(&self) -> bool { + self.has_capability(CAP_WILL_TUNNEL) + } + /// Is this node capable of block storage + #[cfg(feature = "unstable-blockstore")] + pub fn can_blockstore(&self) -> bool { + self.has_capability(CAP_WILL_BLOCKSTORE) + } } diff --git a/veilid-core/src/routing_table/types/node_status.rs b/veilid-core/src/routing_table/types/node_status.rs index c8abe079..2ecb1460 100644 --- a/veilid-core/src/routing_table/types/node_status.rs +++ b/veilid-core/src/routing_table/types/node_status.rs @@ -1,47 +1,9 @@ use super::*; -/// RoutingDomain-specific status for each node -/// is returned by the StatusA call - -pub type Capability = FourCC; -pub const CAP_WILL_ROUTE: Capability = FourCC(*b"ROUT"); -pub const CAP_WILL_TUNNEL: Capability = FourCC(*b"TUNL"); -pub const CAP_WILL_SIGNAL: Capability = FourCC(*b"SGNL"); -pub const CAP_WILL_RELAY: Capability = FourCC(*b"RLAY"); -pub const CAP_WILL_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL"); -pub const CAP_WILL_DHT: Capability = FourCC(*b"DHTV"); -pub const CAP_WILL_APPMESSAGE: Capability = FourCC(*b"APPM"); -pub const MAX_CAPABILITIES: usize = 64; - -/// PublicInternet RoutingDomain Status -#[derive( - Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct PublicInternetNodeStatus { - pub capabilities: Vec, -} - -#[derive( - Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct LocalNetworkNodeStatus { - pub capabilities: Vec, -} +/// Non-nodeinfo status for each node is returned by the StatusA call #[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum NodeStatus { - PublicInternet(PublicInternetNodeStatus), - LocalNetwork(LocalNetworkNodeStatus), -} - -impl NodeStatus { - pub fn has_capability(&self, cap: Capability) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.capabilities.contains(&cap), - NodeStatus::LocalNetwork(ln) => ln.capabilities.contains(&cap), - } - } +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct NodeStatus { + // Reserved for expansion } diff --git a/veilid-core/src/rpc_processor/coders/node_info.rs b/veilid-core/src/rpc_processor/coders/node_info.rs index 9e89da68..288d55d7 100644 --- a/veilid-core/src/rpc_processor/coders/node_info.rs +++ b/veilid-core/src/rpc_processor/coders/node_info.rs @@ -31,6 +31,18 @@ pub fn encode_node_info( s.clone_from_slice(&csvec); } + let mut cap_builder = builder + .reborrow() + .init_capabilities(node_info.capabilities().len() as u32); + if let Some(s) = cap_builder.as_slice() { + let capvec: Vec = node_info + .capabilities() + .iter() + .map(|x| u32::from_be_bytes(x.0)) + .collect(); + + s.clone_from_slice(&capvec); + } let mut didl_builder = builder.reborrow().init_dial_info_detail_list( node_info .dial_info_detail_list() @@ -121,6 +133,18 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result MAX_CAPABILITIES { + return Err(RPCError::protocol("too many capabilities")); + } + let capabilities = cap_reader + .as_slice() + .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) + .unwrap_or_default(); + let didl_reader = reader .reborrow() .get_dial_info_detail_list() @@ -141,6 +165,7 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result Result<(), RPCError> { - let mut cap_builder = builder - .reborrow() - .init_capabilities(public_internet_node_status.capabilities.len() as u32); - if let Some(s) = cap_builder.as_slice() { - let capvec: Vec = public_internet_node_status - .capabilities - .iter() - .map(|x| u32::from_be_bytes(x.0)) - .collect(); - - s.clone_from_slice(&capvec); - } - Ok(()) -} - -pub fn decode_public_internet_node_status( - reader: &veilid_capnp::public_internet_node_status::Reader, -) -> Result { - let cap_reader = reader - .reborrow() - .get_capabilities() - .map_err(RPCError::protocol)?; - if cap_reader.len() as usize > MAX_CAPABILITIES { - return Err(RPCError::protocol("too many capabilities")); - } - let capabilities = cap_reader - .as_slice() - .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) - .unwrap_or_default(); - - Ok(PublicInternetNodeStatus { capabilities }) -} - -pub fn encode_local_network_node_status( - local_network_node_status: &LocalNetworkNodeStatus, - builder: &mut veilid_capnp::local_network_node_status::Builder, -) -> Result<(), RPCError> { - let mut cap_builder = builder - .reborrow() - .init_capabilities(local_network_node_status.capabilities.len() as u32); - if let Some(s) = cap_builder.as_slice() { - let capvec: Vec = local_network_node_status - .capabilities - .iter() - .map(|x| u32::from_be_bytes(x.0)) - .collect(); - - s.clone_from_slice(&capvec); - } - Ok(()) -} - -pub fn decode_local_network_node_status( - reader: &veilid_capnp::local_network_node_status::Reader, -) -> Result { - let cap_reader = reader - .reborrow() - .get_capabilities() - .map_err(RPCError::protocol)?; - if cap_reader.len() as usize > MAX_CAPABILITIES { - return Err(RPCError::protocol("too many capabilities")); - } - let capabilities = cap_reader - .as_slice() - .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) - .unwrap_or_default(); - - Ok(LocalNetworkNodeStatus { capabilities }) -} - pub fn encode_node_status( - node_status: &NodeStatus, - builder: &mut veilid_capnp::node_status::Builder, + _node_status: &NodeStatus, + _builder: &mut veilid_capnp::node_status::Builder, ) -> Result<(), RPCError> { - match node_status { - NodeStatus::PublicInternet(ns) => { - let mut pi_builder = builder.reborrow().init_public_internet(); - encode_public_internet_node_status(&ns, &mut pi_builder) - } - NodeStatus::LocalNetwork(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, + _reader: &veilid_capnp::node_status::Reader, ) -> Result { - Ok( - match reader - .which() - .map_err(RPCError::map_internal("invalid node status"))? - { - veilid_capnp::node_status::PublicInternet(pi) => { - let r = pi.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) - } - }, - ) + Ok(NodeStatus {}) } diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index 1e41497c..f9c9a504 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -117,6 +117,13 @@ impl RPCMessageHeader { // RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.peer_noderef.clone(), // } // } + pub fn routing_domain(&self) -> RoutingDomain { + match &self.detail { + RPCMessageHeaderDetail::Direct(d) => d.routing_domain, + RPCMessageHeaderDetail::SafetyRouted(s) => s.direct.routing_domain, + RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.routing_domain, + } + } pub fn direct_sender_node_id(&self) -> TypedKey { match &self.detail { RPCMessageHeaderDetail::Direct(d) => { diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index 00d1840e..f73762fb 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -54,10 +54,14 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + let routing_table = self.routing_table(); { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { - return Ok(NetworkResult::service_unavailable("appcall is disabled")); + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_appmessage() { + return Ok(NetworkResult::service_unavailable( + "app call is not available", + )); + } } } diff --git a/veilid-core/src/rpc_processor/rpc_app_message.rs b/veilid-core/src/rpc_processor/rpc_app_message.rs index e7b4daaf..891d8a2e 100644 --- a/veilid-core/src/rpc_processor/rpc_app_message.rs +++ b/veilid-core/src/rpc_processor/rpc_app_message.rs @@ -25,10 +25,14 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + let routing_table = self.routing_table(); { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { - return Ok(NetworkResult::service_unavailable("appmessage is disabled")); + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_appmessage() { + return Ok(NetworkResult::service_unavailable( + "app message is not available", + )); + } } } diff --git a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs index 0450ef38..6001eb49 100644 --- a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs @@ -7,12 +7,17 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + #[cfg(feature = "unstable-tunnels")] { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { - return Ok(NetworkResult::service_unavailable( - "cancel tunnel is disabled", - )); + let routing_table = self.routing_table(); + { + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_tunnel() { + return Ok(NetworkResult::service_unavailable( + "tunnel is not available", + )); + } + } } } diff --git a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs index deb40a6d..1b442894 100644 --- a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs @@ -7,12 +7,17 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + #[cfg(feature = "unstable-tunnels")] { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { - return Ok(NetworkResult::service_unavailable( - "complete tunnel is disabled", - )); + let routing_table = self.routing_table(); + { + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_tunnel() { + return Ok(NetworkResult::service_unavailable( + "tunnel is not available", + )); + } + } } } Err(RPCError::unimplemented("process_complete_tunnel_q")) diff --git a/veilid-core/src/rpc_processor/rpc_find_block.rs b/veilid-core/src/rpc_processor/rpc_find_block.rs index 6d92a6d8..600f7d83 100644 --- a/veilid-core/src/rpc_processor/rpc_find_block.rs +++ b/veilid-core/src/rpc_processor/rpc_find_block.rs @@ -7,10 +7,17 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + #[cfg(feature = "unstable-blockstore")] { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_BLOCKSTORE) { - return Ok(NetworkResult::service_unavailable("find block is disabled")); + let routing_table = self.routing_table(); + { + if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) { + if !opi.signed_node_info().node_info().can_blockstore() { + return Ok(NetworkResult::service_unavailable( + "block store is not available", + )); + } + } } } Err(RPCError::unimplemented("process_find_block_q")) diff --git a/veilid-core/src/rpc_processor/rpc_get_value.rs b/veilid-core/src/rpc_processor/rpc_get_value.rs index de3f923f..bab14d2d 100644 --- a/veilid-core/src/rpc_processor/rpc_get_value.rs +++ b/veilid-core/src/rpc_processor/rpc_get_value.rs @@ -163,13 +163,7 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { - // Ignore if disabled - { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_DHT) { - return Ok(NetworkResult::service_unavailable("get value is disabled")); - } - } + // Ensure this never came over a private route, safety route is okay though match &msg.header.detail { RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {} @@ -179,7 +173,17 @@ impl RPCProcessor { )) } } - + // Ignore if disabled + let routing_table = self.routing_table(); + { + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_dht() { + return Ok(NetworkResult::service_unavailable( + "dht is not available", + )); + } + } + } // Get the question let kind = msg.operation.kind().clone(); let get_value_q = match kind { diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index 0274e5aa..ec2337fd 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -366,15 +366,14 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + let routing_table = self.routing_table(); { - let c = self.config.get(); - if c.capabilities - .disable - .contains(&CAP_WILL_ROUTE) - { - return Ok(NetworkResult::service_unavailable( - "route is disabled", - )); + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_route() { + return Ok(NetworkResult::service_unavailable( + "route is not available", + )); + } } } diff --git a/veilid-core/src/rpc_processor/rpc_set_value.rs b/veilid-core/src/rpc_processor/rpc_set_value.rs index 43fd4a6b..b83c03c1 100644 --- a/veilid-core/src/rpc_processor/rpc_set_value.rs +++ b/veilid-core/src/rpc_processor/rpc_set_value.rs @@ -176,10 +176,14 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + let routing_table = self.routing_table(); { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_DHT) { - return Ok(NetworkResult::service_unavailable("set value is disabled")); + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_dht() { + return Ok(NetworkResult::service_unavailable( + "dht is not available", + )); + } } } // Ensure this never came over a private route, safety route is okay though diff --git a/veilid-core/src/rpc_processor/rpc_signal.rs b/veilid-core/src/rpc_processor/rpc_signal.rs index 4d7d33f0..542649ac 100644 --- a/veilid-core/src/rpc_processor/rpc_signal.rs +++ b/veilid-core/src/rpc_processor/rpc_signal.rs @@ -38,10 +38,14 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + let routing_table = self.routing_table(); { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_SIGNAL) { - return Ok(NetworkResult::service_unavailable("signal is disabled")); + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_signal() { + return Ok(NetworkResult::service_unavailable( + "signal is not available", + )); + } } } diff --git a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs index 241e102c..d4608cbe 100644 --- a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs @@ -7,12 +7,17 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + #[cfg(feature = "unstable-tunnels")] { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { - return Ok(NetworkResult::service_unavailable( - "start tunnel is disabled", - )); + let routing_table = self.routing_table(); + { + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_tunnel() { + return Ok(NetworkResult::service_unavailable( + "tunnel is not available", + )); + } + } } } Err(RPCError::unimplemented("process_start_tunnel_q")) diff --git a/veilid-core/src/rpc_processor/rpc_status.rs b/veilid-core/src/rpc_processor/rpc_status.rs index 22bb3817..cf4401ab 100644 --- a/veilid-core/src/rpc_processor/rpc_status.rs +++ b/veilid-core/src/rpc_processor/rpc_status.rs @@ -133,25 +133,8 @@ impl RPCProcessor { // Ensure the returned node status is the kind for the routing domain we asked for if let Some(target_nr) = opt_target_nr { if let Some(a_node_status) = a_node_status { - match routing_domain { - RoutingDomain::PublicInternet => { - if !matches!(a_node_status, NodeStatus::PublicInternet(_)) { - return Ok(NetworkResult::invalid_message( - "node status doesn't match PublicInternet routing domain", - )); - } - } - RoutingDomain::LocalNetwork => { - if !matches!(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 - target_nr.update_node_status(a_node_status.clone()); + target_nr.update_node_status(routing_domain, a_node_status.clone()); } } @@ -236,27 +219,10 @@ impl RPCProcessor { // Ensure the node status from the question is the kind for the routing domain we received the request in if let Some(q_node_status) = q_node_status { - match routing_domain { - RoutingDomain::PublicInternet => { - if !matches!(q_node_status, NodeStatus::PublicInternet(_)) { - return Ok(NetworkResult::invalid_message( - "node status doesn't match PublicInternet routing domain", - )); - } - } - RoutingDomain::LocalNetwork => { - if !matches!(q_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 - sender_nr.update_node_status(q_node_status.clone()); + sender_nr.update_node_status(routing_domain, q_node_status.clone()); } } diff --git a/veilid-core/src/rpc_processor/rpc_supply_block.rs b/veilid-core/src/rpc_processor/rpc_supply_block.rs index a716b6b8..630635eb 100644 --- a/veilid-core/src/rpc_processor/rpc_supply_block.rs +++ b/veilid-core/src/rpc_processor/rpc_supply_block.rs @@ -7,12 +7,17 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + #[cfg(feature = "unstable-blockstore")] { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_BLOCKSTORE) { - return Ok(NetworkResult::service_unavailable( - "supply block is disabled", - )); + let routing_table = self.routing_table(); + { + if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) { + if !opi.signed_node_info().node_info().can_blockstore() { + return Ok(NetworkResult::service_unavailable( + "block store is not available", + )); + } + } } } Err(RPCError::unimplemented("process_supply_block_q")) diff --git a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs index 991b2e2c..9a4a9f64 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -58,19 +58,6 @@ impl RPCProcessor { &self, msg: RPCMessage, ) -> Result, RPCError> { - // Ignore if disabled - { - let c = self.config.get(); - if c.capabilities - .disable - .contains(&CAP_WILL_VALIDATE_DIAL_INFO) - { - return Ok(NetworkResult::service_unavailable( - "validate dial info is disabled", - )); - } - } - let detail = match msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, RPCMessageHeaderDetail::SafetyRouted(_) | RPCMessageHeaderDetail::PrivateRouted(_) => { @@ -80,6 +67,18 @@ impl RPCProcessor { } }; + // Ignore if disabled + let routing_table = self.routing_table(); + { + if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) { + if !opi.signed_node_info().node_info().can_validate_dial_info() { + return Ok(NetworkResult::service_unavailable( + "validate dial info is not available", + )); + } + } + } + // Get the statement let (_, _, _, kind) = msg.operation.destructure(); let (dial_info, receipt, redirect) = match kind { @@ -96,7 +95,6 @@ impl RPCProcessor { // We filter on the -outgoing- protocol capability status not the node's dial info // Use the address type though, to ensure we reach an ipv6 capable node if this is // an ipv6 address - let routing_table = self.routing_table(); let sender_node_id = TypedKey::new( detail.envelope.get_crypto_kind(), detail.envelope.get_sender_id(), @@ -117,11 +115,9 @@ impl RPCProcessor { move |rti: &RoutingTableInner, v: Option>| { let entry = v.unwrap(); entry.with(rti, move |_rti, e| { - if let Some(status) = &e.node_status(routing_domain) { - status.has_capability(CAP_WILL_VALIDATE_DIAL_INFO) - } else { - true - } + e.node_info(routing_domain) + .map(|ni| ni.can_validate_dial_info()) + .unwrap_or(false) }) }, ) as RoutingTableEntryFilter; diff --git a/veilid-core/src/rpc_processor/rpc_value_changed.rs b/veilid-core/src/rpc_processor/rpc_value_changed.rs index 72f599e5..94a0bfb4 100644 --- a/veilid-core/src/rpc_processor/rpc_value_changed.rs +++ b/veilid-core/src/rpc_processor/rpc_value_changed.rs @@ -7,12 +7,12 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + let routing_table = self.routing_table(); { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_DHT) { - return Ok(NetworkResult::service_unavailable( - "value changed is disabled", - )); + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_dht() { + return Ok(NetworkResult::service_unavailable("dht is not available")); + } } } Err(RPCError::unimplemented("process_value_changed")) diff --git a/veilid-core/src/rpc_processor/rpc_watch_value.rs b/veilid-core/src/rpc_processor/rpc_watch_value.rs index 5640bc3b..53179744 100644 --- a/veilid-core/src/rpc_processor/rpc_watch_value.rs +++ b/veilid-core/src/rpc_processor/rpc_watch_value.rs @@ -7,15 +7,14 @@ impl RPCProcessor { msg: RPCMessage, ) -> Result, RPCError> { // Ignore if disabled + let routing_table = self.routing_table(); { - let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_DHT) { - return Ok(NetworkResult::service_unavailable( - "watch value is disabled", - )); + if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { + if !opi.signed_node_info().node_info().can_dht() { + return Ok(NetworkResult::service_unavailable("dht is not available")); + } } } - Err(RPCError::unimplemented("process_watch_value_q")) } } diff --git a/veilid-python/veilid/types.py b/veilid-python/veilid/types.py index 7c5a8bb1..5d7bc6a0 100644 --- a/veilid-python/veilid/types.py +++ b/veilid-python/veilid/types.py @@ -60,6 +60,7 @@ class Capability(StrEnum): CAP_WILL_VALIDATE_DIAL_INFO = "DIAL" CAP_WILL_DHT = "DHTV" CAP_WILL_APPMESSAGE = "APPM" + CAP_WILL_BLOCKSTORE = "BLOC" class Stability(StrEnum): From 907075411d05ceac98778b2a34e6dfccc9be6484 Mon Sep 17 00:00:00 2001 From: John Smith Date: Tue, 4 Jul 2023 12:35:48 -0400 Subject: [PATCH 6/6] protocol level capabilities --- veilid-core/proto/veilid.capnp | 3 +- veilid-core/src/routing_table/bucket_entry.rs | 7 ++ veilid-core/src/routing_table/find_peers.rs | 76 +++++++++----- veilid-core/src/routing_table/mod.rs | 2 +- .../route_spec_store/route_spec_store.rs | 2 +- .../tasks/private_route_management.rs | 2 +- .../routing_table/tasks/relay_management.rs | 40 ++++---- .../src/routing_table/types/node_info.rs | 99 +++++++------------ .../coders/operations/operation_find_node.rs | 45 ++++++++- veilid-core/src/rpc_processor/mod.rs | 9 +- veilid-core/src/rpc_processor/rpc_app_call.rs | 6 +- .../src/rpc_processor/rpc_app_message.rs | 6 +- .../src/rpc_processor/rpc_cancel_tunnel.rs | 6 +- .../src/rpc_processor/rpc_complete_tunnel.rs | 6 +- .../src/rpc_processor/rpc_find_node.rs | 17 +++- .../src/rpc_processor/rpc_get_value.rs | 4 +- veilid-core/src/rpc_processor/rpc_route.rs | 2 +- .../src/rpc_processor/rpc_set_value.rs | 4 +- veilid-core/src/rpc_processor/rpc_signal.rs | 2 +- .../src/rpc_processor/rpc_start_tunnel.rs | 6 +- .../rpc_processor/rpc_validate_dial_info.rs | 7 +- .../src/rpc_processor/rpc_value_changed.rs | 2 +- .../src/rpc_processor/rpc_watch_value.rs | 2 +- veilid-python/veilid/types.py | 4 +- 24 files changed, 219 insertions(+), 140 deletions(-) diff --git a/veilid-core/proto/veilid.capnp b/veilid-core/proto/veilid.capnp index 9aef1568..8fabe0f7 100644 --- a/veilid-core/proto/veilid.capnp +++ b/veilid-core/proto/veilid.capnp @@ -273,7 +273,8 @@ struct OperationReturnReceipt @0xeb0fb5b5a9160eeb { } struct OperationFindNodeQ @0xfdef788fe9623bcd { - nodeId @0 :TypedKey; # node id to locate + nodeId @0 :TypedKey; # node id to locate + capabilities @1 :List(Capability); # required capabilities returned peers must have } struct OperationFindNodeA @0xa84cf2fb40c77089 { diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index a4d197d2..f0f6df94 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -170,6 +170,13 @@ impl BucketEntryInner { common_crypto_kinds(&self.validated_node_ids.kinds(), other) } + /// Capability check + pub fn has_capabilities(&self, routing_domain: RoutingDomain, capabilities: &[Capability]) -> bool { + let Some(ni) = self.node_info(routing_domain) else { + return false; + }; + ni.has_capabilities(capabilities) + } // Less is faster pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering { diff --git a/veilid-core/src/routing_table/find_peers.rs b/veilid-core/src/routing_table/find_peers.rs index e7f289ea..52787196 100644 --- a/veilid-core/src/routing_table/find_peers.rs +++ b/veilid-core/src/routing_table/find_peers.rs @@ -2,7 +2,11 @@ use super::*; impl RoutingTable { /// Utility to find all closest nodes to a particular key, including possibly our own node and nodes further away from the key than our own, returning their peer info - pub fn find_all_closest_peers(&self, key: TypedKey) -> NetworkResult> { + pub fn find_all_closest_peers( + &self, + key: TypedKey, + capabilities: &[Capability], + ) -> NetworkResult> { let Some(own_peer_info) = self.get_own_peer_info(RoutingDomain::PublicInternet) else { // Our own node info is not yet available, drop this request. return NetworkResult::service_unavailable("Not finding closest peers because our peer info is not yet available"); @@ -12,11 +16,27 @@ impl RoutingTable { let filter = Box::new( move |rti: &RoutingTableInner, opt_entry: Option>| { // Ensure only things that are valid/signed in the PublicInternet domain are returned - rti.filter_has_valid_signed_node_info( + if !rti.filter_has_valid_signed_node_info( RoutingDomain::PublicInternet, true, - opt_entry, - ) + opt_entry.clone(), + ) { + return false; + } + // Ensure capabilities are met + match opt_entry { + Some(entry) => entry.with(rti, |_rti, e| { + e.has_capabilities(RoutingDomain::PublicInternet, capabilities) + }), + None => rti + .get_own_peer_info(RoutingDomain::PublicInternet) + .map(|pi| { + pi.signed_node_info() + .node_info() + .has_capabilities(capabilities) + }) + .unwrap_or(false), + } }, ) as RoutingTableEntryFilter; let filters = VecDeque::from([filter]); @@ -40,7 +60,12 @@ impl RoutingTable { } /// Utility to find nodes that are closer to a key than our own node, returning their peer info - pub fn find_peers_closer_to_key(&self, key: TypedKey) -> NetworkResult> { + /// Can filter based on a particular set of capabiltiies + pub fn find_peers_closer_to_key( + &self, + key: TypedKey, + required_capabilities: Vec, + ) -> NetworkResult> { // add node information for the requesting node to our routing table let crypto_kind = key.kind; let own_node_id = self.node_id(crypto_kind); @@ -59,24 +84,29 @@ impl RoutingTable { let Some(entry) = opt_entry else { return false; }; - // Ensure only things that are valid/signed in the PublicInternet domain are returned - if !rti.filter_has_valid_signed_node_info( - RoutingDomain::PublicInternet, - true, - Some(entry.clone()), - ) { - return false; - } - // Ensure things further from the key than our own node are not included - let Some(entry_node_id) = entry.with(rti, |_rti, e| e.node_ids().get(crypto_kind)) else { - return false; - }; - let entry_distance = vcrypto.distance(&entry_node_id.value, &key.value); - if entry_distance >= own_distance { - return false; - } - - true + // Ensure only things that have a minimum set of capabilities are returned + entry.with(rti, |rti, e| { + if !e.has_capabilities(RoutingDomain::PublicInternet, &required_capabilities) { + return false; + } + // Ensure only things that are valid/signed in the PublicInternet domain are returned + if !rti.filter_has_valid_signed_node_info( + RoutingDomain::PublicInternet, + true, + Some(entry.clone()), + ) { + return false; + } + // Ensure things further from the key than our own node are not included + let Some(entry_node_id) = e.node_ids().get(crypto_kind) else { + return false; + }; + let entry_distance = vcrypto.distance(&entry_node_id.value, &key.value); + if entry_distance >= own_distance { + return false; + } + true + }) }, ) as RoutingTableEntryFilter; let filters = VecDeque::from([filter]); diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 618faa77..2d6c29d9 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -1072,7 +1072,7 @@ impl RoutingTable { let res = network_result_try!( rpc_processor .clone() - .rpc_call_find_node(Destination::direct(node_ref), node_id) + .rpc_call_find_node(Destination::direct(node_ref), node_id, vec![]) .await? ); diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs index 0ebeb351..f1ae27c4 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs @@ -258,7 +258,7 @@ impl RouteSpecStore { // Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route entry.with_inner(|e| { e.signed_node_info(RoutingDomain::PublicInternet).map(|sni| - sni.has_sequencing_matched_dial_info(sequencing) && sni.node_info().can_route() + sni.has_sequencing_matched_dial_info(sequencing) && sni.node_info().has_capability(CAP_ROUTE) ).unwrap_or(false) }) }, diff --git a/veilid-core/src/routing_table/tasks/private_route_management.rs b/veilid-core/src/routing_table/tasks/private_route_management.rs index 1abdd51b..715c25aa 100644 --- a/veilid-core/src/routing_table/tasks/private_route_management.rs +++ b/veilid-core/src/routing_table/tasks/private_route_management.rs @@ -9,7 +9,7 @@ const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2; impl RoutingTable { fn get_background_safety_route_count(&self) -> usize { let c = self.config.get(); - if c.capabilities.disable.contains(&CAP_WILL_ROUTE) { + if c.capabilities.disable.contains(&CAP_ROUTE) { 0 } else { BACKGROUND_SAFETY_ROUTE_COUNT diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index 543fdffd..960c5bc9 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -100,6 +100,11 @@ impl RoutingTable { let can_serve_as_relay = e .node_info(RoutingDomain::PublicInternet) .map(|n| { + if !(n.has_capability(CAP_RELAY) && n.is_signal_capable()) { + // Needs to be able to signal and relay + return false; + } + let dids = n.all_filtered_dial_info_details(DialInfoDetail::NO_SORT, |did| { did.matches_filter(&outbound_dif) }); @@ -145,26 +150,23 @@ impl RoutingTable { inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| { let entry2 = entry.clone(); entry.with(rti, |rti, e| { - // Ensure we have the node's status - if let Some(node_info) = e.node_info(routing_domain) { - // Ensure the node will relay - if node_info.can_inbound_relay() { - // Compare against previous candidate - if let Some(best_inbound_relay) = best_inbound_relay.as_mut() { - // Less is faster - let better = best_inbound_relay.with(rti, |_rti, best| { - // choose low latency stability for relays - BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best) - == std::cmp::Ordering::Less - }); - // Now apply filter function and see if this node should be included - if better && relay_node_filter(e) { - *best_inbound_relay = entry2; - } - } else if relay_node_filter(e) { - // Always store the first candidate - best_inbound_relay = Some(entry2); + // Filter this node + if relay_node_filter(e) { + // Compare against previous candidate + if let Some(best_inbound_relay) = best_inbound_relay.as_mut() { + // Less is faster + let better = best_inbound_relay.with(rti, |_rti, best| { + // choose low latency stability for relays + BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best) + == std::cmp::Ordering::Less + }); + // Now apply filter function and see if this node should be included + if better { + *best_inbound_relay = entry2; } + } else { + // Always store the first candidate + best_inbound_relay = Some(entry2); } } }); diff --git a/veilid-core/src/routing_table/types/node_info.rs b/veilid-core/src/routing_table/types/node_info.rs index 067d2fed..96c40637 100644 --- a/veilid-core/src/routing_table/types/node_info.rs +++ b/veilid-core/src/routing_table/types/node_info.rs @@ -1,16 +1,16 @@ use super::*; pub type Capability = FourCC; -pub const CAP_WILL_ROUTE: Capability = FourCC(*b"ROUT"); +pub const CAP_ROUTE: Capability = FourCC(*b"ROUT"); #[cfg(feature = "unstable-tunnels")] -pub const CAP_WILL_TUNNEL: Capability = FourCC(*b"TUNL"); -pub const CAP_WILL_SIGNAL: Capability = FourCC(*b"SGNL"); -pub const CAP_WILL_RELAY: Capability = FourCC(*b"RLAY"); -pub const CAP_WILL_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL"); -pub const CAP_WILL_DHT: Capability = FourCC(*b"DHTV"); -pub const CAP_WILL_APPMESSAGE: Capability = FourCC(*b"APPM"); +pub const CAP_TUNNEL: Capability = FourCC(*b"TUNL"); +pub const CAP_SIGNAL: Capability = FourCC(*b"SGNL"); +pub const CAP_RELAY: Capability = FourCC(*b"RLAY"); +pub const CAP_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL"); +pub const CAP_DHT: Capability = FourCC(*b"DHTV"); +pub const CAP_APPMESSAGE: Capability = FourCC(*b"APPM"); #[cfg(feature = "unstable-blockstore")] -pub const CAP_WILL_BLOCKSTORE: Capability = FourCC(*b"BLOC"); +pub const CAP_BLOCKSTORE: Capability = FourCC(*b"BLOC"); cfg_if! { if #[cfg(all(feature = "unstable-blockstore", feature="unstable-tunnels"))] { @@ -22,16 +22,16 @@ cfg_if! { } } pub const PUBLIC_INTERNET_CAPABILITIES: [Capability; PUBLIC_INTERNET_CAPABILITIES_LEN] = [ - CAP_WILL_ROUTE, + CAP_ROUTE, #[cfg(feature = "unstable-tunnels")] - CAP_WILL_TUNNEL, - CAP_WILL_SIGNAL, - CAP_WILL_RELAY, - CAP_WILL_VALIDATE_DIAL_INFO, - CAP_WILL_DHT, - CAP_WILL_APPMESSAGE, + CAP_TUNNEL, + CAP_SIGNAL, + CAP_RELAY, + CAP_VALIDATE_DIAL_INFO, + CAP_DHT, + CAP_APPMESSAGE, #[cfg(feature = "unstable-blockstore")] - CAP_WILL_BLOCKSTORE, + CAP_BLOCKSTORE, ]; #[cfg(feature = "unstable-blockstore")] @@ -40,11 +40,11 @@ const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 4; const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 3; pub const LOCAL_NETWORK_CAPABILITIES: [Capability; LOCAL_NETWORK_CAPABILITIES_LEN] = [ - CAP_WILL_RELAY, - CAP_WILL_DHT, - CAP_WILL_APPMESSAGE, + CAP_RELAY, + CAP_DHT, + CAP_APPMESSAGE, #[cfg(feature = "unstable-blockstore")] - CAP_WILL_BLOCKSTORE, + CAP_BLOCKSTORE, ]; pub const MAX_CAPABILITIES: usize = 64; @@ -199,14 +199,24 @@ impl NodeInfo { false } - fn has_capability(&self, cap: Capability) -> bool { + pub fn has_capability(&self, cap: Capability) -> bool { self.capabilities.contains(&cap) } + pub fn has_capabilities(&self, capabilities: &[Capability]) -> bool { + for cap in capabilities { + if !self.has_capability(*cap) { + return false; + } + } + true + } /// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself. - pub fn can_signal(&self) -> bool { + /// Also used to determine if nodes are capable of validation of dial info, as that operation + /// has the same requirements, inbound capability and a dial info that requires no assistance + pub fn is_signal_capable(&self) -> bool { // Has capability? - if !self.has_capability(CAP_WILL_SIGNAL) { + if !self.has_capability(CAP_SIGNAL) { return false; } @@ -222,47 +232,4 @@ impl NodeInfo { } true } - - /// Can this node relay be an inbound relay? - pub fn can_inbound_relay(&self) -> bool { - // Has capability? - if !self.has_capability(CAP_WILL_RELAY) { - return false; - } - - // For now this is the same - self.can_signal() - } - - /// Is this node capable of validating dial info - pub fn can_validate_dial_info(&self) -> bool { - // Has capability? - if !self.has_capability(CAP_WILL_VALIDATE_DIAL_INFO) { - return false; - } - // For now this is the same - self.can_signal() - } - /// Is this node capable of private routing - pub fn can_route(&self) -> bool { - self.has_capability(CAP_WILL_ROUTE) - } - /// Is this node capable of dht operations - pub fn can_dht(&self) -> bool { - self.has_capability(CAP_WILL_DHT) - } - /// Is this node capable of app_message and app_call - pub fn can_appmessage(&self) -> bool { - self.has_capability(CAP_WILL_APPMESSAGE) - } - /// Is this node capable of tunneling - #[cfg(feature = "unstable-tunnels")] - pub fn can_tunnel(&self) -> bool { - self.has_capability(CAP_WILL_TUNNEL) - } - /// Is this node capable of block storage - #[cfg(feature = "unstable-blockstore")] - pub fn can_blockstore(&self) -> bool { - self.has_capability(CAP_WILL_BLOCKSTORE) - } } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs index 607dacbc..10848648 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs @@ -5,11 +5,15 @@ const MAX_FIND_NODE_A_PEERS_LEN: usize = 20; #[derive(Debug, Clone)] pub struct RPCOperationFindNodeQ { node_id: TypedKey, + capabilities: Vec, } impl RPCOperationFindNodeQ { - pub fn new(node_id: TypedKey) -> Self { - Self { node_id } + pub fn new(node_id: TypedKey, capabilities: Vec) -> Self { + Self { + node_id, + capabilities, + } } pub fn validate(&mut self, _validate_context: &RPCValidateContext) -> Result<(), RPCError> { Ok(()) @@ -18,15 +22,33 @@ impl RPCOperationFindNodeQ { // pub fn node_id(&self) -> &TypedKey { // &self.node_id // } + // pub fn capabilities(&self) -> &[Capability] { + // &self.capabilities + // } - pub fn destructure(self) -> TypedKey { - self.node_id + pub fn destructure(self) -> (TypedKey, Vec) { + (self.node_id, self.capabilities) } pub fn decode(reader: &veilid_capnp::operation_find_node_q::Reader) -> Result { let ni_reader = reader.get_node_id().map_err(RPCError::protocol)?; let node_id = decode_typed_key(&ni_reader)?; - Ok(Self { node_id }) + let cap_reader = reader + .reborrow() + .get_capabilities() + .map_err(RPCError::protocol)?; + if cap_reader.len() as usize > MAX_CAPABILITIES { + return Err(RPCError::protocol("too many capabilities")); + } + let capabilities = cap_reader + .as_slice() + .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) + .unwrap_or_default(); + + Ok(Self { + node_id, + capabilities, + }) } pub fn encode( &self, @@ -34,6 +56,19 @@ impl RPCOperationFindNodeQ { ) -> Result<(), RPCError> { let mut ni_builder = builder.reborrow().init_node_id(); encode_typed_key(&self.node_id, &mut ni_builder); + + let mut cap_builder = builder + .reborrow() + .init_capabilities(self.capabilities.len() as u32); + if let Some(s) = cap_builder.as_slice() { + let capvec: Vec = self + .capabilities + .iter() + .map(|x| u32::from_be_bytes(x.0)) + .collect(); + + s.clone_from_slice(&capvec); + } Ok(()) } } diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index f9c9a504..06931555 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -442,9 +442,11 @@ impl RPCProcessor { &self, routing_domain: RoutingDomain, signed_node_info: &SignedNodeInfo, + capabilities: &[Capability], ) -> bool { let routing_table = self.routing_table(); routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info) + && signed_node_info.node_info().has_capabilities(capabilities) } ////////////////////////////////////////////////////////////////////// @@ -470,6 +472,7 @@ impl RPCProcessor { .rpc_call_find_node( Destination::direct(next_node).with_safety(safety_selection), node_id, + vec![], ) .await { @@ -1474,7 +1477,11 @@ impl RPCProcessor { // Ensure the sender peer info is for the actual sender specified in the envelope // Sender PeerInfo was specified, update our routing table with it - if !self.verify_node_info(routing_domain, sender_peer_info.signed_node_info()) { + if !self.verify_node_info( + routing_domain, + sender_peer_info.signed_node_info(), + &[], + ) { return Ok(NetworkResult::invalid_message( "sender peerinfo has invalid peer scope", )); diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index f73762fb..4b54452a 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -57,7 +57,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_appmessage() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_APPMESSAGE) + { return Ok(NetworkResult::service_unavailable( "app call is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_app_message.rs b/veilid-core/src/rpc_processor/rpc_app_message.rs index 891d8a2e..56800099 100644 --- a/veilid-core/src/rpc_processor/rpc_app_message.rs +++ b/veilid-core/src/rpc_processor/rpc_app_message.rs @@ -28,7 +28,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_appmessage() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_APPMESSAGE) + { return Ok(NetworkResult::service_unavailable( "app message is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs index 6001eb49..2e761488 100644 --- a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs @@ -12,7 +12,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_tunnel() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_TUNNEL) + { return Ok(NetworkResult::service_unavailable( "tunnel is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs index 1b442894..c13088c1 100644 --- a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs @@ -12,7 +12,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_tunnel() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_TUNNEL) + { return Ok(NetworkResult::service_unavailable( "tunnel is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_find_node.rs b/veilid-core/src/rpc_processor/rpc_find_node.rs index afd05e18..6bbcf5fa 100644 --- a/veilid-core/src/rpc_processor/rpc_find_node.rs +++ b/veilid-core/src/rpc_processor/rpc_find_node.rs @@ -15,6 +15,7 @@ impl RPCProcessor { self, dest: Destination, node_id: TypedKey, + capabilities: Vec, ) -> Result>>, RPCError> { // Ensure destination never has a private route if matches!( @@ -29,7 +30,8 @@ impl RPCProcessor { )); } - let find_node_q_detail = RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ::new(node_id)); + let find_node_q_detail = + RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ::new(node_id, capabilities.clone())); let find_node_q = RPCQuestion::new( network_result_try!(self.get_destination_respond_to(&dest)?), find_node_q_detail, @@ -60,9 +62,13 @@ impl RPCProcessor { let peers = find_node_a.destructure(); for peer_info in &peers { - if !self.verify_node_info(RoutingDomain::PublicInternet, peer_info.signed_node_info()) { + if !self.verify_node_info( + RoutingDomain::PublicInternet, + peer_info.signed_node_info(), + &capabilities, + ) { return Ok(NetworkResult::invalid_message( - "find_node response has invalid peer scope", + "find_node response does not meet peer criteria", )); } } @@ -94,11 +100,12 @@ impl RPCProcessor { }, _ => panic!("not a question"), }; - let node_id = find_node_q.destructure(); + let (node_id, capabilities) = find_node_q.destructure(); // Get a chunk of the routing table near the requested node id let routing_table = self.routing_table(); - let closest_nodes = network_result_try!(routing_table.find_all_closest_peers(node_id)); + let closest_nodes = + network_result_try!(routing_table.find_all_closest_peers(node_id, &capabilities)); // Make FindNode answer let find_node_a = RPCOperationFindNodeA::new(closest_nodes)?; diff --git a/veilid-core/src/rpc_processor/rpc_get_value.rs b/veilid-core/src/rpc_processor/rpc_get_value.rs index bab14d2d..085bb0bc 100644 --- a/veilid-core/src/rpc_processor/rpc_get_value.rs +++ b/veilid-core/src/rpc_processor/rpc_get_value.rs @@ -177,7 +177,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable( "dht is not available", )); @@ -199,7 +199,7 @@ impl RPCProcessor { // Get the nodes that we know about that are closer to the the key than our own node let routing_table = self.routing_table(); - let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key)); + let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key, vec![CAP_DHT])); let debug_string = format!( "IN <=== GetValueQ({} #{}{}) <== {}", diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index ec2337fd..0c1b14be 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -369,7 +369,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_route() { + if !opi.signed_node_info().node_info().has_capability(CAP_ROUTE) { return Ok(NetworkResult::service_unavailable( "route is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_set_value.rs b/veilid-core/src/rpc_processor/rpc_set_value.rs index b83c03c1..9973b5ef 100644 --- a/veilid-core/src/rpc_processor/rpc_set_value.rs +++ b/veilid-core/src/rpc_processor/rpc_set_value.rs @@ -179,7 +179,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable( "dht is not available", )); @@ -211,7 +211,7 @@ impl RPCProcessor { // Get the nodes that we know about that are closer to the the key than our own node let routing_table = self.routing_table(); - let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key)); + let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key, vec![CAP_DHT])); let debug_string = format!( "IN <=== SetValueQ({} #{} len={} seq={} writer={}{}) <== {}", diff --git a/veilid-core/src/rpc_processor/rpc_signal.rs b/veilid-core/src/rpc_processor/rpc_signal.rs index 542649ac..4e32952a 100644 --- a/veilid-core/src/rpc_processor/rpc_signal.rs +++ b/veilid-core/src/rpc_processor/rpc_signal.rs @@ -41,7 +41,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_signal() { + if !opi.signed_node_info().node_info().is_signal_capable() { return Ok(NetworkResult::service_unavailable( "signal is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs index d4608cbe..972ba67b 100644 --- a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs @@ -12,7 +12,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_tunnel() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_TUNNEL) + { return Ok(NetworkResult::service_unavailable( "tunnel is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs index 9a4a9f64..16598b31 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -71,7 +71,8 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) { - if !opi.signed_node_info().node_info().can_validate_dial_info() { + let ni = opi.signed_node_info().node_info(); + if !ni.has_capability(CAP_VALIDATE_DIAL_INFO) || !ni.is_signal_capable() { return Ok(NetworkResult::service_unavailable( "validate dial info is not available", )); @@ -116,7 +117,9 @@ impl RPCProcessor { let entry = v.unwrap(); entry.with(rti, move |_rti, e| { e.node_info(routing_domain) - .map(|ni| ni.can_validate_dial_info()) + .map(|ni| { + ni.has_capability(CAP_VALIDATE_DIAL_INFO) && ni.is_signal_capable() + }) .unwrap_or(false) }) }, diff --git a/veilid-core/src/rpc_processor/rpc_value_changed.rs b/veilid-core/src/rpc_processor/rpc_value_changed.rs index 94a0bfb4..fd5b1910 100644 --- a/veilid-core/src/rpc_processor/rpc_value_changed.rs +++ b/veilid-core/src/rpc_processor/rpc_value_changed.rs @@ -10,7 +10,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable("dht is not available")); } } diff --git a/veilid-core/src/rpc_processor/rpc_watch_value.rs b/veilid-core/src/rpc_processor/rpc_watch_value.rs index 53179744..e6f28474 100644 --- a/veilid-core/src/rpc_processor/rpc_watch_value.rs +++ b/veilid-core/src/rpc_processor/rpc_watch_value.rs @@ -10,7 +10,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable("dht is not available")); } } diff --git a/veilid-python/veilid/types.py b/veilid-python/veilid/types.py index 5d7bc6a0..eef8cd1d 100644 --- a/veilid-python/veilid/types.py +++ b/veilid-python/veilid/types.py @@ -54,13 +54,13 @@ class CryptoKind(StrEnum): class Capability(StrEnum): CAP_WILL_ROUTE = "ROUT" - CAP_WILL_TUNNEL = "TUNL" + CAP_TUNNEL = "TUNL" CAP_WILL_SIGNAL = "SGNL" CAP_WILL_RELAY = "RLAY" CAP_WILL_VALIDATE_DIAL_INFO = "DIAL" CAP_WILL_DHT = "DHTV" CAP_WILL_APPMESSAGE = "APPM" - CAP_WILL_BLOCKSTORE = "BLOC" + CAP_BLOCKSTORE = "BLOC" class Stability(StrEnum):