stp: give auto-edge back to a port held by its own BPDU

stp_loop_hold_peer() takes operEdge away from the port that sent the
BPDU we got back, though that port heard nothing itself. When the hold
ran out the port stayed non-edge until its next link change, and every
later state change on it counted as a topology change. Restore edge when
the hold ends with the port still silent and auto-edge on, before the
transition is reported, so the recovery itself is not a change. Hellos
go out on edge ports too, so a loop that persists is caught again.

The console column that shows seconds since the last BPDU is now called
age[s]; it never was a receive count.
This commit is contained in:
João Silva authored and d00f committed 2026-10-27 22:11:24 +01:00
1 parent eb51128ec7
commit 4d009bc8e8
2 files changed
+17 -1

No files matched your search

+9
View File
@@ -268,6 +268,15 @@ is reported with one of the roles Root, Designated, Alternate, Backup or
Disabled; Disabled covers a port with STP turned off on it, one shut down by
BPDU guard and one without link.
The console's `age[s]` column (previously labelled `bpdu`) is the elapsed
time since the last BPDU, capped at 255 seconds; it is not a receive count.
Use the web counters or `/stpcnt.json` for full receive counts.
An own BPDU received back on another port can temporarily hold its sending
port in discarding, even if that sending port has received no BPDUs itself.
When the hold expires, auto-edge is restored if the sending port has remained
silent. A continuing loop keeps the hold active.
BPDUs shorter than their type allows, judged by the 802.3 length field, are
dropped: 7 bytes for a TCN, 38 for a Config BPDU and 39 for an RST BPDU,
LLC header included.
+8 -1
View File
@@ -297,7 +297,7 @@ void stp_status(void) __banked
print_string("changes ");
print_short(stp_tc_count);
write_char('\n');
print_string("port state role edge tx bpdu\n");
print_string("port state role edge tx age[s]\n");
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++) {
if (!stp_ent_active(stp_i))
continue;
@@ -1297,6 +1297,13 @@ void stp_timers(void) __banked
print_string("STP: port learning ");
print_port_nl(stp_i);
} else {
/* A returned BPDU can hold its sending port even though
* that port never received one. Once the hold expires,
* recover auto-edge before the forwarding timer stops. */
if (stp_loop_held[stp_i] && (stp_pflags[stp_i] & STP_PF_AUTOEDGE)
&& !((stp_heard >> stp_i) & 1)
&& stp_bpdu_age[stp_i] > STP_EDGE_DELAY)
stp_pflags[stp_i] |= STP_PF_OPEREDGE;
stp_loop_held[stp_i] = 0;
stp_backup &= ~((uint16_t)1 << stp_i);
stp_state_set(stp_i, 0b11);