From 7812ef04e8716b8dead97281ada517a81d6c21e2 Mon Sep 17 00:00:00 2001 From: d00f Date: Tue, 4 Aug 2026 03:32:04 +0200 Subject: [PATCH] stp: answer TCN BPDUs and validate the received BPDU length Accept legacy Topology Change Notification BPDUs (v0, type 0x80): reply on the ingress port with a Config BPDU carrying Topology Change Acknowledgment so the sender stops repeating, and count the change. Also stop reading fields past the end of short frames: require the header through bpdu_type (33 bytes with the CPU/VLAN prefix) before classifying, and the full 35-byte body before the election logic - truncated or fuzzed BPDUs are dropped instead of parsed as garbage. --- rtl837x_stp.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/rtl837x_stp.c b/rtl837x_stp.c index 2764731..3e6a677 100644 --- a/rtl837x_stp.c +++ b/rtl837x_stp.c @@ -94,6 +94,8 @@ __xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */ /* Scratch (8051: locals would overflow the internal-RAM overlay area) */ __xdata uint8_t stp_scratch; +__xdata uint8_t stp_tx_flags_extra; /* one-shot flags OR-ed into the next BPDU (TCA) */ +__xdata uint16_t stp_rxlen; /* received frame length, saved before uip_len is consumed */ __xdata uint8_t stp_i; /* shared loop iterator (DSEG relief) */ __xdata uint32_t stp_cost_scratch; @@ -233,6 +235,8 @@ void stp_cnf_send(uint8_t port) __reentrant STP_O->bpdu_type = 0x00; /* Config BPDU */ STP_O->flags = 0x00; } + STP_O->flags |= stp_tx_flags_extra; /* e.g. TCA in reply to a TCN */ + stp_tx_flags_extra = 0; memcpy(STP_O->src_addr, uip_ethaddr.addr, 6); memcpy(STP_O->root.mac, root_bridge.mac, 6); @@ -276,7 +280,16 @@ void stp_cnf_send(uint8_t port) __reentrant void stp_in(void) __banked { - // By default we do not send anything out + /* Robustness: never read fields past the received frame. 33 covers the + * header through bpdu_type; the full Config/RST body is re-checked below. + * (uip_len is consumed and zeroed at the end - keep a local view.) */ + if (uip_len < 33) { + uip_len = 0; + return; + } + stp_rxlen = uip_len; + + // By default we do not send anything out (handle_rx would TX otherwise) uip_len = 0; /* Ingress port: low nibble of the CPU tag's pmask on RX */ @@ -293,9 +306,11 @@ void stp_in(void) __banked return; if (STP_I->proto) return; - /* Accept RSTP BPDUs (v2 type 2) and legacy Config BPDUs (v0 type 0) */ + /* Accept RSTP BPDUs (v2 type 2), legacy Config BPDUs (v0 type 0) and + * legacy TCN BPDUs (v0 type 0x80, 4-byte body) */ if (!((STP_I->version == 2 && STP_I->bpdu_type == 2) - || (STP_I->version == 0 && STP_I->bpdu_type == 0))) + || (STP_I->version == 0 + && (STP_I->bpdu_type == 0 || STP_I->bpdu_type == 0x80)))) return; if (!(stp_pflags[port] & STP_PF_ENABLED) || (stp_pflags[port] & STP_PF_FILTER)) @@ -313,6 +328,21 @@ void stp_in(void) __banked stp_bpdu_age[port] = 0; + if (STP_I->bpdu_type == 0x80) { + /* TCN: a downstream bridge reports a topology change. Acknowledge it + * on this port so the sender stops repeating; the change itself is + * counted (and, once implemented, propagated rootward). */ + stp_tx_flags_extra = 0x80; /* Topology Change Acknowledgment */ + stp_cnf_send(port); /* transmits internally */ + uip_len = 0; /* ...so handle_rx must not TX again */ + stp_tc_count++; + return; + } + + /* Everything below reads the full Config/RST body. */ + if (stp_rxlen < 64) + return; + /* Our own BPDU coming back at us = a loop in the network. Block the port * for a listen period; if the loop persists the BPDUs keep arriving and * the port stays blocked. */