Skip to main content

BACnetRouter

Struct BACnetRouter 

Source
pub struct BACnetRouter { /* private fields */ }
Expand description

BACnet router connecting multiple networks.

The router holds multiple ports, each bound to a different BACnet network. When an NPDU arrives on one port with a destination network that maps to another port, the router forwards the message. See the receive-queue contract for raw/tracked local receivers, admission limits, counters and lifecycle.

Every routing decision is bacnet-router-core’s engine (#1679), which the router keeps behind one lock: each port’s dispatch task takes it for one frame at a time, as the sweep and Self::table do, and nothing holds it across an await. The engine’s timers run on Tokio time from the router’s start. It follows Clause 6 where the router before #1679 didn’t (R1 to R7 of #1679): egress limits, rejects that keep routes, network numbers at startup, reachable-only and arrival-port-aware route answers, the hop count, and the router’s own address on any port.

Implementations§

Source§

impl BACnetRouter

Source

pub fn network_control_ingress_sequence(&self) -> u64

Sequence assigned to the most recent control offered to the receiver from RouterOptions::network_control_receiver or RouterOptions::network_control_receiver_with_admission, including one the receiver dropped. Saturates at u64::MAX.

Source§

impl BACnetRouter

Source

pub async fn start<T, A, C>( ports: Vec<RouterPort<T>>, options: RouterOptions<A, C>, ) -> Result<StartedRouter<A, C>, Error>

Create and start a router from a list of ports, set up as options asks (#1220).

Returns the router with its local APDU receiver, for messages without a remote destination or for which this router is the final hop, plus the network-control receiver when the options ask for it. RouterOptions::new is the plain router; see RouterOptions for the tracked APDU receiver, the wire-control policy and the network-control receiver, which combine freely.

Fails if there are no ports or more than 255 (a routing table’s Port ID names at most 255), if two ports share a network number, if a transport fails to start, or if a transport’s own MAC is longer than NpduAddress::MAX_MAC_LEN octets.

Source

pub fn control_snapshot(&self) -> ControlDecisionCounters

Count-only RB-09 decision totals (allow/deny/policy-deny per class).

Source

pub fn counters(&self) -> Counters

The engine’s counts since start: the four drop counts below, and what else the engine counts, such as the frames it refused as malformed, the relays a spent hop count stopped, the rejects it sent, the sends too long for their egress port (egress_too_long: the limit its transport’s TransportPort::max_npdu reports, #1665), and the rewrites it couldn’t write (write_failures: longer than its 65,556-octet scratch, or with a field no NPDU may carry). They saturate and name no peer. The table’s own counts are claim_counters.

They are the counts as of the last frame, sweep or table change the engine finished, copied out before its lock was let go, so reading them takes no lock: it is safe while a RouterTableGuard lives, and so are the four drop counts below, which read them.

Source

pub fn address_length_drops(&self) -> u64

NPDUs refused on any port since start because an address in them was past NpduAddress::MAX_MAC_LEN: their DLEN or SLEN (#1141), or the link-layer source MAC the port’s transport reported (#1198). Saturates at u64::MAX.

Such an NPDU is neither forwarded nor delivered locally, and a network message in it changes no route. When its DLEN or SLEN is the long one and it names a specific DNET, the router also answers the sender with Reject-Message-To-Network reason 6, ADDRESSING_ERROR (Clause 6.4.4), the way it rejects a DNET it cannot reach. A global broadcast, or an NPDU without a DNET, is only dropped. So is a frame from an over-long link-layer source MAC: that is a fault in the transport, not in the NPDU, and the router sends nothing back to such a MAC. No built-in transport reports one.

Source

pub fn global_broadcast_dadr_drops(&self) -> u64

NPDUs dropped on any port since start because their DNET was 0xFFFF and they also carried a DADR (#1379). Saturates at u64::MAX.

DNET 0xFFFF already names every device on every network (Clauses 6.2.2 and 6.3.2), so a DADR beside it contradicts it. The router does not pass such an NPDU on to its other ports, deliver it locally, act on a network message in it, or answer it with a reject: a global broadcast never draws one. The address lengths are within bounds, so the drop is counted here and not in Self::address_length_drops.

Source

pub fn broadcast_pdu_type_drops(&self) -> u64

NPDUs dropped on any port since start because they were addressed to a broadcast, global (DNET 0xFFFF) or remote (a DNET with DLEN 0), and carried an APDU other than an Unconfirmed-Request (#1491). Saturates at u64::MAX.

Only an Unconfirmed-Request may go to a broadcast network address (Clause 6.3). Anything else belongs to one peer’s transaction, so the router neither passes it on to any network nor delivers it locally, and answers it with no reject. The PDU type comes from the first APDU octet, with no decode; network messages are not affected.

Source

pub fn group_dadr_drops(&self) -> u64

NPDUs dropped on any port since start because the router would have delivered them, on a directly connected port, to a DADR that is a group destination there, such as a B/IP multicast address, and their APDU isn’t an Unconfirmed-Request (#1504). Saturates at u64::MAX.

Sent as one unicast, such an NPDU would reach every node in the group without the broadcast network addresses that Self::broadcast_pdu_type_drops filters: a confirmed request would draw an answer from each node, and an answer would reach nodes that asked nothing. The port’s rule is TransportPort::group_destinations, so a custom transport that keeps the default recognizes none. The router delivers it nowhere and answers it with no reject. A network message routed to such a DADR, or a Reject-Message-To-Network relayed to one, is dropped and counted too: it must not reach every router on that network as a broadcast. An Unconfirmed-Request for one of the groups the port’s broadcast stands for (TransportPort::local_broadcast_destinations) isn’t counted: it goes out on that port as a local broadcast, and reaches this router’s application where a DLEN 0 broadcast would (#1677). On B/IP, one for a group at another UDP port, another B/IP network, is counted.

Source

pub fn table(&self) -> RouterTableGuard<'_>

The routing table, locked for local management: read routes, or change them with no wire-control policy in the way.

Every port’s dispatch waits while the guard lives, so drop it soon, and never hold it across an await. Times in it are the router’s own (Self::now).

Don’t call it again while a guard lives on the same thread, or from a ControlAuthorizer: the lock isn’t reentrant, so that call never returns. The counts, Self::now and Self::control_snapshot take no lock and are safe to call while the guard lives.

Source

pub fn now(&self) -> Instant

The router’s time now: the clock its table and timers run on, Tokio time since the router started.

Source

pub async fn stop(&mut self)

Stop the router.

Aborts and awaits dispatch, sender and aging tasks without waiting for the local application consumer, even with a full queue. Queued local APDUs remain drainable, retaining their reply senders until consumed or the receiver is dropped. Stop is not an admission drop and does not clear depth/high-water/drop totals. See the receive-queue contract.

Auto Trait Implementations§

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