checkpoint

This commit is contained in:
John Smith
2022-12-08 10:24:33 -05:00
parent 847623f2b4
commit 0b059e0ef9
16 changed files with 427 additions and 241 deletions

View File

@@ -50,8 +50,8 @@ pub struct LastConnectionKey(ProtocolType, AddressType);
pub struct BucketEntryPublicInternet {
/// The PublicInternet node info
signed_node_info: Option<Box<SignedNodeInfo>>,
/// If this node has seen our publicinternet node info
seen_our_node_info: bool,
/// The last node info timestamp of ours that this entry has seen
last_seen_our_node_info_ts: u64,
/// Last known node status
node_status: Option<PublicInternetNodeStatus>,
}
@@ -62,8 +62,8 @@ pub struct BucketEntryPublicInternet {
pub struct BucketEntryLocalNetwork {
/// The LocalNetwork node info
signed_node_info: Option<Box<SignedNodeInfo>>,
/// If this node has seen our localnetwork node info
seen_our_node_info: bool,
/// The last node info timestamp of ours that this entry has seen
last_seen_our_node_info_ts: u64,
/// Last known node status
node_status: Option<LocalNetworkNodeStatus>,
}
@@ -427,21 +427,29 @@ impl BucketEntryInner {
}
}
pub fn set_seen_our_node_info(&mut self, routing_domain: RoutingDomain, seen: bool) {
pub fn set_our_node_info_ts(&mut self, routing_domain: RoutingDomain, seen_ts: u64) {
match routing_domain {
RoutingDomain::LocalNetwork => {
self.local_network.seen_our_node_info = seen;
self.local_network.last_seen_our_node_info_ts = seen_ts;
}
RoutingDomain::PublicInternet => {
self.public_internet.seen_our_node_info = seen;
self.public_internet.last_seen_our_node_info_ts = seen_ts;
}
}
}
pub fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
pub fn has_seen_our_node_info_ts(
&self,
routing_domain: RoutingDomain,
our_node_info_ts: u64,
) -> bool {
match routing_domain {
RoutingDomain::LocalNetwork => self.local_network.seen_our_node_info,
RoutingDomain::PublicInternet => self.public_internet.seen_our_node_info,
RoutingDomain::LocalNetwork => {
our_node_info_ts == self.local_network.last_seen_our_node_info_ts
}
RoutingDomain::PublicInternet => {
our_node_info_ts == self.public_internet.last_seen_our_node_info_ts
}
}
}
@@ -680,12 +688,12 @@ impl BucketEntry {
updated_since_last_network_change: false,
last_connections: BTreeMap::new(),
local_network: BucketEntryLocalNetwork {
seen_our_node_info: false,
last_seen_our_node_info_ts: 0,
signed_node_info: None,
node_status: None,
},
public_internet: BucketEntryPublicInternet {
seen_our_node_info: false,
last_seen_our_node_info_ts: 0,
signed_node_info: None,
node_status: None,
},

View File

@@ -374,15 +374,30 @@ impl RoutingTable {
}
/// Return a copy of our node's peerinfo
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
self.inner.read().get_own_peer_info(routing_domain)
}
/// Return the best effort copy of our node's peerinfo
/// This may be invalid and should not be passed to other nodes,
/// but may be used for contact method calculation
pub fn get_best_effort_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
self.inner
.read()
.get_best_effort_own_peer_info(routing_domain)
}
/// If we have a valid network class in this routing domain, then our 'NodeInfo' is valid
/// If this is true, we can get our final peer info, otherwise we only have a 'best effort' peer info
pub fn has_valid_own_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.inner.read().has_valid_own_node_info(routing_domain)
}
/// Return our current node info timestamp
pub fn get_own_node_info_ts(&self, routing_domain: RoutingDomain) -> Option<u64> {
self.inner.read().get_own_node_info_ts(routing_domain)
}
/// Return the domain's currently registered network class
pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
self.inner.read().get_network_class(routing_domain)

View File

@@ -143,11 +143,22 @@ pub trait NodeRefBase: Sized {
.unwrap_or(false)
})
}
fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| e.has_seen_our_node_info(routing_domain))
fn node_info_ts(&self, routing_domain: RoutingDomain) -> u64 {
self.operate(|_rti, e| {
e.signed_node_info(routing_domain)
.map(|sni| sni.timestamp())
.unwrap_or(0u64)
})
}
fn set_seen_our_node_info(&self, routing_domain: RoutingDomain) {
self.operate_mut(|_rti, e| e.set_seen_our_node_info(routing_domain, true));
fn has_seen_our_node_info_ts(
&self,
routing_domain: RoutingDomain,
our_node_info_ts: u64,
) -> bool {
self.operate(|_rti, e| e.has_seen_our_node_info_ts(routing_domain, our_node_info_ts))
}
fn set_our_node_info_ts(&self, routing_domain: RoutingDomain, seen_ts: u64) {
self.operate_mut(|_rti, e| e.set_our_node_info_ts(routing_domain, seen_ts));
}
fn network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class))

View File

@@ -204,7 +204,7 @@ pub struct RemotePrivateRouteInfo {
// The private route itself
private_route: Option<PrivateRoute>,
/// Did this remote private route see our node info due to no safety route in use
seen_our_node_info: bool,
last_seen_our_node_info_ts: u64,
/// Last time this remote private route was requested for any reason (cache expiration)
last_touched_ts: u64,
/// Stats
@@ -618,6 +618,10 @@ impl RouteSpecStore {
bail!("Not allocating route longer than max route hop count");
}
let Some(our_peer_info) = rti.get_own_peer_info(RoutingDomain::PublicInternet) else {
bail!("Can't allocate route until we have our own peer info");
};
// Get relay node id if we have one
let opt_relay_id = rti
.relay_node(RoutingDomain::PublicInternet)
@@ -764,7 +768,6 @@ impl RouteSpecStore {
// Ensure this route is viable by checking that each node can contact the next one
if directions.contains(Direction::Outbound) {
let our_peer_info = rti.get_own_peer_info(RoutingDomain::PublicInternet);
let mut previous_node = &our_peer_info;
let mut reachable = true;
for n in permutation {
@@ -787,7 +790,6 @@ impl RouteSpecStore {
}
}
if directions.contains(Direction::Inbound) {
let our_peer_info = rti.get_own_peer_info(RoutingDomain::PublicInternet);
let mut next_node = &our_peer_info;
let mut reachable = true;
for n in permutation.iter().rev() {
@@ -1452,9 +1454,15 @@ impl RouteSpecStore {
// Make innermost route hop to our own node
let mut route_hop = RouteHop {
node: if optimized {
if !rti.has_valid_own_node_info(RoutingDomain::PublicInternet) {
bail!("can't make private routes until our node info is valid");
}
RouteNode::NodeId(NodeId::new(routing_table.node_id()))
} else {
RouteNode::PeerInfo(rti.get_own_peer_info(RoutingDomain::PublicInternet))
let Some(pi) = rti.get_own_peer_info(RoutingDomain::PublicInternet) else {
bail!("can't make private routes until our node info is valid");
};
RouteNode::PeerInfo(pi)
},
next_hop: None,
};
@@ -1591,7 +1599,7 @@ impl RouteSpecStore {
.and_modify(|rpr| {
if cur_ts - rpr.last_touched_ts >= REMOTE_PRIVATE_ROUTE_CACHE_EXPIRY {
// Start fresh if this had expired
rpr.seen_our_node_info = false;
rpr.last_seen_our_node_info_ts = 0;
rpr.last_touched_ts = cur_ts;
rpr.stats = RouteStats::new(cur_ts);
} else {
@@ -1602,7 +1610,7 @@ impl RouteSpecStore {
.or_insert_with(|| RemotePrivateRouteInfo {
// New remote private route cache entry
private_route: Some(private_route),
seen_our_node_info: false,
last_seen_our_node_info_ts: 0,
last_touched_ts: cur_ts,
stats: RouteStats::new(cur_ts),
});
@@ -1665,22 +1673,52 @@ impl RouteSpecStore {
}
}
/// Check to see if this remote (not ours) private route has seen our node info yet
/// This returns true if we have sent non-safety-route node info to the
/// private route and gotten a response before
/// Check to see if this remote (not ours) private route has seen our current node info yet
/// This happens when you communicate with a private route without a safety route
pub fn has_remote_private_route_seen_our_node_info(&self, key: &DHTKey) -> bool {
let inner = &mut *self.inner.lock();
let cur_ts = get_timestamp();
Self::with_peek_remote_private_route(inner, cur_ts, key, |rpr| rpr.seen_our_node_info)
.unwrap_or_default()
let our_node_info_ts = {
let rti = &*self.unlocked_inner.routing_table.inner.read();
let Some(ts) = rti.get_own_node_info_ts(RoutingDomain::PublicInternet) else {
return false;
};
ts
};
let opt_rpr_node_info_ts = {
let inner = &mut *self.inner.lock();
let cur_ts = get_timestamp();
Self::with_peek_remote_private_route(inner, cur_ts, key, |rpr| {
rpr.last_seen_our_node_info_ts
})
};
let Some(rpr_node_info_ts) = opt_rpr_node_info_ts else {
return false;
};
our_node_info_ts == rpr_node_info_ts
}
/// Mark a remote private route as having seen our node info
/// Mark a remote private route as having seen our current node info
/// PRIVACY:
/// We do not accept node info timestamps from remote private routes because this would
/// enable a deanonymization attack, whereby a node could be 'pinged' with a doctored node_info with a
/// special 'timestamp', which then may be sent back over a private route, identifying that it
/// was that node that had the private route.
pub fn mark_remote_private_route_seen_our_node_info(
&self,
key: &DHTKey,
cur_ts: u64,
) -> EyreResult<()> {
let our_node_info_ts = {
let rti = &*self.unlocked_inner.routing_table.inner.read();
let Some(ts) = rti.get_own_node_info_ts(RoutingDomain::PublicInternet) else {
// Node info is invalid, skipping this
return Ok(());
};
ts
};
let inner = &mut *self.inner.lock();
// Check for local route. If this is not a remote private route
// then we just skip the recording. We may be running a test and using
@@ -1689,7 +1727,7 @@ impl RouteSpecStore {
return Ok(());
}
if Self::with_get_remote_private_route(inner, cur_ts, key, |rpr| {
rpr.seen_our_node_info = true;
rpr.last_seen_our_node_info_ts = our_node_info_ts;
})
.is_none()
{
@@ -1734,8 +1772,6 @@ impl RouteSpecStore {
// Reset private route cache
for (_k, v) in &mut inner.cache.remote_private_route_cache {
// Our node info has changed
v.seen_our_node_info = false;
// Restart stats for routes so we test the route again
v.stats.reset();
}

View File

@@ -199,9 +199,7 @@ impl RoutingDomainEditor {
}
});
if changed {
// Mark that nothing in the routing table has seen our new node info
inner.reset_all_seen_our_node_info(self.routing_domain);
//
// Allow signed node info updates at same timestamp from dead nodes if our network has changed
inner.reset_all_updated_since_last_network_change();
}
}

View File

@@ -226,16 +226,6 @@ impl RoutingTableInner {
})
}
pub fn reset_all_seen_our_node_info(&mut self, routing_domain: RoutingDomain) {
let cur_ts = get_timestamp();
self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, _, v| {
v.with_mut(rti, |_rti, e| {
e.set_seen_our_node_info(routing_domain, false);
});
Option::<()>::None
});
}
pub fn reset_all_updated_since_last_network_change(&mut self) {
let cur_ts = get_timestamp();
self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, _, v| {
@@ -246,16 +236,43 @@ impl RoutingTableInner {
});
}
/// Return if our node info is valid yet, which is only true if we have a valid network class
pub fn has_valid_own_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.with_routing_domain(routing_domain, |rdd| rdd.common().has_valid_own_node_info())
}
/// Return a copy of our node's peerinfo
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
self.with_routing_domain(routing_domain, |rdd| {
if !rdd.common().has_valid_own_node_info() {
None
} else {
Some(rdd.common().with_peer_info(self, |pi| pi.clone()))
}
})
}
/// Return the best effort copy of our node's peerinfo
/// This may be invalid and should not be passed to other nodes,
/// but may be used for contact method calculation
pub fn get_best_effort_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
self.with_routing_domain(routing_domain, |rdd| {
rdd.common().with_peer_info(self, |pi| pi.clone())
})
}
/// Return our currently registered network class
pub fn has_valid_own_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.with_routing_domain(routing_domain, |rdd| rdd.common().has_valid_own_node_info())
/// Return our current node info timestamp
pub fn get_own_node_info_ts(&self, routing_domain: RoutingDomain) -> Option<u64> {
self.with_routing_domain(routing_domain, |rdd| {
if !rdd.common().has_valid_own_node_info() {
None
} else {
Some(
rdd.common()
.with_peer_info(self, |pi| pi.signed_node_info.timestamp()),
)
}
})
}
/// Return the domain's currently registered network class
@@ -334,7 +351,6 @@ impl RoutingTableInner {
self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, _, e| {
e.with_mut(rti, |_rti, e| {
e.clear_signed_node_info(RoutingDomain::LocalNetwork);
e.set_seen_our_node_info(RoutingDomain::LocalNetwork, false);
e.set_updated_since_last_network_change(false);
});
Option::<()>::None
@@ -504,6 +520,7 @@ impl RoutingTableInner {
let opt_relay_id = self.with_routing_domain(routing_domain, |rd| {
rd.common().relay_node().map(|rn| rn.node_id())
});
let own_node_info_ts = self.get_own_node_info_ts(routing_domain);
// Collect all entries that are 'needs_ping' and have some node info making them reachable somehow
let mut node_refs = Vec::<NodeRef>::with_capacity(self.bucket_entry_count);

View File

@@ -10,66 +10,64 @@ impl RoutingTable {
cur_ts: u64,
) -> EyreResult<()> {
// Get our node's current node info and network class and do the right thing
let own_peer_info = self.get_own_peer_info(RoutingDomain::PublicInternet);
let Some(own_peer_info) = self.get_own_peer_info(RoutingDomain::PublicInternet) else {
return Ok(());
};
let own_node_info = own_peer_info.signed_node_info.node_info();
let network_class = self.get_network_class(RoutingDomain::PublicInternet);
let network_class = own_node_info.network_class;
// Get routing domain editor
let mut editor = self.edit_routing_domain(RoutingDomain::PublicInternet);
// Do we know our network class yet?
if let Some(network_class) = network_class {
// If we already have a relay, see if it is dead, or if we don't need it any more
let has_relay = {
if let Some(relay_node) = self.relay_node(RoutingDomain::PublicInternet) {
let state = relay_node.state(cur_ts);
// Relay node is dead or no longer needed
if matches!(state, BucketEntryState::Dead) {
info!("Relay node died, dropping relay {}", relay_node);
editor.clear_relay_node();
false
} else if !own_node_info.requires_relay() {
info!(
"Relay node no longer required, dropping relay {}",
relay_node
);
editor.clear_relay_node();
false
} else {
true
}
} else {
// If we already have a relay, see if it is dead, or if we don't need it any more
let has_relay = {
if let Some(relay_node) = self.relay_node(RoutingDomain::PublicInternet) {
let state = relay_node.state(cur_ts);
// Relay node is dead or no longer needed
if matches!(state, BucketEntryState::Dead) {
info!("Relay node died, dropping relay {}", relay_node);
editor.clear_relay_node();
false
} else if !own_node_info.requires_relay() {
info!(
"Relay node no longer required, dropping relay {}",
relay_node
);
editor.clear_relay_node();
false
} else {
true
}
};
} else {
false
}
};
// Do we need a relay?
if !has_relay && own_node_info.requires_relay() {
// Do we want an outbound relay?
let mut got_outbound_relay = false;
if network_class.outbound_wants_relay() {
// The outbound relay is the host of the PWA
if let Some(outbound_relay_peerinfo) = intf::get_outbound_relay_peer().await {
// Register new outbound relay
if let Some(nr) = self.register_node_with_signed_node_info(
RoutingDomain::PublicInternet,
outbound_relay_peerinfo.node_id.key,
outbound_relay_peerinfo.signed_node_info,
false,
) {
info!("Outbound relay node selected: {}", nr);
editor.set_relay_node(nr);
got_outbound_relay = true;
}
// Do we need a relay?
if !has_relay && own_node_info.requires_relay() {
// Do we want an outbound relay?
let mut got_outbound_relay = false;
if network_class.outbound_wants_relay() {
// The outbound relay is the host of the PWA
if let Some(outbound_relay_peerinfo) = intf::get_outbound_relay_peer().await {
// Register new outbound relay
if let Some(nr) = self.register_node_with_signed_node_info(
RoutingDomain::PublicInternet,
outbound_relay_peerinfo.node_id.key,
outbound_relay_peerinfo.signed_node_info,
false,
) {
info!("Outbound relay node selected: {}", nr);
editor.set_relay_node(nr);
got_outbound_relay = true;
}
}
if !got_outbound_relay {
// Find a node in our routing table that is an acceptable inbound relay
if let Some(nr) = self.find_inbound_relay(RoutingDomain::PublicInternet, cur_ts)
{
info!("Inbound relay node selected: {}", nr);
editor.set_relay_node(nr);
}
}
if !got_outbound_relay {
// Find a node in our routing table that is an acceptable inbound relay
if let Some(nr) = self.find_inbound_relay(RoutingDomain::PublicInternet, cur_ts) {
info!("Inbound relay node selected: {}", nr);
editor.set_relay_node(nr);
}
}
}