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):