Expand description
NetworkLayer for local BACnet packet assembly and dispatch.
The network layer wraps a transport and provides APDU-level send/receive by handling NPDU encoding/decoding. This is a non-router implementation: it does not forward messages between networks, but it can address remote devices through local routers via NPDU destination fields (DNET/DADR).
§Receive-queue admission
NetworkLayer and BACnetRouter use the
same drop-arriving policy for application delivery. Each receive queue holds
256 items. Admission never waits for a consumer and never evicts an older
queued item. A tracked NetworkLayer APDU queue and an opted-in control queue
have independent capacities, so saturation of either does not stall
admission to the other. All router ports share one local APDU queue; its
saturation does not stop forwarding or inline network-message handling.
The raw NetworkLayer receiver is different: ApduReceiver has no queue
of its own (#1713). It decodes the transport’s NPDUs in the task that polls
it, so network controls flow only while it is polled: while the
consumer is busy in an inline await they wait in the transport’s queue,
which adds latency, not a deadlock. That queue is the only buffer. It
holds 256 NPDUs on B/IP, B/IPv6, BACnet/SC, Ethernet and loopback, and 64
on MS/TP, whose deliveries were buffered 64 + 256 deep before. Under
overload it bounds memory and the transport counts what it drops: B/IP
and B/IPv6 as npdu_channel_full in receive_counters(), MS/TP as
delivery_drops in its diagnostics’ MAC counters, BACnet/SC in
npdu_drop_counts(), and loopback in receive_drops(). Ethernet has no
drop counter, only throttled warnings.
§Raw and tracked receivers
| Receiver kind | Entry points | Capacity | Per-source quota | Accounting |
|---|---|---|---|---|
| Raw APDU, NetworkLayer | NetworkLayer::start (ApduReceiver) | The transport’s queue | None | Drops counted by the transport; no admission snapshot |
| Raw APDU, router | BACnetRouter::start with RouterOptions::new | 256 | None | Full/Closed counted internally; no admission snapshot or depth/high-water tracking |
| Tracked APDU | NetworkLayer::start_with_admission, RouterOptions::track_admission | 256 | 16 queued APDUs per key, defined below | Exact depth/high-water and Full/fairness/Closed totals via AdmissionReceiver::counters |
| Raw control | NetworkLayer::enable_network_control_receiver, RouterOptions::network_control_receiver | 256, separate from APDUs | None | Full/Closed counted internally; no admission snapshot or depth/high-water tracking |
| Tracked control | NetworkLayer::enable_network_control_receiver_with_admission, RouterOptions::network_control_receiver_with_admission | 256, separate from APDUs | None | Exact depth/high-water and Full/Closed totals; fairness always zero |
The router’s raw receivers and the raw control receiver are
tokio::sync::mpsc::Receivers; NetworkLayer’s raw APDU receiver is an
ApduReceiver; tracked receivers are the AdmissionReceiver
alternatives. APDU and control opt-ins are
independent. Routers process network messages inline; their control receiver
gets only the Reject-Message-To-Network messages addressed to the router
itself, and every closed admission attempt counts as Closed without
disabling the stream. priority_channel is not
used by these ingress paths.
§Source keys and drop precedence
Tracked APDU queues cap each key at 16 queued, unconsumed APDUs:
- NetworkLayer: the complete transport-native
ReceivedApdu::source_macbyte value. Its single transport needs no separate port key. - Router local delivery: (ingress port network number,
source_mac). Identical MAC bytes on different ports have separate quotas. Neither key uses routed NPDU SNET/SADR (source_network) or authenticates a peer.
Each successful recv or
try_recv releases a queue slot and, for
tracked APDUs, that key’s quota slot before returning the item. Retaining or
processing the returned item does not occupy either slot. Sixteen keys at
their quota fill the 256-item queue; a key below quota can still encounter
global Full. This is a queued-item cap, not a byte/rate limit or a delivery
guarantee. Raw receivers and control queues have no per-source quota.
Drop attribution is Closed > fairness > Full: closed admission wins; otherwise a tracked APDU whose key already holds 16 items is a fairness drop, even if the queue also holds 256 items; otherwise global saturation is Full. Only the winning reason is counted. Every admission drop releases the arriving item’s owned payload, metadata and any reply sender without sending reply bytes or generating a wire rejection. This does not suppress ordinary router forwarding/reject behavior or change transport no-reply handling.
§Close, drop, stop, and observation
Closing either receiver kind prevents further admission but retains queued items for draining. Dropping a receiver discards them and releases their payloads, metadata and reply senders without sending reply bytes. For tracked receivers, close preserves depth until dequeued; drop sets depth to zero. Neither operation resets high-water/drop totals, and discarding queued items is not an admission drop.
A tracked NetworkLayer ends its dispatcher on the first APDU admission
attempt after that receiver closes/drops, also ending control delivery.
Closing an ApduReceiver stops the transport handing up NPDUs while
those it queued stay drainable; dropping it discards them. A closed/dropped
control receiver instead disables only that stream on the first subsequent
control admission attempt. Later discarded controls (like controls received
without either opt-in) are not admission attempts. Router forwarding and
inline control handling continue after local delivery closes/drops; each
subsequent local admission attempt is counted as Closed.
NetworkLayer::stop after start and BACnetRouter::stop
end dispatch without waiting for application queues to drain; queued items
remain drainable, for an ApduReceiver the NPDUs the transport queued. Stopping is not an admission drop. An independently owned
QueueAdmissionCounters handle remains readable after the receiver, layer,
or router is dropped; it keeps accounting alive, not the channel or tasks.
snapshot is consistent for one queue,
not atomic across queues, and exposes counts only, not payloads, per-source
identities or per-source totals. Its three drop totals saturate at u64::MAX.
Structs§
- Admission
Receiver - Opt-in, one-consumer receiver with exact queue-depth accounting.
- Apdu
Receiver - The APDUs a
NetworkLayerreceives, fromNetworkLayer::start. - Counters
- What a
NetworkLayerhas dropped on receipt since it was created, by reason. Each total saturates atu64::MAX. A dropped NPDU reaches neither the APDU nor the control receiver, so it never shows up in their admission counters. The totals never change a decision, and name no peer. - Issued
Apdu - One APDU handed to a local-issuance send.
- Network
Layer - Non-router BACnet network layer.
- Queue
Admission Counters - Cloneable snapshot handle obtained from an
AdmissionReceiver. - Queue
Admission Snapshot - A consistent, count-only snapshot of one network-layer receive queue.
- Received
Apdu - A received APDU with source addressing information.
- Received
Network Control - A decoded network-layer control message received from a transport peer.
- Received
Network Report - A
NetworkReportand where it came from. - Routed
Target - A remote device reached through a local router.
Enums§
- Network
Report - A network message that tells a non-routing node something about the
internetwork, read from a
ReceivedNetworkControl.
Constants§
- SENDABLE_
NETWORK_ MESSAGES - The network messages
NetworkLayer::send_network_messagesends: the two questions a non-routing node asks. The rest change routing tables, flow control or connections, or announce routes this node doesn’t have, so each joins the list only deliberately, with its own review.