Files
RTLPlayground/rtl837x_stp.h
T
d00f 6fcb8ef11f stp: management failsafe (commit-confirm) + bounded NIC waits
Enabling STP on a bridge whose management rides an in-band VLAN can cut
off that very management - and not only by our own blocking: on this
network the upstream TP-Link Easy Smart switch's "loop prevention"
reacted to our BPDU hellos by blocking ITS port towards us while our
ASIC was all-forwarding, isolating the whole segment until a power
cycle. Recoverable only by going quiet.

Add a commit-confirm watchdog: while STP is enabled, any HTTP request
re-arms a countdown ("stp failsafe <seconds>", default 180, 0 disables);
if management stays silent for the whole window, STP disables itself,
which also stops BPDU TX so a neighbour's loop protection can release
its block. The web UI polls /stp.json every 2 s, so an open browser
naturally keeps the watchdog re-armed. The trip is reported via
/stp.json (fs, fsT) and as a warning on the Spanning Tree page.

Deliberately not conditioned on our own MSTP port states - the incident
above proves the uplink can be dead while every local port forwards.

Also bound the NIC DMA busy-waits (nic_tx_packet, nic_rx_header,
nic_rx_packet): an unbounded spin on SFR_NIC_CTRL freezes the entire
main loop (timers, HTTP, ARP) if the ASIC ever fails to consume a
transfer; give up after ~65k polls and drop the frame instead.

Hardware-verified end to end: with priority 15 against a live RSTP
bridge the uplink died 6 s after "stp on" and the network recovered BY
ITSELF 66 s later (trip at 45 s + neighbour release), fsT=1, LACP and
LAN intact. Telemetry via syslog-to-edge-port host confirmed the full
chain: countdown 44->4, trip, hello TX stopping at the trip.

(cherry picked from commit 1fa9775156fd6d7ebfdda2382f73430b86601230)
2026-08-04 03:26:10 +02:00

51 lines
2.5 KiB
C

#ifndef _RTL837X_STP_H_
#define _RTL837X_STP_H_
#include <stdint.h>
void stp_in(void) __banked;
void stp_setup(void) __banked;
void stp_timers(void) __banked;
void stp_off(void) __banked;
void stp_parse(void) __banked __reentrant; /* "stp ..." CLI handler (cmd_parser delegates here) */
void stp_defaults(void) __banked; /* boot init: 802.1D/w default configuration */
/* Bridge identifier as carried in a BPDU (priority, extension, MAC). */
struct bridge {
uint8_t prio;
uint8_t ext;
uint8_t mac[6];
};
/* ---- Configuration (defaults per 802.1D-2004/802.1w, set in stp_defaults) --- */
extern __xdata uint8_t stpEnabled;
extern __xdata uint8_t stp_prio; /* bridge priority, high byte: 0x80 = 32768; CLI takes 0-15 (steps of 4096) */
extern __xdata uint8_t stp_hello_s; /* hello time, 1-10 s (default 2) */
extern __xdata uint8_t stp_maxage_s; /* max age, 6-40 s (default 20) */
extern __xdata uint8_t stp_fwddelay_s; /* forward delay, 4-30 s (default 15); our listen period */
extern __xdata uint8_t stp_rstp; /* 1 = RSTP BPDUs (v2), 0 = STP-compatible Config BPDUs (v0) */
extern __xdata uint8_t stp_txhold;
extern __xdata uint8_t stp_failsafe_s; /* mgmt watchdog, seconds (0 = off) */
extern __xdata uint8_t stp_failsafe_tripped; /* max BPDUs per port per second (default 6) */
/* Per-port config/status flags (stp_pflags[]) */
#define STP_PF_ENABLED 0x01 /* port participates in STP (default on) */
#define STP_PF_ADMEDGE 0x02 /* admin edge: forwarding immediately */
#define STP_PF_AUTOEDGE 0x04 /* auto edge: forward after 3 s without BPDU */
#define STP_PF_BPDUGUARD 0x08 /* disable port if a BPDU arrives */
#define STP_PF_ROOTGUARD 0x10 /* never accept a better root on this port */
#define STP_PF_FILTER 0x20 /* neither send nor accept BPDUs */
#define STP_PF_OPEREDGE 0x40 /* runtime: port went forwarding as an edge */
#define STP_PF_TRIPPED 0x80 /* runtime: disabled by BPDU guard */
extern __xdata uint8_t stp_pflags[10];
extern __xdata uint32_t stp_pcost[10]; /* path cost; 0 = auto (20000) */
extern __xdata uint8_t stp_pprio[10]; /* port priority (default 0x80) */
/* ---- Status, exposed read-only for the web UI (send_stp) ---- */
extern __xdata struct bridge root_bridge;
extern __xdata uint32_t root_bridge_cost; /* our path cost to the root (0 if we are root) */
extern __xdata uint8_t stp_root_port; /* logical port towards the root; 0xff = we are root */
extern __xdata uint16_t stp_tc_count; /* topology change counter (diagnostics) */
#endif