fix: configure lldpd on bridge interface, not VLAN subinterface

Added _lldpd_fix_interface() helper that detects the management interface
via the default route and strips any .VID suffix (e.g. br-ap.10 → br-ap).
LLDP is L2 and must run on the bridge itself — sending on a VLAN subinterface
produces tagged frames the switch won't recognize as LLDP.

The helper runs every poll so existing wrong configs (e.g. eth0 from the
original install action) are corrected automatically on the next poll.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-06-10 00:23:33 +02:00
co-authored by Claude Sonnet 4.6
parent 77c9d386db
commit 85041c13bb
+33 -8
View File
@@ -493,6 +493,35 @@ class OpenWrtDriver(AccessPointDriver):
) )
return neighbors return neighbors
def _lldpd_fix_interface(self) -> None:
"""Ensure lldpd UCI config uses the actual management interface.
Detects the interface carrying the default route (e.g. br-lan.10 on a
VLAN trunk) and replaces any stale/wrong interface entry in the lldpd
UCI config. Idempotent — only writes if the config needs updating.
"""
mgmt_iface = self._send_command(
"ip route show default 2>/dev/null | head -1 | grep -oE 'dev [^ ]+' | awk '{print $2}'"
).strip()
if not mgmt_iface:
mgmt_iface = "br-lan"
# LLDP is a L2 protocol — it must run on the bridge, not a VLAN subinterface.
# Strip .VID suffix so br-ap.10 → br-ap.
if "." in mgmt_iface:
mgmt_iface = mgmt_iface.rsplit(".", 1)[0]
current = self._send_command("uci get lldpd.config.interface 2>/dev/null").strip()
if mgmt_iface in current:
return # already correct
# Reset the interface list to just the management interface
self._send_command(
"uci delete lldpd.config.interface 2>/dev/null; "
f"uci add_list lldpd.config.interface='{mgmt_iface}' 2>/dev/null; "
"uci commit lldpd 2>/dev/null; "
"/etc/init.d/lldpd restart 2>/dev/null"
)
def _get_lldp_table(self) -> List[Dict]: def _get_lldp_table(self) -> List[Dict]:
"""Parse ``lldpctl -f keyvalue`` into a list of row dicts. """Parse ``lldpctl -f keyvalue`` into a list of row dicts.
@@ -500,11 +529,12 @@ class OpenWrtDriver(AccessPointDriver):
not already running the daemon needs time to discover neighbors so not already running the daemon needs time to discover neighbors so
the first call after a fresh install will return an empty list. the first call after a fresh install will return an empty list.
""" """
# Enable + start lldpd if not already running (idempotent / silent) # Ensure lldpd is running on the correct management interface
self._send_command( self._send_command(
"pgrep lldpd >/dev/null 2>&1 || " "pgrep lldpd >/dev/null 2>&1 || "
"(/etc/init.d/lldpd enable 2>/dev/null; /etc/init.d/lldpd start 2>/dev/null)" "(/etc/init.d/lldpd enable 2>/dev/null; /etc/init.d/lldpd start 2>/dev/null)"
) )
self._lldpd_fix_interface()
output = self._send_command("lldpctl -f keyvalue") output = self._send_command("lldpctl -f keyvalue")
rows: List[Dict] = [] rows: List[Dict] = []
@@ -1949,13 +1979,8 @@ class OpenWrtDriver(AccessPointDriver):
raw = self._send_command("opkg install lldpd 2>&1") raw = self._send_command("opkg install lldpd 2>&1")
out = self._clean_pkg_output(raw) out = self._clean_pkg_output(raw)
# Add eth0 to lldpd UCI interface list (idempotent) # Configure the correct management interface and enable the service
current_ifaces = self._send_command("uci get lldpd.config.interface 2>/dev/null").strip() self._lldpd_fix_interface()
if "eth0" not in current_ifaces:
self._send_command(
"uci add_list lldpd.config.interface='eth0' 2>/dev/null; "
"uci commit lldpd 2>/dev/null"
)
# Enable and start the service # Enable and start the service
self._send_command( self._send_command(