Skip to content

(17g) lldp: fast start, shutdown ordering, and a required datapath namespace - #1925

Draft
daniel-noland wants to merge 3 commits into
pr/daniel-noland/lldp-receivefrom
pr/daniel-noland/lldp-lifecycle
Draft

daniel-noland wants to merge 3 commits into
pr/daniel-noland/lldp-receivefrom
pr/daniel-noland/lldp-lifecycle

Conversation

@daniel-noland

@daniel-noland daniel-noland commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Native LLDP for the gateway. The dataplane owns its NICs, so nothing else can speak LLDP for them, and lldpd/lldpad are hostile to automation. LLDP itself is small, so the dataplane transmits and receives it.

This used to be one PR. It is now a stack of seven, each building on the one before:

Stacked on (16) #1905, capture. (18) #1907, rate limiting, and (19) #1919, traffic protection, build on top.

This PR: fast start, shutdown ordering, and a required datapath namespace

  1. Fast start on a new neighbour (802.1AB newNeighbor). When the table learns a neighbour, that port's transmitter fast-starts, so the newcomer learns about us as quickly as we learned about it.
  2. The shutdown frame now goes out before the workers stop. drain_in_order used to cancel the workers first, so a shutdown frame sent by a mgmt task never left. lifecycle gains a protocols subsystem, drained before the workers. On stop, the transmitter waits up to 500 ms for its injection queue to drain.
  3. A separate datapath namespace is required. Init always isolates both drivers' interfaces, so the runtime's no-bridge branch was dead. A shared namespace is now a startup failure, and the bridge, and with it LLDP, is always built.

Testing

  • Transmitter, in virtual time: a new neighbour restarts fast start, doesn't lengthen one already running, and waits for the link while silent. Stopping waits for the shutdown frame to leave, but a frame that never leaves doesn't hold up stopping.
  • lifecycle: protocols see the workers still running while they shut down.

Not covered

  • No VTEP/loopback fallback for the management address. Deliberately skipped.
  • 802.1AB is cited by section number, not with duvet. The standard is free to read under IEEE GET but not redistributable, and our duvet setup vendors the spec text into the repo.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 16, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.78189% with 71 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dataplane/src/runtime.rs 0.00% 26 Missing ⚠️
lldp/src/transmitter.rs 75.86% 11 Missing and 3 partials ⚠️
dataplane/src/drivers/cpbridge.rs 0.00% 13 Missing ⚠️
dataplane/src/lldp_agent.rs 75.51% 12 Missing ⚠️
lldp/src/sink.rs 0.00% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/lldp-receive branch from 897dec6 to 87e0037 Compare October 16, 2026 05:47
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/lldp-lifecycle branch from a7a9da1 to 1d2adae Compare October 16, 2026 05:47
daniel-noland and others added 3 commits October 16, 2026 02:01
IEEE 802.1AB-2016 9.2.9: hearing a neighbour we did not know sets
newNeighbor, and the transmit timer machine tells it about us at once
and then sends fast (txFastInit frames, msgFastTx apart), so a newcomer
learns about us as quickly as we learned about it.

The transmitter takes a per-port `new_neighbor` watch.  A new neighbour
while steady sends now and goes fast; while already fast, the send
counts as one of the fast ones rather than lengthening the burst; while
silent it waits for the port to come up, whose own fast start covers
it.  The receive task signals the port's transmitter whenever its table
reports a neighbour as new, and only then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
On SIGTERM, `drain_in_order` cancelled the workers first and mgmt last.
LLDP ran under mgmt, so the shutdown frame it sends on the way out was
queued after the datapath had stopped draining the injection queue, and
never left: neighbours kept us until our TTL (121 s) ran out.

- lifecycle: add a `protocols` subsystem for control-plane protocols
  that send through the datapath, drained before the workers.
- lldp: on stop, wait (at most 500 ms) for the sink to report it has
  taken everything queued, so the frame has actually left before the
  workers are cancelled.  `FrameSink::is_drained` defaults to true; an
  mpsc sender is drained when its capacity is back to the maximum.
- dataplane: run the LLDP tasks under `protocols`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Init hands both drivers' interfaces a namespace of their own
(#1890, and #1929 for the kernel driver), so the datapath
namespace is never the one the dataplane runs in.  The runtime still
skipped the control-plane bridge when it was, a branch left over from
before that move: unreachable, and wrong if it were reached, since the
bridge is the control plane's only path to the wire (and LLDP's).

Treat a shared namespace as the startup failure it is, and always build
the bridge.  The bridge stays optional through the drivers for now,
since the bridge-failure path and the driver tests still pass `None`;
making it required belongs with the cleanup of the namespace chapter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/lldp-lifecycle branch from 1d2adae to 5934986 Compare October 16, 2026 08:43
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/lldp-receive branch from 87e0037 to 15c4688 Compare October 16, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant