From ff56bb878608f67386cd7e824a62314ea5618e6b Mon Sep 17 00:00:00 2001 From: Thomas Walsh Date: Sat, 25 Jul 2026 01:52:27 -0400 Subject: [PATCH] lldp: Tolerate multiple management address TLVs Unlike the other basic TLV types (System Name, Port Description, etc.), the Management Address TLV may appear more than once in a single LLDPDU, e.g. one per address family (IPv4, IPv6). Switches commonly do this. Commit 44006eb ("lldp: Reject frames with duplicate TLVs") treated a second Management Address TLV as a fatal frame error and aborted processing the entire frame with `goto out`. This caused all subsequent TLVs (including 802.1Qaz ETS/PFC/APP TLVs and the End TLV) to be skipped, resulting in the ETS state machine never receiving the peer's configuration. This broke PFC/ETS learning from switches that send multiple Management Address TLVs, such as Nvidia Cumulus Linux switches (MSN4600, MSN4700). Fix by removing the checks for if an additional TLV type 8 is received. Instead leaving the idempotent check that the RCVD_LLDP_TLV_TYPE8 bit is set and then freeing the tlv and continuing on with the processing of TLVs. Due to mgmtadd being write-only, it has been removed as there is no need for it in rxProcessFrame. Signed-off-by: Michal Schmidt Signed-off-by: Thomas Walsh --- lldp/agent.h | 1 - lldp/rx.c | 21 +++++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/lldp/agent.h b/lldp/agent.h index 1a6e1f1..9c9c525 100644 --- a/lldp/agent.h +++ b/lldp/agent.h @@ -103,7 +103,6 @@ typedef struct rxmanifest{ struct unpacked_tlv *sysname; struct unpacked_tlv *sysdesc; struct unpacked_tlv *syscap; - struct unpacked_tlv *mgmtadd; } rxmanifest; struct agentrx { diff --git a/lldp/rx.c b/lldp/rx.c index 0ff2008..a13d95c 100644 --- a/lldp/rx.c +++ b/lldp/rx.c @@ -135,7 +135,7 @@ void rxProcessFrame(struct port *port, struct lldp_agent *agent) bool msap_compare_1 = false; bool msap_compare_2 = false; bool good_neighbor = false; - bool tlv_stored = false; + bool tlv_stored; int err; struct lldp_module *np; @@ -164,6 +164,7 @@ void rxProcessFrame(struct port *port, struct lldp_agent *agent) tlv_offset = sizeof(struct l2_ethhdr); /* Points to 1st TLV */ do { + tlv_stored = false; tlv_cnt++; if (tlv_offset > agent->rx.sizein) { LLDPAD_INFO("ERROR: Frame overrun!\n"); @@ -394,17 +395,9 @@ void rxProcessFrame(struct port *port, struct lldp_agent *agent) } } if (tlv->type == TYPE_8) { /* mgmt address */ - if (agent->lldpdu & RCVD_LLDP_TLV_TYPE8) { - LLDPAD_INFO("Received multiple mgmt address" - " TLVs in this LLDPDU\n"); - frame_error++; - free_unpkd_tlv(tlv); - goto out; - } else { - agent->lldpdu |= RCVD_LLDP_TLV_TYPE8; - agent->rx.manifest->mgmtadd = tlv; - tlv_stored = true; - } + agent->lldpdu |= RCVD_LLDP_TLV_TYPE8; + free_unpkd_tlv(tlv); + continue; } /* rx per lldp module */ @@ -429,8 +422,6 @@ void rxProcessFrame(struct port *port, struct lldp_agent *agent) free_unpkd_tlv(tlv); agent->stats.statsTLVsUnrecognizedTotal++; } - tlv = NULL; - tlv_stored = false; } while(tlv_type != 0); out: @@ -687,8 +678,6 @@ void rx_change_state(struct lldp_agent *agent, u8 newstate) void clear_manifest(struct lldp_agent *agent) { - if (agent->rx.manifest->mgmtadd) - free_unpkd_tlv(agent->rx.manifest->mgmtadd); if (agent->rx.manifest->syscap) free_unpkd_tlv(agent->rx.manifest->syscap); if (agent->rx.manifest->sysdesc)