pub struct RouterOptions<A = Receiver<ReceivedApdu>, C = Receiver<ReceivedNetworkControl>> { /* private fields */ }Expand description
How BACnetRouter::start sets up a router.
Self::new gives the plain router: a raw local APDU receiver, the
permissive wire-control policy with no authorizer, and no network-control
receiver. Each builder method turns on one option, and they combine freely.
A is the local APDU receiver type the start returns, a raw
mpsc::Receiver unless Self::track_admission switches it to an
AdmissionReceiver. C is the network-control receiver type in the same
way: raw, unless Self::network_control_receiver_with_admission asks for
the tracked one.
let options = RouterOptions::new()
.track_admission()
.control_policy(ControlPolicy::Hardened)
.network_control_receiver();
let StartedRouter { router, apdus, network_control } =
BACnetRouter::start(ports, options).await?;
let counters = apdus.counters();Implementations§
Source§impl RouterOptions
impl RouterOptions
Sourcepub fn new() -> Self
pub fn new() -> Self
The plain router, with every option off.
Its local APDU receiver is a raw mpsc::Receiver. All ports share
one 256-item local queue. Full/Closed admission drops the arriving APDU
with its payload, metadata and reply sender without sending reply bytes
or a wire rejection, never evicting an older item. Admission never
waits for the consumer or stops forwarding/inline control handling.
This raw receiver has no per-source quota, admission snapshot or
depth/high-water tracking. Full/Closed drops are counted internally,
with Closed > Full precedence.
Closing retains queued items; dropping discards them. Both leave
routing active, and each later local admission attempt counts as
Closed. BACnetRouter::stop also leaves queued items drainable. Use
Self::track_admission for snapshots and per-source fairness; see the
receive-queue contract for the
raw/tracked matrix and ownership/lifecycle details.
Source§impl<A, C> RouterOptions<A, C>
impl<A, C> RouterOptions<A, C>
Sourcepub fn track_admission(
self,
) -> RouterOptions<AdmissionReceiver<ReceivedApdu>, C>
pub fn track_admission( self, ) -> RouterOptions<AdmissionReceiver<ReceivedApdu>, C>
Return the local APDU receiver as an AdmissionReceiver, which
exposes queue-admission snapshots and enforces a per-source quota.
Routing and the router’s lifecycle are unchanged. All ports share one
256-item queue and one accounting state; AdmissionReceiver::counters
returns cloneable count-only handles for depth/high-water and
admission-drop totals, readable after receiver or router drop. Failed
admission drops the arriving APDU, never an older one, and releases its
payload, metadata and reply sender without reply bytes or wire
rejections. Forwarding and inline control handling remain independent.
Closing the receiver retains queued items for draining and counts each
subsequent local arrival as a closed drop while forwarding continues.
BACnetRouter::stop also leaves queued items drainable. Dropping the
receiver discards queued items and releases their reply channels
without sending reply bytes; discarding queued items is not an
admission drop.
Each (ingress port network number,
complete source_mac byte value) may hold
at most 16 queued local APDUs, never keyed by routed NPDU SNET/SADR.
Identical MAC bytes on different ports have separate quotas.
Dequeuing releases one slot, even if the consumer retains the APDU.
Closed > fairness > Full selects one drop reason; over-quota
arrivals increment
fairness_drops
even if the queue is also globally full, unless admission is closed.
The raw receiver from RouterOptions::new does not enforce a quota.
See the receive-queue contract
for exact keys, the raw/tracked matrix and complete ownership/lifecycle
rules.
Sourcepub fn control_policy(self, policy: ControlPolicy) -> Self
pub fn control_policy(self, policy: ControlPolicy) -> Self
RB-09: the wire-control policy for state-changing routing controls.
ControlPolicy::Permissive, the default, admits them.
ControlPolicy::Hardened denies a protected control unless an
authorizer approves it, as a silent drop. Read the totals with
BACnetRouter::control_snapshot.
RB-09: a callback that decides every protected wire control, whatever
the policy. See ControlAuthorizer for what
it may and may not do.
Sourcepub fn network_control_receiver(
self,
) -> RouterOptions<A, Receiver<ReceivedNetworkControl>>
pub fn network_control_receiver( self, ) -> RouterOptions<A, Receiver<ReceivedNetworkControl>>
Also return the router’s own network-control receiver (#1175), in
StartedRouter::network_control.
The receiver gets each Reject-Message-To-Network addressed to the router itself: one with no DNET, or whose DADR is the router’s MAC on the port attached to that DNET. Such a reject still updates the routing table and is never relayed. Without this receiver, the table update is all that happens. Every other network message is still handled inline.
Records match what a non-router
NetworkLayer::enable_network_control_receiver
delivers, numbered from BACnetRouter::network_control_ingress_sequence.
The queue holds 256 controls, apart from the APDU queue. Admission never
waits: a full or closed receiver drops the arriving control, and
routing carries on. This receiver is a raw mpsc::Receiver, which
counts its drops internally only; use
Self::network_control_receiver_with_admission to read them. The two
are alternatives, and the later call decides the receiver type.
Sourcepub fn network_control_receiver_with_admission(
self,
) -> RouterOptions<A, AdmissionReceiver<ReceivedNetworkControl>>
pub fn network_control_receiver_with_admission( self, ) -> RouterOptions<A, AdmissionReceiver<ReceivedNetworkControl>>
Self::network_control_receiver, with the receiver as an
AdmissionReceiver that reports on its queue (#1242), as
NetworkLayer::enable_network_control_receiver_with_admission
does for a non-router.
AdmissionReceiver::counters reads the queue’s depth, its high-water
mark and its full and closed drop totals. Controls have no per-source
quota, so the fairness total stays zero. The records, the 256-control
queue and the drop-arriving admission are those of the raw receiver;
see the receive-queue contract
for what closing, dropping and stopping do to the counts.
Sourcepub fn congestion(self, policy: Congestion) -> Self
pub fn congestion(self, policy: Congestion) -> Self
The router’s own Router-Busy for a clogged port (#1697): off unless this sets a policy.
Each port queues at most 256 sends. With a policy set, a port whose
queue stays full for Congestion::clog_after counts as clogged:
the router broadcasts Router-Busy-To-Network on its other ports for
the networks it reaches through that one, answers each send the full
queue then refuses, when it has an origin to answer, with
Reject-Message-To-Network reason 2, and broadcasts
Router-Available-To-Network once the queue falls under the policy’s
low-water mark (Clauses 6.6.3.6, 6.6.3.7, 6.6.4).
Congestion::DEFAULT clogs after 2 s and drains under half.
The counts are in Counters.
Sourcepub fn next_hop_rejects(self) -> Self
pub fn next_hop_rejects(self) -> Self
Apply a received Reject-Message-To-Network to a learned route only when it came from that route’s next router (#1698).
Off by default: Clause 6.6.3.5 has a router update the route a
reject names whoever sent it, so a reject relayed by any router
changes it. On, one from another router is still passed on toward
its destination, but leaves the route as it was, and is counted in
Counters::not_next_hop.
Router-Busy and Router-Available are always scoped to the routes
through their sender.
Sourcepub fn duplicates(self, policy: Duplicates) -> Self
pub fn duplicates(self, policy: Duplicates) -> Self
Drop a global broadcast that repeats one seen within
Duplicates::ttl (#1698): off unless this sets it.
In a topology with a loop, routers can pass one global broadcast
around it until its hop count runs out. With this on, the router
remembers the last 512 global broadcasts by origin (SNET and SADR,
or the arrival network and link source) and a hash of the APDU,
and drops a repeat seen within the time to live, 1 s for
Duplicates::DEFAULT: not relayed, not delivered, counted in
Counters::duplicate_drops.
The standard defines no such suppression, and a sender’s own
identical retry within the time to live is dropped too.
The same record covers the network messages the router passes on with no origin, I-Am-Router, Router-Busy and Router-Available (#1810), which carry no hop count, so around a loop nothing else stops them. A repeat within the time to live is neither applied to the table nor passed on. An I-Am-Router is kept by its data alone, so two routers announcing the same networks within one time to live look like one, and the second is dropped until it announces again. A Router-Busy or Router-Available is kept by its sender (arrival network and link source) and data, so another router’s identical message is still applied, and accepting one forgets its opposite from the same sender, so Busy, Available, Busy within one time to live applies all three; at most four such resets per sender within one time to live, so a pair looping round a ring still ends. A Who-Is-Router the router passes on for a network it doesn’t know (#2020) is kept, like a global broadcast, by the origin its SNET and SADR name, and its data.
Without this option too, a global broadcast or Who-Is-Router whose
SNET and SADR name the router itself is dropped, as its own come
back round a loop, and counted in
Counters::own_source_drops
(#2021).
Entries are keyed by origin, so in a looped site this router’s own
global broadcast sent from each port’s own address (a bare copy per
port) is a different origin per port to the other routers, and may
reach their applications once per port. Send it from one port’s
address there
(SourceSelection::Port), so every
copy names one origin.
Sizing: the record must hold every global broadcast the router sees within one time to live, or the oldest is forgotten before its looped copy returns. 512 covers 512 broadcasts a second at the default; a longer time to live needs proportionally fewer a second. The relayed Who-Is-Router, I-Am-Router, Router-Busy and Router-Available entries share those 512 slots with the global broadcasts, the flow-control ones one per sender and message.