From bd06f7f5e58a5ba606cd2284f383f5dc0f13dbfe Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:26:05 +0200 Subject: [PATCH] stp: do not let auto edge undo a loop block A port held out of forwarding because a loop was seen on it stops hearing the frames that justified the hold, so its BPDU age climbs. The auto edge branch lives inside the same countdown as the hold, reads that age, and promotes the port back to forwarding after three seconds. The loop opens, a BPDU comes back, the block is applied again, and the two take turns. Measured on a looped pair present when the tree came up: twenty cycles in one window and fifteen in the next, each block followed by a promotion, still going when the capture ended. The countdown alone cannot tell a loop hold from an ordinary listen, so record the hold and leave the port alone while it stands. --- rtl837x_stp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rtl837x_stp.c b/rtl837x_stp.c index 2dc8337..cc4e916 100644 --- a/rtl837x_stp.c +++ b/rtl837x_stp.c @@ -70,6 +70,7 @@ __xdata uint16_t stp_scratch16; /* scratch for status printing only */ __xdata uint16_t port_timers[10]; /* listen-period countdown (0 = not listening) */ __xdata uint16_t port_hello[10]; /* hello TX countdown */ __xdata uint16_t stp_bpdu_age[10]; /* ticks since last BPDU seen on port (saturating) */ +__xdata uint8_t stp_loop_held[10]; /* port is out of forwarding because a loop was seen on it */ __xdata uint8_t stp_tx_budget[10]; /* tx hold: BPDUs left in the current second */ __xdata uint8_t stp_tx_count[10]; /* BPDUs actually put on the wire, wraps at 256 */ __xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */ @@ -309,6 +310,7 @@ static void stp_loop_hold_peer(uint8_t port) __reentrant stp_pflags[port] &= ~STP_PF_OPEREDGE; stp_topology_change(port); } + stp_loop_held[port] = 1; port_timers[port] = (uint16_t)stp_fwddelay_s * STP_HZ; } @@ -624,11 +626,13 @@ void stp_timers(void) __banked * the designated bridge on that port). */ if (port_timers[stp_i]) { if (!--port_timers[stp_i]) { + stp_loop_held[stp_i] = 0; stp_state_set(stp_i, 0b11); print_string("STP: port forwarding "); print_port_nl(stp_i); stp_topology_change(stp_i); } else if ((stp_pflags[stp_i] & STP_PF_AUTOEDGE) + && !stp_loop_held[stp_i] && stp_bpdu_age[stp_i] > STP_EDGE_DELAY) { /* Auto edge: nothing talks (R)STP on this port - it is * host-facing, go to forwarding without the full wait. */ @@ -711,6 +715,7 @@ void stp_setup(void) __banked sfr_data[0] = sfr_data[1] = sfr_data[2] = sfr_data[3] = 0; for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) { stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED); + stp_loop_held[stp_i] = 0; stp_bpdu_age[stp_i] = 0; stp_tx_budget[stp_i] = stp_txhold; stp_tx_count[stp_i] = 0; @@ -762,6 +767,7 @@ void stp_off(void) __banked // States are: 00 disable, 01 blocking, 10 learning, 11 forwarding sfr_data[3 - (stp_i >> 2)] |= (uint8_t)(0b11 << ((stp_i << 1) & 0x7)); stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED); + stp_loop_held[stp_i] = 0; port_timers[stp_i] = 0; } sfr_data[1] |= 0x0c; // Do not block the CPU port (bits 3:2 of byte 1 = port 9)