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

+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);