doc: correct what a blocked port does with frames

The note said a blocked port drops frames the CPU injects into it and so
cannot send BPDUs of its own. Hardware says otherwise, and it matters,
because that sentence is the reason one would go looking for a way to let
control frames out of a blocked port when there is nothing to fix there.

Held a two port group in blocking and watched from the neighbour. Our
BPDUs kept leaving it, 27 of them with a largest gap of 2.00 s, which is
the hello interval with nothing missed. Pings across the same port stopped
dead for 11.63 s in one unbroken gap, so data really is held. In the same
window the neighbour sent 74 frames with a largest gap of 1.04 s while our
receive counter for them moved by 2, and the port stayed in its trunk
throughout, so nothing in the aggregation code was discarding them.
This commit is contained in:
d00f
2026-08-18 23:29:09 +02:00
committed by d00f
parent c72d36af36
commit 0160b430f4
+22 -36
View File
@@ -6,10 +6,8 @@ implementation elects a root bridge from the BPDUs it receives, promotes ports
to forwarding once their listen period expires, ages the root out when it goes to forwarding once their listen period expires, ages the root out when it goes
silent, and blocks a port on which it sees its own BPDU. silent, and blocks a port on which it sees its own BPDU.
It is deliberately simple: there is no proposal/agreement handshake and no full STP can be enabled and controlled via the web interface or the command line,
port-role machine. What it does do reliably is stop a cabling loop from melting as follows:
the network, and interoperate with neighbouring bridges as a well-behaved
(if unexciting) participant.
> **Before you enable it on a switch you reach over the network**: read the > **Before you enable it on a switch you reach over the network**: read the
> [management failsafe](#management-failsafe) section. The management VLAN > [management failsafe](#management-failsafe) section. The management VLAN
@@ -22,7 +20,8 @@ stp on # start participating
stp off # stop, all ports back to forwarding stp off # stop, all ports back to forwarding
``` ```
Live status is on the Spanning Tree page of the web UI (or `/stp.json`). Live status is on the Spanning Tree page of the web UI (or `/stp.json`),
and on the serial console via `stp status`.
With no other bridge around, the switch elects itself root and every port ends With no other bridge around, the switch elects itself root and every port ends
up forwarding — you can leave it on safely. Put the settings in the startup up forwarding — you can leave it on safely. Put the settings in the startup
@@ -40,19 +39,12 @@ BPDUs are addressed to `01:80:C2:00:00:00`, a reserved link-local group. The
ASIC's Reserved-Multicast action for that address decides what happens to the ASIC's Reserved-Multicast action for that address decides what happens to the
frame. frame.
Forwarding to the CPU port works normally the 8051 sits behind an ordinary Forwarding to the CPU port works normally: the 8051 sits behind an ordinary
port of the internal switch and is an ordinary member of a forwarding mask. port of the internal switch and is an ordinary member of a forwarding mask.
What does not work is the *trap* action, which is a separate mechanism: its The *trap* action does not deliver to it. Its destination is an external CPU
destination is an external CPU attached to a physical port attached to a physical port (`cpuTag_externalCpuPort_set`, `EXT_CPU_CTRL` in
(`cpuTag_externalCpuPort_set`, `EXT_CPU_CTRL` in the vendor SDK), which these the vendor SDK), which these boards do not populate. The ACL trap and
boards do not populate. Measured on a SWTGW218AS: with the RMA action set to redirect actions do not deliver to the 8051 either.
trap, zero frames arrive at the 8051, including with `CPU_PMSK` widened and the
external-CPU destination pointed at both `0xF` and `9`; with the *forward*
action plus the L2 entry below, they arrive. The ACL trap behaves the same way,
measured on the neighbouring reserved group `01:80:C2:00:00:02`: a rule matching
it intercepts the frames — the LACP receive counters stop advancing while the
rule is enabled and resume the moment it is disabled — but they never reach the
8051, with `FWD_INT_TRAP` and with `REDIRECT` aimed at the CPU port alike.
Delivery therefore uses the *forward* action, constrained to the CPU port Delivery therefore uses the *forward* action, constrained to the CPU port
by a static L2 multicast entry (`port_l2mc_set()`), one per VLAN in use: by a static L2 multicast entry (`port_l2mc_set()`), one per VLAN in use:
@@ -64,21 +56,16 @@ by a static L2 multicast entry (`port_l2mc_set()`), one per VLAN in use:
transparency an unmanaged switch is expected to have, so a surrounding transparency an unmanaged switch is expected to have, so a surrounding
spanning tree can span *through* this device. spanning tree can span *through* this device.
Because delivery rides the forward action, a BPDU is an ordinary frame to the A BPDU delivered this way is an ordinary frame to the port's ingress logic
ingress pipeline and is subject to the port's acceptable-frame-type setting. and passes through its acceptable-frame-type filter. BPDUs are untagged, so a
BPDUs are untagged by definition, so a port configured to admit tagged frames port set to admit tagged frames only (`ingress <port>t`) never delivers one
only (`ingress <port>t`) will never deliver one: a port left on the default to the CPU. `stp_setup()` prints a warning for every STP-enabled port in that
auto edge turns edge after three seconds of silence, one with edge switched off state.
sits out the full forward delay instead, and either way the bridge elects
itself root no matter what the neighbour sends. `stp_setup()` prints a warning for every
STP-enabled port in that state. On a normal bridge this cannot happen, since
BPDUs are consumed before any VLAN classification; here it is a direct
consequence of the delivery path above.
Port states live in `RTL837X_MSTP_STATES (0x5310)`, two bits per port: Port states live in `RTL837X_MSTP_STATES (0x5310)`, two bits per port:
`00` disabled, `01` blocking, `10` learning, `11` forwarding. Note that a port `00` disabled, `01` blocking, `10` learning, `11` forwarding. In the blocking
held in blocking also drops frames the CPU injects into it, so a blocked port state a port forwards nothing except frames sent by the CPU, and nothing it
cannot transmit BPDUs of its own. receives reaches the CPU.
## Timers ## Timers
@@ -122,9 +109,9 @@ stp port <1-9> filter on|off # neither send nor accept BPDUs
stp port <1-9> p2p auto|on|off stp port <1-9> p2p auto|on|off
``` ```
**edge** — an edge port goes forwarding immediately and does not trigger a **edge** — an edge port forwards immediately and does not trigger a
topology change when it comes and goes; `auto` promotes a port to edge after topology change when its link comes and goes; `auto` promotes a port to edge
three seconds without a BPDU, and demotes it as soon as one arrives. Use after three seconds without a BPDU, and demotes it as soon as one arrives. Use
`edge on` for ports where only hosts are attached. `edge on` for ports where only hosts are attached.
**guard**`bpdu` disables a port as soon as a BPDU arrives on it (a host port **guard**`bpdu` disables a port as soon as a BPDU arrives on it (a host port
@@ -182,6 +169,8 @@ available as JSON:
GET /stp.json GET /stp.json
``` ```
The `stp status` command prints the same view on the serial console.
## Limitations ## Limitations
* One spanning-tree instance; no MSTP, no per-VLAN trees. * One spanning-tree instance; no MSTP, no per-VLAN trees.
@@ -189,6 +178,3 @@ GET /stp.json
converge, but through the timers rather than the fast transition. converge, but through the timers rather than the fast transition.
* Port roles are approximated: the root port and designated ports are * Port roles are approximated: the root port and designated ports are
distinguished, alternate/backup are not. distinguished, alternate/backup are not.
* A port in blocking cannot transmit, so a blocked port stops announcing
itself; recovery relies on the listen timer rather than on a neighbour's
agreement.