* stp: select the root port by cost and block redundant links
* stp: move the protocol to BANK4 and keep the command line in BANK2
* stp: implement the RSTP state machines of 802.1D-2004 clause 17
* stp: counters, more settings and a fuller Spanning Tree page
* stp: make a link aggregation group one port of the RSTP tree
* web: serve the Spanning Tree JSON from BANK4 as well
Kept in BANK1, the larger Spanning Tree page would leave under 1 kB
there for the rest of the web server and every page still to come, so
send_stp() and send_stp_counters() move to httpd/page_stp.c in BANK4
with the protocol, as banked calls, with their own copies of the few output
helpers and of the JSON header, so nothing in BANK4 calls into BANK1
directly or hands it a pointer into its code space. BANK4 takes the
free flash between BANK3 and the web files.
KP_9000_6XHML_X2: BANK1 44336, BANK2 36969, BANK3 11942, BANK4 20965
bytes of 49152.
Dynamic link aggregation on top of the static LAGG, for RTL8372/8373:
receive, selection and mux state machines (coupled mux, one aggregator
per group), with "lacp show" to look inside. Each of the four trunk
groups runs its own aggregator on the candidate ports assigned with
"lag <n> lacp <ports>"; the engine starts with the first such group and
stops with the last.
LACPDUs reach the CPU through the reserved-multicast trap, so delivery
needs no L2 entry and nothing written per VLAN or per PVID. The trap
acts on the address before the VLAN lookup and before the ingress
filters, so a LACPDU also arrives on a port that admits tagged frames
only and on a port STP holds in blocking, neither of which a forwarded
one reaches. A trapped frame carries no VLAN tag,
so lacp_in() takes the offset of the PDU body from TRAP_RX_BODY(). The
action is set with trap_rma_set(), which leaves the flags sharing that
register alone, and "lacp off" puts the slow-protocols drop default
back. doc/lacp.md and doc/link_aggregation.md record what was measured,
including why the trap looked like a dead end at first.
The web UI gets a Static/LACP mode per group on the LAG page with live
status, and the startup-config validator knows the per-group syntax. A
host-side sandbox for the state machines runs in the test harness
together with the LAG hash check. It drives both forms of an incoming
LACPDU, so the body offset of a trapped one is covered as well.
The L2 table page iterated with the "next unicast" read method, so
static multicast entries such as an IGMP group were invisible. It now
iterates with "next address", skips L3 entries (their layout carries IP
addresses where a MAC is expected), and reports the lowest member of a
multicast entry's port mask, so such an entry shows up as a static
entry on that port.
A port taken out of LACP forgets its partner, so "lacp show" after
"lacp off" no longer reports one. Applying the same candidate set to a
group again, as the LAG page does on every Apply, leaves the trunk
alone instead of tearing it down for a second or two. lacp_in() checks
the frame length and ethertype before reading a LACPDU, so a runt or a
foreign frame on the slow-protocols address cannot leave stale buffer
bytes as a partner. The sandbox covers these three in T20-T22. On the
LAG page, LACP mode with no ports checked sends "lag N lacp off", and
the startup-config validator wants at least one port after "lacp".
Moving the global on/off onto the LLDP page
Building a table of LLDP status from the S ports. Later on, if
we wish to implement an lldp receiver we can display the remote
details here
Adding support to the cmd parser for the lldp commands
A matrix of the ports, one row per ingress port, showing which ports a
frame entering there may leave through. The port number of a row turns
red when the port is not fully open, and the cell for the port itself is
marked since it is always allowed. Apply sends one "isolate" command per
changed row, and Allow all ticks the whole matrix.
The switch side is a new /isolation.json, which reports each port's
allowance mask read back from the hardware. The page is an Isolation
section of the single-page UI.
Building with SDCC 4.6.0 emits tons of warnings e.g.:
cmd_parser.c:31: warning 356: object in read-only code space should be const
cmd_parser.c:36: warning 357: pointer to object in read-only code space should be pointer to const
These of course become fatal when building with --Werror.
Comparing resultant binaries, it seems that a single string is shifted
(deduplication?) but the numbers emitted at the end of the build are
otherwise identical.
The session cookie carried neither Expires nor Max-Age, so browsers that
clean up session cookies aggressively (LibreWolf, for one) may drop it
while the session is still valid. Send Max-Age=session_timeout so the
declared lifetime matches the timeout already enforced by the firmware.
itoa16_html() only converted four digits, so extend it to five to print
timeouts up to 65535. That also fixes information.json, which used the
same helper and reported wrong values from 10000 s up.
The /cmd path carried its own copy of the plain-text response header,
which was the same string page_impl.c already keeps in
HTTP_RESPONCE_TXT, minus one difference: the shared one never said
Connection: close.
This httpd closes after every response, so a reply that does not
advertise it invites the browser to reuse the socket and lose the next
request. HTTP_RESPONCE_JSON has always said so; the text one was the
odd one out, and send_config() and send_cmd_log() were answering
without it.
The copy is gone and the shared string carries the header. The check
for a command that printed nothing now compares against the length
strtox() returned instead of sizeof() on the macro.
cmd_parser.c conflicted twice: main rewrote the pvid command around the
new port-separator and atoi helpers, next to the line where this branch
delegates "stp" to stp_parse(). Both belong, so the STP delegation keeps
main's pvid body.
The linker caught what the merge could not see: main changed atoi_byte()
to return the digit count and leave the value in atoi_results_u8, where
it used to return non-zero on failure and write through a pointer. Note
the sense is inverted, so the three calls in rtl837x_stp.c are adjusted
rather than just re-arranged.
Add a Spanning Tree page: an on/off toggle driving the existing "stp"
command over /cmd, and a live status section fed by a new /stp.json
endpoint - the elected root bridge (priority + MAC), our path cost,
whether we are the root, and the per-port STP state read live from the
ASIC's MSTP register (same 2-bit encoding stp_setup() writes). Ports are
reported by their physical numbers.
Recovered-from: 3132319, 9365c86
Returns a JSON array of all configured VLANs with their IDs and names,
e.g. [{"id":1,"name":""},{"id":20,"name":"IoT"}].
The endpoint iterates VLAN IDs 1..4094 and filters by the validity bit
in sfr_data[0] (0x02), following the same pattern as vlan_create() and
vlan_setup() in rtl837x_port.c.
Also adds a small itoa16_html() helper for emitting decimal numbers
up to 4 digits (analogous to the existing 8-bit itoa_html()), used
for VLAN IDs which can reach 4094. Response builder uses the existing
vlan_name() helper for the name lookup, consistent with send_vlan().
Buffer overflow is prevented by breaking out of the iteration loop at
TCP_OUTBUF_SIZE - 60.
This endpoint is the foundation for upcoming UI improvements
(VLAN selector dropdown and overview table).
flash_read_bulk() is kind of a printf function. But did not work as well.
flash_read_bulk() was used in the http generation so this needed to be changed as well.
For the website we decided that we are going to use CSR(Client Side Rendering).
So every HTML and JS files are now static.
Variables are fetched via json calls and output is render on the client side.
Also usefull for the future when we want a REST like API.
execute_config() was also using flash_read_bulk().
This have been replaced with flash_read_bulk() and parsing the flash_buf buffer.