private route loopbacks

This commit is contained in:
John Smith 2022-11-24 16:23:33 -05:00
parent 0b2ecd53c7
commit 4d573a966f
4 changed files with 175 additions and 138 deletions

View File

@ -434,15 +434,6 @@ impl DiscoveryContext {
return Ok(true); return Ok(true);
} }
// XXX: is this necessary?
// Redo our external_1 dial info detection because a failed port mapping attempt
// may cause it to become invalid
// Get our external address from some fast node, call it node 1
// if !self.protocol_get_external_address_1().await {
// // If we couldn't get an external address, then we should just try the whole network class detection again later
// return Ok(false);
// }
// Get the external dial info for our use here // Get the external dial info for our use here
let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = { let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = {
let inner = self.inner.lock(); let inner = self.inner.lock();

View File

@ -864,7 +864,20 @@ impl RouteSpecStore {
safety_selection, safety_selection,
} }
} else { } else {
let target = rsd.hop_node_refs[rsd.hops.len() - 2].clone(); // let target = rsd.hop_node_refs[rsd.hops.len() - 2].clone();
// let safety_spec = SafetySpec {
// preferred_route: Some(key.clone()),
// hop_count,
// stability,
// sequencing,
// };
// let safety_selection = SafetySelection::Safe(safety_spec);
// Destination::Direct {
// target,
// safety_selection,
// }
let safety_spec = SafetySpec { let safety_spec = SafetySpec {
preferred_route: Some(key.clone()), preferred_route: Some(key.clone()),
hop_count, hop_count,
@ -873,38 +886,22 @@ impl RouteSpecStore {
}; };
let safety_selection = SafetySelection::Safe(safety_spec); let safety_selection = SafetySelection::Safe(safety_spec);
Destination::Direct { Destination::PrivateRoute {
target, private_route,
safety_selection, safety_selection,
} }
} }
}; };
// Test with ping to end // Test with double-round trip ping to self
let cur_ts = intf::get_timestamp(); let _res = match rpc_processor.rpc_call_status(dest).await? {
let res = match rpc_processor.rpc_call_status(dest).await? {
NetworkResult::Value(v) => v, NetworkResult::Value(v) => v,
_ => { _ => {
// // Do route stats for single hop route test because it
// // won't get stats for the route since it's done Direct
// if matches!(safety_selection, SafetySelection::Unsafe(_)) {
// self.with_route_stats(cur_ts, &key, |s| s.record_question_lost());
// }
// Did not error, but did not come back, just return false // Did not error, but did not come back, just return false
return Ok(false); return Ok(false);
} }
}; };
// // Do route stats for single hop route test because it
// // won't get stats for the route since it's done Direct
// if matches!(safety_selection, SafetySelection::Unsafe(_)) {
// self.with_route_stats(cur_ts, &key, |s| {
// s.record_tested(cur_ts);
// s.record_latency(res.latency);
// });
// }
Ok(true) Ok(true)
} }
@ -1065,14 +1062,21 @@ impl RouteSpecStore {
bail!("compiled private route should have first hop"); bail!("compiled private route should have first hop");
}; };
// Get the safety route to use from the spec // If the safety route requested is also the private route, this is a loopback test, just accept it
let avoid_node_id = match &pr_first_hop.node { let sr_pubkey = if safety_spec.preferred_route == Some(private_route.public_key) {
RouteNode::NodeId(n) => n.key, // Private route is also safety route during loopback test
RouteNode::PeerInfo(p) => p.node_id.key, private_route.public_key
}; } else {
let Some(sr_pubkey) = self.get_route_for_safety_spec_inner(inner, rti, &safety_spec, Direction::Outbound.into(), &[avoid_node_id])? else { // Get the safety route to use from the spec
// No safety route could be found for this spec let avoid_node_id = match &pr_first_hop.node {
return Ok(None); RouteNode::NodeId(n) => n.key,
RouteNode::PeerInfo(p) => p.node_id.key,
};
let Some(sr_pubkey) = self.get_route_for_safety_spec_inner(inner, rti, &safety_spec, Direction::Outbound.into(), &[avoid_node_id])? else {
// No safety route could be found for this spec
return Ok(None);
};
sr_pubkey
}; };
let safety_rsd = Self::detail_mut(inner, &sr_pubkey).unwrap(); let safety_rsd = Self::detail_mut(inner, &sr_pubkey).unwrap();

View File

@ -228,18 +228,29 @@ impl RPCProcessor {
))) )))
} }
SafetySelection::Safe(safety_spec) => { SafetySelection::Safe(safety_spec) => {
// Sent directly but with a safety route, respond to private route // Sent to a private route via a safety route, respond to private route
let avoid_node_id = match &pr_first_hop.node {
RouteNode::NodeId(n) => n.key,
RouteNode::PeerInfo(p) => p.node_id.key,
};
let Some(pr_key) = rss // Check for loopback test
.get_private_route_for_safety_spec(safety_spec, &[avoid_node_id]) let pr_key = if safety_spec.preferred_route
.map_err(RPCError::internal)? else { == Some(private_route.public_key)
return Ok(NetworkResult::no_connection_other("no private route for response at this time")); {
// Private route is also safety route during loopback test
private_route.public_key
} else {
// Get the privat route to respond to that matches the safety route spec we sent the request with
let avoid_node_id = match &pr_first_hop.node {
RouteNode::NodeId(n) => n.key,
RouteNode::PeerInfo(p) => p.node_id.key,
}; };
let Some(pr_key) = rss
.get_private_route_for_safety_spec(safety_spec, &[avoid_node_id])
.map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
};
pr_key
};
// Get the assembled route for response // Get the assembled route for response
let private_route = rss let private_route = rss
.assemble_private_route(&pr_key, None) .assemble_private_route(&pr_key, None)

View File

@ -4,16 +4,17 @@ impl RPCProcessor {
#[instrument(level = "trace", skip_all, err)] #[instrument(level = "trace", skip_all, err)]
async fn process_route_safety_route_hop( async fn process_route_safety_route_hop(
&self, &self,
route: RPCOperationRoute, routed_operation: RoutedOperation,
route_hop: RouteHop, route_hop: RouteHop,
safety_route: SafetyRoute,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Make sure hop count makes sense // Make sure hop count makes sense
if route.safety_route.hop_count as usize > self.unlocked_inner.max_route_hop_count { if safety_route.hop_count as usize > self.unlocked_inner.max_route_hop_count {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(
"Safety route hop count too high to process", "Safety route hop count too high to process",
)); ));
} }
if route.safety_route.hop_count == 0 { if safety_route.hop_count == 0 {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(
"Safety route hop count should not be zero if there are more hops", "Safety route hop count should not be zero if there are more hops",
)); ));
@ -55,11 +56,11 @@ impl RPCProcessor {
// Pass along the route // Pass along the route
let next_hop_route = RPCOperationRoute { let next_hop_route = RPCOperationRoute {
safety_route: SafetyRoute { safety_route: SafetyRoute {
public_key: route.safety_route.public_key, public_key: safety_route.public_key,
hop_count: route.safety_route.hop_count - 1, hop_count: safety_route.hop_count - 1,
hops: SafetyRouteHops::Data(route_hop.next_hop.unwrap()), hops: SafetyRouteHops::Data(route_hop.next_hop.unwrap()),
}, },
operation: route.operation, operation: routed_operation,
}; };
let next_hop_route_stmt = RPCStatement::new(RPCStatementDetail::Route(next_hop_route)); let next_hop_route_stmt = RPCStatement::new(RPCStatementDetail::Route(next_hop_route));
@ -135,7 +136,7 @@ impl RPCProcessor {
&self, &self,
detail: RPCMessageHeaderDetailDirect, detail: RPCMessageHeaderDetailDirect,
routed_operation: RoutedOperation, routed_operation: RoutedOperation,
remote_safety_route: &SafetyRoute, remote_sr_pubkey: DHTKey,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Get sequencing preference // Get sequencing preference
let sequencing = if detail let sequencing = if detail
@ -153,7 +154,7 @@ impl RPCProcessor {
let node_id_secret = self.routing_table.node_id_secret(); let node_id_secret = self.routing_table.node_id_secret();
let dh_secret = self let dh_secret = self
.crypto .crypto
.cached_dh(&remote_safety_route.public_key, &node_id_secret) .cached_dh(&remote_sr_pubkey, &node_id_secret)
.map_err(RPCError::protocol)?; .map_err(RPCError::protocol)?;
let body = match Crypto::decrypt_aead( let body = match Crypto::decrypt_aead(
&routed_operation.data, &routed_operation.data,
@ -168,7 +169,7 @@ impl RPCProcessor {
}; };
// Pass message to RPC system // Pass message to RPC system
self.enqueue_safety_routed_message(remote_safety_route.public_key, sequencing, body) self.enqueue_safety_routed_message(remote_sr_pubkey, sequencing, body)
.map_err(RPCError::internal)?; .map_err(RPCError::internal)?;
Ok(NetworkResult::value(())) Ok(NetworkResult::value(()))
@ -180,8 +181,8 @@ impl RPCProcessor {
&self, &self,
detail: RPCMessageHeaderDetailDirect, detail: RPCMessageHeaderDetailDirect,
routed_operation: RoutedOperation, routed_operation: RoutedOperation,
remote_safety_route: &SafetyRoute, remote_sr_pubkey: DHTKey,
private_route: &PrivateRoute, pr_pubkey: DHTKey,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Get sender id // Get sender id
let sender_id = detail.envelope.get_sender_id(); let sender_id = detail.envelope.get_sender_id();
@ -190,7 +191,7 @@ impl RPCProcessor {
let rss = self.routing_table.route_spec_store(); let rss = self.routing_table.route_spec_store();
let Some((secret_key, safety_spec)) = rss let Some((secret_key, safety_spec)) = rss
.validate_signatures( .validate_signatures(
&private_route.public_key, &pr_pubkey,
&routed_operation.signatures, &routed_operation.signatures,
&routed_operation.data, &routed_operation.data,
sender_id, sender_id,
@ -204,7 +205,7 @@ impl RPCProcessor {
// xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes? // xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes?
let dh_secret = self let dh_secret = self
.crypto .crypto
.cached_dh(&remote_safety_route.public_key, &secret_key) .cached_dh(&remote_sr_pubkey, &secret_key)
.map_err(RPCError::protocol)?; .map_err(RPCError::protocol)?;
let body = Crypto::decrypt_aead( let body = Crypto::decrypt_aead(
&routed_operation.data, &routed_operation.data,
@ -217,7 +218,7 @@ impl RPCProcessor {
))?; ))?;
// Pass message to RPC system // Pass message to RPC system
self.enqueue_private_routed_message(remote_safety_route.public_key, private_route.public_key, safety_spec, body) self.enqueue_private_routed_message(remote_sr_pubkey, pr_pubkey, safety_spec, body)
.map_err(RPCError::internal)?; .map_err(RPCError::internal)?;
Ok(NetworkResult::value(())) Ok(NetworkResult::value(()))
@ -228,65 +229,123 @@ impl RPCProcessor {
&self, &self,
detail: RPCMessageHeaderDetailDirect, detail: RPCMessageHeaderDetailDirect,
routed_operation: RoutedOperation, routed_operation: RoutedOperation,
safety_route: &SafetyRoute, remote_sr_pubkey: DHTKey,
private_route: &PrivateRoute, pr_pubkey: DHTKey,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Make sure hop count makes sense
if safety_route.hop_count != 0 {
return Ok(NetworkResult::invalid_message(
"Safety hop count should be zero if switched to private route",
));
}
if private_route.hop_count != 0 {
return Ok(NetworkResult::invalid_message(
"Private route hop count should be zero if we are at the end",
));
}
// If the private route public key is our node id, then this was sent via safety route to our node directly // If the private route public key is our node id, then this was sent via safety route to our node directly
// so there will be no signatures to validate // so there will be no signatures to validate
if private_route.public_key == self.routing_table.node_id() { if pr_pubkey == self.routing_table.node_id() {
// The private route was a stub // The private route was a stub
self.process_safety_routed_operation(detail, routed_operation, safety_route) self.process_safety_routed_operation(detail, routed_operation, remote_sr_pubkey)
} else { } else {
// Both safety and private routes used, should reply with a safety route // Both safety and private routes used, should reply with a safety route
self.process_private_routed_operation( self.process_private_routed_operation(
detail, detail,
routed_operation, routed_operation,
safety_route, remote_sr_pubkey,
private_route, pr_pubkey,
) )
} }
} }
#[instrument(level = "trace", skip_all, err)] #[instrument(level = "trace", skip_all, err)]
pub(crate) async fn process_private_route_first_hop( pub(crate) async fn process_private_route_first_hop(
&self, &self,
operation: RoutedOperation, mut routed_operation: RoutedOperation,
sr_pubkey: DHTKey, sr_pubkey: DHTKey,
private_route: &PrivateRoute, mut private_route: PrivateRoute,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
let PrivateRouteHops::FirstHop(pr_first_hop) = &private_route.hops else { let Some(pr_first_hop) = private_route.pop_first_hop() else {
return Ok(NetworkResult::invalid_message("switching from safety route to private route requires first hop")); return Ok(NetworkResult::invalid_message("switching from safety route to private route requires first hop"));
}; };
// Check for loopback test where private route is the same as safety route
if sr_pubkey == private_route.public_key {
// If so, we're going to turn this thing right around without transiting the network
let PrivateRouteHops::Data(route_hop_data) = private_route.hops else {
return Ok(NetworkResult::invalid_message("Loopback test requires hops"));
};
// Decrypt route hop data
let route_hop = network_result_try!(self.decrypt_private_route_hop_data(&route_hop_data, &private_route.public_key, &mut routed_operation)?);
// Ensure hop count > 0
if private_route.hop_count == 0 {
return Ok(NetworkResult::invalid_message(
"route should not be at the end",
));
}
// Make next PrivateRoute and pass it on
return self.process_route_private_route_hop(
routed_operation,
route_hop.node,
sr_pubkey,
PrivateRoute {
public_key: private_route.public_key,
hop_count: private_route.hop_count - 1,
hops: route_hop
.next_hop
.map(|rhd| PrivateRouteHops::Data(rhd))
.unwrap_or(PrivateRouteHops::Empty),
},
)
.await;
}
// Switching to private route from safety route // Switching to private route from safety route
self.process_route_private_route_hop( self.process_route_private_route_hop(
operation, routed_operation,
pr_first_hop.node.clone(), pr_first_hop,
sr_pubkey, sr_pubkey,
PrivateRoute { private_route,
public_key: private_route.public_key,
hop_count: private_route.hop_count - 1,
hops: pr_first_hop
.next_hop
.clone()
.map(|rhd| PrivateRouteHops::Data(rhd))
.unwrap_or(PrivateRouteHops::Empty),
},
) )
.await .await
} }
/// Decrypt route hop data and sign routed operation
pub(crate) fn decrypt_private_route_hop_data(&self, route_hop_data: &RouteHopData, pr_pubkey: &DHTKey, route_operation: &mut RoutedOperation) -> Result<NetworkResult<RouteHop>, RPCError>
{
// Decrypt the blob with DEC(nonce, DH(the PR's public key, this hop's secret)
let node_id_secret = self.routing_table.node_id_secret();
let dh_secret = self
.crypto
.cached_dh(&pr_pubkey, &node_id_secret)
.map_err(RPCError::protocol)?;
let dec_blob_data = match Crypto::decrypt_aead(
&route_hop_data.blob,
&route_hop_data.nonce,
&dh_secret,
None,
) {
Ok(v) => v,
Err(e) => {
return Ok(NetworkResult::invalid_message(format!("unable to decrypt private route hop data: {}", e)));
}
};
let dec_blob_reader = RPCMessageData::new(dec_blob_data).get_reader()?;
// Decode next RouteHop
let route_hop = {
let rh_reader = dec_blob_reader
.get_root::<veilid_capnp::route_hop::Reader>()
.map_err(RPCError::protocol)?;
decode_route_hop(&rh_reader)?
};
// Sign the operation if this is not our last hop
// as the last hop is already signed by the envelope
if route_hop.next_hop.is_some() {
let node_id = self.routing_table.node_id();
let node_id_secret = self.routing_table.node_id_secret();
let sig = sign(&node_id, &node_id_secret, &route_operation.data)
.map_err(RPCError::internal)?;
route_operation.signatures.push(sig);
}
Ok(NetworkResult::value(route_hop))
}
#[instrument(level = "trace", skip(self, msg), ret, err)] #[instrument(level = "trace", skip(self, msg), ret, err)]
pub(crate) async fn process_route( pub(crate) async fn process_route(
&self, &self,
@ -322,14 +381,14 @@ impl RPCProcessor {
// See what kind of safety route we have going on here // See what kind of safety route we have going on here
match route.safety_route.hops { match route.safety_route.hops {
// There is a safety route hop // There is a safety route hop
SafetyRouteHops::Data(ref d) => { SafetyRouteHops::Data(ref route_hop_data) => {
// Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret) // Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret)
let node_id_secret = self.routing_table.node_id_secret(); let node_id_secret = self.routing_table.node_id_secret();
let dh_secret = self let dh_secret = self
.crypto .crypto
.cached_dh(&route.safety_route.public_key, &node_id_secret) .cached_dh(&route.safety_route.public_key, &node_id_secret)
.map_err(RPCError::protocol)?; .map_err(RPCError::protocol)?;
let mut dec_blob_data = Crypto::decrypt_aead(&d.blob, &d.nonce, &dh_secret, None) let mut dec_blob_data = Crypto::decrypt_aead(&route_hop_data.blob, &route_hop_data.nonce, &dh_secret, None)
.map_err(RPCError::protocol)?; .map_err(RPCError::protocol)?;
// See if this is last hop in safety route, if so, we're decoding a PrivateRoute not a RouteHop // See if this is last hop in safety route, if so, we're decoding a PrivateRoute not a RouteHop
@ -353,7 +412,7 @@ impl RPCProcessor {
network_result_try!(self.process_private_route_first_hop( network_result_try!(self.process_private_route_first_hop(
route.operation, route.operation,
route.safety_route.public_key, route.safety_route.public_key,
&private_route, private_route,
) )
.await?); .await?);
} else if dec_blob_tag == 0 { } else if dec_blob_tag == 0 {
@ -366,16 +425,16 @@ impl RPCProcessor {
}; };
// Continue the full safety route with another hop // Continue the full safety route with another hop
network_result_try!(self.process_route_safety_route_hop(route, route_hop) network_result_try!(self.process_route_safety_route_hop(route.operation, route_hop, route.safety_route)
.await?); .await?);
} else { } else {
return Ok(NetworkResult::invalid_message("invalid blob tag")); return Ok(NetworkResult::invalid_message("invalid blob tag"));
} }
} }
// No safety route left, now doing private route // No safety route left, now doing private route
SafetyRouteHops::Private(ref private_route) => { SafetyRouteHops::Private(private_route) => {
// See if we have a hop, if not, we are at the end of the private route // See if we have a hop, if not, we are at the end of the private route
match &private_route.hops { match private_route.hops {
PrivateRouteHops::FirstHop(_) => { PrivateRouteHops::FirstHop(_) => {
// Safety route was a stub, start with the beginning of the private route // Safety route was a stub, start with the beginning of the private route
network_result_try!(self.process_private_route_first_hop( network_result_try!(self.process_private_route_first_hop(
@ -386,33 +445,10 @@ impl RPCProcessor {
.await?); .await?);
} }
PrivateRouteHops::Data(route_hop_data) => { PrivateRouteHops::Data(route_hop_data) => {
// Decrypt the blob with DEC(nonce, DH(the PR's public key, this hop's secret)
let node_id_secret = self.routing_table.node_id_secret(); // Decrypt route hop data
let dh_secret = self let route_hop = network_result_try!(self.decrypt_private_route_hop_data(&route_hop_data, &private_route.public_key, &mut route.operation)?);
.crypto
.cached_dh(&private_route.public_key, &node_id_secret)
.map_err(RPCError::protocol)?;
let dec_blob_data = match Crypto::decrypt_aead(
&route_hop_data.blob,
&route_hop_data.nonce,
&dh_secret,
None,
) {
Ok(v) => v,
Err(e) => {
return Ok(NetworkResult::invalid_message(format!("unable to decrypt private route hop data: {}", e)));
}
};
let dec_blob_reader = RPCMessageData::new(dec_blob_data).get_reader()?;
// Decode next RouteHop
let route_hop = {
let rh_reader = dec_blob_reader
.get_root::<veilid_capnp::route_hop::Reader>()
.map_err(RPCError::protocol)?;
decode_route_hop(&rh_reader)?
};
// Ensure hop count > 0 // Ensure hop count > 0
if private_route.hop_count == 0 { if private_route.hop_count == 0 {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(
@ -420,16 +456,6 @@ impl RPCProcessor {
)); ));
} }
// Sign the operation if this is not our last hop
// as the last hop is already signed by the envelope
if route_hop.next_hop.is_some() {
let node_id = self.routing_table.node_id();
let node_id_secret = self.routing_table.node_id_secret();
let sig = sign(&node_id, &node_id_secret, &route.operation.data)
.map_err(RPCError::internal)?;
route.operation.signatures.push(sig);
}
// Make next PrivateRoute and pass it on // Make next PrivateRoute and pass it on
network_result_try!(self.process_route_private_route_hop( network_result_try!(self.process_route_private_route_hop(
route.operation, route.operation,
@ -453,13 +479,18 @@ impl RPCProcessor {
"route should be at the end", "route should be at the end",
)); ));
} }
if route.safety_route.hop_count != 0 {
return Ok(NetworkResult::invalid_message(
"Safety hop count should be zero if switched to private route",
));
}
// No hops left, time to process the routed operation // No hops left, time to process the routed operation
network_result_try!(self.process_routed_operation( network_result_try!(self.process_routed_operation(
detail, detail,
route.operation, route.operation,
&route.safety_route, route.safety_route.public_key,
private_route, private_route.public_key,
)?); )?);
} }
} }