Expand description
§rlldpctl
A pure-Rust client for lldpd’s control
socket: list interfaces and read discovered LLDP neighbors without
shelling out to lldpcli or linking liblldpctl.so.
let mut client = rlldpctl::Client::connect()?;
for iface in client.interfaces()? {
let details = client.interface(&iface.name)?;
for neighbor in &details.neighbors {
println!(
"{}: {} ({:?})",
iface.name,
neighbor.chassis.name.as_deref().unwrap_or("?"),
neighbor.port_id_str(),
);
}
}§What this actually talks to, and why that’s fragile
lldpd does not define a stable IPC protocol for lldpcli/liblldpctl
to talk to it with. What’s actually on the wire is lldpd’s own internal
C structures (struct lldpd_port, struct lldpd_chassis, …), memcpy’d
with a small pointer-graph-following envelope around them (see
src/marshal.c and src/ctl.c upstream). It is coupled to:
- The exact
lldpdversion - and this is a narrower window than you’d guess. Diffingsrc/lldpd-structs.hacross every tag from0.9.0tomastershows the framing and marshaling mechanism has been stable since 2008, but several fields this crate actually reads are recent:lldpd_interface.aliasandlldpd_hardware.h_ifaliaswere only added in1.0.21,lldpd_port.p_vlan_advertise_patternin1.0.20, and thehmsg_typeenum gained a new member at1.0.14that shiftsGET_INTERFACE’s numeric value from 5 to 6 (sending 6 to an older daemon would silently hitGET_DEFAULT_PORTinstead).masterhas already added an unreleasedlldpd_hardware.h_flags_previousfield on top of1.0.22. Net effect: this crate is only verified againstlldpd1.0.21-1.0.22 (matching this workspace’s Yocto-packaged1.0.22) - not “any 1.0.x”, and not yet whatever ships after1.0.22. A different version can and does change these structs, which would silently desync parsing rather than fail loudly. - The build-time feature flags
lldpdwas compiled with. Several struct fields exist only under#ifdef ENABLE_DOT1/ENABLE_DOT3/ENABLE_LLDPMED/ENABLE_CUSTOM. This crate assumesdot1,dot3,cdp,fdpandlldpmedare enabled andcustomis not, matching this workspace’slldpdrecipe (PACKAGECONFIG ??= "cdp fdp edp sonmp lldpmed dot1 dot3"). - The host’s C ABI, specifically pointer width (raw structs are
declared
#[repr(C)]so this is handled automatically for whichever target you build for, as long as it matches thelldpdbinary you’re talking to) andtime_t’s width tracking pointer width (true of the traditional glibc/musl Linux ABI on both 32- and 64-bit; not true of a 32-bit target using a 64-bit-time_t“time64” C library).
In short: this is not a stable protocol client the way an HTTP or DBus
client is. It’s the same trade-off upstream itself accepts internally
between lldpd and liblldpctl.so - just reimplemented without linking
that library. See README.md for the full rationale and scope.
§Scope
v1 implements four requests: GET_INTERFACES (list interfaces),
GET_INTERFACE (one interface’s local info + discovered neighbors), and
SUBSCRIBE/NOTIFICATION (a live feed of neighbor changes, via
Client::subscribe and Subscription). Nothing that changes daemon
state (SET_PORT, SET_CONFIG, …) is implemented.
§Async
Enable the tokio feature for AsyncClient/AsyncSubscription, the
same API backed by tokio::net::UnixStream instead of
std::os::unix::net::UnixStream. Both share this crate’s wire
encode/decode logic verbatim - only the I/O differs - so they can’t
disagree about the wire format. This is entirely additive: the sync API
has no tokio dependency and keeps working with the tokio feature off.
Structs§
- Capabilities
- System capabilities bitmap (IEEE 802.1AB Table 8-4). Wraps the raw
u16lldpd sends; useCapabilities::containsto test individual bits. - Chassis
- A neighbor (or local) chassis: the “this whole box” identity, as opposed to one of its ports.
- Client
- A connection to a running
lldpd. - Interface
- One local interface, as returned by
crate::Client::interfaces. - Interface
Details - Full state of one local interface (
GET_INTERFACE): its own identity plus every neighbor lldpd has heard from on it. - Management
Address - A management address advertised by a chassis (IEEE 802.1AB Management Address TLV).
- MedInventory
- LLDP-MED “Inventory” TLVs (IEC/TR 61936, TIA-1057 §12.3). Populated only if the neighbor implements LLDP-MED and advertises them.
- Neighbor
- One neighbor device seen on an interface.
- Neighbor
Change - One neighbor-table change, as pushed by
crate::Subscription(NOTIFICATION, after aSUBSCRIBE). - Subscription
- A live feed of neighbor-table changes, obtained from
crate::Client::subscribe(SUBSCRIBE+NOTIFICATION).
Enums§
- Chassis
IdSubtype IEEE 802.1ABchassis ID subtype (LLDP_CHASSISID_SUBTYPE_*inlldp-const.h).- Error
- Errors returned by this crate.
- Neighbor
Change Kind - What happened to a neighbor entry (
NEIGHBOR_CHANGE_*insrc/lldpd-structs.h). - Port
IdSubtype IEEE 802.1ABport ID subtype (LLDP_PORTID_SUBTYPE_*inlldp-const.h).
Constants§
- DEFAULT_
SOCKET_ PATH - Default lldpd control socket path (
--with-lldpd-ctl-socket’s upstream default,${runstatedir}/lldpd.socket, which is/var/run/lldpd.socketon any system where/var/runis the usual symlink to/run).