Trust directly-scanned mgmt IP over neighbor-reported LLDP value

Some vendors (e.g. HP/Aruba ProCurve) advertise a per-port Management
Address TLV that reflects whatever VLAN is routable out that link,
not the switch's real management IP. A neighbor's LLDP report could
therefore overwrite a switch's correct mgmt_ip with a wrong one.
upsert_switch() now takes a trusted flag: a direct scan's mgmt_ip
always wins, while a neighbor-reported one only fills in a blank
value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
dcstephenson
2026-09-18 11:50:57 -05:00
parent 5ffadfae17
commit a84c09d9cd
2 changed files with 19 additions and 4 deletions
+18 -4
View File
@@ -82,21 +82,35 @@ def init_db():
conn.close()
def upsert_switch(chassis_id, hostname, mgmt_ip, description, firmware='', vendor='', serial=''):
def upsert_switch(chassis_id, hostname, mgmt_ip, description, firmware='', vendor='', serial='', trusted=False):
"""
trusted=True means mgmt_ip came from directly scanning this switch (its own
SSH session / local LLDP info) — always authoritative. trusted=False means
it came from another switch's LLDP neighbor report about this one: some
switches (e.g. HP/Aruba ProCurve) advertise a per-port Management Address
TLV that reflects whatever VLAN happens to be routable out that specific
link, not the switch's real management IP — so an untrusted mgmt_ip is
only used to fill in a still-unknown value, never to overwrite one already
established by a trusted (direct) scan.
"""
conn = get_conn()
conn.execute("""
INSERT INTO switches (chassis_id, hostname, mgmt_ip, description, firmware, vendor, serial, last_seen)
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(chassis_id) DO UPDATE SET
hostname = excluded.hostname,
mgmt_ip = CASE WHEN excluded.mgmt_ip != '' AND excluded.mgmt_ip LIKE '%.%.%.%'
THEN excluded.mgmt_ip ELSE mgmt_ip END,
mgmt_ip = CASE
WHEN excluded.mgmt_ip = '' OR excluded.mgmt_ip NOT LIKE '%.%.%.%' THEN mgmt_ip
WHEN ? THEN excluded.mgmt_ip
WHEN mgmt_ip = '' OR mgmt_ip IS NULL THEN excluded.mgmt_ip
ELSE mgmt_ip
END,
description = CASE WHEN excluded.description != '' THEN excluded.description ELSE description END,
firmware = CASE WHEN excluded.firmware != '' THEN excluded.firmware ELSE firmware END,
vendor = CASE WHEN excluded.vendor != '' THEN excluded.vendor ELSE vendor END,
serial = CASE WHEN excluded.serial != '' THEN excluded.serial ELSE serial END,
last_seen = excluded.last_seen
""", (chassis_id, hostname, mgmt_ip, description, firmware, vendor, serial))
""", (chassis_id, hostname, mgmt_ip, description, firmware, vendor, serial, bool(trusted)))
conn.commit()
conn.close()
+1
View File
@@ -105,6 +105,7 @@ def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3):
firmware=result.get("firmware", ""),
vendor=result.get("vendor", ""),
serial=result.get("serial", ""),
trusted=True,
)
for neighbor in result["neighbors"]: