pub struct RouterTable { /* private fields */ }Expand description
BACnet routing table.
Maps network numbers to the port through which they can be reached.
Standard convergence (ConvergenceMode::Standard) is the default;
hardened corroboration is an explicit opt-in via Self::new_hardened
or Self::set_convergence.
Implementations§
Source§impl RouterTable
impl RouterTable
Sourcepub fn new_hardened() -> Self
pub fn new_hardened() -> Self
Create an empty routing table with the hardened K=2/60s gate enabled.
Explicit opt-in only: cross-port learned moves wait for a second same-(network, port) claim within 60s, with flap warnings retained. Alternating challengers reset the slot and single-shot advertisers need the reaper plus a fresh claim to converge.
Sourcepub fn set_convergence(&mut self, mode: ConvergenceMode)
pub fn set_convergence(&mut self, mode: ConvergenceMode)
Select the convergence mode. Switching to standard clears no routes but leaves any hardened pending slots to lazy/reaper expiry; switching to hardened keeps installed routes and applies the gate to later claims.
Sourcepub fn convergence(&self) -> ConvergenceMode
pub fn convergence(&self) -> ConvergenceMode
Current convergence mode.
Sourcepub fn pending_count(&self) -> usize
pub fn pending_count(&self) -> usize
Number of pending hardened challengers (bounded by live learned routes).
Sourcepub fn claim_snapshot(&self) -> RoutingClaimSnapshot
pub fn claim_snapshot(&self) -> RoutingClaimSnapshot
Read a consistent, non-resetting copy of this table’s routing-claim totals.
For a live router, call router.table().lock().await.claim_snapshot().
The snapshot owns only counts and remains readable after table drop;
cloning the table copies, rather than shares, its counters and records.
Sourcepub fn expire_pending_at(&mut self, now: Instant) -> Vec<u16>
pub fn expire_pending_at(&mut self, now: Instant) -> Vec<u16>
Discard hardened pending slots older than 60s at now.
Periodic reaper for idle routes: lazy expiry alone cannot clear a slot
with no further claims. Counts one pending_expired per discarded
slot; other cleanup (removal, aging, manual edits, refresh) does not
count. Returns the discarded network numbers in ascending order.
Sourcepub fn add_direct(&mut self, network: u16, port_index: usize)
pub fn add_direct(&mut self, network: u16, port_index: usize)
Add a directly-connected network on the given port. Network 0 and 0xFFFF are reserved and will be silently ignored.
Sourcepub fn add_learned(
&mut self,
network: u16,
port_index: usize,
next_hop_mac: MacAddr,
)
pub fn add_learned( &mut self, network: u16, port_index: usize, next_hop_mac: MacAddr, )
Add a learned route (network reachable via a next-hop router on the given port).
Network 0 and 0xFFFF are reserved and will be silently ignored.
Does not overwrite direct routes. Control-plane confirmation: sets
last_seen; preserves last_used on a same-port refresh, resets it
on a new or moved path.
Sourcepub fn add_learned_at(
&mut self,
network: u16,
port_index: usize,
next_hop_mac: MacAddr,
now: Instant,
)
pub fn add_learned_at( &mut self, network: u16, port_index: usize, next_hop_mac: MacAddr, now: Instant, )
Injectable-time variant of Self::add_learned for deterministic tests.
Sourcepub fn wire_port_id(port_index: usize) -> Option<u8>
pub fn wire_port_id(port_index: usize) -> Option<u8>
Wire Port ID for a dispatch port index (135-2020 6.4.7, Fig. 6-11).
A zero Port ID instructs the receiver to delete every row it holds for
that DNET, so 0 is a removal trigger, never a real port. The stable
mapping is port_index + 1; None when the index cannot fit the
1-octet field (unreachable for a bounded port vector, handled by
skipping the entry on encode).
Sourcepub fn port_index_for_wire_id(port_id: u8, port_count: usize) -> Option<usize>
pub fn port_index_for_wire_id(port_id: u8, port_count: usize) -> Option<usize>
Dispatch port index for a wire Port ID, given the local port count.
Inverse of Self::wire_port_id: Port ID 0 (the purge trigger) and
IDs past the local port count map to None; the caller applies purge
versus unknown-port skip semantics.
Sourcepub fn sorted_networks(&self) -> Vec<u16>
pub fn sorted_networks(&self) -> Vec<u16>
Network numbers in ascending order for deterministic query replies.
Sourcepub fn add_learned_with_flap_detection(
&mut self,
network: u16,
port_index: usize,
next_hop_mac: MacAddr,
) -> bool
pub fn add_learned_with_flap_detection( &mut self, network: u16, port_index: usize, next_hop_mac: MacAddr, ) -> bool
Add a learned route immediately (manual table update, without corroboration). Detects rapid port changes for operator visibility but never suppresses updates.
Returns true if the route was inserted/updated.
Sourcepub fn add_learned_with_flap_detection_at(
&mut self,
network: u16,
port_index: usize,
next_hop_mac: MacAddr,
now: Instant,
) -> bool
pub fn add_learned_with_flap_detection_at( &mut self, network: u16, port_index: usize, next_hop_mac: MacAddr, now: Instant, ) -> bool
Injectable-time variant of Self::add_learned_with_flap_detection.
A port change installs last_seen = now with last_used = None (the
new path has not forwarded yet); a same-port refresh preserves
last_used via Self::add_learned_at.
Sourcepub fn mark_busy(&mut self, network: u16, deadline: Instant)
pub fn mark_busy(&mut self, network: u16, deadline: Instant)
Mark a network as busy with a deadline for auto-clear (spec 6.6.3.6).
Directly-connected paths are never overridden by Busy: they are served
locally, not via the announcing peer. A permanently Unreachable entry
is likewise left untouched: a temporary congestion claim must never
resurrect a failed route via the 30s auto-clear — only Available or
fresh learning lifts Unreachable. Touches neither last_seen nor
last_used (RB-06 freshness split).
Sourcepub fn mark_available(&mut self, network: u16)
pub fn mark_available(&mut self, network: u16)
Mark a network as available, clearing any busy state (spec 6.6.3.7).
Directly-connected paths are never touched: Busy/Available only covers
routes served via the announcing peer. Touches neither last_seen nor
last_used (RB-06 freshness split).
Sourcepub fn is_served_via_peer(
&self,
network: u16,
port_index: usize,
next_hop: &MacAddr,
) -> bool
pub fn is_served_via_peer( &self, network: u16, port_index: usize, next_hop: &MacAddr, ) -> bool
Whether network is served via the announcing peer on this ingress
path: a learned (never directly-connected) route whose egress port and
next-hop MAC match the immediate link peer — not the routed SNET/SADR.
RB-04 locks this predicate to 135-2020 Clauses 6.6.3.6/6.6.3.7: a Busy
that omits its network list asks that traffic be held back for every
network the router normally serves, and an Available without a list
lifts that hold for all of them. Explicit lists intersect with the
same set: listed nets outside the announcing path are not marked.
Sourcepub fn routes_served_via_peer(
&self,
port_index: usize,
next_hop: &MacAddr,
) -> Vec<u16>
pub fn routes_served_via_peer( &self, port_index: usize, next_hop: &MacAddr, ) -> Vec<u16>
All networks served via the announcing peer: the omitted-list scope for Router-Busy/Router-Available-To-Network (Clauses 6.6.3.6/6.6.3.7).
Sourcepub fn mark_unreachable(&mut self, network: u16)
pub fn mark_unreachable(&mut self, network: u16)
Mark a network as permanently unreachable (spec 6.6.3.5, reject reason 1).
Keeps the entry in the table (unlike remove).
Sourcepub fn clear_expired_busy(&mut self)
pub fn clear_expired_busy(&mut self)
Clear busy state for entries whose busy_until deadline has elapsed.
Sourcepub fn effective_reachability(&self, network: u16) -> Option<ReachabilityStatus>
pub fn effective_reachability(&self, network: u16) -> Option<ReachabilityStatus>
Get effective reachability, checking busy_until inline for immediate accuracy. This avoids up to 90s worst-case from the 60s aging sweep granularity.
Sourcepub fn lookup(&self, network: u16) -> Option<&RouteEntry>
pub fn lookup(&self, network: u16) -> Option<&RouteEntry>
Look up the route for a network number.
Sourcepub fn lookup_mut(&mut self, network: u16) -> Option<&mut RouteEntry>
pub fn lookup_mut(&mut self, network: u16) -> Option<&mut RouteEntry>
Lookup a mutable route entry by network number. Clears any pending challenger since the caller can replace the route state.
Sourcepub fn remove(&mut self, network: u16) -> Option<RouteEntry>
pub fn remove(&mut self, network: u16) -> Option<RouteEntry>
Remove a route.
Sourcepub fn networks_not_on_port(&self, exclude_port: usize) -> Vec<u16>
pub fn networks_not_on_port(&self, exclude_port: usize) -> Vec<u16>
List networks reachable via ports OTHER than exclude_port.
Sourcepub fn networks_on_port(&self, port_index: usize) -> Vec<u16>
pub fn networks_on_port(&self, port_index: usize) -> Vec<u16>
List networks reachable on a given port.
Sourcepub fn touch(&mut self, network: u16)
pub fn touch(&mut self, network: u16)
Record data-plane forwarding use for a learned route.
Sets last_used only; last_seen (control-plane confirmation) is
untouched so traffic cannot extend expiry. Direct routes are unaffected
since they never expire.
Sourcepub fn touch_used_at(&mut self, network: u16, now: Instant)
pub fn touch_used_at(&mut self, network: u16, now: Instant)
Injectable-time variant of Self::touch for deterministic tests.
Sourcepub fn purge_stale(&mut self, max_age: Duration) -> Vec<u16>
pub fn purge_stale(&mut self, max_age: Duration) -> Vec<u16>
Remove learned routes not confirmed within max_age.
Keys off last_seen only; last_used never extends lifetime.
Direct routes (last_seen == None) never expire.
Returns the network numbers that were purged.
Sourcepub fn purge_stale_at(&mut self, now: Instant, max_age: Duration) -> Vec<u16>
pub fn purge_stale_at(&mut self, now: Instant, max_age: Duration) -> Vec<u16>
Injectable-time variant of Self::purge_stale for deterministic tests.