use super::*;
impl Node {
pub(crate) fn is_tree_peer(&self, peer_addr: &NodeAddr) -> bool {
if !self.tree_state.is_root() && self.tree_state.my_declaration().parent_id() == peer_addr {
return true;
}
if let Some(decl) = self.tree_state.peer_declaration(peer_addr)
&& decl.parent_id() == self.node_addr()
{
return true;
}
false
}
pub fn find_next_hop(&mut self, dest_node_addr: &NodeAddr) -> Option<&ActivePeer> {
if dest_node_addr == self.node_addr() {
return None;
}
let now_ms = Self::now_ms();
let direct_session_degraded =
self.session_direct_path_blocks_direct_payload(dest_node_addr, now_ms);
let healthy_direct_route = self
.peers
.get(dest_node_addr)
.filter(|peer| peer.is_healthy() && !direct_session_degraded)
.map(|_| *dest_node_addr);
if let Some(direct_addr) = healthy_direct_route
&& self
.peers
.get(&direct_addr)
.is_some_and(|peer| peer.link_cost() <= 1.0 + ROUTING_FALLBACK_MIN_COST_ADVANTAGE)
{
return self.peers.get(&direct_addr);
}
let direct_payload_eligible = healthy_direct_route.is_some();
let payload_candidate_can_send = |addr: &NodeAddr, peer: &ActivePeer| {
if addr == dest_node_addr {
direct_payload_eligible
} else {
peer.is_healthy()
}
};
let fallback_beats_direct = |node: &Self, fallback_addr: NodeAddr| {
node.route_candidate_beats_direct(healthy_direct_route, fallback_addr)
};
let sendable_learned_peers = if self.config.node.routing.mode == RoutingMode::ReplyLearned {
Some(
self.peers
.iter()
.filter(|(addr, peer)| payload_candidate_can_send(addr, peer))
.map(|(addr, _)| *addr)
.collect::<HashSet<_>>(),
)
} else {
None
};
let explore_fallback = sendable_learned_peers.as_ref().is_some_and(|sendable| {
self.learned_routes.should_explore_fallback(
dest_node_addr,
now_ms,
self.config.node.routing.learned_fallback_explore_interval,
|addr| sendable.contains(addr),
)
});
if let Some(sendable) = &sendable_learned_peers
&& !explore_fallback
{
let eligible = sendable
.iter()
.copied()
.filter(|addr| fallback_beats_direct(self, *addr))
.collect::<HashSet<_>>();
if !eligible.is_empty()
&& let Some(next_hop_addr) =
self.learned_routes
.select_next_hop(dest_node_addr, now_ms, |addr| eligible.contains(addr))
{
return self.peers.get(&next_hop_addr);
}
}
let Some(dest_coords) = self
.coord_cache
.get_and_touch(dest_node_addr, now_ms)
.cloned()
else {
if (healthy_direct_route.is_none() || explore_fallback)
&& let Some(sendable) = &sendable_learned_peers
&& let Some(next_hop_addr) =
self.learned_routes
.select_next_hop(dest_node_addr, now_ms, |addr| sendable.contains(addr))
{
return self.peers.get(&next_hop_addr);
}
if let Some(direct_addr) = healthy_direct_route {
return self.peers.get(&direct_addr);
}
return None;
};
let coordinate_route_addr = {
let candidates: Vec<&ActivePeer> = self
.peers
.iter()
.filter(|(addr, peer)| {
payload_candidate_can_send(addr, peer) && peer.may_reach(dest_node_addr)
})
.map(|(_, peer)| peer)
.collect();
if !candidates.is_empty() {
self.select_best_candidate(&candidates, &dest_coords)
.map(|peer| *peer.node_addr())
} else {
None
}
};
if let Some(next_hop_addr) = coordinate_route_addr
&& fallback_beats_direct(self, next_hop_addr)
{
return self.peers.get(&next_hop_addr);
}
let tree_route_addr = self.select_tree_payload_candidate(
&dest_coords,
dest_node_addr,
direct_payload_eligible,
);
if let Some(next_hop_addr) = tree_route_addr
&& fallback_beats_direct(self, next_hop_addr)
{
return self.peers.get(&next_hop_addr);
}
if explore_fallback {
return sendable_learned_peers.as_ref().and_then(|sendable| {
self.learned_routes
.select_next_hop(dest_node_addr, now_ms, |addr| sendable.contains(addr))
.and_then(|next_hop_addr| self.peers.get(&next_hop_addr))
});
}
if let Some(direct_addr) = healthy_direct_route {
return self.peers.get(&direct_addr);
}
if let Some(sendable) = &sendable_learned_peers
&& let Some(next_hop_addr) =
self.learned_routes
.select_next_hop(dest_node_addr, now_ms, |addr| sendable.contains(addr))
{
return self.peers.get(&next_hop_addr);
}
None
}
pub(in crate::node) fn find_transit_next_hop(
&mut self,
dest_node_addr: &NodeAddr,
previous_hop: &NodeAddr,
) -> Option<NodeAddr> {
if dest_node_addr == self.node_addr() {
return None;
}
if dest_node_addr != previous_hop
&& self
.peers
.get(dest_node_addr)
.is_some_and(|peer| peer.is_healthy())
{
return Some(*dest_node_addr);
}
let next_hop_addr = *self.find_next_hop(dest_node_addr)?.node_addr();
if &next_hop_addr == previous_hop {
self.record_route_failure(*dest_node_addr, next_hop_addr);
return None;
}
Some(next_hop_addr)
}
pub(super) fn route_candidate_beats_direct(
&self,
healthy_direct_route: Option<NodeAddr>,
candidate_addr: NodeAddr,
) -> bool {
let Some(direct_addr) = healthy_direct_route else {
return true;
};
if candidate_addr == direct_addr {
return false;
}
let Some(direct) = self.peers.get(&direct_addr) else {
return true;
};
if self.active_peer_uses_configured_static_udp_path(&direct_addr) {
return false;
}
let Some(candidate) = self.peers.get(&candidate_addr) else {
return false;
};
if !candidate.is_healthy() {
return false;
}
let direct_cost = direct.link_cost();
let candidate_cost = candidate.link_cost();
candidate_cost + ROUTING_FALLBACK_MIN_COST_ADVANTAGE < direct_cost
}
pub(super) fn select_tree_payload_candidate(
&self,
dest_coords: &crate::tree::TreeCoordinate,
direct_dest: &NodeAddr,
direct_payload_eligible: bool,
) -> Option<NodeAddr> {
if self.tree_state.my_coords().root_id() != dest_coords.root_id() {
return None;
}
let my_distance = self.tree_state.my_coords().distance_to(dest_coords);
let mut best: Option<(NodeAddr, usize)> = None;
for (peer_addr, peer) in &self.peers {
if peer_addr == direct_dest {
if !direct_payload_eligible {
continue;
}
} else if !peer.is_healthy() {
continue;
}
let Some(peer_coords) = self.tree_state.peer_coords(peer_addr) else {
continue;
};
let distance = peer_coords.distance_to(dest_coords);
if distance >= my_distance {
continue;
}
let dominated = match &best {
None => true,
Some((best_id, best_dist)) => {
distance < *best_dist || (distance == *best_dist && peer_addr < best_id)
}
};
if dominated {
best = Some((*peer_addr, distance));
}
}
best.map(|(peer_addr, _)| peer_addr)
}
pub(in crate::node) fn session_direct_path_is_degraded(
&mut self,
dest: &NodeAddr,
now_ms: u64,
) -> bool {
self.session_direct_degradation.is_degraded(dest, now_ms)
}
pub(in crate::node) fn session_direct_path_blocks_direct_payload(
&mut self,
dest: &NodeAddr,
now_ms: u64,
) -> bool {
self.session_direct_path_is_degraded(dest, now_ms)
&& !self.active_peer_uses_configured_static_udp_path(dest)
}
pub(in crate::node) fn mark_session_direct_path_degraded(
&mut self,
dest: NodeAddr,
now_ms: u64,
) -> bool {
self.session_direct_degradation
.mark_degraded(dest, now_ms, SESSION_DIRECT_DEGRADED_HOLD_MS)
}
pub(in crate::node) fn clear_session_direct_path_degraded(&mut self, dest: &NodeAddr) -> bool {
self.session_direct_degradation.clear(dest)
}
pub(in crate::node) fn learn_reverse_route(
&mut self,
destination: NodeAddr,
next_hop: NodeAddr,
) {
if self.config.node.routing.mode != RoutingMode::ReplyLearned
|| destination == *self.node_addr()
{
return;
}
let now_ms = Self::now_ms();
self.learned_routes.learn(
destination,
next_hop,
now_ms,
self.config.node.routing.learned_ttl_secs,
self.config.node.routing.max_learned_routes_per_dest,
);
}
pub(in crate::node) fn record_route_failure(
&mut self,
destination: NodeAddr,
next_hop: NodeAddr,
) {
if self.config.node.routing.mode != RoutingMode::ReplyLearned {
return;
}
self.learned_routes.record_failure(&destination, &next_hop);
}
pub(crate) fn learned_route_table_snapshot(&self, now_ms: u64) -> LearnedRouteTableSnapshot {
self.learned_routes.snapshot(now_ms)
}
pub(in crate::node) fn purge_learned_routes(&mut self, now_ms: u64) {
self.learned_routes.purge_expired(now_ms);
}
pub(super) fn select_best_candidate<'a>(
&'a self,
candidates: &[&'a ActivePeer],
dest_coords: &crate::tree::TreeCoordinate,
) -> Option<&'a ActivePeer> {
let my_distance = self.tree_state.my_coords().distance_to(dest_coords);
let mut best: Option<(&ActivePeer, f64, usize)> = None;
for &candidate in candidates {
if !candidate.can_send() {
continue;
}
let cost = candidate.link_cost();
let dist = self
.tree_state
.peer_coords(candidate.node_addr())
.map(|pc| pc.distance_to(dest_coords))
.unwrap_or(usize::MAX);
if dist >= my_distance {
continue;
}
let dominated = match &best {
None => true,
Some((_, best_cost, best_dist)) => {
cost < *best_cost
|| (cost == *best_cost && dist < *best_dist)
|| (cost == *best_cost
&& dist == *best_dist
&& candidate.node_addr() < best.as_ref().unwrap().0.node_addr())
}
};
if dominated {
best = Some((candidate, cost, dist));
}
}
best.map(|(peer, _, _)| peer)
}
pub fn destination_in_filters(&self, dest: &NodeAddr) -> Vec<&ActivePeer> {
self.peers.values().filter(|p| p.may_reach(dest)).collect()
}
}