checkpoint
This commit is contained in:
@@ -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,
|
||||
},
|
||||
|
@@ -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)
|
||||
|
@@ -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))
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user