mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
trap: deliver reserved multicast to the CPU by trap, STP first
A reserved-multicast trap goes to the port named in EXT_CPU_CTRL, which names no port out of reset, so trapped frames were dropped at ingress. trap_init() points it at the CPU port once at startup, and trap_rma_set() changes only the action field of an RMA register. A trapped frame arrives without the VLAN tag a forwarded one gets, so TRAP_RX_BODY() gives the offset of the payload for both forms. STP now traps its address while it runs instead of steering it with a static L2 entry per VLAN, so turning it on no longer walks the VLAN table and a pvid change no longer needs stp off and on. With STP off the flooding entries are restored as before. A trapped BPDU passes the tagged-only ingress filter that drops a forwarded one, so the stp_setup() warning about such ports goes.
This commit is contained in:
1 parent
84d9408151
commit
14b2ce1c49
8 files changed
+97
-48
No files matched your search
@@ -89,7 +89,8 @@ SRCS += \
|
||||
rtl837x_port.c \
|
||||
rtl837x_stp.c \
|
||||
rtl837x_lldp.c \
|
||||
rtl837x_storm.c
|
||||
rtl837x_storm.c \
|
||||
rtl837x_trap.c
|
||||
SRCS += \
|
||||
httpd/httpd.c \
|
||||
httpd/page_impl.c
|
||||
|
||||
@@ -73,13 +73,14 @@ without being flooded to the other ports. No bridge relays these frames:
|
||||
their addresses are in the set that 802.1D-2004 clause 7.12.6 forbids a
|
||||
bridge to forward, and what travels the network is the information, with
|
||||
every bridge regenerating BPDUs of its own on its designated ports. The reserved-multicast *trap* action
|
||||
cannot do that on this hardware, because its destination is an external CPU
|
||||
attached to a physical port, which these boards do not populate. The protocol
|
||||
modules therefore leave the reserved-multicast action at *forward* and constrain
|
||||
the egress with a static L2 multicast entry instead: the lookup hits the entry's
|
||||
own port mask rather than the VLAN flood mask. Verified on a SWTGW218AS both
|
||||
ways, with the CPU bit cleared, where delivery stops, and with the CPU bit alone,
|
||||
where nothing egresses.
|
||||
does that: it delivers to the port named in `EXT_CPU_CTRL` (0x6724), which
|
||||
`trap_init()` sets to 9, the CPU port, at startup; with the reset value 0xf it
|
||||
names no port and the trapped frame is dropped. STP traps its address while it
|
||||
runs (see stp.md). Where the action is left at *forward*, the egress can be
|
||||
constrained with a static L2 multicast entry instead: the lookup hits the
|
||||
entry's own port mask rather than the VLAN flood mask. Verified on a SWTGW218AS
|
||||
both ways, with the CPU bit cleared, where delivery stops, and with the CPU bit
|
||||
alone, where nothing egresses.
|
||||
|
||||
`port_l2mc_set()` writes one such entry. The SMI layout is the L2 multicast
|
||||
variant of the table entry:
|
||||
|
||||
+25
-19
@@ -35,28 +35,34 @@ 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
|
||||
frame.
|
||||
|
||||
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.
|
||||
The *trap* action does not deliver to it. Its destination is an external CPU
|
||||
attached to a physical port (`cpuTag_externalCpuPort_set`, `EXT_CPU_CTRL` in
|
||||
the vendor SDK), which these boards do not populate. The ACL trap and
|
||||
redirect actions do not deliver to the 8051 either.
|
||||
While STP runs, the action is *trap*. A trap delivers to the port named in
|
||||
`EXT_CPU_CTRL` (0x6724), whose reset value 0xf names no port, and with that
|
||||
value the trapped frame is dropped at ingress. `trap_init()` sets it to 9, the
|
||||
port the 8051 sits behind, once at startup. The trap needs no per-VLAN L2
|
||||
entries, so it does not depend on which VLANs exist or on the PVIDs, and turning
|
||||
STP on no longer walks the whole VLAN table. BPDUs reach the CPU on a port that
|
||||
STP holds in blocking with either action: measured on a SWTGW218AS by sending
|
||||
BPDUs into a blocked port with forward and with trap, both ports stopped being
|
||||
edge and waited out the forward delay. The chip exempts this address from the
|
||||
ingress state check; the slow-protocols address used by LACP is not exempt.
|
||||
|
||||
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:
|
||||
A trapped frame reaches the CPU in the format it came in with: unlike a
|
||||
forwarded one it has no VLAN tag inserted after the RTL tag, and the RTL tag
|
||||
carries the trap reason. `stp_in()` reads the BPDU from the offset
|
||||
`TRAP_RX_BODY()` returns, which works for both.
|
||||
|
||||
* while STP runs, the entry's member mask is the CPU port only — BPDUs reach
|
||||
the CPU and are not flooded to other ports, as a participating bridge
|
||||
requires;
|
||||
* with STP off, the same entries are retargeted to all ports, restoring the
|
||||
transparency an unmanaged switch is expected to have, so a surrounding
|
||||
spanning tree can span *through* this device.
|
||||
With STP off, the action goes back to *forward* and a static L2 multicast
|
||||
entry per VLAN in use (`port_l2mc_set()`) targets all ports, restoring the
|
||||
transparency an unmanaged switch is expected to have, so a surrounding
|
||||
spanning tree can span *through* this device.
|
||||
|
||||
A BPDU delivered this way is an ordinary frame to the port's ingress logic
|
||||
and passes through its acceptable-frame-type filter. BPDUs are untagged, so a
|
||||
port set to admit tagged frames only (`ingress <port>t`) never delivers one
|
||||
to the CPU. `stp_setup()` prints a warning for every STP-enabled port in that
|
||||
state.
|
||||
The port's acceptable-frame-type filter treats the two actions differently.
|
||||
BPDUs are untagged, so a port set to admit tagged frames only
|
||||
(`ingress <port>t`) drops a forwarded BPDU before it reaches the CPU, while a
|
||||
trapped one passes. Both were measured on a SWTGW218AS with `ingress 3t`:
|
||||
LACPDUs, delivered by forward, stopped arriving on port 3, and BPDUs sent into
|
||||
the same port, delivered by trap, kept arriving. A port that admits tagged
|
||||
frames only therefore takes part in STP.
|
||||
|
||||
## Link aggregation
|
||||
|
||||
|
||||
@@ -285,6 +285,12 @@
|
||||
*/
|
||||
#define RTL8373_RLDP_TIMER 0x1074
|
||||
#define RTL837X_RMA0_CONF 0x4ecc
|
||||
#define RTL837X_RMA2_CONF 0x4ed4
|
||||
#define RTL837X_RMA_ACT_MASK 0x30
|
||||
#define RTL837X_RMA_ACT_FORWARD 0x00
|
||||
#define RTL837X_RMA_ACT_TRAP 0x10
|
||||
#define RTL837X_RMA_ACT_DROP 0x20
|
||||
#define RTL837X_EXT_CPU_CTRL 0x6724
|
||||
#define RTL837X_IMR_INT_PORT_LINK_STS_CHG 0x5f34
|
||||
#define RTL837X_ISR_SW_INT_MODE 0x5f84
|
||||
#define RTL837X_ISR_INT_PORT_LINK_CHG 0x5f88
|
||||
|
||||
+17
-21
@@ -12,6 +12,7 @@
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "rtl837x_trap.h"
|
||||
#include "uip.h"
|
||||
#include "machine.h"
|
||||
|
||||
@@ -85,6 +86,7 @@ __xdata uint16_t stp_link_now;
|
||||
|
||||
__xdata uint8_t stp_scratch;
|
||||
__xdata uint8_t stp_tx_flags_extra; /* one-shot flags OR-ed into the next BPDU (TCA) */
|
||||
__xdata uint8_t stp_rxbody;
|
||||
__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 */
|
||||
@@ -121,11 +123,13 @@ struct stp_pkt {
|
||||
uint8_t version1_length; /* RST BPDU only: length of the (empty) v1 part */
|
||||
};
|
||||
|
||||
struct stp_pkt_in {
|
||||
struct stp_pkt_in_hdr {
|
||||
uint8_t stp_addr[6];
|
||||
uint8_t src_addr[6];
|
||||
struct rtl_tag rtl_tag;
|
||||
struct vlan_tag vlan_tag;
|
||||
};
|
||||
|
||||
struct stp_pkt_in {
|
||||
uint16_t msg_len;
|
||||
uint8_t dsap;
|
||||
uint8_t ssap;
|
||||
@@ -147,7 +151,8 @@ struct stp_pkt_in {
|
||||
};
|
||||
|
||||
#define STP_O ((__xdata struct stp_pkt *)&uip_buf[RTL_FRAME_DESC_SIZE])
|
||||
#define STP_I ((__xdata struct stp_pkt_in *)&uip_buf[0])
|
||||
#define STP_H ((__xdata struct stp_pkt_in_hdr *)&uip_buf[0])
|
||||
#define STP_I ((__xdata struct stp_pkt_in *)&uip_buf[stp_rxbody])
|
||||
|
||||
#define BPDU_VER_STP 0x00
|
||||
#define BPDU_VER_RSTP 0x02
|
||||
@@ -158,7 +163,8 @@ struct stp_pkt_in {
|
||||
|
||||
#define BPDU_LEN_CONFIG 0x26 // LLC and a 35 byte body
|
||||
#define BPDU_LEN_RST 0x27 // LLC and a 36 byte body
|
||||
#define BPDU_LEN_MIN_HEADER 33 // addresses through bpdu_type
|
||||
#define BPDU_LEN_MIN_BODY 9
|
||||
#define BPDU_LEN_FULL_BODY 40
|
||||
|
||||
#define BPDU_FLAG_TC 0x01
|
||||
#define BPDU_FLAG_LEARNING 0x10
|
||||
@@ -547,7 +553,8 @@ void stp_in(void) __banked
|
||||
{
|
||||
uint8_t port;
|
||||
|
||||
if (uip_len < BPDU_LEN_MIN_HEADER) {
|
||||
stp_rxbody = TRAP_RX_BODY();
|
||||
if (uip_len < stp_rxbody + BPDU_LEN_MIN_BODY) {
|
||||
uip_len = 0;
|
||||
return;
|
||||
}
|
||||
@@ -557,7 +564,7 @@ void stp_in(void) __banked
|
||||
uip_len = 0;
|
||||
|
||||
/* Ingress port: low nibble of the CPU tag's pmask on RX */
|
||||
stp_scratch = ((uint8_t)HTONS(STP_I->rtl_tag.pmask)) & 0x0f;
|
||||
stp_scratch = ((uint8_t)HTONS(STP_H->rtl_tag.pmask)) & 0x0f;
|
||||
if (stp_scratch < machine.min_port || stp_scratch > machine.max_port)
|
||||
return;
|
||||
port = stp_ent_of[stp_scratch];
|
||||
@@ -607,7 +614,7 @@ void stp_in(void) __banked
|
||||
}
|
||||
|
||||
/* Everything below reads the full Config/RST body. */
|
||||
if (stp_rxlen < 64)
|
||||
if (stp_rxlen < stp_rxbody + BPDU_LEN_FULL_BODY)
|
||||
return;
|
||||
|
||||
/* Our own BPDU coming back: two of our ports sit on one segment. Only
|
||||
@@ -865,8 +872,7 @@ void stp_defaults(void) __banked
|
||||
|
||||
|
||||
/*
|
||||
* Steer BPDUs while STP runs, and restore flooding when it stops.
|
||||
* Changing a port's PVID while STP runs needs "stp off" then "stp on".
|
||||
* Restore BPDU flooding when STP stops.
|
||||
*/
|
||||
static void stp_fdb_update(__xdata uint16_t pmask)
|
||||
{
|
||||
@@ -928,16 +934,6 @@ void stp_setup(void) __banked
|
||||
|
||||
print_reg(RTL837X_MSTP_STATES); write_char('\n');
|
||||
|
||||
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
|
||||
if (!(stp_pflags[stp_i] & STP_PF_ENABLED))
|
||||
continue;
|
||||
if (port_ingress_filter_get(stp_i) != VLAN_TAGGED)
|
||||
continue;
|
||||
print_string("STP: port ");
|
||||
write_char('0' + machine.log_to_phys_port[stp_i]);
|
||||
print_string(" admits tagged frames only - BPDUs are untagged and will not arrive\n");
|
||||
}
|
||||
|
||||
/* Seed the carrier bitmap, so turning STP on does not report every
|
||||
* port that was already down as a fresh topology change. */
|
||||
reg_read_m(RTL837X_REG_LINKS_STS);
|
||||
@@ -949,8 +945,7 @@ void stp_setup(void) __banked
|
||||
|
||||
stp_claim_root();
|
||||
|
||||
/* Take BPDUs to the CPU only - we are a participating bridge now. */
|
||||
stp_fdb_update(PMASK_CPU);
|
||||
trap_rma_set(RTL837X_RMA0_CONF, RTL837X_RMA_ACT_TRAP);
|
||||
}
|
||||
|
||||
|
||||
@@ -972,6 +967,7 @@ void stp_off(void) __banked
|
||||
|
||||
/* Restore BPDU transparency: flood them again like an unmanaged switch. */
|
||||
stp_fdb_update(PMASK_CPU | (machine_detected.isRTL8373 ? PMASK_9 : PMASK_6));
|
||||
trap_rma_set(RTL837X_RMA0_CONF, RTL837X_RMA_ACT_FORWARD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_trap.h"
|
||||
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
|
||||
void trap_init(void) __banked
|
||||
{
|
||||
REG_SET(RTL837X_EXT_CPU_CTRL, CPU_PORT);
|
||||
}
|
||||
|
||||
void trap_rma_set(uint16_t reg, uint8_t act) __banked
|
||||
{
|
||||
reg_read_m(reg);
|
||||
sfr_data[3] = (sfr_data[3] & ~RTL837X_RMA_ACT_MASK) | act;
|
||||
reg_write_m(reg);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef _RTL837X_TRAP_H_
|
||||
#define _RTL837X_TRAP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
|
||||
#define TRAP_RX_HDR_SIZE (12 + RTL_TAG_SIZE)
|
||||
|
||||
#define TRAP_RX_BODY() \
|
||||
((uip_buf[TRAP_RX_HDR_SIZE] == 0x81 && uip_buf[TRAP_RX_HDR_SIZE + 1] == 0x00) \
|
||||
? TRAP_RX_HDR_SIZE + VLAN_TAG_SIZE : TRAP_RX_HDR_SIZE)
|
||||
|
||||
void trap_init(void) __banked;
|
||||
void trap_rma_set(uint16_t reg, uint8_t act) __banked;
|
||||
|
||||
#endif
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "rtl837x_pins.h"
|
||||
#include "rtl837x_phy.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "rtl837x_trap.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "rtl837x_igmp.h"
|
||||
#include "rtl837x_leds.h"
|
||||
@@ -1793,6 +1794,7 @@ void main(void)
|
||||
stp_defaults(); /* 802.1D/w default config before any "stp ..." replay */
|
||||
nic_setup();
|
||||
REG_SET(RTL837X_NIC_INT_MSK, NIC_INT_RXIE);
|
||||
trap_init();
|
||||
vlan_setup();
|
||||
port_l2_setup();
|
||||
igmp_setup();
|
||||
|
||||
Reference in new issue
Block a user