stp: announce topology changes and flush the port that changed

A port entering forwarding, or being blocked because its own BPDU came
back, changes where MAC addresses live - but the counter was bumped and
nothing else happened: our forwarding table kept the stale entries and
the neighbours were never told.

Flush the affected port's dynamic entries (bounded single-port flush)
and set the Topology Change flag in our BPDUs for max age + forward
delay, so neighbours age their tables out as well. Edge ports are
exempt: a host coming or going is not a topology change.
This commit is contained in:
d00f
2026-08-04 03:35:06 +02:00
parent f635e1d213
commit de53fcdc7a
+24 -2
View File
@@ -97,6 +97,7 @@ __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_msg_age; /* message age of the root info we hold, seconds */
__xdata uint16_t stp_tc_while; /* ticks left to set the TC flag in our BPDUs */
__xdata uint8_t stp_i; /* shared loop iterator (DSEG relief) */
__xdata uint32_t stp_cost_scratch;
@@ -184,6 +185,21 @@ static void stp_state_set(uint8_t port, uint8_t state) __reentrant
}
/* Signal a topology change: flush the stale forwarding entries of the port
* that changed, count it, and set the TC flag in our BPDUs for one
* max-age+forward-delay period (802.1D 8.6.14) so the neighbours age their
* own tables out too. Edge ports are exempt: a host appearing or leaving is
* not a topology change. */
static void stp_topology_change(uint8_t port) __reentrant
{
if (stp_pflags[port] & STP_PF_OPEREDGE)
return;
stp_tc_count++;
stp_tc_while = ((uint16_t)stp_maxage_s + stp_fwddelay_s) * STP_HZ;
port_l2_forget_port(port);
}
/* Take the bridge back as root of its own tree (initial state / root aged out) */
static void stp_claim_root(void)
{
@@ -237,6 +253,8 @@ void stp_cnf_send(uint8_t port) __reentrant
STP_O->bpdu_type = 0x00; /* Config BPDU */
STP_O->flags = 0x00;
}
if (stp_tc_while)
STP_O->flags |= 0x01; /* Topology Change */
STP_O->flags |= stp_tx_flags_extra; /* e.g. TCA in reply to a TCN */
stp_tx_flags_extra = 0;
@@ -359,7 +377,7 @@ void stp_in(void) __banked
stp_state_set(port, 0b01);
port_timers[port] = (uint16_t)stp_fwddelay_s * STP_HZ;
stp_pflags[port] &= ~STP_PF_OPEREDGE;
stp_tc_count++;
stp_topology_change(port);
}
return;
}
@@ -458,7 +476,7 @@ void stp_timers(void) __banked
stp_state_set(stp_i, 0b11);
print_string("STP: port forwarding ");
print_byte(stp_i); write_char('\n');
stp_tc_count++;
stp_topology_change(stp_i);
} else if ((stp_pflags[stp_i] & STP_PF_AUTOEDGE)
&& stp_bpdu_age[stp_i] > STP_EDGE_DELAY) {
/* Auto edge: nothing talks (R)STP on this port - it is
@@ -472,6 +490,9 @@ void stp_timers(void) __banked
}
}
if (stp_tc_while)
stp_tc_while--;
/* Age out a root that went silent: reclaim the tree. */
if (stp_root_port != 0xff
&& stp_bpdu_age[stp_root_port] > (uint16_t)stp_maxage_s * STP_HZ) {
@@ -506,6 +527,7 @@ void stp_defaults(void) __banked
stp_tx_budget[stp_i] = 6;
}
stp_tc_count = 0;
stp_tc_while = 0;
stp_claim_root();
}