mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
Merge pull request #444 from DrDoof/feat/rstp
stp: full RSTP and a reworked Spanning Tree page
This commit is contained in:
14 files changed
+3927
-681
No files matched your search
@@ -150,10 +150,12 @@ SRCS += \
|
|||||||
rtl837x_lldp.c \
|
rtl837x_lldp.c \
|
||||||
rtl837x_storm.c \
|
rtl837x_storm.c \
|
||||||
rtl837x_lacp.c \
|
rtl837x_lacp.c \
|
||||||
rtl837x_trap.c
|
rtl837x_trap.c \
|
||||||
|
rtl837x_stp_cli.c
|
||||||
SRCS += \
|
SRCS += \
|
||||||
httpd/httpd.c \
|
httpd/httpd.c \
|
||||||
httpd/page_impl.c
|
httpd/page_impl.c \
|
||||||
|
httpd/page_stp.c
|
||||||
SRCS += \
|
SRCS += \
|
||||||
uip/timer.c \
|
uip/timer.c \
|
||||||
uip/uip.c \
|
uip/uip.c \
|
||||||
@@ -246,7 +248,7 @@ $(BUILDDIR)/%.rel: %.asm $(CCFLAGS_STAMP) | create_build_dir
|
|||||||
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
|
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
|
||||||
|
|
||||||
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel
|
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel
|
||||||
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-r -o $@ $^
|
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-bBANK4=0x44000 -Wl-r -o $@ $^
|
||||||
|
|
||||||
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
|
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
|
||||||
objcopy --input-target=ihex -O binary $< $@
|
objcopy --input-target=ihex -O binary $< $@
|
||||||
|
|||||||
+152
-50
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
The switch can take part in a spanning tree (IEEE 802.1D / 802.1w) so that
|
The switch can take part in a spanning tree (IEEE 802.1D / 802.1w) so that
|
||||||
redundant links between bridges are blocked instead of forming a loop. The
|
redundant links between bridges are blocked instead of forming a loop. The
|
||||||
implementation elects a root bridge from the BPDUs it receives, promotes ports
|
implementation elects a root bridge from the BPDUs it receives, picks the
|
||||||
to forwarding once their listen period expires, ages the root out when it goes
|
cheapest path to it as the root port, blocks ports on which a better bridge is
|
||||||
silent, and blocks a port on which it sees its own BPDU.
|
already designated, promotes ports to forwarding once their listen period
|
||||||
|
expires, drops information a neighbour stopped sending, and blocks a port on
|
||||||
|
which it sees its own BPDU.
|
||||||
|
|
||||||
STP can be enabled and controlled via the web interface or the command line,
|
STP can be enabled and controlled via the web interface or the command line,
|
||||||
as follows:
|
as follows:
|
||||||
@@ -64,32 +66,6 @@ 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
|
the same port, delivered by trap, kept arriving. A port that admits tagged
|
||||||
frames only therefore takes part in STP.
|
frames only therefore takes part in STP.
|
||||||
|
|
||||||
## Link aggregation
|
|
||||||
|
|
||||||
A LAG (Link Aggregation Group) is handled like an additional port by the STP
|
|
||||||
protocol, with its own timers and states. The switch devices support up to 4
|
|
||||||
LAGs, which are shown alongside the physical ports in the STP status. A group
|
|
||||||
carries its own path cost, priority, edge and guard settings, and gets its own
|
|
||||||
row on the Spanning Tree page.
|
|
||||||
|
|
||||||
```
|
|
||||||
stp lag 1 cost 10000 # the group decides, not its members
|
|
||||||
stp lag 1 edge off
|
|
||||||
```
|
|
||||||
|
|
||||||
LAGs are configured via the `lag` command. Once a port is a member of a LAG it
|
|
||||||
can no longer be configured individually for STP, so `stp port <n>` on a member
|
|
||||||
tells you which group to configure instead. Membership is re-read from the
|
|
||||||
aggregation registers once a second, so a group changing under LACP is picked
|
|
||||||
up without any coordination between the two.
|
|
||||||
|
|
||||||
The switch hardware does not handle STP state for a LAG as a whole. State
|
|
||||||
changes for the members have to be done by the firmware, updating every member
|
|
||||||
port, which is possible using a single register write. BPDUs go out through the
|
|
||||||
lowest member and carry the group's own port id. Losing one member of a live
|
|
||||||
LAG is not a topology change; the logical port only goes down with its last
|
|
||||||
link.
|
|
||||||
|
|
||||||
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. A port in
|
`00` disabled, `01` blocking, `10` learning, `11` forwarding. A port in
|
||||||
blocking forwards nothing between ports, but it still sends what the CPU
|
blocking forwards nothing between ports, but it still sends what the CPU
|
||||||
@@ -109,19 +85,81 @@ encodes. All configured values are in seconds:
|
|||||||
| `stp fwd <n>` | 15 | 4–30 |
|
| `stp fwd <n>` | 15 | 4–30 |
|
||||||
| `stp txhold <n>` | 6 | 1–10 |
|
| `stp txhold <n>` | 6 | 1–10 |
|
||||||
|
|
||||||
A port entering the tree spends `fwd` seconds in blocking before it forwards
|
802.1D also requires `2 * (fwd - 1) >= maxage >= 2 * (hello + 1)`. A
|
||||||
(an edge port skips the wait). Root information is discarded after `maxage`
|
command typed on the console or sent by the web page that would break it is
|
||||||
seconds without a BPDU, and the switch then reclaims the root role.
|
refused and the value stays as it was, so change the values in an order that
|
||||||
|
keeps the rule at every step (for a larger max age raise the forward delay
|
||||||
|
first). The web page shows the error, and a refused command is not recorded
|
||||||
|
in the command history, so saving the configuration does not pick it up. Lines of the saved configuration are taken one by one whatever their
|
||||||
|
order, and `stp on` checks the result: if the rule is broken it warns and
|
||||||
|
starts with 2/20/15 instead.
|
||||||
|
|
||||||
|
The values above are what the switch uses while it is the root. Otherwise it
|
||||||
|
takes max age and forward delay from the BPDUs on its root port, as 802.1D
|
||||||
|
requires, and passes them on in its own BPDUs with the message age one second
|
||||||
|
higher; the hello time stays its own.
|
||||||
|
|
||||||
|
A port entering the tree discards for one forward delay, then learns
|
||||||
|
addresses without forwarding for a second one, and only then forwards (an
|
||||||
|
edge port skips the wait). In RSTP mode on a point-to-point link
|
||||||
|
(`p2p auto` or `on`) it can go faster: a designated port that is still
|
||||||
|
discarding sets the proposal flag, and forwards as soon as the bridge on the
|
||||||
|
other side answers with an agreement. When the root port receives a proposal,
|
||||||
|
the switch first puts its other non-edge designated ports back to discarding
|
||||||
|
(they propose in turn), then forwards the root port and answers with an
|
||||||
|
agreement; this sync happens once per root port, later proposals are only
|
||||||
|
answered. An alternate or backup port that receives a proposal does the same
|
||||||
|
sync once for the information it holds and answers with an agreement in a
|
||||||
|
BPDU with the alternate role, so the bridge on the other side does not have
|
||||||
|
to wait out its timers; it stays discarding itself. An agreement is accepted
|
||||||
|
from a root or an alternate port on the other side. A new root port forwards at once, after every port that was root within
|
||||||
|
the last forward delay has been put back to discarding; such a recent root
|
||||||
|
port stays closed, even to an agreement, until that forward delay is over.
|
||||||
|
A new root port that was a backup port within the last two hello times does
|
||||||
|
not take the fast path and waits for its timers.
|
||||||
|
|
||||||
|
A designated port that hears worse information from a bridge that still
|
||||||
|
claims to be designated and learning or forwarding on the same link has a
|
||||||
|
dispute: usually one direction of the link is lost and the other bridge does
|
||||||
|
not hear this switch's BPDUs. The port goes back to discarding, and every
|
||||||
|
further such BPDU restarts its listen period, so it does not forward while
|
||||||
|
the dispute lasts.
|
||||||
|
|
||||||
|
With `stp version rstp` each port still talks 802.1D to a neighbour that
|
||||||
|
does: once a Config BPDU or TCN arrives and the migrate time (3 s since the
|
||||||
|
port came up or last switched) is over, the port sends Config BPDUs, uses
|
||||||
|
the 802.1D TC window and reports changes with TCN if it is the root port. An
|
||||||
|
RST BPDU after the migrate time switches it back, and so does a link that
|
||||||
|
comes up again or `stp port <n> mcheck`. Any worse information heard on a designated port, disputed or
|
||||||
|
not, is answered with a BPDU at once instead of at the next hello, so the
|
||||||
|
other bridge learns sooner that it is not the designated one. Information heard on a port stays
|
||||||
|
valid for three of the sender's hello times; a BPDU whose message age has
|
||||||
|
reached its max age is not used at all. When the root port's information
|
||||||
|
expires the switch moves to the next best port, or reclaims the root role if
|
||||||
|
there is none.
|
||||||
|
|
||||||
|
Among ports offering the same root, the lowest root path cost wins, then the
|
||||||
|
designated bridge, the designated port ID, and finally this switch's own port
|
||||||
|
ID. A worse BPDU from a different bridge does not replace what a port holds;
|
||||||
|
a worse one from the same designated bridge and port does.
|
||||||
|
|
||||||
## Topology changes
|
## Topology changes
|
||||||
|
|
||||||
A change on a local non-edge port (the link coming or going, a port promoted
|
A topology change is detected when a non-edge port enters forwarding. That
|
||||||
to forwarding) flushes the addresses learned on it and sets the TC flag in
|
port, and every other forwarding non-edge port, then carries the TC flag in
|
||||||
our BPDUs for `maxage + fwd` seconds. A TC flag received in a BPDU is passed
|
its BPDUs for hello time plus one second (max age plus forward delay with
|
||||||
on: the switch flushes the other non-edge ports once and keeps the flag in
|
`stp version stp`), and each of the other ports flushes the addresses it
|
||||||
its own BPDUs until one hello after the last flagged frame, so the
|
learned. The root port takes part too: while its TC window runs it sends
|
||||||
notification crosses the switch instead of dying at it. A legacy TCN is
|
BPDUs toward the root, so the change reaches the rest of the network. With
|
||||||
acknowledged with TCA and then treated like a local change.
|
`stp version stp` the root port sends TCN instead, until a Config BPDU with
|
||||||
|
TCA comes back.
|
||||||
|
|
||||||
|
A TC flag received on a forwarding root or designated port is passed on the
|
||||||
|
same way to the other ports, not back to the port it came in on; a port that
|
||||||
|
is still discarding or learning drops it. A legacy TCN is acknowledged with
|
||||||
|
TCA and then treated like a received TC. A port that turns
|
||||||
|
alternate, loses its link, or is blocked for a loop flushes its own addresses
|
||||||
|
without raising a topology change, and an edge port never raises one.
|
||||||
|
|
||||||
## Bridge settings
|
## Bridge settings
|
||||||
|
|
||||||
@@ -129,6 +167,8 @@ acknowledged with TCA and then treated like a local change.
|
|||||||
stp prio <0-15> # bridge priority = n * 4096, default 8 (32768)
|
stp prio <0-15> # bridge priority = n * 4096, default 8 (32768)
|
||||||
stp version rstp|stp # RST BPDUs (default) or legacy Config BPDUs
|
stp version rstp|stp # RST BPDUs (default) or legacy Config BPDUs
|
||||||
stp hello|maxage|fwd|txhold <seconds>
|
stp hello|maxage|fwd|txhold <seconds>
|
||||||
|
stp pathcost long|short # automatic port costs, 802.1D-2004 (default) or 802.1D-1998
|
||||||
|
stp bpdu flood|filter # BPDUs while STP is off: pass them on (default) or drop them
|
||||||
```
|
```
|
||||||
|
|
||||||
The bridge with the lowest priority wins the root election; ties are broken by
|
The bridge with the lowest priority wins the root election; ties are broken by
|
||||||
@@ -136,6 +176,17 @@ the MAC address. If you do not want this switch to become the root of an
|
|||||||
existing network, give it a worse priority than the current root — `stp prio 15`
|
existing network, give it a worse priority than the current root — `stp prio 15`
|
||||||
(61440) is the usual "never me" value.
|
(61440) is the usual "never me" value.
|
||||||
|
|
||||||
|
A port without a configured cost takes it from its link speed. The long
|
||||||
|
method uses the 802.1D-2004 values (20000 for 1G, 2000 for 10G); the short
|
||||||
|
one the 802.1D-1998 table for bridges that still use 16-bit costs (100 for
|
||||||
|
10M, 19 for 100M, 4 for 1G, 2 for 10G, and 3 for 2.5G and 5G, which that
|
||||||
|
table does not list). All bridges in a network should use the same method.
|
||||||
|
|
||||||
|
`stp bpdu filter` keeps BPDUs from being passed between ports while STP is
|
||||||
|
off, instead of the default flooding that lets a surrounding spanning tree
|
||||||
|
run through the switch. It takes effect at once when STP is off, otherwise
|
||||||
|
when STP is turned off.
|
||||||
|
|
||||||
## Per-port settings
|
## Per-port settings
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -146,6 +197,7 @@ stp port <1-9> prio <0-240> # port priority, steps of 16
|
|||||||
stp port <1-9> guard none|bpdu|root
|
stp port <1-9> guard none|bpdu|root
|
||||||
stp port <1-9> filter on|off # neither send nor accept BPDUs
|
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
|
||||||
|
stp port <1-9> mcheck # try RSTP again on a port that fell back to STP
|
||||||
```
|
```
|
||||||
|
|
||||||
**edge** — an edge port forwards immediately and does not trigger a
|
**edge** — an edge port forwards immediately and does not trigger a
|
||||||
@@ -162,26 +214,76 @@ claims a better priority.
|
|||||||
on the far side reacts badly to them (some unmanaged switches with loop
|
on the far side reacts badly to them (some unmanaged switches with loop
|
||||||
prevention cut the link) but you still want STP on the rest of the ports.
|
prevention cut the link) but you still want STP on the rest of the ports.
|
||||||
|
|
||||||
|
## Link aggregation
|
||||||
|
|
||||||
|
A LAG (Link Aggregation Group) is one port of the tree, with its own roles,
|
||||||
|
timers and states. The switch supports up to 4 LAGs; each one takes the place
|
||||||
|
of its member ports in the STP status and gets its own row on the Spanning
|
||||||
|
Tree page. A group carries its own path cost, priority, edge, guard and
|
||||||
|
point-to-point settings, and takes the same commands as a port:
|
||||||
|
|
||||||
|
```
|
||||||
|
stp lag <1-4> on|off|mcheck|edge|cost|prio|guard|filter|p2p ...
|
||||||
|
stp lag 1 cost 10000 # the group decides, not its members
|
||||||
|
```
|
||||||
|
|
||||||
|
LAGs are configured with the `lag` command. Once a port is a member of a LAG
|
||||||
|
it can no longer be configured on its own for STP, and `stp port <n>` on a
|
||||||
|
member names the group to configure instead. Membership is read from the
|
||||||
|
aggregation registers once a second, so a group changing under LACP is picked
|
||||||
|
up without any coordination between the two; a port that joins or leaves a
|
||||||
|
group, and the group itself, start over from discarding. The automatic path
|
||||||
|
cost of a group is the one of its lowest member that has a link.
|
||||||
|
|
||||||
|
The hardware keeps no STP state for a LAG as a whole, so every state change is
|
||||||
|
written to all member ports, in a single register write. BPDUs go out through
|
||||||
|
the lowest member that has a link and carry the group's own port id, and a
|
||||||
|
BPDU received on any member belongs to the group. Losing one member of a live
|
||||||
|
LAG is not a topology change; the group only goes down with its last link.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
The Spanning Tree page shows the elected root (priority and MAC), the path cost
|
The Spanning Tree page shows the elected root (priority and MAC), the path cost
|
||||||
to it, the root port, the topology-change counter and, per port, the live state
|
to it, the root port, the topology-change counter and the time since it last
|
||||||
read from the ASIC together with the configured options. The same data is
|
moved, the max age and forward
|
||||||
available as JSON:
|
delay in use (the root's, when this switch is not the root) and, per port, the
|
||||||
|
live state read from the ASIC, or "No link", together with the configured
|
||||||
|
options. For a port taking part in the tree it also shows the designated
|
||||||
|
bridge, port and cost, and whether it talks RSTP or 802.1D to its neighbour;
|
||||||
|
the Check button runs `stp port <n> mcheck`. The same data is available as
|
||||||
|
JSON:
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /stp.json
|
GET /stp.json
|
||||||
```
|
```
|
||||||
|
|
||||||
The `stp status` command prints the same view on the serial console.
|
The Counters section of the same page (or `/stpcnt.json`) shows per port the
|
||||||
|
BPDUs received and sent, those of them that carried the topology change flag
|
||||||
|
(TCNs included), and the time since the last BPDU arrived. The counters start
|
||||||
|
from zero with `stp on`, and `stp clear` resets them; the topology change
|
||||||
|
counter is not reset.
|
||||||
|
|
||||||
|
The `stp status` command prints the same view on the serial console. A port
|
||||||
|
is reported with one of the roles Root, Designated, Alternate, Backup or
|
||||||
|
Disabled; Disabled covers a port with STP turned off on it, one shut down by
|
||||||
|
BPDU guard and one without link.
|
||||||
|
|
||||||
|
The console's `age[s]` column (previously labelled `bpdu`) is the elapsed
|
||||||
|
time since the last BPDU, capped at 255 seconds; it is not a receive count.
|
||||||
|
Use the web counters or `/stpcnt.json` for full receive counts.
|
||||||
|
|
||||||
|
An own BPDU received back on another port can temporarily hold its sending
|
||||||
|
port in discarding, even if that sending port has received no BPDUs itself.
|
||||||
|
When the hold expires, auto-edge is restored if the sending port has remained
|
||||||
|
silent. A continuing loop keeps the hold active.
|
||||||
|
|
||||||
|
BPDUs shorter than their type allows, judged by the 802.3 length field, are
|
||||||
|
dropped: 7 bytes for a TCN, 38 for a Config BPDU and 39 for an RST BPDU,
|
||||||
|
LLC header included.
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
* One spanning-tree instance; no MSTP, no per-VLAN trees.
|
* One spanning-tree instance; no MSTP, no per-VLAN trees.
|
||||||
* No proposal/agreement handshake — an RST-capable neighbour will still
|
* A backup port, one that hears the BPDUs of another port of this switch,
|
||||||
converge, but through the timers rather than the fast transition.
|
is blocked and reported as Backup, but keeps sending BPDUs: loop detection
|
||||||
* Port roles are approximated: the root port and designated ports are
|
on that segment relies on them.
|
||||||
distinguished, alternate/backup are not.
|
|
||||||
* Topology changes propagate away from the root only: nothing is announced
|
|
||||||
on the root port (no TCN and no BPDUs at all), so bridges upstream rely on
|
|
||||||
their own detection.
|
|
||||||
+44
-10
@@ -41,6 +41,11 @@ stp_en_d:"Ports start blocked and take up to twice the forward delay to reach fo
|
|||||||
stp_dis_q:"Disable spanning tree?",stp_dis_d:"All ports go straight to forwarding; loop protection is lost.",
|
stp_dis_q:"Disable spanning tree?",stp_dis_d:"All ports go straight to forwarding; loop protection is lost.",
|
||||||
stp_cost_err:"Path cost must be 0-200000000",
|
stp_cost_err:"Path cost must be 0-200000000",
|
||||||
lldp_enabled: "LLDP Enabled",lldp_title:"Service Status",lldp_portname:"Name",lldp_enable:"Send Frames",
|
lldp_enabled: "LLDP Enabled",lldp_title:"Service Status",lldp_portname:"Name",lldp_enable:"Send Frames",
|
||||||
|
stp_pcs:"Path cost",stp_pcs_long:"Long (32 bit)",stp_pcs_short:"Short (16 bit)",
|
||||||
|
stp_bh:"BPDUs while disabled",stp_bh_flood:"Flood",stp_bh_filter:"Filter",
|
||||||
|
stp_r4:"Backup",stp_times:"max age / forward delay",stp_proto:"Protocol",stp_nolink:"no link",stp_lasttc:"last",
|
||||||
|
stp_mcheck:"Check",stp_mcheck_t:"Send RST BPDUs again to find out whether the neighbour still needs 802.1D",
|
||||||
|
stp_cnt:"Counters",stp_crx:"BPDUs received",stp_ctx:"BPDUs sent",stp_ctrx:"TC received",stp_cttx:"TC sent",stp_cage:"Last BPDU [s]",
|
||||||
st_title:"Port statistics",st_h:"totals since boot",st_txg:"TX good",st_txb:"TX bad",st_rxg:"RX good",
|
st_title:"Port statistics",st_h:"totals since boot",st_txg:"TX good",st_txb:"TX bad",st_rxg:"RX good",
|
||||||
st_rxb:"RX bad",st_details:"Details",st_counters:"MIB counters",st_nonzero:"non-zero only",
|
st_rxb:"RX bad",st_details:"Details",st_counters:"MIB counters",st_nonzero:"non-zero only",
|
||||||
st_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed to load counters",
|
st_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed to load counters",
|
||||||
@@ -834,6 +839,8 @@ var CONF_CMDS=[
|
|||||||
/^isolate\s+\d+(\s+(off|\d+))+$/,
|
/^isolate\s+\d+(\s+(off|\d+))+$/,
|
||||||
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d+$/,
|
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d+$/,
|
||||||
/^stp\s+version\s+(rstp|stp)$/,
|
/^stp\s+version\s+(rstp|stp)$/,
|
||||||
|
/^stp\s+pathcost\s+(long|short)$/,
|
||||||
|
/^stp\s+bpdu\s+(filter|flood)$/,
|
||||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+(on|off)$/,
|
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+(on|off)$/,
|
||||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
|
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
|
||||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+cost\s+\d+$/,
|
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+cost\s+\d+$/,
|
||||||
@@ -1223,12 +1230,14 @@ function fmtBridge(hex){
|
|||||||
if(!hex||hex.length<16)return"-";
|
if(!hex||hex.length<16)return"-";
|
||||||
return parseInt(hex.slice(0,4),16)+" / "+hex.slice(4).replace(/(..)(?=.)/g,"$1:");
|
return parseInt(hex.slice(0,4),16)+" / "+hex.slice(4).replace(/(..)(?=.)/g,"$1:");
|
||||||
}
|
}
|
||||||
|
function stpEnt(pt){pt.lag=pt.p>100?pt.p-100:0}
|
||||||
function stpKey(pt){return pt.lag?"L"+pt.lag:String(pt.p)}
|
function stpKey(pt){return pt.lag?"L"+pt.lag:String(pt.p)}
|
||||||
function stpName(pt){return pt.lag?"LAG"+pt.lag+" ("+maskToPorts(pt.mbr).join(",")+")":String(pt.p)}
|
function stpName(pt){return pt.lag?"LAG"+pt.lag:String(pt.p)}
|
||||||
|
function stpDhms(x){return Math.floor(x/86400)+"d "+Math.floor(x/3600)%24+"h "+Math.floor(x/60)%60+"m "+x%60+"s"}
|
||||||
function stpPre(k){return k.charAt(0)==="L"?"stp lag "+k.slice(1)+" ":"stp port "+k+" "}
|
function stpPre(k){return k.charAt(0)==="L"?"stp lag "+k.slice(1)+" ":"stp port "+k+" "}
|
||||||
function stpBuild(ports){
|
function stpBuild(ports){
|
||||||
var tb=$("stpcfg").tBodies[0],st=$("stpstat").tBodies[0];
|
var tb=$("stpcfg").tBodies[0],st=$("stpstat").tBodies[0],ct=$("stpcnt").tBodies[0];
|
||||||
tb.innerHTML="";st.innerHTML="";
|
tb.innerHTML="";st.innerHTML="";ct.innerHTML="";
|
||||||
ports.forEach(function(pt){
|
ports.forEach(function(pt){
|
||||||
var p=stpKey(pt);
|
var p=stpKey(pt);
|
||||||
var tr=tb.insertRow();
|
var tr=tb.insertRow();
|
||||||
@@ -1246,7 +1255,12 @@ function stpBuild(ports){
|
|||||||
tr.insertCell().appendChild(h("button",{class:"ctl",text:t("c_apply"),onclick:function(){stpApplyPort(p)}}));
|
tr.insertCell().appendChild(h("button",{class:"ctl",text:t("c_apply"),onclick:function(){stpApplyPort(p)}}));
|
||||||
var sr=st.insertRow();
|
var sr=st.insertRow();
|
||||||
sr.insertCell().textContent=stpName(pt);
|
sr.insertCell().textContent=stpName(pt);
|
||||||
["ro","st","db","dp","dc","oe","op"].forEach(function(k){sr.insertCell().id="st"+k+p});
|
["ro","st","db","dp","dc","oe","op","pv"].forEach(function(k){sr.insertCell().id="st"+k+p});
|
||||||
|
sr.insertCell().appendChild(h("button",{class:"ctl",id:"stmc"+p,text:t("stp_mcheck"),title:t("stp_mcheck_t"),
|
||||||
|
onclick:function(){postCmd(stpPre(p)+"mcheck").then(stpLoad).catch(function(){})}}));
|
||||||
|
var cr=ct.insertRow();
|
||||||
|
cr.insertCell().textContent=stpName(pt);
|
||||||
|
["rx","tx","trx","ttx","age"].forEach(function(k){cr.insertCell().id="sc"+k+p});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function stpPortVals(pt){
|
function stpPortVals(pt){
|
||||||
@@ -1260,10 +1274,21 @@ function stpPortVals(pt){
|
|||||||
p2p:["auto","on","off"][pt.p2]||"auto",
|
p2p:["auto","on","off"][pt.p2]||"auto",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function stpCnt(c){
|
||||||
|
c.ports.forEach(function(pt){
|
||||||
|
stpEnt(pt);
|
||||||
|
var p=stpKey(pt);
|
||||||
|
if(!$("scrx"+p))return;
|
||||||
|
var v=[0,1,2,3].map(function(k){return parseInt(pt.c.slice(8*k,8*k+8),16)}),age=parseInt(pt.c.slice(32,36),16);
|
||||||
|
["rx","tx","trx","ttx"].forEach(function(k,i){$("sc"+k+p).textContent=v[i]});
|
||||||
|
$("scage"+p).textContent=!v[0]?"-":(age===0xffff?">":"")+Math.floor(age/c.hz);
|
||||||
|
});
|
||||||
|
}
|
||||||
function stpLoad(){
|
function stpLoad(){
|
||||||
return getJSON("/stp.json").then(function(s){
|
return getJSON("/stp.json").then(function(s){
|
||||||
s.ports.sort(function(a,b){return((a.lag||0)-(b.lag||0))||(a.p-b.p)});
|
s.ports.forEach(stpEnt);
|
||||||
var sig=s.ports.map(function(pt){return stpKey(pt)+":"+(pt.mbr||0)}).join();
|
s.ports.sort(function(a,b){return(a.lag-b.lag)||(a.p-b.p)});
|
||||||
|
var sig=s.ports.map(stpKey).join();
|
||||||
if(sig!==stpSig){stpSig=sig;stpBuild(s.ports);}
|
if(sig!==stpSig){stpSig=sig;stpBuild(s.ports);}
|
||||||
stpCur=s;
|
stpCur=s;
|
||||||
var en=$("stpen");
|
var en=$("stpen");
|
||||||
@@ -1271,16 +1296,19 @@ function stpLoad(){
|
|||||||
var msg;
|
var msg;
|
||||||
if(!s.on)msg=t("stp_off_msg");
|
if(!s.on)msg=t("stp_off_msg");
|
||||||
else{
|
else{
|
||||||
var me=(s.prio*4096).toString(16).padStart(4,"0")+s.myMac;
|
var me=(s.prio*4096).toString(16).padStart(4,"0")+s.myMac,tc=parseInt(s.tc,16),rp="-";
|
||||||
|
s.ports.forEach(function(pt){if(pt.role===1)rp=stpName(pt)});
|
||||||
msg=t("stp_bridge")+" "+fmtBridge(me)+(s.weRoot
|
msg=t("stp_bridge")+" "+fmtBridge(me)+(s.weRoot
|
||||||
?" ("+t("stp_root_self")+")"
|
?" ("+t("stp_root_self")+")"
|
||||||
:"; "+t("stp_root")+" "+fmtBridge(s.rootPrio+s.rootMac)+" "+t("stp_via")+" "+s.rootPort+", "+t("stp_cost")+" "+parseInt(s.cost,16))
|
:"; "+t("stp_root")+" "+fmtBridge(s.rootPrio+s.rootMac)+" "+t("stp_via")+" "+rp+", "+t("stp_cost")+" "+parseInt(s.cost,16))
|
||||||
+"; "+t("stp_tc")+": "+parseInt(s.tc,16);
|
+"; "+t("stp_tc")+": "+tc+(tc?" ("+t("stp_lasttc")+" "+stpDhms(parseInt(s.tcs,16))+")":"")
|
||||||
|
+"; "+t("stp_times")+" "+s.rMaxage+" / "+s.rFwd+" s";
|
||||||
}
|
}
|
||||||
$("stpids").textContent=msg;
|
$("stpids").textContent=msg;
|
||||||
var bc=$("stpbridge");
|
var bc=$("stpbridge");
|
||||||
if(!bc.dataset.dirty){
|
if(!bc.dataset.dirty){
|
||||||
$("stpver").value=s.rstp?"rstp":"stp";
|
$("stpver").value=s.rstp?"rstp":"stp";
|
||||||
|
$("stppcs").value=s.pcs?"short":"long";$("stpbh").value=s.bh?"filter":"flood";
|
||||||
$("stpprio").value=String(s.prio);
|
$("stpprio").value=String(s.prio);
|
||||||
$("stphello").value=s.hello;$("stpmaxage").value=s.maxage;
|
$("stphello").value=s.hello;$("stpmaxage").value=s.maxage;
|
||||||
$("stpfwd").value=s.fwd;$("stptxhold").value=s.txhold;
|
$("stpfwd").value=s.fwd;$("stptxhold").value=s.txhold;
|
||||||
@@ -1289,18 +1317,21 @@ function stpLoad(){
|
|||||||
var p=stpKey(pt);
|
var p=stpKey(pt);
|
||||||
var trip=(pt.f&STP_PF.TRIP)?" "+badge(t("stp_trip"),"bad"):"";
|
var trip=(pt.f&STP_PF.TRIP)?" "+badge(t("stp_trip"),"bad"):"";
|
||||||
$("stro"+p).textContent=s.on&&pt.role?t("stp_r"+pt.role):"-";
|
$("stro"+p).textContent=s.on&&pt.role?t("stp_r"+pt.role):"-";
|
||||||
$("stst"+p).innerHTML=s.on?badge(t("stp_s"+pt.st),pt.st===3?"ok":"")+trip:"-";
|
$("stst"+p).innerHTML=s.on?(pt.lk?badge(t("stp_s"+pt.st),pt.st===3?"ok":""):badge(t("stp_nolink")))+trip:"-";
|
||||||
$("stdb"+p).textContent=s.on?fmtBridge(pt.db):"-";
|
$("stdb"+p).textContent=s.on?fmtBridge(pt.db):"-";
|
||||||
$("stdp"+p).textContent=s.on?parseInt(pt.dp.slice(0,2),16)+"."+parseInt(pt.dp.slice(2),16):"-";
|
$("stdp"+p).textContent=s.on?parseInt(pt.dp.slice(0,2),16)+"."+parseInt(pt.dp.slice(2),16):"-";
|
||||||
$("stdc"+p).textContent=s.on?parseInt(pt.dc,16):"-";
|
$("stdc"+p).textContent=s.on?parseInt(pt.dc,16):"-";
|
||||||
$("stoe"+p).textContent=s.on?t((pt.f&STP_PF.OPEREDGE)?"c_yes":"c_no"):"-";
|
$("stoe"+p).textContent=s.on?t((pt.f&STP_PF.OPEREDGE)?"c_yes":"c_no"):"-";
|
||||||
$("stop"+p).textContent=s.on?t(pt.p2===2?"c_no":"c_yes"):"-";
|
$("stop"+p).textContent=s.on?t(pt.p2===2?"c_no":"c_yes"):"-";
|
||||||
|
$("stpv"+p).textContent=s.on&&pt.role?(s.rstp&&!pt.lg?"RSTP":"STP"):"-";
|
||||||
|
$("stmc"+p).disabled=!(s.on&&pt.role&&s.rstp);
|
||||||
var row=$("sten"+p).closest("tr");
|
var row=$("sten"+p).closest("tr");
|
||||||
if(row.dataset.dirty)return;
|
if(row.dataset.dirty)return;
|
||||||
var v=stpPortVals(pt);
|
var v=stpPortVals(pt);
|
||||||
$("sten"+p).checked=v.en;$("sted"+p).value=v.edge;$("stco"+p).value=v.cost;
|
$("sten"+p).checked=v.en;$("sted"+p).value=v.edge;$("stco"+p).value=v.cost;
|
||||||
$("stpr"+p).value=v.prio;$("stgu"+p).value=v.guard;$("stfi"+p).value=v.filter;$("stpp"+p).value=v.p2p;
|
$("stpr"+p).value=v.prio;$("stgu"+p).value=v.guard;$("stfi"+p).value=v.filter;$("stpp"+p).value=v.p2p;
|
||||||
});
|
});
|
||||||
|
if($("stpcntd").open)return getJSON("/stpcnt.json").then(stpCnt);
|
||||||
}).catch(function(){});
|
}).catch(function(){});
|
||||||
}
|
}
|
||||||
function stpApplyPort(p){
|
function stpApplyPort(p){
|
||||||
@@ -1328,6 +1359,8 @@ $("stpbapply").addEventListener("click",function(){
|
|||||||
var ver=$("stpver").value,prio=$("stpprio").value;
|
var ver=$("stpver").value,prio=$("stpprio").value;
|
||||||
if(ver!==(s.rstp?"rstp":"stp"))cmds.push("stp version "+ver);
|
if(ver!==(s.rstp?"rstp":"stp"))cmds.push("stp version "+ver);
|
||||||
if(prio!==String(s.prio))cmds.push("stp prio "+prio);
|
if(prio!==String(s.prio))cmds.push("stp prio "+prio);
|
||||||
|
if($("stppcs").value!==(s.pcs?"short":"long"))cmds.push("stp pathcost "+$("stppcs").value);
|
||||||
|
if($("stpbh").value!==(s.bh?"filter":"flood"))cmds.push("stp bpdu "+$("stpbh").value);
|
||||||
[["stphello","hello"],["stpmaxage","maxage"],["stpfwd","fwd"],["stptxhold","txhold"]].forEach(function(f){
|
[["stphello","hello"],["stpmaxage","maxage"],["stpfwd","fwd"],["stptxhold","txhold"]].forEach(function(f){
|
||||||
var v=$(f[0]).value;
|
var v=$(f[0]).value;
|
||||||
if(String(v)!==String(s[f[1]]))cmds.push("stp "+f[1]+" "+v);
|
if(String(v)!==String(s[f[1]]))cmds.push("stp "+f[1]+" "+v);
|
||||||
@@ -1346,6 +1379,7 @@ $("stpen").addEventListener("change",function(){
|
|||||||
var sel=$("stpprio");
|
var sel=$("stpprio");
|
||||||
for(var i=0;i<16;i++)sel.appendChild(h("option",{value:String(i),text:i+" ("+(i*4096)+")"}));
|
for(var i=0;i<16;i++)sel.appendChild(h("option",{value:String(i),text:i+" ("+(i*4096)+")"}));
|
||||||
})();
|
})();
|
||||||
|
$("stpcntd").addEventListener("toggle",function(){if(this.open)stpLoad()});
|
||||||
var stpPoller=new Poller(stpLoad,3000);
|
var stpPoller=new Poller(stpLoad,3000);
|
||||||
tabHooks.stp={enter:function(){stpPoller.start()},leave:function(){stpPoller.stop()}};
|
tabHooks.stp={enter:function(){stpPoller.start()},leave:function(){stpPoller.stop()}};
|
||||||
|
|
||||||
|
|||||||
+8
-1
@@ -264,6 +264,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
|||||||
<p class="small mono" id="stpids">-</p>
|
<p class="small mono" id="stpids">-</p>
|
||||||
<div id="stpbridge" style="display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end;margin-top:10px">
|
<div id="stpbridge" style="display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end;margin-top:10px">
|
||||||
<label><span class="small mut" data-i18n="stp_version">Version</span><br><select class="in" id="stpver"><option value="rstp">RSTP</option><option value="stp" data-i18n="stp_v_stp">STP compatible</option></select></label>
|
<label><span class="small mut" data-i18n="stp_version">Version</span><br><select class="in" id="stpver"><option value="rstp">RSTP</option><option value="stp" data-i18n="stp_v_stp">STP compatible</option></select></label>
|
||||||
|
<label><span class="small mut" data-i18n="stp_pcs">Path cost</span><br><select class="in" id="stppcs"><option value="long" data-i18n="stp_pcs_long">Long (32 bit)</option><option value="short" data-i18n="stp_pcs_short">Short (16 bit)</option></select></label>
|
||||||
|
<label><span class="small mut" data-i18n="stp_bh">BPDUs while disabled</span><br><select class="in" id="stpbh"><option value="flood" data-i18n="stp_bh_flood">Flood</option><option value="filter" data-i18n="stp_bh_filter">Filter</option></select></label>
|
||||||
<label><span class="small mut" data-i18n="stp_prio">Bridge priority</span><br><select class="in" id="stpprio"></select></label>
|
<label><span class="small mut" data-i18n="stp_prio">Bridge priority</span><br><select class="in" id="stpprio"></select></label>
|
||||||
<label><span class="small mut" data-i18n="stp_hello">Hello [s]</span><br><input class="in sm" id="stphello" type="number" min="1" max="10"></label>
|
<label><span class="small mut" data-i18n="stp_hello">Hello [s]</span><br><input class="in sm" id="stphello" type="number" min="1" max="10"></label>
|
||||||
<label><span class="small mut" data-i18n="stp_maxage">Max age [s]</span><br><input class="in sm" id="stpmaxage" type="number" min="6" max="40"></label>
|
<label><span class="small mut" data-i18n="stp_maxage">Max age [s]</span><br><input class="in sm" id="stpmaxage" type="number" min="6" max="40"></label>
|
||||||
@@ -281,9 +283,14 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
|||||||
</div>
|
</div>
|
||||||
<div class="card"><h2 data-i18n="stp_status">Port status</h2>
|
<div class="card"><h2 data-i18n="stp_status">Port status</h2>
|
||||||
<div class="scrollx"><table class="t" id="stpstat">
|
<div class="scrollx"><table class="t" id="stpstat">
|
||||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th></tr></thead>
|
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th><th data-i18n="stp_proto">Protocol</th><th></th></tr></thead>
|
||||||
<tbody></tbody></table></div>
|
<tbody></tbody></table></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card"><details id="stpcntd"><summary><h2 style="display:inline" data-i18n="stp_cnt">Counters</h2></summary>
|
||||||
|
<div class="scrollx"><table class="t" id="stpcnt">
|
||||||
|
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_crx">BPDUs received</th><th data-i18n="stp_ctx">BPDUs sent</th><th data-i18n="stp_ctrx">TC received</th><th data-i18n="stp_cttx">TC sent</th><th data-i18n="stp_cage">Last BPDU [s]</th></tr></thead>
|
||||||
|
<tbody></tbody></table></div></details>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="tab" id="tab-stats">
|
<section class="tab" id="tab-stats">
|
||||||
|
|||||||
@@ -1055,6 +1055,8 @@ void httpd_appcall(void)
|
|||||||
send_lldp();
|
send_lldp();
|
||||||
} else if (is_word(q, "/lacp.json")) {
|
} else if (is_word(q, "/lacp.json")) {
|
||||||
send_lacp();
|
send_lacp();
|
||||||
|
} else if (is_word(q, "/stpcnt.json")) {
|
||||||
|
send_stp_counters();
|
||||||
} else if (is_word(q, "/vlanlist")) {
|
} else if (is_word(q, "/vlanlist")) {
|
||||||
send_vlanlist();
|
send_vlanlist();
|
||||||
} else if (is_word(q, "/config")) {
|
} else if (is_word(q, "/config")) {
|
||||||
|
|||||||
+1
-137
@@ -600,143 +600,6 @@ void send_lag(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static __xdata uint32_t pi_u32;
|
|
||||||
static __xdata uint8_t pi_prio, pi_ext;
|
|
||||||
static __xdata uint8_t * __xdata pi_mac;
|
|
||||||
|
|
||||||
|
|
||||||
static void u32hex_html(void)
|
|
||||||
{
|
|
||||||
__xdata uint8_t *b = (__xdata uint8_t *)&pi_u32;
|
|
||||||
byte_to_html(b[3]);
|
|
||||||
byte_to_html(b[2]);
|
|
||||||
byte_to_html(b[1]);
|
|
||||||
byte_to_html(b[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void bridge_to_html(void)
|
|
||||||
{
|
|
||||||
byte_to_html(pi_prio);
|
|
||||||
byte_to_html(pi_ext);
|
|
||||||
for (uint8_t i = 0; i < 6; i++)
|
|
||||||
byte_to_html(pi_mac[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_stp(void)
|
|
||||||
{
|
|
||||||
uint8_t i, j, st, dsg;
|
|
||||||
|
|
||||||
dbg_string("send_stp called\n");
|
|
||||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
|
||||||
|
|
||||||
slen += strtox(outbuf + slen, "{\"on\":");
|
|
||||||
bool_to_html(stp_enabled);
|
|
||||||
slen += strtox(outbuf + slen, ",\"rstp\":");
|
|
||||||
bool_to_html(stp_rstp);
|
|
||||||
slen += strtox(outbuf + slen, ",\"prio\":");
|
|
||||||
itoa_html(stp_prio >> 4);
|
|
||||||
slen += strtox(outbuf + slen, ",\"hello\":");
|
|
||||||
itoa_html(stp_hello_s);
|
|
||||||
slen += strtox(outbuf + slen, ",\"maxage\":");
|
|
||||||
itoa_html(stp_maxage_s);
|
|
||||||
slen += strtox(outbuf + slen, ",\"fwd\":");
|
|
||||||
itoa_html(stp_fwddelay_s);
|
|
||||||
slen += strtox(outbuf + slen, ",\"txhold\":");
|
|
||||||
itoa_html(stp_txhold);
|
|
||||||
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
|
|
||||||
byte_to_html(root_bridge.prio);
|
|
||||||
byte_to_html(root_bridge.ext);
|
|
||||||
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
byte_to_html(root_bridge.mac[j]);
|
|
||||||
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
byte_to_html(uip_ethaddr.addr[j]);
|
|
||||||
slen += strtox(outbuf + slen, "\",\"cost\":\"");
|
|
||||||
byte_to_html(root_bridge_cost >> 24);
|
|
||||||
byte_to_html(root_bridge_cost >> 16);
|
|
||||||
byte_to_html(root_bridge_cost >> 8);
|
|
||||||
byte_to_html(root_bridge_cost);
|
|
||||||
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
|
||||||
bool_to_html(stp_root_port == 0xff ? 1 : 0);
|
|
||||||
slen += strtox(outbuf + slen, ",\"rootPort\":");
|
|
||||||
j = stp_root_port;
|
|
||||||
if (j != 0xff && j >= STP_LAG_BASE) {
|
|
||||||
j = 0;
|
|
||||||
while (j < STP_LAG_BASE && !((stp_lag_mask[stp_root_port - STP_LAG_BASE] >> j) & 1))
|
|
||||||
j++;
|
|
||||||
if (j >= STP_LAG_BASE)
|
|
||||||
j = 0;
|
|
||||||
}
|
|
||||||
itoa_html(j == 0xff ? 0 : machine.log_to_phys_port[j]);
|
|
||||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
|
||||||
byte_to_html(stp_tc_count >> 8);
|
|
||||||
byte_to_html(stp_tc_count);
|
|
||||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
|
||||||
reg_read_m(RTL837X_MSTP_STATES);
|
|
||||||
for (i = 0; i < STP_ENTITIES; i++) {
|
|
||||||
if (i < STP_LAG_BASE && !MACHINE_PORT_VALID(i))
|
|
||||||
continue;
|
|
||||||
if (i < STP_LAG_BASE && stp_ent_of[i] != i)
|
|
||||||
continue;
|
|
||||||
if (i >= STP_LAG_BASE && !stp_lag_mask[i - STP_LAG_BASE])
|
|
||||||
continue;
|
|
||||||
slen += strtox(outbuf + slen, "{\"p\":");
|
|
||||||
itoa_html(i < STP_LAG_BASE ? machine.log_to_phys_port[i] : 0);
|
|
||||||
slen += strtox(outbuf + slen, ",\"lag\":");
|
|
||||||
itoa_html(i < STP_LAG_BASE ? 0 : i - STP_LAG_BASE + 1);
|
|
||||||
slen += strtox(outbuf + slen, ",\"mbr\":");
|
|
||||||
itoa16_html(i < STP_LAG_BASE ? 0 : stp_lag_mask[i - STP_LAG_BASE]);
|
|
||||||
slen += strtox(outbuf + slen, ",\"st\":");
|
|
||||||
j = i;
|
|
||||||
if (i >= STP_LAG_BASE) {
|
|
||||||
j = 0;
|
|
||||||
while (j < STP_LAG_BASE && !((stp_lag_mask[i - STP_LAG_BASE] >> j) & 1))
|
|
||||||
j++;
|
|
||||||
if (j >= STP_LAG_BASE)
|
|
||||||
j = 0;
|
|
||||||
}
|
|
||||||
st = (sfr_data[3 - (j >> 2)] >> ((j << 1) & 0x7)) & 0x3;
|
|
||||||
itoa_html(st);
|
|
||||||
slen += strtox(outbuf + slen, ",\"role\":");
|
|
||||||
if (!(stp_pflags[i] & STP_PF_ENABLED) || (stp_pflags[i] & STP_PF_TRIPPED))
|
|
||||||
itoa_html(0);
|
|
||||||
else if (i == stp_root_port)
|
|
||||||
itoa_html(1);
|
|
||||||
else if (st == 3)
|
|
||||||
itoa_html(2);
|
|
||||||
else
|
|
||||||
itoa_html(3);
|
|
||||||
slen += strtox(outbuf + slen, ",\"f\":");
|
|
||||||
itoa_html(stp_pflags[i]);
|
|
||||||
slen += strtox(outbuf + slen, ",\"pc\":\"");
|
|
||||||
pi_u32 = stp_pcost[i]; u32hex_html();
|
|
||||||
slen += strtox(outbuf + slen, "\",\"prio\":");
|
|
||||||
itoa_html(stp_pprio[i]);
|
|
||||||
slen += strtox(outbuf + slen, ",\"p2\":");
|
|
||||||
itoa_html(stp_pp2p[i]);
|
|
||||||
dsg = stp_dpid[i] && stp_bpdu_age[i] < (uint16_t)stp_maxage_s * STP_HZ;
|
|
||||||
slen += strtox(outbuf + slen, ",\"db\":\"");
|
|
||||||
if (dsg) {
|
|
||||||
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
|
|
||||||
pi_mac = stp_dbridge[i].mac;
|
|
||||||
} else {
|
|
||||||
pi_prio = stp_prio; pi_ext = 0;
|
|
||||||
pi_mac = uip_ethaddr.addr;
|
|
||||||
}
|
|
||||||
bridge_to_html();
|
|
||||||
slen += strtox(outbuf + slen, "\",\"dp\":\"");
|
|
||||||
byte_to_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
|
|
||||||
byte_to_html(dsg ? stp_dpid[i] : (i + 1));
|
|
||||||
slen += strtox(outbuf + slen, "\",\"dc\":\"");
|
|
||||||
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
|
|
||||||
slen += strtox(outbuf + slen, "\"},");
|
|
||||||
}
|
|
||||||
slen -= 1; // remove comma
|
|
||||||
slen += strtox(outbuf + slen, "]}");
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_lacp(void)
|
void send_lacp(void)
|
||||||
{
|
{
|
||||||
dbg_string("send_lacp called\n");
|
dbg_string("send_lacp called\n");
|
||||||
@@ -816,6 +679,7 @@ void send_lldp(void)
|
|||||||
char_to_html('}');
|
char_to_html('}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void send_eee(void)
|
void send_eee(void)
|
||||||
{
|
{
|
||||||
dbg_string("send_eee called\nsending EEE status\n");
|
dbg_string("send_eee called\nsending EEE status\n");
|
||||||
|
|||||||
+2
-1
@@ -22,9 +22,10 @@ void send_isolation(void);
|
|||||||
void send_config(void);
|
void send_config(void);
|
||||||
void send_cmd_log(void);
|
void send_cmd_log(void);
|
||||||
void send_lag(void);
|
void send_lag(void);
|
||||||
void send_stp(void);
|
void send_stp(void) __banked;
|
||||||
void send_lldp(void);
|
void send_lldp(void);
|
||||||
void send_lacp(void);
|
void send_lacp(void);
|
||||||
|
void send_stp_counters(void) __banked;
|
||||||
void send_vlanlist(void);
|
void send_vlanlist(void);
|
||||||
|
|
||||||
/* Convert only the lower nibble to ascii HEX char.
|
/* Convert only the lower nibble to ascii HEX char.
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#include "rtl837x_sfr.h"
|
||||||
|
#include "rtl837x_common.h"
|
||||||
|
#include "rtl837x_regs.h"
|
||||||
|
#include "rtl837x_port.h"
|
||||||
|
#include "uip.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "phy.h"
|
||||||
|
#include "machine.h"
|
||||||
|
#include "rtl837x_stp.h"
|
||||||
|
#include "page_impl.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#pragma codeseg BANK4
|
||||||
|
#pragma constseg BANK4
|
||||||
|
|
||||||
|
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||||
|
extern __xdata uint16_t slen;
|
||||||
|
|
||||||
|
static __code const uint8_t stp_json_hdr[] = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n";
|
||||||
|
|
||||||
|
static void stp_bool_html(char c)
|
||||||
|
{
|
||||||
|
outbuf[slen++] = c ? '1' : '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stp_byte_html(uint8_t val)
|
||||||
|
{
|
||||||
|
outbuf[slen++] = itohex(val >> 4);
|
||||||
|
outbuf[slen++] = itohex(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stp_itoa_html(uint8_t v)
|
||||||
|
{
|
||||||
|
uint8_t t = v / 100;
|
||||||
|
uint8_t print_zeros = t;
|
||||||
|
if (print_zeros)
|
||||||
|
outbuf[slen++] = '0' + t;
|
||||||
|
t = (v / 10) % 10;
|
||||||
|
print_zeros |= t;
|
||||||
|
if (print_zeros)
|
||||||
|
outbuf[slen++] = '0' + t;
|
||||||
|
outbuf[slen++] = '0' + (v % 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static __xdata uint32_t pi_u32;
|
||||||
|
static __xdata uint8_t pi_prio, pi_ext;
|
||||||
|
static __xdata uint8_t * __xdata pi_mac;
|
||||||
|
|
||||||
|
|
||||||
|
static void u32hex_html(void)
|
||||||
|
{
|
||||||
|
__xdata uint8_t *b = (__xdata uint8_t *)&pi_u32;
|
||||||
|
stp_byte_html(b[3]);
|
||||||
|
stp_byte_html(b[2]);
|
||||||
|
stp_byte_html(b[1]);
|
||||||
|
stp_byte_html(b[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void bridge_to_html(void)
|
||||||
|
{
|
||||||
|
stp_byte_html(pi_prio);
|
||||||
|
stp_byte_html(pi_ext);
|
||||||
|
for (uint8_t i = 0; i < 6; i++)
|
||||||
|
stp_byte_html(pi_mac[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void send_stp(void) __banked
|
||||||
|
{
|
||||||
|
static __xdata uint8_t i, j, st, dsg;
|
||||||
|
|
||||||
|
dbg_string("send_stp called\n");
|
||||||
|
slen = strtox(outbuf, stp_json_hdr);
|
||||||
|
|
||||||
|
slen += strtox(outbuf + slen, "{\"on\":");
|
||||||
|
stp_bool_html(stp_enabled);
|
||||||
|
slen += strtox(outbuf + slen, ",\"rstp\":");
|
||||||
|
stp_bool_html(stp_rstp);
|
||||||
|
slen += strtox(outbuf + slen, ",\"prio\":");
|
||||||
|
stp_itoa_html(stp_prio >> 4);
|
||||||
|
slen += strtox(outbuf + slen, ",\"hello\":");
|
||||||
|
stp_itoa_html(stp_hello_s);
|
||||||
|
slen += strtox(outbuf + slen, ",\"maxage\":");
|
||||||
|
stp_itoa_html(stp_maxage_s);
|
||||||
|
slen += strtox(outbuf + slen, ",\"fwd\":");
|
||||||
|
stp_itoa_html(stp_fwddelay_s);
|
||||||
|
slen += strtox(outbuf + slen, ",\"txhold\":");
|
||||||
|
stp_itoa_html(stp_txhold);
|
||||||
|
slen += strtox(outbuf + slen, ",\"pcs\":");
|
||||||
|
stp_itoa_html(stp_pcost_short);
|
||||||
|
slen += strtox(outbuf + slen, ",\"bh\":");
|
||||||
|
stp_itoa_html(stp_bpdu_filter);
|
||||||
|
slen += strtox(outbuf + slen, ",\"tcs\":\"");
|
||||||
|
pi_u32 = stp_tc_secs; u32hex_html();
|
||||||
|
slen += strtox(outbuf + slen, "\",\"rMaxage\":");
|
||||||
|
stp_itoa_html(stp_root_port == 0xff ? stp_maxage_s : stp_root_maxage);
|
||||||
|
slen += strtox(outbuf + slen, ",\"rFwd\":");
|
||||||
|
stp_itoa_html(stp_root_port == 0xff ? stp_fwddelay_s : stp_root_fwd);
|
||||||
|
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
|
||||||
|
stp_byte_html(root_bridge.prio);
|
||||||
|
stp_byte_html(root_bridge.ext);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
|
||||||
|
for (j = 0; j < 6; j++)
|
||||||
|
stp_byte_html(root_bridge.mac[j]);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
|
||||||
|
for (j = 0; j < 6; j++)
|
||||||
|
stp_byte_html(uip_ethaddr.addr[j]);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"cost\":\"");
|
||||||
|
stp_byte_html(root_bridge_cost >> 24);
|
||||||
|
stp_byte_html(root_bridge_cost >> 16);
|
||||||
|
stp_byte_html(root_bridge_cost >> 8);
|
||||||
|
stp_byte_html(root_bridge_cost);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
||||||
|
stp_bool_html(stp_root_port == 0xff ? 1 : 0);
|
||||||
|
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||||
|
stp_byte_html(stp_tc_count >> 8);
|
||||||
|
stp_byte_html(stp_tc_count);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||||
|
for (i = 0; i < STP_ENTITIES; i++) {
|
||||||
|
j = stp_ent_id(i);
|
||||||
|
if (!j)
|
||||||
|
continue;
|
||||||
|
slen += strtox(outbuf + slen, "{\"p\":");
|
||||||
|
stp_itoa_html(j);
|
||||||
|
slen += strtox(outbuf + slen, ",\"st\":");
|
||||||
|
st = stp_port_state(i);
|
||||||
|
stp_itoa_html(st);
|
||||||
|
slen += strtox(outbuf + slen, ",\"role\":");
|
||||||
|
stp_itoa_html(stp_port_role(i));
|
||||||
|
slen += strtox(outbuf + slen, ",\"f\":");
|
||||||
|
stp_itoa_html(stp_pflags[i]);
|
||||||
|
slen += strtox(outbuf + slen, ",\"pc\":\"");
|
||||||
|
pi_u32 = stp_pcost[i]; u32hex_html();
|
||||||
|
slen += strtox(outbuf + slen, "\",\"prio\":");
|
||||||
|
stp_itoa_html(stp_pprio[i]);
|
||||||
|
slen += strtox(outbuf + slen, ",\"p2\":");
|
||||||
|
stp_itoa_html(stp_pp2p[i]);
|
||||||
|
slen += strtox(outbuf + slen, ",\"lk\":");
|
||||||
|
stp_itoa_html((stp_link_prev >> i) & 1);
|
||||||
|
slen += strtox(outbuf + slen, ",\"lg\":");
|
||||||
|
stp_itoa_html((stp_legacy >> i) & 1);
|
||||||
|
dsg = stp_dpid[i] && stp_info_while[i];
|
||||||
|
slen += strtox(outbuf + slen, ",\"db\":\"");
|
||||||
|
if (dsg) {
|
||||||
|
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
|
||||||
|
pi_mac = stp_dbridge[i].mac;
|
||||||
|
} else {
|
||||||
|
pi_prio = stp_prio; pi_ext = 0;
|
||||||
|
pi_mac = uip_ethaddr.addr;
|
||||||
|
}
|
||||||
|
bridge_to_html();
|
||||||
|
slen += strtox(outbuf + slen, "\",\"dp\":\"");
|
||||||
|
stp_byte_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
|
||||||
|
stp_byte_html(dsg ? stp_dpid[i] : (i + 1));
|
||||||
|
slen += strtox(outbuf + slen, "\",\"dc\":\"");
|
||||||
|
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
|
||||||
|
slen += strtox(outbuf + slen, "\"},");
|
||||||
|
}
|
||||||
|
slen -= 1; // remove comma
|
||||||
|
slen += strtox(outbuf + slen, "]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void send_stp_counters(void) __banked
|
||||||
|
{
|
||||||
|
uint8_t i, j;
|
||||||
|
|
||||||
|
slen = strtox(outbuf, stp_json_hdr);
|
||||||
|
slen += strtox(outbuf + slen, "{\"on\":");
|
||||||
|
stp_bool_html(stp_enabled);
|
||||||
|
slen += strtox(outbuf + slen, ",\"hz\":");
|
||||||
|
stp_itoa_html(STP_HZ);
|
||||||
|
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||||
|
stp_byte_html(stp_tc_count >> 8);
|
||||||
|
stp_byte_html(stp_tc_count);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||||
|
for (i = 0; i < STP_ENTITIES; i++) {
|
||||||
|
j = stp_ent_id(i);
|
||||||
|
if (!j)
|
||||||
|
continue;
|
||||||
|
slen += strtox(outbuf + slen, "{\"p\":");
|
||||||
|
stp_itoa_html(j);
|
||||||
|
slen += strtox(outbuf + slen, ",\"c\":\"");
|
||||||
|
for (j = 0; j < STP_CNT_N; j++) {
|
||||||
|
pi_u32 = stp_cnt[j][i]; u32hex_html();
|
||||||
|
}
|
||||||
|
stp_byte_html(stp_bpdu_age[i] >> 8);
|
||||||
|
stp_byte_html(stp_bpdu_age[i]);
|
||||||
|
slen += strtox(outbuf + slen, "\"},");
|
||||||
|
}
|
||||||
|
slen -= 1;
|
||||||
|
slen += strtox(outbuf + slen, "]}");
|
||||||
|
}
|
||||||
+862
-472
File diff suppressed because it is too large.
Load diff
+31
-7
@@ -2,13 +2,21 @@
|
|||||||
#define _RTL837X_STP_H_
|
#define _RTL837X_STP_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "rtl837x_common.h"
|
|
||||||
void stp_in(void) __banked;
|
void stp_in(void) __banked;
|
||||||
void stp_setup(void) __banked;
|
void stp_setup(void) __banked;
|
||||||
void stp_timers(void) __banked;
|
void stp_timers(void) __banked;
|
||||||
void stp_off(void) __banked;
|
void stp_off(void) __banked;
|
||||||
void stp_parse(void) __banked __reentrant;
|
void stp_parse(void) __banked __reentrant;
|
||||||
void stp_defaults(void) __banked;
|
void stp_defaults(void) __banked;
|
||||||
|
void stp_status(void) __banked;
|
||||||
|
void stp_port_admin(uint8_t port, uint8_t on) __banked;
|
||||||
|
void stp_prio_apply(void) __banked;
|
||||||
|
void stp_port_mcheck(uint8_t port) __banked;
|
||||||
|
uint8_t stp_port_role(uint8_t port) __banked;
|
||||||
|
uint8_t stp_port_state(uint8_t port) __banked;
|
||||||
|
uint8_t stp_ent_id(uint8_t e) __banked;
|
||||||
|
void stp_lag_map(void) __banked;
|
||||||
|
void stp_counters_clear(void) __banked __reentrant;
|
||||||
|
|
||||||
/* Tick rate of stp_timers(), also used by the web UI. */
|
/* Tick rate of stp_timers(), also used by the web UI. */
|
||||||
#define STP_HZ 50
|
#define STP_HZ 50
|
||||||
@@ -43,22 +51,38 @@ extern __xdata uint8_t stp_txhold;
|
|||||||
#define STP_PF_TRIPPED 0x80 /* runtime: disabled by BPDU guard */
|
#define STP_PF_TRIPPED 0x80 /* runtime: disabled by BPDU guard */
|
||||||
|
|
||||||
extern __xdata uint8_t stp_pflags[STP_ENTITIES];
|
extern __xdata uint8_t stp_pflags[STP_ENTITIES];
|
||||||
extern __xdata uint32_t stp_pcost[STP_ENTITIES]; /* path cost; 0 = auto (20000) */
|
extern __xdata uint32_t stp_pcost[STP_ENTITIES];
|
||||||
extern __xdata uint8_t stp_pprio[STP_ENTITIES];
|
extern __xdata uint8_t stp_pprio[STP_ENTITIES];
|
||||||
extern __xdata uint8_t stp_pp2p[STP_ENTITIES];
|
extern __xdata uint8_t stp_pp2p[STP_ENTITIES];
|
||||||
extern __xdata uint8_t stp_ent_of[STP_PORTS]; /* the STP entity a port answers to: itself, or its lag */
|
extern __xdata uint8_t stp_ent_of[STP_PORTS];
|
||||||
extern __xdata uint16_t stp_lag_mask[STP_LAG_COUNT]; /* member ports of each lag; 0 = not part of STP */
|
extern __xdata uint16_t stp_lag_mask[STP_LAG_COUNT];
|
||||||
|
|
||||||
/* Last-heard designated info per port (from received BPDUs); consult
|
|
||||||
* stp_bpdu_age to decide whether it is still current. */
|
|
||||||
extern __xdata struct bridge stp_dbridge[STP_ENTITIES];
|
extern __xdata struct bridge stp_dbridge[STP_ENTITIES];
|
||||||
extern __xdata uint16_t stp_dpid[STP_ENTITIES];
|
extern __xdata uint16_t stp_dpid[STP_ENTITIES];
|
||||||
extern __xdata uint32_t stp_dcost[STP_ENTITIES];
|
extern __xdata uint32_t stp_dcost[STP_ENTITIES];
|
||||||
extern __xdata uint16_t stp_bpdu_age[STP_ENTITIES]; /* ticks since a BPDU was heard */
|
extern __xdata uint16_t stp_bpdu_age[STP_ENTITIES];
|
||||||
|
#define STP_CNT_RX 0 /* BPDUs taken in on an STP port */
|
||||||
|
#define STP_CNT_TX 1 /* BPDUs put on the wire */
|
||||||
|
#define STP_CNT_TCRX 2 /* BPDUs received with the TC flag, TCNs */
|
||||||
|
#define STP_CNT_TCTX 3 /* BPDUs sent with the TC flag, TCNs */
|
||||||
|
#define STP_CNT_N 4
|
||||||
|
extern __xdata uint32_t stp_cnt[STP_CNT_N][STP_ENTITIES];
|
||||||
|
extern __xdata uint16_t stp_info_while[STP_ENTITIES];
|
||||||
|
|
||||||
extern __xdata struct bridge root_bridge;
|
extern __xdata struct bridge root_bridge;
|
||||||
extern __xdata uint32_t root_bridge_cost;
|
extern __xdata uint32_t root_bridge_cost;
|
||||||
extern __xdata uint8_t stp_root_port;
|
extern __xdata uint8_t stp_root_port;
|
||||||
|
extern __xdata uint8_t stp_root_maxage;
|
||||||
|
extern __xdata uint8_t stp_root_fwd;
|
||||||
|
extern __xdata uint16_t stp_link_prev;
|
||||||
|
extern __xdata uint16_t stp_alt;
|
||||||
|
extern __xdata uint16_t stp_backup;
|
||||||
|
extern __xdata uint16_t stp_legacy;
|
||||||
|
extern __xdata uint16_t stp_rrwhile[STP_ENTITIES];
|
||||||
|
extern __xdata uint16_t stp_rbwhile[STP_ENTITIES];
|
||||||
extern __xdata uint16_t stp_tc_count;
|
extern __xdata uint16_t stp_tc_count;
|
||||||
|
extern __xdata uint32_t stp_tc_secs;
|
||||||
|
extern __xdata uint8_t stp_pcost_short;
|
||||||
|
extern __xdata uint8_t stp_bpdu_filter;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* Command line interface of the Spanning Tree Protocol for the RTL837x platform
|
||||||
|
* This code is in the Public Domain
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "rtl837x_common.h"
|
||||||
|
#include "rtl837x_stp.h"
|
||||||
|
#include "machine.h"
|
||||||
|
|
||||||
|
#pragma codeseg BANK2
|
||||||
|
#pragma constseg BANK2
|
||||||
|
|
||||||
|
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||||
|
extern __xdata uint8_t cmd_words_len;
|
||||||
|
extern __xdata uint8_t cmd_words_b[15];
|
||||||
|
uint8_t cmd_compare(uint8_t start, __code const uint8_t * cmd);
|
||||||
|
uint8_t atoi_byte(uint8_t idx);
|
||||||
|
uint8_t cmd_parse_port_separator(uint8_t idx);
|
||||||
|
extern __xdata uint8_t atoi_results_u8;
|
||||||
|
extern __xdata char save_cmd;
|
||||||
|
extern __xdata uint8_t err_status;
|
||||||
|
|
||||||
|
#define TIMES_OK(h, m, f) ((uint16_t)2 * ((f) - 1) >= (m) && (m) >= (uint16_t)2 * ((h) + 1))
|
||||||
|
|
||||||
|
__xdata uint32_t stp_cli_cost;
|
||||||
|
__xdata uint8_t stp_cli_val;
|
||||||
|
|
||||||
|
|
||||||
|
void stp_parse(void) __banked __reentrant
|
||||||
|
{
|
||||||
|
uint8_t port;
|
||||||
|
|
||||||
|
if (cmd_compare(1, "on")) {
|
||||||
|
print_string("STP enabled\n");
|
||||||
|
stp_enabled = 1;
|
||||||
|
stp_setup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "off")) {
|
||||||
|
print_string("STP disabled\n");
|
||||||
|
stp_off();
|
||||||
|
stp_enabled = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "status")) {
|
||||||
|
stp_status();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "clear")) {
|
||||||
|
stp_counters_clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_words_len < 3)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (cmd_compare(1, "port") || cmd_compare(1, "lag")) {
|
||||||
|
if (cmd_words_len < 4)
|
||||||
|
goto err;
|
||||||
|
stp_lag_map();
|
||||||
|
if (cmd_compare(1, "lag")) {
|
||||||
|
if (!atoi_byte(cmd_words_b[2]) || !atoi_results_u8 || atoi_results_u8 > STP_LAG_COUNT)
|
||||||
|
goto err;
|
||||||
|
port = STP_LAG_BASE - 1 + atoi_results_u8;
|
||||||
|
} else {
|
||||||
|
if (!cmd_parse_port_separator(cmd_words_b[2]))
|
||||||
|
goto err;
|
||||||
|
port = atoi_results_u8;
|
||||||
|
if (stp_ent_of[port] != port) {
|
||||||
|
print_string("Error: the port is in lag ");
|
||||||
|
write_char('1' + stp_ent_of[port] - STP_LAG_BASE);
|
||||||
|
write_char('\n');
|
||||||
|
err_status = ERR_INVALID_ARGUMENT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cmd_words_len < 5 && !cmd_compare(3, "on") && !cmd_compare(3, "off")
|
||||||
|
&& !cmd_compare(3, "mcheck"))
|
||||||
|
goto err;
|
||||||
|
if (cmd_compare(3, "on")) {
|
||||||
|
stp_pflags[port] |= STP_PF_ENABLED;
|
||||||
|
stp_pflags[port] &= ~STP_PF_TRIPPED;
|
||||||
|
if (stp_enabled) { /* (re)join: listen first */
|
||||||
|
stp_port_admin(port, 1);
|
||||||
|
}
|
||||||
|
} else if (cmd_compare(3, "mcheck")) {
|
||||||
|
if (stp_enabled)
|
||||||
|
stp_port_mcheck(port);
|
||||||
|
} else if (cmd_compare(3, "off")) {
|
||||||
|
stp_pflags[port] &= ~STP_PF_ENABLED;
|
||||||
|
if (stp_enabled)
|
||||||
|
stp_port_admin(port, 0); /* plain forwarding */
|
||||||
|
} else if (cmd_compare(3, "edge")) {
|
||||||
|
/* Also drop the *operational* edge flag: it is what exempts the
|
||||||
|
* port from topology changes and lets it skip the listen period,
|
||||||
|
* so leaving it set would keep the old behaviour until the next
|
||||||
|
* "stp off"/"stp on". An admin edge is operational immediately. */
|
||||||
|
stp_pflags[port] &= ~(STP_PF_ADMEDGE | STP_PF_AUTOEDGE | STP_PF_OPEREDGE);
|
||||||
|
if (cmd_compare(4, "on"))
|
||||||
|
stp_pflags[port] |= STP_PF_ADMEDGE | STP_PF_OPEREDGE;
|
||||||
|
else if (cmd_compare(4, "auto"))
|
||||||
|
stp_pflags[port] |= STP_PF_AUTOEDGE;
|
||||||
|
else if (!cmd_compare(4, "off"))
|
||||||
|
goto err;
|
||||||
|
} else if (cmd_compare(3, "cost")) {
|
||||||
|
/* raw 802.1D value, 0..200000000; 0 = auto (speed-based) */
|
||||||
|
stp_cli_cost = 0;
|
||||||
|
{
|
||||||
|
__xdata uint8_t *cp = &cmd_buffer[cmd_words_b[4]];
|
||||||
|
if (*cp < '0' || *cp > '9')
|
||||||
|
goto err;
|
||||||
|
while (*cp >= '0' && *cp <= '9') {
|
||||||
|
stp_cli_cost = stp_cli_cost * 10 + (*cp - '0');
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stp_cli_cost > 200000000UL)
|
||||||
|
goto err;
|
||||||
|
stp_pcost[port] = stp_cli_cost;
|
||||||
|
} else if (cmd_compare(3, "p2p")) {
|
||||||
|
if (cmd_compare(4, "auto"))
|
||||||
|
stp_pp2p[port] = 0;
|
||||||
|
else if (cmd_compare(4, "on"))
|
||||||
|
stp_pp2p[port] = 1;
|
||||||
|
else if (cmd_compare(4, "off"))
|
||||||
|
stp_pp2p[port] = 2;
|
||||||
|
else
|
||||||
|
goto err;
|
||||||
|
} else if (cmd_compare(3, "prio")) {
|
||||||
|
if (!atoi_byte(cmd_words_b[4]))
|
||||||
|
goto err;
|
||||||
|
if (atoi_results_u8 > 240 || (atoi_results_u8 & 0x0f))
|
||||||
|
goto err;
|
||||||
|
stp_pprio[port] = atoi_results_u8;
|
||||||
|
} else if (cmd_compare(3, "guard")) {
|
||||||
|
stp_pflags[port] &= ~(STP_PF_BPDUGUARD | STP_PF_ROOTGUARD);
|
||||||
|
if (cmd_compare(4, "bpdu"))
|
||||||
|
stp_pflags[port] |= STP_PF_BPDUGUARD;
|
||||||
|
else if (cmd_compare(4, "root"))
|
||||||
|
stp_pflags[port] |= STP_PF_ROOTGUARD;
|
||||||
|
else if (!cmd_compare(4, "none"))
|
||||||
|
goto err;
|
||||||
|
} else if (cmd_compare(3, "filter")) {
|
||||||
|
if (cmd_compare(4, "on"))
|
||||||
|
stp_pflags[port] |= STP_PF_FILTER;
|
||||||
|
else if (cmd_compare(4, "off"))
|
||||||
|
stp_pflags[port] &= ~STP_PF_FILTER;
|
||||||
|
else
|
||||||
|
goto err;
|
||||||
|
} else {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!atoi_byte(cmd_words_b[2])) {
|
||||||
|
if (cmd_compare(1, "version")) {
|
||||||
|
if (cmd_compare(2, "rstp"))
|
||||||
|
stp_rstp = 1;
|
||||||
|
else if (cmd_compare(2, "stp"))
|
||||||
|
stp_rstp = 0;
|
||||||
|
else
|
||||||
|
goto err;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "pathcost")) {
|
||||||
|
if (cmd_compare(2, "long"))
|
||||||
|
stp_pcost_short = 0;
|
||||||
|
else if (cmd_compare(2, "short"))
|
||||||
|
stp_pcost_short = 1;
|
||||||
|
else
|
||||||
|
goto err;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "bpdu")) {
|
||||||
|
if (cmd_compare(2, "filter"))
|
||||||
|
stp_bpdu_filter = 1;
|
||||||
|
else if (cmd_compare(2, "flood"))
|
||||||
|
stp_bpdu_filter = 0;
|
||||||
|
else
|
||||||
|
goto err;
|
||||||
|
if (!stp_enabled)
|
||||||
|
stp_off();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
stp_cli_val = atoi_results_u8;
|
||||||
|
|
||||||
|
if (cmd_compare(1, "prio")) {
|
||||||
|
if (stp_cli_val > 15)
|
||||||
|
goto err;
|
||||||
|
stp_prio = stp_cli_val << 4; /* n * 4096, as the BPDU's high byte */
|
||||||
|
stp_prio_apply();
|
||||||
|
} else if (cmd_compare(1, "hello")) {
|
||||||
|
if (stp_cli_val < 1 || stp_cli_val > 10)
|
||||||
|
goto err;
|
||||||
|
if (save_cmd && !TIMES_OK(stp_cli_val, stp_maxage_s, stp_fwddelay_s))
|
||||||
|
goto times;
|
||||||
|
stp_hello_s = stp_cli_val;
|
||||||
|
} else if (cmd_compare(1, "maxage")) {
|
||||||
|
if (stp_cli_val < 6 || stp_cli_val > 40)
|
||||||
|
goto err;
|
||||||
|
if (save_cmd && !TIMES_OK(stp_hello_s, stp_cli_val, stp_fwddelay_s))
|
||||||
|
goto times;
|
||||||
|
stp_maxage_s = stp_cli_val;
|
||||||
|
} else if (cmd_compare(1, "fwd")) {
|
||||||
|
if (stp_cli_val < 4 || stp_cli_val > 30)
|
||||||
|
goto err;
|
||||||
|
if (save_cmd && !TIMES_OK(stp_hello_s, stp_maxage_s, stp_cli_val))
|
||||||
|
goto times;
|
||||||
|
stp_fwddelay_s = stp_cli_val;
|
||||||
|
} else if (cmd_compare(1, "txhold")) {
|
||||||
|
if (stp_cli_val < 1 || stp_cli_val > 10)
|
||||||
|
goto err;
|
||||||
|
stp_txhold = stp_cli_val;
|
||||||
|
} else {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
times:
|
||||||
|
print_string("Error: needs 2*(fwd-1) >= maxage >= 2*(hello+1)\n");
|
||||||
|
err_status = ERR_INVALID_ARGUMENT;
|
||||||
|
return;
|
||||||
|
err:
|
||||||
|
err_status = ERR_INVALID_ARGUMENT;
|
||||||
|
print_string("Error: stp on|off|status|clear | prio <0-15> | hello <1-10> | maxage <6-40> | fwd <4-30> | txhold <1-10> | version rstp|stp | pathcost long|short | bpdu filter|flood | port <1-9>|lag <1-4> on|off|mcheck|edge|cost|prio|guard|filter ...\n");
|
||||||
|
}
|
||||||
@@ -121,6 +121,12 @@ uint8_t stp_pflags[STP_ENTITIES];
|
|||||||
uint8_t stp_pp2p[STP_ENTITIES];
|
uint8_t stp_pp2p[STP_ENTITIES];
|
||||||
uint8_t stp_pprio[STP_ENTITIES];
|
uint8_t stp_pprio[STP_ENTITIES];
|
||||||
uint8_t stp_prio = 0x80, stp_root_port = 0xff, stp_rstp = 1, stp_txhold = 6;
|
uint8_t stp_prio = 0x80, stp_root_port = 0xff, stp_rstp = 1, stp_txhold = 6;
|
||||||
|
uint8_t stp_bpdu_filter, stp_pcost_short, stp_root_fwd = 15, stp_root_maxage = 20;
|
||||||
|
uint16_t stp_legacy, stp_link_prev = 0x3ff;
|
||||||
|
uint16_t stp_info_while[STP_ENTITIES];
|
||||||
|
uint32_t stp_cnt[STP_CNT_N][STP_ENTITIES];
|
||||||
|
uint32_t stp_tc_secs;
|
||||||
|
uint8_t stp_ent_id(uint8_t e) { return e >= STP_LAG_BASE ? e + 101 - STP_LAG_BASE : machine.log_to_phys_port[e]; }
|
||||||
|
|
||||||
/* ---- SFP: no module present, reads fail ---- */
|
/* ---- SFP: no module present, reads fail ---- */
|
||||||
uint8_t sfp_buf[16];
|
uint8_t sfp_buf[16];
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
CC ?= cc
|
||||||
|
SRCROOT = ../..
|
||||||
|
CFLAGS = -std=c11 -g -O1 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-pragmas \
|
||||||
|
-Wno-pointer-sign \
|
||||||
|
-fpack-struct=1 \
|
||||||
|
-DRTLP_HOST_TEST \
|
||||||
|
-fsanitize=address,undefined -fno-omit-frame-pointer \
|
||||||
|
-include ../sdcc_shim.h -I../stub -I$(SRCROOT) -I$(SRCROOT)/httpd -I$(SRCROOT)/uip
|
||||||
|
ifeq ($(findstring clang,$(shell $(CC) --version 2>/dev/null)),)
|
||||||
|
CFLAGS += -Wno-builtin-declaration-mismatch
|
||||||
|
endif
|
||||||
|
LDFLAGS = -fsanitize=address,undefined
|
||||||
|
|
||||||
|
SRCS = stp_sim.c $(SRCROOT)/rtl837x_stp.c $(SRCROOT)/rtl837x_stp_cli.c
|
||||||
|
|
||||||
|
all: build/stp_sim
|
||||||
|
./build/stp_sim
|
||||||
|
|
||||||
|
verbose: build/stp_sim
|
||||||
|
./build/stp_sim -v
|
||||||
|
|
||||||
|
build/stp_sim: $(SRCS) $(SRCROOT)/rtl837x_stp.h
|
||||||
|
mkdir -p build
|
||||||
|
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
.PHONY: all verbose clean
|
||||||
+2362
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user