diff --git a/veilid-core/src/routing_table/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store.rs index 12dddeed..844e5a21 100644 --- a/veilid-core/src/routing_table/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store.rs @@ -208,8 +208,15 @@ impl RouteSpecStore { } pub async fn load(routing_table: RoutingTable) -> EyreResult { - let config = routing_table.network_manager().config(); - let c = config.get(); + let (max_route_hop_count, default_route_hop_count) = { + let config = routing_table.network_manager().config(); + let c = config.get(); + ( + c.network.rpc.max_route_hop_count as usize, + c.network.rpc.default_route_hop_count as usize, + ) + }; + // Get cbor blob from table store let table_store = routing_table.network_manager().table_store(); let rsstdb = table_store.open("RouteSpecStore", 1).await?; @@ -251,8 +258,8 @@ impl RouteSpecStore { let rss = RouteSpecStore { unlocked_inner: Arc::new(RouteSpecStoreUnlockedInner { - max_route_hop_count: c.network.rpc.max_route_hop_count.into(), - default_route_hop_count: c.network.rpc.default_route_hop_count.into(), + max_route_hop_count, + default_route_hop_count, routing_table, }), inner: Arc::new(Mutex::new(inner)), diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index e020aa45..ac21e054 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -52,8 +52,16 @@ struct RPCMessageHeaderDetailDirect { routing_domain: RoutingDomain, } +/// Header details for rpc messages received over only a safety route but not a private route #[derive(Debug, Clone)] -struct RPCMessageHeaderDetailPrivateRoute { +struct RPCMessageHeaderDetailSafetyRouted { + /// The sequencing used for this route + sequencing: Sequencing, +} + +/// Header details for rpc messages received over a private route +#[derive(Debug, Clone)] +struct RPCMessageHeaderDetailPrivateRouted { /// The private route we received the rpc over private_route: DHTKey, // The safety selection for replying to this private routed rpc @@ -63,7 +71,8 @@ struct RPCMessageHeaderDetailPrivateRoute { #[derive(Debug, Clone)] enum RPCMessageHeaderDetail { Direct(RPCMessageHeaderDetailDirect), - PrivateRoute(RPCMessageHeaderDetailPrivateRoute), + SafetyRouted(RPCMessageHeaderDetailSafetyRouted), + PrivateRouted(RPCMessageHeaderDetailPrivateRouted), } /// The decoded header of an RPC message @@ -766,10 +775,11 @@ impl RPCProcessor { // Parse out the header detail from the question let detail = match &request.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, - RPCMessageHeaderDetail::PrivateRoute(_) => { + RPCMessageHeaderDetail::SafetyRouted(_) + | RPCMessageHeaderDetail::PrivateRouted(_) => { // If this was sent via a private route, we don't know what the sender was, so drop this return NetworkResult::invalid_message( - "not responding directly to question from private route", + "can't respond directly to non-direct question", ); } }; @@ -789,20 +799,29 @@ impl RPCProcessor { } } RespondTo::PrivateRoute(pr) => { - let detail = match &request.header.detail { + match &request.header.detail { RPCMessageHeaderDetail::Direct(_) => { - // If this was sent directly, don't respond to a private route as this could give away this node's safety routes + // If this was sent directly, we should only ever respond directly return NetworkResult::invalid_message( "not responding to private route from direct question", ); } - RPCMessageHeaderDetail::PrivateRoute(detail) => detail, - }; - - NetworkResult::value(Destination::private_route( - pr.clone(), - detail.safety_selection.clone(), - )) + RPCMessageHeaderDetail::SafetyRouted(detail) => { + // If this was sent via a safety route, but no received over our private route, don't respond with a safety route, + // it would give away which safety routes belong to this node + NetworkResult::value(Destination::private_route( + pr.clone(), + SafetySelection::Unsafe(detail.sequencing), + )) + } + RPCMessageHeaderDetail::PrivateRouted(detail) => { + // If this was received over our private route, it's okay to respond to a private route via our safety route + NetworkResult::value(Destination::private_route( + pr.clone(), + detail.safety_selection.clone(), + )) + } + } } } } @@ -916,7 +935,7 @@ impl RPCProcessor { opt_sender_nr, } } - RPCMessageHeaderDetail::PrivateRoute(_) => { + RPCMessageHeaderDetail::SafetyRouted(_) | RPCMessageHeaderDetail::PrivateRouted(_) => { // Decode the RPC message let operation = { let reader = capnp::message::Reader::new(encoded_msg.data, Default::default()); @@ -1047,7 +1066,38 @@ impl RPCProcessor { } #[instrument(level = "trace", skip(self, body), err)] - pub fn enqueue_private_route_message( + pub fn enqueue_safety_routed_message( + &self, xxx keep pushing this through + private_route: DHTKey, + safety_selection: SafetySelection, + body: Vec, + ) -> EyreResult<()> { + let msg = RPCMessageEncoded { + header: RPCMessageHeader { + detail: RPCMessageHeaderDetail::PrivateRouted( + RPCMessageHeaderDetailPrivateRouted { + private_route, + safety_selection, + }, + ), + timestamp: intf::get_timestamp(), + body_len: body.len() as u64, + }, + data: RPCMessageData { contents: body }, + }; + let send_channel = { + let inner = self.inner.lock(); + inner.send_channel.as_ref().unwrap().clone() + }; + let span_id = Span::current().id(); + send_channel + .try_send((span_id, msg)) + .wrap_err("failed to enqueue received RPC message")?; + Ok(()) + } + + #[instrument(level = "trace", skip(self, body), err)] + pub fn enqueue_private_routed_message( &self, private_route: DHTKey, safety_selection: SafetySelection, @@ -1055,10 +1105,12 @@ impl RPCProcessor { ) -> EyreResult<()> { let msg = RPCMessageEncoded { header: RPCMessageHeader { - detail: RPCMessageHeaderDetail::PrivateRoute(RPCMessageHeaderDetailPrivateRoute { - private_route, - safety_selection, - }), + detail: RPCMessageHeaderDetail::PrivateRouted( + RPCMessageHeaderDetailPrivateRouted { + private_route, + safety_selection, + }, + ), timestamp: intf::get_timestamp(), body_len: body.len() as u64, }, diff --git a/veilid-core/src/rpc_processor/rpc_find_node.rs b/veilid-core/src/rpc_processor/rpc_find_node.rs index 65dde614..493b4d5c 100644 --- a/veilid-core/src/rpc_processor/rpc_find_node.rs +++ b/veilid-core/src/rpc_processor/rpc_find_node.rs @@ -1,14 +1,31 @@ use super::*; impl RPCProcessor { - // Send FindNodeQ RPC request, receive FindNodeA answer - // Can be sent via all methods including relays and routes + /// Send FindNodeQ RPC request, receive FindNodeA answer + /// Can be sent via all methods including relays + /// Safety routes may be used, but never private routes. + /// Because this leaks information about the identity of the node itself, + /// replying to this request received over a private route will leak + /// the identity of the node and defeat the private route. #[instrument(level = "trace", skip(self), ret, err)] pub async fn rpc_call_find_node( self, dest: Destination, key: DHTKey, ) -> Result>>, RPCError> { + // Ensure destination never has a private route + if matches!( + dest, + Destination::PrivateRoute { + private_route: _, + safety_selection: _ + } + ) { + return Err(RPCError::internal( + "Never send find node requests over private routes", + )); + } + let find_node_q_detail = RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ { node_id: key }); let find_node_q = RPCQuestion::new(RespondTo::Sender, find_node_q_detail); @@ -51,6 +68,23 @@ impl RPCProcessor { #[instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id, res), err)] pub(crate) async fn process_find_node_q(&self, msg: RPCMessage) -> Result<(), RPCError> { + // Ensure this never came over a private route + match msg.header.detail { + RPCMessageHeaderDetail::Direct(_) => todo!(), + RPCMessageHeaderDetail::PrivateRouted(_) => todo!(), + } + if matches!( + dest, + Destination::PrivateRoute { + private_route: _, + safety_selection: _ + } + ) { + return Err(RPCError::internal( + "Never send find node requests over private routes", + )); + } + // Get the question let find_node_q = match msg.operation.kind() { RPCOperationKind::Question(q) => match q.detail() { diff --git a/veilid-core/src/rpc_processor/rpc_node_info_update.rs b/veilid-core/src/rpc_processor/rpc_node_info_update.rs index 0e317d9c..d14877b1 100644 --- a/veilid-core/src/rpc_processor/rpc_node_info_update.rs +++ b/veilid-core/src/rpc_processor/rpc_node_info_update.rs @@ -33,7 +33,7 @@ impl RPCProcessor { pub(crate) async fn process_node_info_update(&self, msg: RPCMessage) -> Result<(), RPCError> { let detail = match msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, - RPCMessageHeaderDetail::PrivateRoute(_) => { + RPCMessageHeaderDetail::PrivateRouted(_) => { return Err(RPCError::protocol("node_info_update must be direct")); } }; diff --git a/veilid-core/src/rpc_processor/rpc_return_receipt.rs b/veilid-core/src/rpc_processor/rpc_return_receipt.rs index 3087dd0f..09d8f309 100644 --- a/veilid-core/src/rpc_processor/rpc_return_receipt.rs +++ b/veilid-core/src/rpc_processor/rpc_return_receipt.rs @@ -42,7 +42,7 @@ impl RPCProcessor { .await => {} ); } - RPCMessageHeaderDetail::PrivateRoute(detail) => { + RPCMessageHeaderDetail::PrivateRouted(detail) => { network_result_value_or_log!(debug network_manager .handle_private_receipt(receipt, detail.private_route) diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index 6c3f4384..63a56482 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -227,7 +227,7 @@ impl RPCProcessor { ))?; // Pass message to RPC system - self.enqueue_private_route_message(private_route.public_key, safety_selection, body) + self.enqueue_private_routed_message(private_route.public_key, safety_selection, body) .map_err(RPCError::internal)?; Ok(()) @@ -238,7 +238,7 @@ impl RPCProcessor { // Get header detail, must be direct and not inside a route itself let detail = match msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, - RPCMessageHeaderDetail::PrivateRoute(_) => { + RPCMessageHeaderDetail::SafetyRouted(_) | RPCMessageHeaderDetail::PrivateRouted(_) => { return Err(RPCError::protocol( "route operation can not be inside route", )) diff --git a/veilid-core/src/rpc_processor/rpc_status.rs b/veilid-core/src/rpc_processor/rpc_status.rs index feebaa5c..af02a6c3 100644 --- a/veilid-core/src/rpc_processor/rpc_status.rs +++ b/veilid-core/src/rpc_processor/rpc_status.rs @@ -105,7 +105,7 @@ impl RPCProcessor { pub(crate) async fn process_status_q(&self, msg: RPCMessage) -> Result<(), RPCError> { let detail = match &msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, - RPCMessageHeaderDetail::PrivateRoute(_) => { + RPCMessageHeaderDetail::PrivateRouted(_) => { return Err(RPCError::protocol("status_q must be direct")); } }; 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 167f9527..11de3e23 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -57,7 +57,7 @@ impl RPCProcessor { pub(crate) async fn process_validate_dial_info(&self, msg: RPCMessage) -> Result<(), RPCError> { let detail = match msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, - RPCMessageHeaderDetail::PrivateRoute(_) => { + RPCMessageHeaderDetail::SafetyRouted(_) | RPCMessageHeaderDetail::PrivateRouted(_) => { return Err(RPCError::protocol("validate_dial_info must be direct")); } };