diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index 4bb5178a..83a9ed73 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -1021,7 +1021,7 @@ impl NetworkManager { // We expect the inbound noderef to be the same as the target noderef // if they aren't the same, we should error on this and figure out what then hell is up - if target_nr != inbound_nr { + if target_nr.node_id() != inbound_nr.node_id() { bail!("unexpected noderef mismatch on reverse connect"); } @@ -1122,7 +1122,7 @@ impl NetworkManager { // We expect the inbound noderef to be the same as the target noderef // if they aren't the same, we should error on this and figure out what then hell is up - if target_nr != inbound_nr { + if target_nr.node_id() != inbound_nr.node_id() { bail!( "unexpected noderef mismatch on hole punch {}, expected {}", inbound_nr, diff --git a/veilid-core/src/receipt_manager.rs b/veilid-core/src/receipt_manager.rs index 4ad56db9..02bf8a75 100644 --- a/veilid-core/src/receipt_manager.rs +++ b/veilid-core/src/receipt_manager.rs @@ -7,7 +7,7 @@ use routing_table::*; use stop_token::future::FutureExt; use xx::*; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug)] pub enum ReceiptEvent { ReturnedOutOfBand, ReturnedInBand { inbound_noderef: NodeRef }, diff --git a/veilid-core/src/routing_table/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store.rs index 9d6e6485..b0805baa 100644 --- a/veilid-core/src/routing_table/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store.rs @@ -2,18 +2,6 @@ use super::*; use crate::veilid_api::*; use serde::*; -/// Options for safety routes (sender privacy) -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub struct SafetySpec { - /// preferred safety route if it still exists - pub preferred_route: Option, - /// 0 = no safety route, just use node's node id, more hops is safer but slower - pub hop_count: usize, - /// prefer reliability over speed - pub stability: Stability, - /// prefer connection-oriented sequenced protocols - pub sequencing: Sequencing, -} /// Compiled route (safety route + private route) #[derive(Clone, Debug)] @@ -98,15 +86,6 @@ pub struct RouteSpecStore { cache: RouteSpecStoreCache, } -/// The choice of safety route including in compiled routes -#[derive(Debug, Clone)] -pub enum SafetySelection { - /// Don't use a safety route, only specify the sequencing preference - Unsafe(Sequencing), - /// Use a safety route and parameters specified by a SafetySpec - Safe(SafetySpec), -} - fn route_hops_to_hop_cache(hops: &[DHTKey]) -> Vec { let mut cache: Vec = Vec::with_capacity(hops.len() * DHT_KEY_LENGTH); for hop in hops { diff --git a/veilid-core/src/routing_table/routing_domains.rs b/veilid-core/src/routing_table/routing_domains.rs index 0e164b34..7a7cc0e8 100644 --- a/veilid-core/src/routing_table/routing_domains.rs +++ b/veilid-core/src/routing_table/routing_domains.rs @@ -204,21 +204,34 @@ fn first_filtered_dial_info_detail( from_node: &NodeInfo, to_node: &NodeInfo, dial_info_filter: &DialInfoFilter, - reliable: bool, xxx continue here + sequencing: Sequencing, ) -> Option { - let direct_dial_info_filter = dial_info_filter.clone().filtered( + let dial_info_filter = dial_info_filter.clone().filtered( &DialInfoFilter::all() .with_address_type_set(from_node.address_types) .with_protocol_type_set(from_node.outbound_protocols), ); // Get first filtered dialinfo - let sort = if reliable { - Some(DialInfoDetail::ordered_sequencing_sort) - } else { - None + let (sort, dial_info_filter) = match sequencing { + Sequencing::NoPreference => (None, dial_info_filter), + Sequencing::PreferOrdered => ( + Some(DialInfoDetail::ordered_sequencing_sort), + dial_info_filter, + ), + Sequencing::EnsureOrdered => ( + Some(DialInfoDetail::ordered_sequencing_sort), + dial_info_filter.filtered( + &DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()), + ), + ), }; - let direct_filter = |did: &DialInfoDetail| did.matches_filter(&direct_dial_info_filter); + // If the filter is dead then we won't be able to connect + if dial_info_filter.is_dead() { + return None; + } + + let direct_filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter); // Get the best match dial info for node B if we have it to_node.first_filtered_dial_info_detail(sort, direct_filter) @@ -242,11 +255,11 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { node_b_id: &DHTKey, node_b: &NodeInfo, dial_info_filter: DialInfoFilter, - reliable: bool, + sequencing: Sequencing, ) -> ContactMethod { // Get the best match dial info for node B if we have it if let Some(target_did) = - first_filtered_dial_info_detail(node_a, node_b, &dial_info_filter, reliable) + first_filtered_dial_info_detail(node_a, node_b, &dial_info_filter, sequencing) { // Do we need to signal before going inbound? if !target_did.class.requires_signal() { @@ -267,7 +280,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { node_a, &inbound_relay.signed_node_info.node_info, &dial_info_filter, - reliable, + sequencing, ) .is_some() { @@ -280,7 +293,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { node_b, node_a, &dial_info_filter, - reliable, + sequencing, ) { // Ensure we aren't on the same public IP address (no hairpin nat) if reverse_did.dial_info.to_ip_addr() @@ -306,14 +319,14 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { node_a, node_b, &udp_dial_info_filter, - reliable, + sequencing, ) { // Does node A have a direct udp dialinfo that node B can reach? if let Some(reverse_udp_did) = first_filtered_dial_info_detail( node_b, node_a, &udp_dial_info_filter, - reliable, + sequencing, ) { // Ensure we aren't on the same public IP address (no hairpin nat) if reverse_udp_did.dial_info.to_ip_addr() @@ -341,7 +354,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { node_a, &inbound_relay.signed_node_info.node_info, &dial_info_filter, - reliable, + sequencing, ) .is_some() { @@ -412,7 +425,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { _node_b_id: &DHTKey, node_b: &NodeInfo, dial_info_filter: DialInfoFilter, - reliable: bool, + sequencing: Sequencing, ) -> ContactMethod { // Scope the filter down to protocols node A can do outbound let dial_info_filter = dial_info_filter.filtered( @@ -421,17 +434,25 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { .with_protocol_type_set(node_a.outbound_protocols), ); + // Get first filtered dialinfo + let (sort, dial_info_filter) = match sequencing { + Sequencing::NoPreference => (None, dial_info_filter), + Sequencing::PreferOrdered => ( + Some(DialInfoDetail::ordered_sequencing_sort), + dial_info_filter, + ), + Sequencing::EnsureOrdered => ( + Some(DialInfoDetail::ordered_sequencing_sort), + dial_info_filter.filtered( + &DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()), + ), + ), + }; // If the filter is dead then we won't be able to connect if dial_info_filter.is_dead() { return ContactMethod::Unreachable; } - // Get first filtered dialinfo - let sort = if reliable { - Some(DialInfoDetail::ordered_sequencing_sort) - } else { - None - }; let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter); let opt_target_did = node_b.first_filtered_dial_info_detail(sort, filter); diff --git a/veilid-core/src/rpc_processor/destination.rs b/veilid-core/src/rpc_processor/destination.rs index a611275b..d68ba5f4 100644 --- a/veilid-core/src/rpc_processor/destination.rs +++ b/veilid-core/src/rpc_processor/destination.rs @@ -8,7 +8,7 @@ pub enum Destination { /// The node to send to target: NodeRef, /// Require safety route or not - safety_spec: Option, + safety_selection: SafetySelection, }, /// Send to node for relay purposes Relay { @@ -17,16 +17,14 @@ pub enum Destination { /// The final destination the relay should send to target: DHTKey, /// Require safety route or not - safety_spec: Option, + safety_selection: SafetySelection, }, /// Send to private route (privateroute) PrivateRoute { /// A private route to send to private_route: PrivateRoute, /// Require safety route or not - safety_spec: Option, - /// Prefer reliability or not - reliable: bool, + safety_selection: SafetySelection, }, } @@ -34,70 +32,66 @@ impl Destination { pub fn direct(target: NodeRef) -> Self { Self::Direct { target, - safety_spec: None, + safety_selection: SafetySelection::Unsafe(target.sequencing()), } } pub fn relay(relay: NodeRef, target: DHTKey) -> Self { Self::Relay { relay, target, - safety_spec: None, + safety_selection: SafetySelection::Unsafe(relay.sequencing()), } } - pub fn private_route(private_route: PrivateRoute, reliable: bool) -> Self { + pub fn private_route(private_route: PrivateRoute, safety_selection: SafetySelection) -> Self { Self::PrivateRoute { private_route, - safety_spec: None, - reliable, + safety_selection, } } - pub fn with_safety(self, safety_spec: SafetySpec) -> Self { + pub fn with_safety(self, safety_selection: SafetySelection) -> Self { match self { Destination::Direct { target, - safety_spec: _, + safety_selection: _, } => Self::Direct { target, - safety_spec: Some(safety_spec), + safety_selection, }, Destination::Relay { relay, target, - safety_spec: _, + safety_selection: _, } => Self::Relay { relay, target, - safety_spec: Some(safety_spec), + safety_selection, }, Destination::PrivateRoute { private_route, - safety_spec: _, - reliable, + safety_selection: _, } => Self::PrivateRoute { private_route, - safety_spec: Some(safety_spec), - reliable, + safety_selection, }, } } - pub fn get_safety_spec(&self) -> &Option { + pub fn get_safety_selection(&self) -> &SafetySelection { match self { Destination::Direct { target: _, - safety_spec, - } => safety_spec, + safety_selection, + } => safety_selection, Destination::Relay { relay: _, target: _, - safety_spec, - } => safety_spec, + safety_selection, + } => safety_selection, Destination::PrivateRoute { private_route: _, - safety_spec, - reliable: _, - } => safety_spec, + safety_selection, + } => safety_selection, } } } @@ -107,30 +101,40 @@ impl fmt::Display for Destination { match self { Destination::Direct { target, - safety_spec, + safety_selection, } => { - let sr = if safety_spec.is_some() { "+SR" } else { "" }; + let sr = if matches!(safety_selection, SafetySelection::Safe(_)) { + "+SR" + } else { + "" + }; write!(f, "{}{}", target, sr) } Destination::Relay { relay, target, - safety_spec, + safety_selection, } => { - let sr = if safety_spec.is_some() { "+SR" } else { "" }; + let sr = if matches!(safety_selection, SafetySelection::Safe(_)) { + "+SR" + } else { + "" + }; write!(f, "{}@{}{}", target.encode(), relay, sr) } Destination::PrivateRoute { private_route, - safety_spec, - reliable, + safety_selection, } => { - let sr = if safety_spec.is_some() { "+SR" } else { "" }; - let rl = if *reliable { "+RL" } else { "" }; + let sr = if matches!(safety_selection, SafetySelection::Safe(_)) { + "+SR" + } else { + "" + }; - write!(f, "{}{}{}", private_route, sr, rl) + write!(f, "{}{}", private_route, sr) } } } diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index 709ab9b1..7064b256 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -400,9 +400,8 @@ impl RPCProcessor { // Wrap an operation with a private route inside a safety route pub(super) fn wrap_with_route( &self, - safety_spec: Option, + safety_selection: SafetySelection, private_route: PrivateRoute, - reliable: bool, message_data: Vec, ) -> Result, RPCError> { let routing_table = self.routing_table(); @@ -412,7 +411,7 @@ impl RPCProcessor { let compiled_route: CompiledRoute = match self.routing_table().with_route_spec_store_mut(|rss, rti| { // Compile the safety route with the private route - rss.compile_safety_route(rti, routing_table, safety_spec, private_route, reliable) + rss.compile_safety_route(rti, routing_table, safety_selection, private_route) })? { Some(cr) => cr, None => { @@ -456,7 +455,6 @@ impl RPCProcessor { let out_node_id = compiled_route.first_hop.node_id(); let out_hop_count = (1 + sr_hop_count + pr_hop_count) as usize; - let out = RenderedOperation { message: out_message, node_id: out_node_id, @@ -491,12 +489,12 @@ impl RPCProcessor { match dest { Destination::Direct { target: ref node_ref, - safety_spec, + safety_selection, } | Destination::Relay { relay: ref node_ref, target: _, - safety_spec, + safety_selection, } => { // Send to a node without a private route // -------------------------------------- @@ -505,7 +503,7 @@ impl RPCProcessor { let (node_ref, node_id) = if let Destination::Relay { relay: _, target: ref dht_key, - safety_spec: _, + safety_selection: _, } = dest { (node_ref.clone(), dht_key.clone()) @@ -515,8 +513,13 @@ impl RPCProcessor { }; // Handle the existence of safety route - match safety_spec { - None => { + match safety_selection { + SafetySelection::Unsafe(sequencing) => { + // Apply safety selection sequencing requirement if it is more strict than the node_ref's sequencing requirement + if sequencing > node_ref.sequencing() { + node_ref.set_sequencing(sequencing) + } + // If no safety route is being used, and we're not sending to a private // route, we can use a direct envelope instead of routing out = NetworkResult::value(RenderedOperation { @@ -526,25 +529,24 @@ impl RPCProcessor { hop_count: 1, }); } - Some(safety_spec) => { + SafetySelection::Safe(_) => { // No private route was specified for the request // but we are using a safety route, so we must create an empty private route let private_route = PrivateRoute::new_stub(node_id); // Wrap with safety route - out = self.wrap_with_route(Some(safety_spec), private_route, message)?; + out = self.wrap_with_route(safety_selection, private_route, message)?; } }; } Destination::PrivateRoute { private_route, - safety_spec, - reliable, xxxx does this need to be here? what about None safety spec, reliable is in there, does it need to not be? or something? + safety_selection, } => { // Send to private route // --------------------- // Reply with 'route' operation - out = self.wrap_with_route(safety_spec, private_route, reliable, message)?; + out = self.wrap_with_route(safety_selection, private_route, message)?; } } @@ -559,8 +561,11 @@ impl RPCProcessor { // Don't do this if the sender is to remain private // Otherwise we would be attaching the original sender's identity to the final destination, // thus defeating the purpose of the safety route entirely :P - if dest.get_safety_spec().is_some() { - return None; + match dest.get_safety_selection() { + SafetySelection::Unsafe(_) => {} + SafetySelection::Safe(_) => { + return None; + } } // Don't do this if our own signed node info isn't valid yet let routing_table = self.routing_table(); @@ -571,7 +576,7 @@ impl RPCProcessor { match dest { Destination::Direct { target, - safety_spec: _, + safety_selection: _, } => { // If the target has seen our node info already don't do this if target.has_seen_our_node_info(RoutingDomain::PublicInternet) { @@ -582,7 +587,7 @@ impl RPCProcessor { Destination::Relay { relay: _, target, - safety_spec: _, + safety_selection: _, } => { if let Some(target) = routing_table.lookup_node_ref(*target) { if target.has_seen_our_node_info(RoutingDomain::PublicInternet) { @@ -595,8 +600,7 @@ impl RPCProcessor { } Destination::PrivateRoute { private_route: _, - safety_spec: _, - reliable: _, + safety_selection: _, } => None, } } @@ -741,6 +745,7 @@ impl RPCProcessor { Destination::relay(peer_noderef, sender_id) } } + //xxx needs to know what route the request came in on in order to reply over that same route as the preferred safety route RespondTo::PrivateRoute(pr) => Destination::private_route( pr.clone(), request diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index 29729bb7..3833318a 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -7,6 +7,9 @@ impl RPCProcessor { pub(crate) async fn process_route(&self, msg: RPCMessage) -> Result<(), RPCError> { // xxx do not process latency for routed messages // tracing::Span::current().record("res", &tracing::field::display(res)); + + xxx continue here + Err(RPCError::unimplemented("process_route")) } } diff --git a/veilid-core/src/veilid_api/mod.rs b/veilid-core/src/veilid_api/mod.rs index 8f2ac241..9d16b15b 100644 --- a/veilid-core/src/veilid_api/mod.rs +++ b/veilid-core/src/veilid_api/mod.rs @@ -420,6 +420,28 @@ pub enum Stability { Reliable, } +/// The choice of safety route including in compiled routes +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub enum SafetySelection { + /// Don't use a safety route, only specify the sequencing preference + Unsafe(Sequencing), + /// Use a safety route and parameters specified by a SafetySpec + Safe(SafetySpec), +} + +/// Options for safety routes (sender privacy) +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct SafetySpec { + /// preferred safety route if it still exists + pub preferred_route: Option, + /// 0 = no safety route, just use node's node id, more hops is safer but slower + pub hop_count: usize, + /// prefer reliability over speed + pub stability: Stability, + /// prefer connection-oriented sequenced protocols + pub sequencing: Sequencing, +} + // Keep member order appropriate for sorting < preference #[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize, Hash)] pub struct DialInfoDetail { @@ -1551,8 +1573,8 @@ impl DialInfo { } pub fn ordered_sequencing_sort(a: &DialInfo, b: &DialInfo) -> core::cmp::Ordering { - let ca = a.protocol_type().sort_order(true); - let cb = b.protocol_type().sort_order(true); + let ca = a.protocol_type().sort_order(Sequencing::EnsureOrdered); + let cb = b.protocol_type().sort_order(Sequencing::EnsureOrdered); if ca < cb { return core::cmp::Ordering::Less; } diff --git a/veilid-core/src/veilid_api/routing_context.rs b/veilid-core/src/veilid_api/routing_context.rs index e0c3a8b4..47efb369 100644 --- a/veilid-core/src/veilid_api/routing_context.rs +++ b/veilid-core/src/veilid_api/routing_context.rs @@ -10,10 +10,8 @@ pub enum Target { pub struct RoutingContextInner {} pub struct RoutingContextUnlockedInner { - /// Enforce use of private routing - privacy: usize, - /// Choose reliable protocols over unreliable/faster protocols when available - reliable: bool, + /// Safety routing requirements + safety_selection: SafetySelection, } impl Drop for RoutingContextInner { @@ -41,8 +39,7 @@ impl RoutingContext { api, inner: Arc::new(Mutex::new(RoutingContextInner {})), unlocked_inner: Arc::new(RoutingContextUnlockedInner { - privacy: 0, - reliable: false, + safety_selection: SafetySelection::Unsafe(Sequencing::NoPreference), }), } } @@ -54,44 +51,54 @@ impl RoutingContext { api: self.api.clone(), inner: Arc::new(Mutex::new(RoutingContextInner {})), unlocked_inner: Arc::new(RoutingContextUnlockedInner { - privacy: c.network.rpc.default_route_hop_count as usize, - reliable: self.unlocked_inner.reliable, + safety_selection: SafetySelection::Safe(SafetySpec { + preferred_route: None, + hop_count: c.network.rpc.default_route_hop_count as usize, + stability: Stability::LowLatency, + sequencing: Sequencing::NoPreference, + }), }), }) } - pub fn with_privacy(self, hops: usize) -> Result { - let config = self.api.config()?; - let c = config.get(); - - let privacy = if hops > 0 && hops <= c.network.rpc.max_route_hop_count as usize { - hops - } else { - return Err(VeilidAPIError::invalid_argument( - "hops value is too large", - "hops", - hops, - )); - }; + pub fn with_privacy(self, safety_spec: SafetySpec) -> Result { Ok(Self { api: self.api.clone(), inner: Arc::new(Mutex::new(RoutingContextInner {})), unlocked_inner: Arc::new(RoutingContextUnlockedInner { - privacy, - reliable: self.unlocked_inner.reliable, + safety_selection: SafetySelection::Safe(safety_spec), }), }) } - pub fn with_reliability(self) -> Self { + pub fn with_sequencing(self, sequencing: Sequencing) -> Self { Self { api: self.api.clone(), inner: Arc::new(Mutex::new(RoutingContextInner {})), unlocked_inner: Arc::new(RoutingContextUnlockedInner { - privacy: self.unlocked_inner.privacy, - reliable: true, + safety_selection: match self.unlocked_inner.safety_selection { + SafetySelection::Unsafe(_) => SafetySelection::Unsafe(sequencing), + SafetySelection::Safe(safety_spec) => SafetySelection::Safe(SafetySpec { + preferred_route: safety_spec.preferred_route, + hop_count: safety_spec.hop_count, + stability: safety_spec.stability, + sequencing, + }), + }, }), } } + pub fn sequencing(&self) -> Sequencing { + match self.unlocked_inner.safety_selection { + SafetySelection::Unsafe(sequencing) => sequencing, + SafetySelection::Safe(safety_spec) => safety_spec.sequencing, + } + } + pub fn safety_spec(&self) -> Option { + match self.unlocked_inner.safety_selection { + SafetySelection::Unsafe(_) => None, + SafetySelection::Safe(safety_spec) => Some(safety_spec.clone()), + } + } pub fn api(&self) -> VeilidAPI { self.api.clone() @@ -111,27 +118,17 @@ impl RoutingContext { Ok(None) => return Err(VeilidAPIError::NodeNotFound { node_id }), Err(e) => return Err(e.into()), }; - // Apply reliability sort - if self.unlocked_inner.reliable { - nr.set_reliable(); - } + // Apply sequencing to match safety selection + nr.set_sequencing(self.sequencing()); + Ok(rpc_processor::Destination::Direct { target: nr, - safety_spec: Some(routing_table::SafetySpec { - preferred_route: None, - hop_count: self.unlocked_inner.privacy, - reliable: self.unlocked_inner.reliable, - }), + safety_selection: self.unlocked_inner.safety_selection, }) } Target::PrivateRoute(pr) => Ok(rpc_processor::Destination::PrivateRoute { private_route: pr, - safety_spec: Some(routing_table::SafetySpec { - preferred_route: None, - hop_count: self.unlocked_inner.privacy, - reliable: self.unlocked_inner.reliable, - }), - reliable: self.unlocked_inner.reliable, + safety_selection: self.unlocked_inner.safety_selection, }), } }