Skip to main content

RouterOptions

Struct RouterOptions 

Source
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

Source

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>

Source

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.

Source

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.

Source

pub fn control_authorizer(self, authorizer: ControlAuthorizer) -> Self

RB-09: a callback that decides every protected wire control, whatever the policy. See ControlAuthorizer for what it may and may not do.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Trait Implementations§

Source§

impl<A, C> Debug for RouterOptions<A, C>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RouterOptions

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<A = Receiver<ReceivedApdu>, C = Receiver<ReceivedNetworkControl>> !RefUnwindSafe for RouterOptions<A, C>

§

impl<A = Receiver<ReceivedApdu>, C = Receiver<ReceivedNetworkControl>> !UnwindSafe for RouterOptions<A, C>

§

impl<A, C> Freeze for RouterOptions<A, C>
where PhantomData<fn() -> (A, C)>: Freeze,

§

impl<A, C> Send for RouterOptions<A, C>
where PhantomData<fn() -> (A, C)>: Send,

§

impl<A, C> Sync for RouterOptions<A, C>
where PhantomData<fn() -> (A, C)>: Sync,

§

impl<A, C> Unpin for RouterOptions<A, C>
where PhantomData<fn() -> (A, C)>: Unpin,

§

impl<A, C> UnsafeUnpin for RouterOptions<A, C>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more