98 Commits
Author SHA1 Message Date
logicog 7e11be0d97 Add ZoeRax HG0402XG V1.1 to supported devices 2026-10-27 22:05:22 +01:00
logicog 23862bc648 New Device: Add support for ZoeRax HG0402XG V1.1
The ZoeRax HG0402XG is an unmanaged 4 2.5GBit Ethernet, 2 SFP+
device.
2026-10-27 22:05:22 +01:00
feelfree69 413690a748 Merge pull request #711 from DrDoof/fix/installer-toolchain
installer: build with the toolchain from the tree
2026-10-27 22:03:33 +01:00
feelfree69 73bb4c8af2 Merge pull request #709 from L4ky/add-config-path-to-makefile
Add CONFIG parameter to make
2026-10-27 18:21:42 +01:00
feelfree69 0443ba428b Merge pull request #661 from Kamikaze94/feature/improve-strip-rj45-icon
WebUI: Change port strip icon for rj45 ports, latch added
2026-10-27 18:12:43 +01:00
feelfree69 62abd08aa2 Merge pull request #660 from Kamikaze94/bugfix/strip-sfp-icon
WebUI: Fix port strip icon for sfp ports
2026-10-27 18:11:25 +01:00
feelfree69 70426ff24a Merge pull request #720 from brunoz78/de-vlan
i18n(de): translate the new VLAN manager texts
2026-10-27 16:39:20 +01:00
feelfree69 2d62cc493d Merge pull request #719 from nappairam/swtg018as-v2-factory-mac
machine: read factory MAC on SWTG018AS-A V2.0
2026-10-27 16:37:08 +01:00
Mariappan Ramasamy 36de7b7241 machine: read factory MAC on SWTG018AS-A V2.0
Horaco HC-SWTGW218AS units on this PCB keep their factory MAC at
0x1FC000, the offset the other boards in machine/ read it from, so use
it instead of a generated locally administered one. If the flash holds
no valid MAC there, the generated one is still used.
2026-10-27 18:47:22 +08:00
Brunoz78andClaude Opus 5.5 ea320d813d i18n(de): translate the new VLAN manager texts
#609 added six texts to the VLAN page without a German translation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:02:36 +01:00
Luca S. ca2abcc4e1 Merge branch 'main' into add-config-path-to-makefile 2026-10-26 20:01:58 +01:00
René van Dorst 4d7159092c Merge pull request #673 from andrebrait/fix/xdata-startup
startup: clear SFR 0x97 before the C runtime initialises xdata
2026-10-26 17:15:32 +00:00
Andre Brait 98763e3bff startup: clear MAP_FLASH_PAGE_TO_XDATA before the C runtime initialises xdata
Hardware reset starts the firmware with SFR 0x97 = 1, which maps xdata
0x4000-0xbfff to the flash image: reads return flash bytes, writes are
lost. main() cleared it only after __mcs51_genXINIT and the XSEG clear
had run, so XISEG (initialised globals) and the part of XSEG above
0x4000 kept stale RAM contents, e.g. session_timeout = 4484 and LLDP
blocked on logical ports 0, 3 and 5. Clear it in
__sdcc_external_startup(), which SDCC calls before both.

Rename SFR_97 to MAP_FLASH_PAGE_TO_XDATA.
2026-10-26 17:08:27 +01:00
logicog 80a30b705e Merge pull request #706 from DrDoof/feat/banner-compiler
build: show the compiler and the toolchain in the version banner
2026-10-26 14:41:50 +01:00
feelfree69 4c3e1fde81 Merge pull request #609 from mintsoft/vlan-manager-fixes
VLAN manager UI/fixes
2026-10-26 13:59:39 +01:00
feelfree69 5cd14e6227 Merge pull request #714 from rwalli/doc-hc-swtg124as-19650
doc: add HC-SWTG124AS on PCB-SWTG024AS-A-V2.0.1_19650 with SW1 reset …
2026-10-26 12:34:47 +01:00
rwalli@gmx.at 5105018d90 machine: reset button on GPIO48 for HC-SWTG124AS, drop braces around _19650
SW1 on PCB-SWTG024AS-A-V2.0.1_19650 is wired to GPIO48. Measured on
HC-SWTG124AS: GPIO48 reads 1 idle and 0 while the button is held.
HC_SWTG124AS.c is a symlink to PCB_SWTG024AS_A_2_0_1.c.
2026-10-26 12:23:28 +01:00
rwalli@gmx.at 5bc7cf43db doc: merge duplicate HC-SWTG124AS rows, note _19650 variant in existing entry 2026-10-26 12:00:38 +01:00
rwalli@gmx.at c776d10129 doc: add HC-SWTG124AS on PCB-SWTG024AS-A-V2.0.1_19650 with SW1 reset note 2026-10-26 11:30:31 +01:00
feelfree69 7b4d0d55d2 Merge pull request #713 from dobodu/main
Fix double sy_sysport_err and add new translations
2026-10-26 08:45:15 +01:00
Luca S. e95e450813 Add CONFIG parameter on make 2026-10-26 08:27:28 +01:00
dobodu fe1d4119d0 Fix double sy_sysport_err and add new translations
Removes
double entries for sy_sysport_err corrected
Add
sy_dns1, sy_dns2, sy_dns_err & sy_dns_dhcp translations
2026-10-26 00:13:49 +01:00
d00f c5f62bff95 build: name the sdcc/ revision in the toolchain line
A commit hash can be looked up directly, a checksum cannot. Print the last commit that touched sdcc/, with -dirty for local changes, and fall back to the checksum when git is not available. The toolchain directory keeps the checksum.
2026-10-25 22:49:37 +01:00
d00f 7535c0d9b7 installer: build with the toolchain from the tree
The installer Makefile called whatever sdcc was on the PATH, so an
installer built by hand came out of the system compiler, without the
fixes in sdcc/. Use the same toolchain/sdcc-<cksum> as the main Makefile,
built on demand by sdcc/build.sh if a bare installer build gets there
first, and keep CC= on the command line as the way to pick another
compiler. The README example shows the new command lines.
2026-10-25 22:44:51 +01:00
Luca S. a57319557d Add CONFIG parameter on make 2026-10-25 22:42:06 +01:00
d00f 1b29fb687f build: count the sdcc patches in the toolchain line
The checksum changes with every patch but does not say how many there are. Print the number of patches in sdcc/ next to it.
2026-10-25 21:23:32 +01:00
Rob Emery 2e62f9eea7 Adding the name of the port to the UI so it's easier to see what
you are changing
2026-10-25 16:33:54 +00:00
feelfree69 5c6a82d50f Merge pull request #568 from DrDoof/fix/upload-boundary-cr
httpd: re-check the byte that breaks a partial boundary match
2026-10-25 16:52:47 +01:00
d00f 1d158d21d1 build: show the compiler and the toolchain in the version banner
The boot banner and the version command print the SDCC release and
revision from the compiler's own macros, and the name of the in-tree
toolchain the Makefile built, which is the checksum of sdcc/ and so
changes with every patch in it. A build with CC given on the command
line says "system" instead, since that compiler need not carry the
patches. A plain release and the same release with the patches report
the same revision, so the toolchain line is what tells them apart.

Costs 66 bytes of BANK2.
2026-10-25 16:04:49 +01:00
feelfree69 26c420d26a Merge pull request #701 from hlyi/PSW8_25MG
Add REALHD_PSW8_25MG support and makefile enhancement
2026-10-25 15:52:26 +01:00
HL Yi eb394af95f add REALHD_PSW8_25MG support 2026-10-25 09:11:35 -05:00
feelfree69 4a4caeffd7 Merge pull request #703 from hlyi/make-chg
makefile enhancement for machine build
2026-10-25 14:37:47 +01:00
HL Yi 391360f517 makefile enhancement that only when both .h and .c exist to be considered as a device. It allows generic pcb-specific c not listed as a device 2026-10-25 06:21:45 -05:00
feelfree69 ed8e4b1a56 Merge pull request #560 from brunoz78/multi-session
httpd: four login sessions instead of one
2026-10-25 11:52:56 +01:00
feelfree69 78f28488c1 Merge pull request #534 from DrDoof/fix/html-min-stamp
build: minify the web files only when they change
2026-10-25 11:48:16 +01:00
Brunoz78andClaude Sonnet 5.5 e1c79ec4cd httpd: reuse the slot of a valid cookie, end sessions on passwd
Review feedback on #560 (DrDoof):

- A login that came with a cookie that is still valid took a second
  slot. The first one looked fresh, so the next login could push out
  someone who is using the switch. The slot of the cookie now gets the
  new id.
- Changing the password leaves no old login valid: session_init() from
  parse_passwd(). With one session the next login pushed a leftover one
  out, with several it would stay valid until the next restart.
- The 32-bit comparison of the last use times moves into a leaf function,
  so the compiler does not give it a 4-byte scratch in internal RAM.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 10:51:23 +01:00
Brunoz78andClaude Opus 5.5 a7db0ca8b6 httpd: keep the login sessions in bank 3
The web server's bank had no room left for the four sessions once the
original firmware's more robust header handling came in. Picking a slot,
checking a cookie and starting a session move to sessions.c in bank 3;
httpd.c only calls session_init(), session_check() and session_new().
The random id is written straight into the reply, which drops
gen_random_hex_chars() from bank 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:51:05 +01:00
Claude 70f59b64f5 httpd: four login sessions instead of one
Logging in from a second browser ended the first one's session at its next
request: two browsers open on the switch logged each other out every few
seconds. Up to four sessions now, each with its own cookie and expiry; a
fifth login replaces the least recently used. The expiry check is a leaf
function so its 32-bit scratch stays out of internal RAM (stack still
159 bytes with HEALTH).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tx2W7xVecMoKbBCkD2i8PG
(cherry picked from commit 03330f4077ef8872d861d951c8e44ece3c573935)
2026-10-25 10:51:05 +01:00
feelfree69 f0cbaba7b1 Merge pull request #672 from dobodu/main
French translation update
2026-10-25 10:20:24 +01:00
Rob Emery 2d5424fedc Fixing double firing of load vlan 1 when the tab is loaded 2026-10-25 09:06:30 +00:00
Rob Emery 23c8b2a8b3 Fixing the unsaved changes not appearing. Previously if a set of
changes were sent as a single cmd using \n separation then the
dirty regex would skip it as $ wouldn't match \n. with /m modifier
the regex will match
2026-10-25 09:06:30 +00:00
Rob Emery c9f6362da5 Previously the vlan 2 regex was assuming there were ports, now
it'll accept just the definition.
Also throughout the regex we have over-specified the number of digits
sometimes ports are 4 digits long, sometimes they are 2. This seems
unnecessary, 1 or more is sufficient
2026-10-25 09:06:30 +00:00
Rob Emery 742aef5461 Adding highlighting to the currently selected VLAN in the top table
so that it is more clear it is selected
2026-10-25 09:06:30 +00:00
Rob Emery a77d219baf Moving the mgmt switch into the bottom section so it applies when
the user clicks apply on the specific vlan. In order for this to
work when the user is simultaneously changing the vlan that their
machine is connected through and the mgmt vlan id, we need to be
able to send a batch of commands through to the switch atomically
so the switch can process all of them and the momentary disconnection
of the machine doesn't matter
2026-10-25 09:06:30 +00:00
Rob Emery f94204e56f Adding rename support for vlan 2026-10-25 09:06:30 +00:00
Rob Emery 8d6adedb56 Removing my changes to the sim, in favour of #629 2026-10-25 09:06:30 +00:00
Rob Emery c3f9acea10 Tidying up the new VLAN page and adding error messages
When adding a new VLAN, we should load it after addition
Prevent deleting the mgmt vlan
Adding a button to promote a vlan to be the management VLAN
We can't create a vlan called d or mgmt as that clashes with the
commands to delete and promote a vlan
Making the Management VLAN more obvious and adding a "confused"
disabled button to where delete and promote are
Switching to the i image and making it so you can click the entire
row apart from the cell where the buttons are (to prevent accidents)
Cleaning up the last bits of ingress filtering I can see
2026-10-25 09:06:30 +00:00
Rob Emery c46261e052 Using PVID=0 for the situation where the port is only in tagged
membership. This was tested on HI_K0801WS and it appears to not
have any negative side-effects
2026-10-25 09:06:30 +00:00
Rob Emery 834403b97b Parking ports with only tagged membership into VLAN 4094 for now
I think this should also resolve the work-flow where you can get
a port's PVID and tagged membership to the same vlan
2026-10-25 09:06:30 +00:00
Rob Emery 48087d3520 Decoupling the load from the spinner so we can get rid of it
Also making the first VLAN in the table load when we load the UI

Removing the tickbox for the PVID, I'll swap this out to ticks
or something later on, but the PVID will be automatically set
based on the untagged membership, otherwise it will be 1

Send the vlan with name command when attempting to add a new VLAN
Because of the structure of the vlan command, we need to send the name as well if its set

Set the PVID based on the untagged membership and reload the
table when applied. We need to block setting the PVID to the tagged by proxy

Blue button

Need to provide array of pvid commands not concat
Storing state of each vlan so we can refer later

Blue button
2026-10-25 09:06:30 +00:00
Rob Emery 1d359afba5 Adding vlanlist support into http_sim so it's easier to work on
Also improving vlan.json to mirror the real output
2026-10-25 09:06:30 +00:00
logicog a18c334139 Merge pull request #649 from DrDoof/build/sdcc-in-tree
build: build SDCC 4.6.0 with the mcs51 fixes in the tree
2026-10-25 06:18:24 +01:00
DrDoof 337b171ac4 build: minify the web files only when they change
html_min was phony, so every make deleted output/html_min and minified
all of html/ again, even with nothing changed. A stamp file now stands for
the minified copy and depends on the sources and on minify.py. A list of
the source names, rewritten only when it changes, is a dependency too, so
adding or removing a file in html/ still redoes the copy and a removed
file does not stay behind in the image. If output/html_min is missing,
the stamp is remade in the same run.

The note on version.h in webui-compression.md still said it was phony.
2026-10-25 03:11:03 +01:00
d00f 53eb3f5486 Merge pull request #583 from avicarmeli/fix/cpu-vlan-init
Fix CPU port PVID initialization independent of user port range
2026-10-25 02:44:18 +01:00
d00f 241edfdbda test: every last byte, CR runs and broken matches through the upload
Three more bench scenarios around the closing boundary. Every value
of the last image byte, uploaded whole and split right after the
payload, must end the part. A run of CR before the boundary of even
length passed before the fix and an odd one did not, so both are
checked. An image made of CR only, longer than a flash page, and one
made of repeated "\r\n-" exercise the broken matches back to back:
each one appends to the page buffer without a byte written, and the
page flush after both paths has to keep up.
2026-10-25 02:49:21 +02:00
DrDoof 714c9eb343 httpd: re-check the byte that breaks a partial boundary match
stream_upload() matches the closing multipart boundary byte by byte.
When a byte breaks a partial match, the matched prefix goes to flash as
data, and so does the breaking byte, without being compared against the
start of the boundary.

The boundary starts with "\r\n--", so an image whose last byte is '\r'
never completes: that '\r' starts a match, the '\r' of the boundary
breaks it and is written as data, and the rest of the boundary no
longer matches. The browser waits for a reply that never comes.

Flush the matched prefix and look at the breaking byte again from the
start of the boundary. A boundary may not contain CR, so no longer
suffix of the matched prefix can start a new match.
2026-10-25 02:49:14 +02:00
feelfree69 f2c89fd75b Merge pull request #651 from Kamikaze94/bugfix/sidebar-scrolling
WebUI: Lock sidebar layout and move scrolling to main element
2026-10-25 00:21:08 +02:00
avicarmeli ca2f1d101d Merge upstream main into CPU VLAN fix 2026-10-24 23:38:25 +03:00
d00f 9cc859ad30 build: carry the sdas fix for SDCC bug 4070
The 8051 assembler sign-extends every value to 24 bits, so the top
byte of a __sfr32 address list like 0xa4a5a6a7 comes out as 0xff.
The patch keeps 32-bit arithmetic while leaving the 24-bit T line
addresses alone, so the relocations are unchanged. Proposed upstream
in the bug thread. With it SFR_DATA_U32 can be used again, which
saves about 240 bytes of code in REG_SET.
2026-10-24 21:59:37 +02:00
d00f 0181276ec6 ci: build the in-tree SDCC and keep it between runs
The workflow and the Dockerfile install what building SDCC needs instead
of the distribution's sdcc, and the workflow caches toolchain/ keyed on
the contents of sdcc/.
2026-10-24 21:59:37 +02:00
d00f 905e588071 build: build SDCC 4.6.0 with two mcs51 fixes in the tree
Released SDCC 4.5.0 and 4.6.0 miscompile some of our code in the mcs51
register allocator (SDCC bugs 4153 and 4154, fixed in trunk r17025 and
r17039). The first make now downloads the 4.6.0 sources, checks their
sha256, applies the two fixes from sdcc/ and installs the compiler into
toolchain/, named after a checksum of sdcc/, so a changed patch builds a
new one. make CC=sdcc still uses an installed compiler.
2026-10-24 21:58:46 +02:00
feelfree69 bb16302a80 Merge pull request #622 from bloqaudio/fix/port-counter-lines
web: give each packet counter of a port its own line
2026-10-24 19:56:44 +02:00
feelfree69 50fda40fd9 Merge pull request #584 from bloqaudio/fix/mirror-status
mirror: show the configured monitor port in "mirror status"
2026-10-24 19:54:44 +02:00
feelfree69 9c65039bab Merge pull request #623 from bloqaudio/fix/save-button-dirty
web: highlight Save to flash only when there are unsaved changes
2026-10-24 19:39:55 +02:00
feelfree69 7e0bc93b55 Merge pull request #503 from bloqaudio/fix/uip-fragment-log-rate-limit
uip: rate-limit the dropped-fragment log
2026-10-24 16:40:33 +02:00
feelfree69 e1e1292ab0 Merge pull request #621 from bloqaudio/fix/num-header-align
web: right-align the headers of number columns
2026-10-24 16:36:11 +02:00
feelfree69 975caaff02 Merge pull request #633 from bloqaudio/fix/save-merge-lldp
web: keep LLDP lines from piling up in the saved configuration
2026-10-24 16:05:59 +02:00
feelfree69 cffe4793b1 Merge pull request #695 from L4ky/add-keeplink-kp90009xhpx
Add KeepLink KP-9000-9XHP-X as brand of POE-2G080110GS
2026-10-24 13:53:01 +02:00
feelfree69 9ed1e89c8b Merge pull request #698 from rwalli/fix/firefox-pw-autofill
Web UI: keep Firefox from autofilling the new admin password
2026-10-24 13:50:26 +02:00
feelfree69 ba2f759df3 Merge pull request #696 from rwalli/fix/uptime-ram-counter
rtl8372_init: set SDS 0 to SGMII as the stock firmware does
2026-10-24 12:44:58 +02:00
robs 884a1ac016 CHANGELOG: SDS 0 init on RTL8372 boards without 10G ports 2026-10-24 08:45:03 +02:00
robs 51ed063dcb rtl8372_init: set SDS 0 to SGMII as the stock firmware does
The masks were 0, so 0x3e2 was ORed into the reset value 0xbff and both
SerDes stayed off on boards without 10G ports (n_10g == 0), e.g. the
HC-SWTG124AS. With SDS 0 off RTL837X_REG_SEC_COUNTER does not hold a
written value: it reads 0, so NTP never retried after a failed first
attempt at boot and froze the clock after a sync.

Tested with regset on an HC-SWTG124AS: the register keeps a write with
SDS 0 in SGMII or 10GR, not with SDS 0 off (also not with only SDS 1
on).
2026-10-24 08:30:27 +02:00
robs 0da6318777 Web UI: keep Firefox from autofilling the new admin password
Firefox filled a saved login into the System tab's password fields and
its user name into the NTP poll interval, the input before them. That
input event marked the Time card as edited, so ntpLoad() left NTP off,
the server empty and the time zone at UTC-12:00.
2026-10-24 01:20:45 +02:00
Luca S. d13a5d125d Add KeepLink KP-9000-9XHP-X as brand of POE-2G080110GS 2026-10-24 00:21:28 +02:00
feelfree69 889c94404c Merge pull request #656 from andrebrait/fix/upload-any-part-type
Accept firmware uploads independent of file MIME type
2026-10-23 08:15:07 +02:00
feelfree69 0f98734ae2 Merge branch 'main' into fix/upload-any-part-type 2026-10-23 08:09:07 +02:00
feelfree69 2d0abbbca0 Merge pull request #691 from hlyi/clean-min-max-port
clean up min_port and max_port from struct after PR#684
2026-10-23 08:08:14 +02:00
feelfree69 fdcdfa9b45 Merge pull request #659 from andrebrait/fix/health-lldp-phase
health: give the LLDP phase a valid slot in the phase arrays
2026-10-23 08:07:31 +02:00
HL Yi c043c671d7 clean up min_port and max_port from struct after PR#684 2026-10-22 18:34:44 -05:00
Andre Brait 7d7585fa6d fix(health): give the LLDP phase a valid slot in the phase arrays
In HEALTH=1 builds health_phase(HEALTH_LLDP) used id 7 while ph_max[] and
ph_slow[] had HEALTH_PHASES = 6 entries. The LLDP checkpoint therefore
wrote one byte past ph_max (into the high byte of ph_slow[0]) and, when
the phase took two ticks or more, incremented two bytes past ph_slow
(dhcp_state.state and transaction_id on KP_9000_6XHML_X2).

Rename it HEALTH_PH_LLDP = 6, derive HEALTH_PHASES from it so the arrays
always cover the last phase, print an lldp line in "health" and document
it.

Fixes #658
2026-10-22 11:32:05 +02:00
Andre Brait 13801241ca fix: accept firmware uploads independent of part MIME type
Chrome on macOS labels .bin files application/macbinary. Searching for application/octet-stream mistakes the image bytes for more part headers and aborts when the preamble buffer fills.

Find the multipart header terminator instead, after the HTTP separator retained by handle_post. Keep authentication, CRC checks and staging limits unchanged. Cover MacBinary, an omitted part Content-Type, and a split header terminator with exact staged-byte checks.
2026-10-22 11:26:31 +02:00
Kamikaze94 7b0a07efaa Merge branch 'logicog:main' into bugfix/sidebar-scrolling 2026-10-22 01:37:32 +02:00
Kamikaze94 3c00ab0ae9 WebUI: Cleanup color settings 2026-10-22 00:40:46 +02:00
dobodu 3deee14f94 French translation update
Add LLDP
Add storm control
Add NTP and syslog 
translations
2026-10-21 10:49:29 +02:00
Kamikaze94 6d539bfb1b WebUI: Change port strip sfp icon 2026-10-20 22:46:15 +02:00
avicarmeli 827d3b389c Fix Makefile test list continuation after merge 2026-10-20 16:55:24 +03:00
avicarmeli ef224ec8f4 Merge branch 'main' into fix/cpu-vlan-init 2026-10-20 16:44:04 +03:00
Kamikaze94 2a9f9b2508 WebUI: Change port strip rj45 icon, latch added 2026-10-19 23:58:57 +02:00
Kamikaze94 723d16b4cb WebUI: Fix port strip icon for sfp ports 2026-10-19 18:32:37 +02:00
Kamikaze94 a1998c70d7 WebUI: Lock sidebar layout and move scrolling to main element 2026-10-19 00:51:19 +02:00
bloqaudio 196d22d7c3 web: keep LLDP lines from piling up in the saved configuration
Save to flash merges the running log into the startup configuration.
Neither its toggle nor its overwrite rules knew "lldp on|off", so every
switch of LLDP added another line: four switches left four lines.
"port <n> lldp permit|block" fell under the rule for "port <n> <speed>",
so setting a port's speed dropped its LLDP setting from the saved
configuration, and the other way round.

"lldp on|off" is now a toggle, and a per-port LLDP setting replaces only
an earlier per-port LLDP setting of that port.
2026-10-16 10:43:08 -05:00
bloqaudio 680a40dab5 web: highlight Save to flash only when there are unsaved changes
The button in the header was always drawn as the primary action, on
every page, so it competed with each page's own buttons, for example
the upload button on the Firmware page. It is now a plain button and
turns primary together with the "unsaved changes" badge.
2026-10-15 00:12:14 -05:00
bloqaudio 97b15f4022 web: give each packet counter of a port its own line
The port tooltip and the port details showed "TX good / bad: 8794 / 0
pkts", which reads as 8794 bad packets. Each counter now has its own
line, labelled as on the Statistics page, and the two strings only that
line used are removed from every language.
2026-10-15 00:11:04 -05:00
bloqaudio d5ccd38dd3 web: right-align the headers of number columns
"table.t th" sets text-align:left and is more specific than ".num", so
the headers of the counter columns on the dashboard and the statistics
page stayed left-aligned above right-aligned values.
2026-10-14 23:48:46 -05:00
avicarmeli 6bae5c5c90 test: run CPU VLAN variants in the standard host suite 2026-10-12 20:16:21 +03:00
bloqaudio c2980e42d8 mirror: show the configured monitor port in "mirror status"
"mirror status" printed the local mirroring_port, which is still 0 at
that point, so it named the same port whatever was configured. Print
the port from RTL837x_MIRROR_CTRL instead, which the function has
already read to tell whether mirroring is enabled. send_mirror() decodes
the register the same way for the web interface.
2026-10-12 12:02:02 -05:00
avicarmeli 875933b9f9 fix: initialize CPU MAC9 VLAN state independently of user port count 2026-10-12 10:43:42 +03:00
bloqaudio d0374541c5 uip: rate-limit the dropped-fragment log
uip has no reassembly, so every fragment it receives is discarded and
logged. A neighbour that broadcasts fragmented traffic therefore produces a
continuous stream of "ip: fragment dropped." rather than an occasional one,
at roughly thirteen lines a second on a quiet segment, which buries every
other console message.

The message carries real information: a sudden burst of it says something
about the network. What carries none is repeating it per fragment. Report
the count once per five second window instead, which keeps the signal and
ends the flood.

The count is printed through the no_syslog primitives, as the original
message was, so the flood cannot move from the console into the log ring.
itoa() writes through write_char(), which also feeds the ring, so it gets
a no_syslog twin beside it rather than a private copy of its digits.
2026-09-25 08:20:27 -05:00
57 changed files with 1508 additions and 330 deletions

No files matched your search

+1
View File
@@ -3,6 +3,7 @@
.gitattributes .gitattributes
.github/ .github/
output/ output/
toolchain/
installer/output/ installer/output/
*.bin *.bin
html_data.c html_data.c
+10 -2
View File
@@ -29,7 +29,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
apt update apt update
apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev -y apt install make gcc g++ bison flex libboost-dev patch bzip2 curl ca-certificates xxd python-is-python3 libjson-c-dev zlib1g-dev -y
- name: Cache the patched SDCC
uses: actions/cache@v4
with:
path: branch/toolchain
key: sdcc-${{ hashFiles('branch/sdcc/*') }}
- name: Run the host-side unit tests - name: Run the host-side unit tests
run: cd branch && make -C test run: cd branch && make -C test
- name: Check that every machine definition can be compiled - name: Check that every machine definition can be compiled
@@ -41,7 +46,10 @@ jobs:
- name: Make project with the health instrumentation - name: Make project with the health instrumentation
run: cd branch && make clean && make CI=1 MACHINE="KP_9000_6XHML_X2" HEALTH=1 run: cd branch && make clean && make CI=1 MACHINE="KP_9000_6XHML_X2" HEALTH=1
- name: Make main - name: Make main
run: cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map run: |
ln -s ../branch/toolchain main/toolchain
PATH="$(echo "$PWD"/branch/toolchain/sdcc-*/bin):$PATH"
cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map
- name: Calculate bank growth - name: Calculate bank growth
run: | run: |
sed -rn 's/^([XCD]SEG|(BANK[0-9])).*=\s+([0-9]+)\. bytes.*$/MAIN_\1=\3/p' /tmp/main.map | sort -u >> "$GITHUB_ENV" sed -rn 's/^([XCD]SEG|(BANK[0-9])).*=\s+([0-9]+)\. bytes.*$/MAIN_\1=\3/p' /tmp/main.map | sort -u >> "$GITHUB_ENV"
+1
View File
@@ -2,6 +2,7 @@
.idea/ .idea/
output/ output/
output-main/ output-main/
toolchain/
html_data.c html_data.c
html_data.h html_data.h
version.h version.h
+22
View File
@@ -25,12 +25,34 @@
## Fixed ## Fixed
- Time
- `rtl8372_init()` sets SDS 0 to SGMII as the stock firmware does. The masks were 0, so on RTL8372
boards without 10G ports both SerDes stayed off, and with SDS 0 off `RTL837X_REG_SEC_COUNTER`
does not hold a written value: NTP never retried after a failed first attempt at boot and
froze the clock after a sync.
- Web UI
- Firefox no longer fills a saved login into the new admin password fields. It also put the
user name into the NTP poll interval, which marked the Time card as edited, so it kept its
defaults (NTP off, no server, UTC-12:00) instead of the switch's settings.
- Accept firmware uploads regardless of the file part's MIME type, including
`application/macbinary` sent by Chrome on macOS for `.bin` files.
- Health
- `HEALTH=1` builds no longer write past the phase timing arrays at the LLDP checkpoint, which
corrupted the `link` slow counter and the DHCP client state. #658
- Config - Config
- A startup configuration line longer than the command buffer no longer stops the replay: - A startup configuration line longer than the command buffer no longer stops the replay:
the offending line is skipped and the following lines are still applied. the offending line is skipped and the following lines are still applied.
- The web UI and the configuration upload now refuse a line the replay cannot take, - The web UI and the configuration upload now refuse a line the replay cannot take,
instead of writing it to flash. instead of writing it to flash.
- Startup
- Initialised and zero-initialised globals above xdata 0x4000 now get their values at boot.
Hardware reset leaves SFR 0x97 at 1, which maps xdata 0x4000-0xbfff to the flash image,
and it was cleared only in `main()`, after the C startup code had copied and cleared them.
This showed as a random web session timeout and LLDP blocked on some ports without config.
## Breaking changes ## Breaking changes
- Config - Config
+8 -1
View File
@@ -3,7 +3,14 @@ FROM debian:13-slim
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
make \ make \
gcc \ gcc \
sdcc \ g++ \
bison \
flex \
libboost-dev \
patch \
bzip2 \
curl \
ca-certificates \
xxd \ xxd \
python3 \ python3 \
libjson-c-dev \ libjson-c-dev \
+71 -20
View File
@@ -4,8 +4,25 @@ DEFAULT_CONFIG_LOCATION = 454656
CONFIG_LOCATION = 458752 CONFIG_LOCATION = 458752
HTML_LOCATION = 262144 HTML_LOCATION = 262144
# Startup configuration embedded in the image. Override it to build a specific
# machine with its own configuration: make MACHINE=<name> CONFIG=<file>
CONFIG ?= config.txt
SDCC_TOOLCHAIN = sdcc-$(shell cat sdcc/* | cksum | cut -d' ' -f1)
SDCC_PREFIX = toolchain/$(SDCC_TOOLCHAIN)
ifeq ($(origin CC),default) ifeq ($(origin CC),default)
CC = sdcc SDCC = $(SDCC_PREFIX)/bin/sdcc
CC = $(SDCC)
ASM = $(SDCC_PREFIX)/bin/sdas8051
SDCC_REV := $(shell git log -1 --format=%h -- sdcc 2>/dev/null)
ifneq ($(SDCC_REV),)
ifneq ($(shell git status --porcelain --untracked-files=no -- sdcc 2>/dev/null),)
SDCC_REV := $(SDCC_REV)-dirty
endif
endif
SDCC_BANNER = $(if $(SDCC_REV),sdcc/ at $(SDCC_REV),$(SDCC_TOOLCHAIN)) ($(words $(wildcard sdcc/*.patch)) patches)
else
SDCC_BANNER = system
endif endif
CC_FLAGS = -mmcs51 -I. -Imachine -Ihttpd -Iuip CC_FLAGS = -mmcs51 -I. -Imachine -Ihttpd -Iuip
ASM ?= sdas8051 ASM ?= sdas8051
@@ -43,6 +60,7 @@ ifndef BUILDDIR
BUILDDIR = output/$(MACHINE) BUILDDIR = output/$(MACHINE)
endif endif
CONFIG_STAMP := $(BUILDDIR)/.config
VERSION_HEADER := version.h VERSION_HEADER := version.h
GIT_VERSION := $(shell git rev-parse --short HEAD) GIT_VERSION := $(shell git rev-parse --short HEAD)
@@ -65,14 +83,15 @@ BUILD_DATE := $(shell date -u -d @$(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S" 2>/d
|| date -u -r $(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S") || date -u -r $(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S")
endif endif
# $(MACHINE_SRC) is listed here so an unknown $MACHINE fails before anything else # $(MACHINE_SRC) and $(CONFIG) are listed here so an unknown $MACHINE or a
# is built, including the tools/ subdirectory. # missing config file fails before anything else is built, including the tools/
all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin # subdirectory.
all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(CONFIG) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
bank_size: bank_size:
tools/bank_occupancy.sh "$(MACHINE)" tools/bank_occupancy.sh "$(MACHINE)"
create_build_dir: create_build_dir: $(SDCC)
mkdir -p "$(BUILDDIR)" mkdir -p "$(BUILDDIR)"
mkdir -p "$(BUILDDIR)/uip" mkdir -p "$(BUILDDIR)/uip"
mkdir -p "$(BUILDDIR)/httpd" mkdir -p "$(BUILDDIR)/httpd"
@@ -88,6 +107,12 @@ $(MACHINE_SRC):
@echo " Example: make MACHINE=KP_9000_6XHML_X2" >&2 @echo " Example: make MACHINE=KP_9000_6XHML_X2" >&2
@false @false
# Fail with a clear message when CONFIG names a file that does not exist.
$(CONFIG):
@echo "ERROR: CONFIG='$(CONFIG)' does not exist." >&2
@echo " Point CONFIG at a configuration file, or omit it to use config.txt." >&2
@false
# Keep the per-machine definition in first position to fail immediately on an # Keep the per-machine definition in first position to fail immediately on an
# invalid $MACHINE value # invalid $MACHINE value
SRCS = \ SRCS = \
@@ -102,6 +127,7 @@ SRCS = \
sfp.c \ sfp.c \
dns.c \ dns.c \
dns_cli.c \ dns_cli.c \
sessions.c \
ntp.c \ ntp.c \
ntp_cli.c \ ntp_cli.c \
syslog.c \ syslog.c \
@@ -145,19 +171,30 @@ HTML := $(shell find html -name '*.js' -or -name '*.html' -or -name '*.svg' -or
# The raw html/ sources stay untouched for development; the minified # The raw html/ sources stay untouched for development; the minified
# copy is a build artifact under output/. # copy is a build artifact under output/.
HTML_MIN := $(BUILDDIR)/html_min HTML_MIN := $(BUILDDIR)/html_min
HTML_MIN_LIST := $(BUILDDIR)/html_min.list
HTML_MIN_STAMP := $(BUILDDIR)/html_min.stamp
.PHONY: html_min .PHONY: html_min
html_min: $(HTML) html_min: $(HTML_MIN_STAMP)
$(HTML_MIN_LIST): FORCE
@mkdir -p $(BUILDDIR)
@echo $(sort $(HTML)) > $@.tmp
@cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@
$(HTML_MIN_STAMP): $(HTML) $(HTML_MIN_LIST) tools/minify.py $(if $(wildcard $(HTML_MIN)),,FORCE)
rm -rf $(HTML_MIN) rm -rf $(HTML_MIN)
mkdir -p $(HTML_MIN) mkdir -p $(HTML_MIN)
@for f in $(HTML); do python3 tools/minify.py $$f $(HTML_MIN)/$$(basename $$f) || exit 1; done @for f in $(HTML); do python3 tools/minify.py $$f $(HTML_MIN)/$$(basename $$f) || exit 1; done
@touch $@
html_data.c html_data.h &: $(HTML) | tools html_min html_data.c html_data.h &: $(HTML_MIN_STAMP) | tools
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data
$(VERSION_HEADER): FORCE $(VERSION_HEADER): FORCE
@printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \ @printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \
"#define VERSION_SW \"$(VERSION_EXTENSION)\"" \ "#define VERSION_SW \"$(VERSION_EXTENSION)\"" \
"#define BUILD_DATE \"$(BUILD_DATE)\"" \ "#define BUILD_DATE \"$(BUILD_DATE)\"" \
"#define SDCC_TOOLCHAIN \"$(SDCC_BANNER)\"" \
"#endif" > $(VERSION_HEADER).tmp "#endif" > $(VERSION_HEADER).tmp
@cmp -s $(VERSION_HEADER).tmp $(VERSION_HEADER) \ @cmp -s $(VERSION_HEADER).tmp $(VERSION_HEADER) \
&& rm -f $(VERSION_HEADER).tmp \ && rm -f $(VERSION_HEADER).tmp \
@@ -169,12 +206,18 @@ $(SUBDIRS):
$(MAKE) -C $@ BUILDDIR=$(BUILDDIR) $(MAKE) -C $@ BUILDDIR=$(BUILDDIR)
clean: $(SUBDIRSCLEAN) clean: $(SUBDIRSCLEAN)
-rm -f html_data.c html_data.h $(VERSION_HEADER) -rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
-if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -type f ! -name "*.bin" -delete; fi -if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -type f ! -name "*.bin" -delete; fi
distclean: $(SUBDIRSCLEAN) distclean: $(SUBDIRSCLEAN)
-rm -f html_data.c html_data.h $(VERSION_HEADER) -rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
-rm -rf $(BUILDDIR) -rm -rf $(BUILDDIR)
-rm -rf toolchain
ifneq ($(SDCC),)
$(SDCC):
sh sdcc/build.sh $(abspath $(SDCC_PREFIX))
endif
$(SUBDIRSCLEAN): $(SUBDIRSCLEAN):
$(MAKE) -C $(@:clean=) clean $(MAKE) -C $(@:clean=) clean
@@ -189,6 +232,12 @@ FORCE:
$(CCFLAGS_STAMP): FORCE | create_build_dir $(CCFLAGS_STAMP): FORCE | create_build_dir
@echo '$(CC_FLAGS)' | cmp -s - $@ 2>/dev/null || echo '$(CC_FLAGS)' > $@ @echo '$(CC_FLAGS)' | cmp -s - $@ 2>/dev/null || echo '$(CC_FLAGS)' > $@
# The same for $(CONFIG): its path is stamped so switching to a config file
# older than the image still rebuilds it, while $(CONFIG) itself as a normal
# prerequisite covers edits to the selected file.
$(CONFIG_STAMP): FORCE | create_build_dir
@echo '$(CONFIG)' | cmp -s - $@ 2>/dev/null || echo '$(CONFIG)' > $@
$(BUILDDIR)/%.rel: %.c $(CCFLAGS_STAMP) | create_build_dir html_data.h $(BUILDDIR)/%.rel: %.c $(CCFLAGS_STAMP) | create_build_dir html_data.h
$(CC) -MMD $(CC_FLAGS) -o $@ -c $< $(CC) -MMD $(CC_FLAGS) -o $@ -c $<
@@ -202,11 +251,11 @@ $(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx $(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
objcopy --input-target=ihex -O binary $< $@ objcopy --input-target=ihex -O binary $< $@
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img | tools $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img $(CONFIG) $(CONFIG_STAMP) | tools
if [ -e $@ ]; then rm $@; fi if [ -e $@ ]; then rm $@; fi
tools/$(BUILDDIR)/imagebuilder -i $^ $@ tools/$(BUILDDIR)/imagebuilder -i $(BUILDDIR)/rtlplayground.img $@
tools/$(BUILDDIR)/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@ tools/$(BUILDDIR)/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
tools/$(BUILDDIR)/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@ tools/$(BUILDDIR)/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -b BANK1 $@ tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -b BANK1 $@
tools/$(BUILDDIR)/crc_calculator -u $@ tools/$(BUILDDIR)/crc_calculator -u $@
ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin $(BUILDDIR)/../rtlplayground.bin ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin $(BUILDDIR)/../rtlplayground.bin
@@ -214,16 +263,18 @@ $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.i
.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) create_build_dir .PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) create_build_dir
# Every machine/ definition is compiled, so a machine added to machine/ is # Every machine/ definition is compiled, so a machine added to machine/ is
# covered without registering it anywhere. The _init.c files are excluded here: # covered without registering it anywhere. A board only counts once both its
# they are the boot hooks, compiled separately below (the board's own if it # <machine>.c and its <machine>.h are present: the header is what the rest of
# exists, otherwise default_machine_init.c). The machine-specific defines are # the build includes, so a .c without one is an incomplete board. The _init.c
# filtered out of $(CC_FLAGS) and supplied per iteration instead, so this also # files are excluded here: they are the boot hooks, compiled separately below
# works as "make machine_check MACHINE=...". # (the board's own if it exists, otherwise default_machine_init.c). The
MACHINE_DEFS := $(sort $(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$(wildcard machine/*.c)))) # machine-specific defines are filtered out of $(CC_FLAGS) and supplied per
# iteration instead, so this also works as "make machine_check MACHINE=...".
MACHINE_DEFS := $(sort $(filter $(patsubst machine/%.h,%,$(wildcard machine/*.h)),$(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$(wildcard machine/*.c)))))
MACHINE_LESS_CC_FLAGS = $(filter-out -DMACHINE_%,$(CC_FLAGS)) MACHINE_LESS_CC_FLAGS = $(filter-out -DMACHINE_%,$(CC_FLAGS))
.PHONY: machine_check .PHONY: machine_check
machine_check: machine_check: $(SDCC)
@mkdir -p $(BUILDDIR)/tmp @mkdir -p $(BUILDDIR)/tmp
@set -eo pipefail; \ @set -eo pipefail; \
for MACHINE in $(MACHINE_DEFS); \ for MACHINE in $(MACHINE_DEFS); \
+20 -6
View File
@@ -56,12 +56,19 @@ device simulator is provided, which runs entirely under Linux as a local webserv
## (0) Compiling Requirements ## (0) Compiling Requirements
Install the following particular build requisites (Debian 12/13), note that Ubuntu 24.04 Install the following particular build requisites (Debian 12/13):
still has an older version of sdcc, but you will need sdcc version 4.5 for the code to compile:
``` ```
sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev sudo apt install make gcc g++ bison flex libboost-dev patch bzip2 curl xxd python-is-python3 libjson-c-dev zlib1g-dev
``` ```
The first `make` downloads SDCC 4.6.0, applies the fixes in `sdcc/` and builds it into
`toolchain/`, which takes a few minutes. Later builds reuse it. The released SDCC 4.5.0
and 4.6.0 miscompile parts of this code, see [SDCC bug #4153](https://sourceforge.net/p/sdcc/bugs/4153/)
and [#4154](https://sourceforge.net/p/sdcc/bugs/4154/); both fixes are in SDCC trunk since
r17039. The third patch is for [#4070](https://sourceforge.net/p/sdcc/bugs/4070/), where the
assembler drops the top byte of a `__sfr32` address list; it is proposed upstream and lets
`SFR_DATA_U32` be used again. To use an SDCC you installed yourself instead, run `make CC=sdcc ...`.
<details> <details>
<summary>If using Docker (click to expand)</summary> <summary>If using Docker (click to expand)</summary>
@@ -120,6 +127,13 @@ The name selects the board definition `machine/<name>.c`; `make` without a
`MACHINE` value stops with an error listing what is expected. If your board is `MACHINE` value stops with an error listing what is expected. If your board is
not in the list yet, see [Adding a new device](doc/adding-a-device.md). not in the list yet, see [Adding a new device](doc/adding-a-device.md).
To embed a machine-specific startup configuration, pass `CONFIG`:
```
make MACHINE=DEFAULT_8C_1SFP CONFIG=configs/DEFAULT_8C_1SFP.txt
```
`CONFIG` defaults to `config.txt`; the selected file is embedded in the image
and changing it triggers a rebuild.
> [!TIP] > [!TIP]
> You can write configuration parameters in config.txt (see below) in order your switch to get > You can write configuration parameters in config.txt (see below) in order your switch to get
> straight at the first boot, a correct IP configuration. > straight at the first boot, a correct IP configuration.
@@ -163,9 +177,9 @@ example of compilation console output
RTLPlayground/installer$ make RTLPlayground/installer$ make
mkdir -p output mkdir -p output
gcc updatebuilder.c -o output/updatebuilder gcc updatebuilder.c -o output/updatebuilder
sdas8051 -plosgff -o output/crtstart.rel crtstart.asm ../toolchain/sdcc-3390765201/bin/sdas8051 -plosgff -o output/crtstart.rel crtstart.asm
sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c ../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c
sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel ../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel
./output/updatebuilder -i output/rtlinstaller.ihx -o output/rtlplayground_oem_upgrade.bin ../output/rtlplayground.bin ./output/updatebuilder -i output/rtlinstaller.ihx -o output/rtlplayground_oem_upgrade.bin ../output/rtlplayground.bin
Input file size: 524288 Input file size: 524288
Bytes read: 524288 Bytes read: 524288
+10 -3
View File
@@ -24,6 +24,7 @@
#include "sfp.h" #include "sfp.h"
#include "dhcp.h" #include "dhcp.h"
#include "syslog.h" #include "syslog.h"
#include "sessions.h"
#include "dns.h" #include "dns.h"
#include "ntp.h" #include "ntp.h"
#include "uip/uip.h" #include "uip/uip.h"
@@ -849,7 +850,7 @@ void parse_mirror(void)
print_string("NOT Enabled: "); print_string("NOT Enabled: ");
} }
print_string("Mirroring port: "); print_string("Mirroring port: ");
print_phys_port(mirroring_port); print_phys_port(mPort >> 1);
reg_read_m(RTL837x_MIRROR_CONF); reg_read_m(RTL837x_MIRROR_CONF);
uint16_t m = sfr_data[0]; uint16_t m = sfr_data[0];
m = (m << 8) | sfr_data[1]; m = (m << 8) | sfr_data[1];
@@ -1479,6 +1480,9 @@ void parse_passwd(void)
passwd[j++] = c; passwd[j++] = c;
} while (c != NUL && j < 20); } while (c != NUL && j < 20);
passwd[j] = NUL; passwd[j] = NUL;
/* Logins made with the old password end here. With one session the next
* login pushed a leftover one out; with several it would stay valid. */
session_init();
return; return;
} }
cmd_error("Missing password\n"); cmd_error("Missing password\n");
@@ -1836,6 +1840,8 @@ void print_gpio_status(void) {
void print_sw_version(void) __banked { void print_sw_version(void) __banked {
print_string("Software version: " VERSION_SW); print_string("Software version: " VERSION_SW);
print_string("\nBuild date: " BUILD_DATE); print_string("\nBuild date: " BUILD_DATE);
print_string("\nCompiler: SDCC " STR(__SDCC_VERSION_MAJOR) "." STR(__SDCC_VERSION_MINOR) "." STR(__SDCC_VERSION_PATCH) " #" STR(__SDCC_REVISION));
print_string("\nToolchain: " SDCC_TOOLCHAIN);
print_string("\nHardware: "); print_string("\nHardware: ");
print_string(machine.machine_name); print_string(machine.machine_name);
write_char('\n'); write_char('\n');
@@ -2011,10 +2017,10 @@ void cmd_parser(void) __banked
cmd_error("lacp (show | off); a group starts LACP with: lag <1-4> lacp <port>...\n"); cmd_error("lacp (show | off); a group starts LACP with: lag <1-4> lacp <port>...\n");
} else if (cmd_compare(0, "pvid")) { } else if (cmd_compare(0, "pvid")) {
if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0 if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0
&& atoi_short(cmd_words_b[2]) && atoi_results_short && atoi_results_short <= 4094) && atoi_short(cmd_words_b[2]) && atoi_results_short <= 4094)
port_pvid_set(atoi_results_u8, atoi_results_short); port_pvid_set(atoi_results_u8, atoi_results_short);
else { else {
cmd_error("pvid <port> <1-4094>\n"); cmd_error("pvid <port> <0-4094>\n");
} }
} else if (cmd_compare(0, "vlan")) { } else if (cmd_compare(0, "vlan")) {
parse_vlan(); parse_vlan();
@@ -2329,6 +2335,7 @@ void health_show(void) __banked
ph_line("tx ", HEALTH_PH_TX); ph_line("tx ", HEALTH_PH_TX);
ph_line("stp ", HEALTH_PH_STP); ph_line("stp ", HEALTH_PH_STP);
ph_line("cmd ", HEALTH_PH_CMD); ph_line("cmd ", HEALTH_PH_CMD);
ph_line("lldp", HEALTH_PH_LLDP);
/* Stack: current depth plus how much of the painted area is untouched */ /* Stack: current depth plus how much of the painted area is untouched */
print_string("sp "); print_string("sp ");
+2 -2
View File
@@ -19,8 +19,8 @@ extern __xdata uint8_t err_status;
#define HEALTH_PH_TX 3 #define HEALTH_PH_TX 3
#define HEALTH_PH_STP 4 #define HEALTH_PH_STP 4
#define HEALTH_PH_CMD 5 #define HEALTH_PH_CMD 5
#define HEALTH_PHASES 6 #define HEALTH_PH_LLDP 6
#define HEALTH_LLDP 7 #define HEALTH_PHASES (HEALTH_PH_LLDP + 1) /* last id + 1: sizes the arrays in cmd_parser.c */
extern __xdata uint16_t health_rx_frames; extern __xdata uint16_t health_rx_frames;
#define health_rx_frame() health_rx_frames++ #define health_rx_frame() health_rx_frames++
+7 -7
View File
@@ -38,8 +38,8 @@ existing files as the template:
``` ```
`MACHINE_CHIPSET` is `RTL8373` or `RTL8372`; a build without it will fail. It selects the default `MACHINE_CHIPSET` is `RTL8373` or `RTL8372`; a build without it will fail. It selects the default
logical port range: `min_port` is `0` for RTL8373 and `3` for RTL8372, while `max_port` is `8`, logical port range: `MACHINE_MIN_PORT` is `0` for RTL8373 and `3` for RTL8372, while
the highest logical port index. A board with a different range defines `MACHINE_MAX_PORT` is `8`, the highest logical port index. A board with a different range defines
`MACHINE_MIN_PORT` and/or `MACHINE_MAX_PORT` in its header to override the `MACHINE_MIN_PORT` and/or `MACHINE_MAX_PORT` in its header to override the
defaults. defaults.
@@ -94,16 +94,16 @@ __code const struct machine machine = {
}; };
``` ```
`MACHINE_COMMON_SETTING` (from `machine_defs.h`) initializes `machine_name`, `MACHINE_COMMON_SETTING` (from `machine_defs.h`) initializes `machine_name` and
`isRTL8373`, `min_port` and `max_port` from the header's `MACHINE_MODEL` and `isRTL8373` from the header's `MACHINE_MODEL` and `MACHINE_CHIPSET`. The logical
`MACHINE_CHIPSET`. Keep it as the first initializer and list the port range comes from `MACHINE_MIN_PORT` and `MACHINE_MAX_PORT` in `machine.h`.
board-specific fields after it. Keep it as the first initializer and list the board-specific fields after it.
### Fields ### Fields
| Field | Meaning | | Field | Meaning |
|-------|---------| |-------|---------|
| `MACHINE_COMMON_SETTING` | expands to `machine_name` (`MACHINE_MODEL`), `isRTL8373` (`1` for `RTL8373`, `0` for `RTL8372`), `min_port` and `max_port`; all are derived from the header's `MACHINE_CHIPSET` (see Step 1) | | `MACHINE_COMMON_SETTING` | expands to `machine_name` (`MACHINE_MODEL`) and `isRTL8373` (`1` for `RTL8373`, `0` for `RTL8372`); both are derived from the header's `MACHINE_CHIPSET` (see Step 1) |
| `n_sfp` | number of SFP+ cages | | `n_sfp` | number of SFP+ cages |
| `n_10g` | SerDes 10G capability used by the RTL837xN init: `0` none, `1` one, `2` two 10G-capable SerDes | | `n_10g` | SerDes 10G capability used by the RTL837xN init: `0` none, `1` one, `2` two 10G-capable SerDes |
| `log_to_phys_port[9]` | logical port (0-based index) → physical front-panel port number | | `log_to_phys_port[9]` | logical port (0-based index) → physical front-panel port number |
+2
View File
@@ -1,11 +1,13 @@
# MokerLink POE-2G080110GS # MokerLink POE-2G080110GS
The `POE-2G080110GS` is an unmanaged 8x2.5G RJ45 and 1xSFP+ PoE switch sold by Mokerlink. There is also a `POE-2G080110GSM` managed version of the switch which may be similar but this has not been verified on actual hardware. The `POE-2G080110GS` is an unmanaged 8x2.5G RJ45 and 1xSFP+ PoE switch sold by Mokerlink. There is also a `POE-2G080110GSM` managed version of the switch which may be similar but this has not been verified on actual hardware.
The`KP-9000-9XHP-X` is the KeepLink rebrand for same product.
## Brands ## Brands
| Brand | Type |Managed| PCB | Flash | Chip RTL | | Brand | Type |Managed| PCB | Flash | Chip RTL |
|--------|------------------|-------|------------------|-----------------|---------------| |--------|------------------|-------|------------------|-----------------|---------------|
| Mokerlink | POE-2G080110GS | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N | | Mokerlink | POE-2G080110GS | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
| KeepLink | KP-9000-9XHP-X | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
## Hardware overview ## Hardware overview
Front Front
+93
View File
@@ -0,0 +1,93 @@
# PCB-AFGW SWTG218ANS V2.0.2
RTL8373-based 8×2.5G PoE + 1×10G SFP+ web-managed switch, sold as the
**RealHD PSW8-25MG**.
### Label specifications
- **Manufacturer**: RealHD
- **Model**: PSW8-25MG
- **PCB marking**: `PCB-AFGW SWTG218ANS_V2.0.2` (silkscreened as `PCB-AFGW SWTG218ANS_V2.0.2_23308`)
- **Ports**:
- 8 × RJ45: 10/100/1000/2500 Mbps with PoE
- 1 × SFP+: 1G / 2.5G / 10G (front panel marking `9(10G)`)
- **Power input**: internal AC PSU, `100-240V~ 50/60Hz 2A MAX`, DC output `53.5V 2.8A 150W`
- **Front panel LEDs**: `PWR`, `SYS`, per RJ45 port `Link/Act` and `PoE`, per SFP port 2.5G (green) and 10G (blue)
- **Stock firmware defaults**: web-managed PoE switch
### What works
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps (PoE is not configurable via RTLPlayground)
- SFP+ port supporting 1G, 2.5G and 10G modules
- LEDs: green (2.5G) and amber (1G/100M/10M) per RJ45 port with activity, green (1G/2.5G) and blue (10G) on the SFP port, plus the `SYS` LED
### Photos
Front panel:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/device_front.jpg" width="800" />
Board mounted in the case, with the internal PSU above it:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/dev_top_w_pcb.jpg" width="800" />
### PCB overview
Top side:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_front.jpg" width="800" />
Bottom side:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_back.jpg" width="800" />
A 4-pad UART header with `RXD` / `TXD` silkscreened sits next to the right heatsink.
### Port layout
| Logical port | Physical port | Type |
|--------------|---------------|--------|
| 1-8 | 1-8 | RJ45 |
| 9 | 9 | SFP+ |
### LED configuration
RJ45 ports use LED SET0, the SFP+ port uses LED SET1. The board needs the custom
LED mux (`led_mux_custom = 1`), and `RTL837X_REG_LED_GLB_IO_EN` is written to
`0x7f24977f` in `machine_custom_init()` so the LED pins are enabled as wired here.
| SET | LED pin 0 | LED pin 1 |
|------|-----------------------------------------------|-------------------------------------------------------|
| SET0 | Green — lights on 2.5G link with activity | Amber — lights on 1G / 100M / 10M link with activity |
| SET1 | All speeds — lights on any link with activity | 10G — lights on 10G link with activity |
On the front panel this gives one bi-color `Link/Act` LED per RJ45 port (green at
2.5G, amber at 10/100/1000M, blinking with activity) and one `PoE` LED per RJ45
port, which is driven by the PoE hardware and not by the switch. The SFP+ port has
a green 2.5G LED and a blue 10G LED.
### SFP GPIO assignments
| SFP (logical 9) | pin_detect (ModAbs) | pin_los | pin_tx_disable | SerDes | I2C SDA | I2C SCL |
|-----------------|---------------------|---------|----------------|--------|------------------|----------------------|
| SFP | GPIO38 | GPIO_NA | GPIO_NA | SDS1 | GPIO39_I2C_SDA4 | GPIO40_I2C_SCL3_MDC1 |
### Machine configuration
```
make MACHINE=REALHD_PSW8_25MG
```
### Programming
The serial console runs at 8N1 on 115200 baud with the RTLPlayground firmware
(see the UART header note under *PCB overview*). The stock firmware baud rate 57600. The flash chip was 2MB.
### PoE
The 8 RJ45 ports support PoE, powered by the internal 150W AC PSU (DC output
53.5V 2.8A) mounted above the main board.
Note: RTLPlayground does not provide PoE management for this board. The PoE
hardware runs with its own configuration, the same as on the other PoE boards in
this project.
+3 -1
View File
@@ -5,7 +5,7 @@ It is highly similar to SWTG024AS-V2.0, with the only difference being the GPIO
|Brand |Type |Managed|PCB |Flash |Chip RTL| |Brand |Type |Managed|PCB |Flash |Chip RTL|
|---------|--------------|-------|------------------------------|----------------|--------| |---------|--------------|-------|------------------------------|----------------|--------|
| Horaco | ZX-SG4T2 | No | PCB-SWTG024AS-A-V2.0.1_19650 | 512K (P25D40SH)| 8372N | | Horaco | ZX-SG4T2 | No | PCB-SWTG024AS-A-V2.0.1_19650 | 512K (P25D40SH)| 8372N |
| Horaco | HC-SWTG124AS | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M (W25Q16JV)| 8372N | | Horaco | HC-SWTG124AS | Yes | PCB-SWTG024AS-A-V2.0.1_19650 | 2M (W25Q16JV)| 8372N |
| GoodTop | GT-ST024M | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M | 8372N | | GoodTop | GT-ST024M | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M | 8372N |
### Label specifications ### Label specifications
@@ -35,6 +35,8 @@ Bottom
<img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-pcb-bottom.jpg" width="300" /> <img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-pcb-bottom.jpg" width="300" />
On Horaco HC-SWTG124AS with board PCB-SWTG024AS-A-V2.0.1_19650 the reset button is soldered at `SW1` (on the top left, GPIO48).
### J1, serial console ### J1, serial console
| `J1` pin | Signal | | `J1` pin | Signal |
+36
View File
@@ -0,0 +1,36 @@
### HG0402XG V1.1
## Brands
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|---|---|---|---|---|---|
| ZoeRax | HG0402XG | No| HG0402XG V1.1 | 1M (Winbond 25Q) | |
### Label specifications
- **Name**: ZOERAX 2.5G 2SFP 4-Port Gigabit Switch
- **Model**: HG0402XG-EU
- **Ports**:
- 4 × RJ45: 10/100/1000/2500 Mbps
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_bottom.jpg" width="300" />
## PCB
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_top.jpg" width="300" />
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_bottom.jpg" width="300" />
Be aware that the UART header is metric, the pitch is 2mm. The JST PH2.0mm header shown was addeded after purchase
The slider switch controls microcontroller U4 in order to configure the VLAN on/off settings. U4 can reset the SoC
and also write into its registers to configure VLAN. With RTLPlayground the slider cannot be used for anything else
than triggering reset. Any change in the position will trigger this reset.
### Installation
Using SPI clamp on-board is the only method for initial installation.
### What works
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
- SFP ports with various 1G/10G Fiber and Ethernet modules
- LEDs
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

+2 -1
View File
@@ -14,6 +14,7 @@ rx max 00 slow 0x0000
tx max 00 slow 0x0000 tx max 00 slow 0x0000
stp max 04 slow 0x0001 stp max 04 slow 0x0001
cmd max 02 slow 0x0001 cmd max 02 slow 0x0001
lldp max 00 slow 0x0000
sp 80 untouched 62 sp 80 untouched 62
tcp st 00 tmr 00 rtx 00 rto 00 len 0x0000 mss 0x05b4 tcp st 00 tmr 00 rtx 00 rto 00 len 0x0000 mss 0x05b4
lport 0x0000 rport 0x0000 rip 0x0000 0x0000 lport 0x0000 rport 0x0000 rip 0x0000 0x0000
@@ -46,7 +47,7 @@ a flooded CPU port from a stalled loop, which look identical from outside.
## Phases ## Phases
Each line is one section of the main loop: the link poll, SFP handling, Each line is one section of the main loop: the link poll, SFP handling,
frame reception, TCP transmission, the STP timers and the command dispatch. frame reception, TCP transmission, the STP timers, the LLDP timer and the command dispatch.
`max` is the worst time that section has taken, in ticks of 5 ms; `slow` `max` is the worst time that section has taken, in ticks of 5 ms; `slow`
counts the passes that took two ticks or more. A misbehaving subsystem counts the passes that took two ticks or more. A misbehaving subsystem
names itself here, which is the difference between knowing that the loop is names itself here, which is the difference between knowing that the loop is
+3
View File
@@ -29,6 +29,7 @@ The following devices have been tested and are fully working:
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V2.2](devices/2M-PCB23-V2_2.md) | 2M | 8 + 1 | | keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V2.2](devices/2M-PCB23-V2_2.md) | 2M | 8 + 1 |
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.1](devices/2M-PCB23-V3_1.md) | 2M | 8 + 1 | | keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.1](devices/2M-PCB23-V3_1.md) | 2M | 8 + 1 |
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.2](devices/2M-PCB23-V3_2.md) | 2M | 8 + 1 | | keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.2](devices/2M-PCB23-V3_2.md) | 2M | 8 + 1 |
| keepLINK | KP-9000-9XHP-X | No | [2M-PCB51-ML-V1.1](devices/2M-PCB51-ML-V1_1.md) | 4M | 8 + 1 |
| LIANGUO | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 1 | | LIANGUO | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 1 |
| LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 0.5M | 4 + 2 | | LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 0.5M | 4 + 2 |
| LIANGUO | ZX-SWTGW215AS | Yes | [PCB-SWTG115AS-V2.0 / PCB-SWTG115AS-V2.1](devices/SWTGW215AS.md) | 2M | 5 + 1 | | LIANGUO | ZX-SWTGW215AS | Yes | [PCB-SWTG115AS-V2.0 / PCB-SWTG115AS-V2.1](devices/SWTGW215AS.md) | 2M | 5 + 1 |
@@ -38,6 +39,7 @@ The following devices have been tested and are fully working:
| MokerLink| 2G08110GSN | No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | 0.5M | 8 + 1 | | MokerLink| 2G08110GSN | No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | 0.5M | 8 + 1 |
| Netcore | GS9 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 | | Netcore | GS9 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
| ONT | ONT-S207CW-62TS-SE | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 | | ONT | ONT-S207CW-62TS-SE | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
| RealHD | PSW8-25MG | Yes | [PCB-AFGW SWTG218ANS V2.0.2](devices/PCB-AFGW_SWTG218ANS_V2.0.2.md) | 2M | 8 + 1 |
| Ruiying | RY-4GT-2SX | No | [FG-4GT-2SX_V2.0](devices/FG-4GT-2SX_V2.0.md) | 4M | 4 + 2 | | Ruiying | RY-4GT-2SX | No | [FG-4GT-2SX_V2.0](devices/FG-4GT-2SX_V2.0.md) | 4M | 4 + 2 |
| Ruiying | RY-8GT-1SX | No | [FG-8GT-1SX](devices/FG-8GT-1SX.md) | 1M | 8 + 1 | | Ruiying | RY-8GT-1SX | No | [FG-8GT-1SX](devices/FG-8GT-1SX.md) | 1M | 8 + 1 |
| SICSOLINK| SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 | | SICSOLINK| SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
@@ -50,6 +52,7 @@ The following devices have been tested and are fully working:
| YuanLey | YS25-0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | 2M | 4 + 2 | | YuanLey | YS25-0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | 2M | 4 + 2 |
| YuanLey | YS25-0402M | Yes | [SIRIVISION_S25G2206FP_V1_1](devices/SIRIVISION_S25G2206FP_V1_1.md) | 2M | 4 + 2 | | YuanLey | YS25-0402M | Yes | [SIRIVISION_S25G2206FP_V1_1](devices/SIRIVISION_S25G2206FP_V1_1.md) | 2M | 4 + 2 |
| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 | | Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
| ZoeRax | HG0402XG | No | [HG0402XG_V1.1](devices/ZOERAX_HG0402XG_V1.1.md) | 1M | 4 + 2 |
For KP-9000-6XH-X2 / KP-9000-6XHML-X2 / Mokerlink 2G040210GSM devices, select For KP-9000-6XH-X2 / KP-9000-6XHML-X2 / Mokerlink 2G040210GSM devices, select
the machine target by PCB revision. The ML/non-ML or managed/unmanaged label the machine target by PCB revision. The ML/non-ML or managed/unmanaged label
+5 -2
View File
@@ -79,8 +79,11 @@ JSON polling of the visible section).
computation no longer depends on uninitialised buffer content. computation no longer depends on uninitialised buffer content.
- `version.h` is now written by a single atomic `printf` (change from - `version.h` is now written by a single atomic `printf` (change from
upstream) instead of five separate `echo`s, so a parallel build upstream) instead of five separate `echo`s, so a parallel build
(`make -j`) can no longer read a half-written copy. It stays in (`make -j`) can no longer read a half-written copy. It depends on
`.PHONY` so it is regenerated on every build. `FORCE`, so its recipe runs on every build, but the file is only
rewritten when its content changes. The minified copy in
`output/html_min/` is redone only when a web file, the list of web
files or `tools/minify.py` changes.
## Verification ## Verification
+352 -179
View File
@@ -16,7 +16,7 @@ d_system:"System",d_traffic:"Traffic",
d_traffic_h:"packets/s, live",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX bad",d_rxbad:"RX bad", d_traffic_h:"packets/s, live",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX bad",d_rxbad:"RX bad",
i_host:"Hostname",i_ip:"IP address",i_mask:"Netmask",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware", i_host:"Hostname",i_ip:"IP address",i_mask:"Netmask",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
i_built:"Built",i_hw:"Hardware",i_temp:"Temperature",i_flash:"Flash",i_syslog:"Syslog", i_built:"Built",i_hw:"Hardware",i_temp:"Temperature",i_flash:"Flash",i_syslog:"Syslog",
p_state:"State",p_disabled:"disabled",p_up:"up",p_txgb:"TX good / bad",p_rxgb:"RX good / bad", p_state:"State",p_disabled:"disabled",p_up:"up",
p_pkts:"pkts",p_module:"Module",p_temp:"Temperature",p_vcc:"Vcc",p_txbias:"TX bias",p_txpower:"TX power", p_pkts:"pkts",p_module:"Module",p_temp:"Temperature",p_vcc:"Vcc",p_txbias:"TX bias",p_txpower:"TX power",
p_rxpower:"RX power",p_txfault:"TX fault",p_txdis:"TX disabled",p_rxlos:"RX LOS",p_adv:"Advertising", p_rxpower:"RX power",p_txfault:"TX fault",p_txdis:"TX disabled",p_rxlos:"RX LOS",p_adv:"Advertising",
pt_title:"Port configuration",pt_conn:"Devices learned",pt_speed:"Speed",pt_mtu:"MTU", pt_title:"Port configuration",pt_conn:"Devices learned",pt_speed:"Speed",pt_mtu:"MTU",
@@ -44,20 +44,19 @@ lldp_enabled: "LLDP Enabled",lldp_title:"Service Status",lldp_portname:"Name",ll
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",
v_title:"VLANs",v_vid:"VID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on", v_title:"VLANs",v_vid:"VLAN ID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on",
v_empty:"No VLANs configured.",v_mgmt:"Management VLAN",v_mgmt_none:"untagged",v_editor:"VLAN editor", v_empty:"No VLANs configured.",v_mgmt:"Management VLAN",v_mgmt_none:"untagged",v_editor:"VLAN editor",
v_optional:"optional",v_setmgmt:"Set as mgmt VLAN",v_setmgmt_t:"Make this the management VLAN", v_optional:"optional",v_setmgmt_q:"Set {n} as the management ID?",v_setmgmt_t:"Management VLAN",
v_member:"Member",v_pvid:"PVID", v_member:"Member",v_pvid:"PVID",v_add:"Add a new VLAN", v_mgmt_c:"WARNING: You must be able to access this VLAN to continue to manage the switch.",
v_legend:"U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.", v_legend:"U = untagged member, T = tagged member, - = not a member. PVID is automatically assigned based on the untagged membership.",
v_ingress:"Ingress filtering",v_ingress_h:"write-only: the state is not readable from the switch", v_accept:"Accept",v_ing_all:"All",v_del_t:"Delete VLAN",v_rename:"Rename",v_rename_t:"Rename VLAN",
v_accept:"Accept",v_ing_all:"All",v_ing_apply:"Apply ingress modes",v_del_t:"Delete VLAN",
v_del_q:"Delete VLAN {n}?",v_del_d:"Ports keep their PVID until reassigned.", v_del_q:"Delete VLAN {n}?",v_del_d:"Ports keep their PVID until reassigned.",
v_vid_err:"Enter a VLAN ID (1-4094)",v_name_err:"Name must start with a letter (letters, digits, _)", v_vid_err:"Enter a VLAN ID (1-4094)",v_name_err:"Name must start with a letter (letters, digits, _) and cannot be just 'd' or 'mgmt'",
v_loaded:"Loaded VLAN {n}",v_notfound:"VLAN {n} not found (a new one can still be created)", v_loaded:"Loaded VLAN {n}",v_notfound:"VLAN {n} not found (a new one can still be created)",
v_nomember:"Select at least one member port (or delete the VLAN instead)", v_nomember:"Select at least one member port (or delete the VLAN instead)",v_vid_first:"Enter a VLAN ID first",
v_ing_none:"Pick an ingress mode for at least one port",v_vid_first:"Enter a VLAN ID first", v_mgmt_q:"Set VLAN {n} as management VLAN?",v_vid_err:"VLAN ID already exists",
v_mgmt_q:"Set VLAN {n} as management VLAN?",
v_mgmt_d:"The switch will start tagging its own traffic with that VLAN. If the port you are connected through does not carry it, this page becomes unreachable and the setting can only be undone over the console.", v_mgmt_d:"The switch will start tagging its own traffic with that VLAN. If the port you are connected through does not carry it, this page becomes unreachable and the setting can only be undone over the console.",
v_mgmt_tt:"The management VLAN cannot be deleted, you need to assign management to another VLAN first.",
l2_title:"MAC address table",l2_filter:"filter...",l2_flush:"Flush learned entries",l2_static:"static", l2_title:"MAC address table",l2_filter:"filter...",l2_flush:"Flush learned entries",l2_static:"static",
l2_learned:"learned",l2_loading:"loading...",l2_failed:"load failed",l2_entries:"entries", l2_learned:"learned",l2_loading:"loading...",l2_failed:"load failed",l2_entries:"entries",
l2_del_t:"Delete entry",l2_flush_q:"Flush all learned MAC entries?", l2_del_t:"Delete entry",l2_flush_q:"Flush all learned MAC entries?",
@@ -147,7 +146,7 @@ d_system:"システム",d_traffic:"トラフィック",
d_traffic_h:"パケット/秒、リアルタイム",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 異常",d_rxbad:"RX 異常", d_traffic_h:"パケット/秒、リアルタイム",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 異常",d_rxbad:"RX 異常",
i_host:"ホスト名",i_ip:"IP アドレス",i_mask:"ネットマスク",i_gw:"ゲートウェイ",i_mac:"MAC",i_fw:"ファームウェア", i_host:"ホスト名",i_ip:"IP アドレス",i_mask:"ネットマスク",i_gw:"ゲートウェイ",i_mac:"MAC",i_fw:"ファームウェア",
i_built:"ビルド日",i_hw:"ハードウェア",i_temp:"温度",i_flash:"フラッシュ",i_syslog:"Syslog", i_built:"ビルド日",i_hw:"ハードウェア",i_temp:"温度",i_flash:"フラッシュ",i_syslog:"Syslog",
p_state:"状態",p_disabled:"無効",p_up:"アップ",p_txgb:"TX 正常 / 異常",p_rxgb:"RX 正常 / 異常", p_state:"状態",p_disabled:"無効",p_up:"アップ",
p_pkts:"pkts",p_module:"モジュール",p_temp:"温度",p_vcc:"電圧",p_txbias:"TX バイアス",p_txpower:"TX 電力", p_pkts:"pkts",p_module:"モジュール",p_temp:"温度",p_vcc:"電圧",p_txbias:"TX バイアス",p_txpower:"TX 電力",
p_rxpower:"RX 電力",p_txfault:"TX 障害",p_txdis:"TX 無効",p_rxlos:"RX 信号ロス",p_adv:"アドバタイズ", p_rxpower:"RX 電力",p_txfault:"TX 障害",p_txdis:"TX 無効",p_rxlos:"RX 信号ロス",p_adv:"アドバタイズ",
pt_title:"ポート設定",pt_conn:"学習済みデバイス",pt_speed:"速度",pt_mtu:"MTU", pt_title:"ポート設定",pt_conn:"学習済みデバイス",pt_speed:"速度",pt_mtu:"MTU",
@@ -179,14 +178,12 @@ v_empty:"VLAN は設定されていません。",v_mgmt:"管理 VLAN",v_mgmt_non
v_optional:"任意",v_setmgmt:"管理 VLAN に設定",v_setmgmt_t:"この VLAN を管理 VLAN にする", v_optional:"任意",v_setmgmt:"管理 VLAN に設定",v_setmgmt_t:"この VLAN を管理 VLAN にする",
v_member:"メンバー",v_pvid:"PVID", v_member:"メンバー",v_pvid:"PVID",
v_legend:"U = タグ無しメンバー、T = タグ付きメンバー、- = 非メンバー。PVID はタグ無し受信トラフィックをこの VLAN に割り当てます。", v_legend:"U = タグ無しメンバー、T = タグ付きメンバー、- = 非メンバー。PVID はタグ無し受信トラフィックをこの VLAN に割り当てます。",
v_ingress:"受信フィルタ",v_ingress_h:"書き込み専用: 現在の状態はスイッチから読み取れません",
v_accept:"受け入れ",v_ing_all:"すべて",v_ing_apply:"受信モードを適用",v_del_t:"VLAN を削除", v_accept:"受け入れ",v_ing_all:"すべて",v_ing_apply:"受信モードを適用",v_del_t:"VLAN を削除",
v_del_q:"VLAN {n} を削除しますか?",v_del_d:"ポートの PVID は再割り当てされるまで維持されます。", v_del_q:"VLAN {n} を削除しますか?",v_del_d:"ポートの PVID は再割り当てされるまで維持されます。",
v_vid_err:"VLAN ID (1〜4094) を入力してください",v_name_err:"名前は英字で始めてください (英字、数字、_)", v_vid_err:"VLAN ID (1〜4094) を入力してください",v_name_err:"名前は英字で始めてください (英字、数字、_)",
v_loaded:"VLAN {n} を読み込みました",v_notfound:"VLAN {n} は存在しません (新規作成は可能です)", v_loaded:"VLAN {n} を読み込みました",v_notfound:"VLAN {n} は存在しません (新規作成は可能です)",
v_nomember:"メンバーポートを 1 つ以上選択してください (または VLAN を削除してください)", v_nomember:"メンバーポートを 1 つ以上選択してください (または VLAN を削除してください)",
v_ing_none:"1 つ以上のポートの受信モードを選択してください",v_vid_first:"先に VLAN ID を入力してください", v_vid_first:"先に VLAN ID を入力してください",v_mgmt_q:"VLAN {n} を管理 VLAN に設定しますか?",
v_mgmt_q:"VLAN {n} を管理 VLAN に設定しますか?",
v_mgmt_d:"スイッチは自身のトラフィックにこの VLAN のタグを付けるようになります。接続中のポートがこの VLAN を通さない場合、このページにアクセスできなくなり、コンソールからしか元に戻せません。", v_mgmt_d:"スイッチは自身のトラフィックにこの VLAN のタグを付けるようになります。接続中のポートがこの VLAN を通さない場合、このページにアクセスできなくなり、コンソールからしか元に戻せません。",
l2_title:"MAC アドレステーブル",l2_filter:"フィルタ...",l2_flush:"学習エントリを消去",l2_static:"静的", l2_title:"MAC アドレステーブル",l2_filter:"フィルタ...",l2_flush:"学習エントリを消去",l2_static:"静的",
l2_learned:"学習",l2_loading:"読み込み中...",l2_failed:"読み込み失敗",l2_entries:"エントリ", l2_learned:"学習",l2_loading:"読み込み中...",l2_failed:"読み込み失敗",l2_entries:"エントリ",
@@ -260,7 +257,7 @@ d_system:"系统",d_traffic:"流量",
d_traffic_h:"包/秒,实时",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 错误",d_rxbad:"RX 错误", d_traffic_h:"包/秒,实时",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 错误",d_rxbad:"RX 错误",
i_host:"主机名",i_ip:"IP 地址",i_mask:"子网掩码",i_gw:"网关",i_mac:"MAC",i_fw:"固件", i_host:"主机名",i_ip:"IP 地址",i_mask:"子网掩码",i_gw:"网关",i_mac:"MAC",i_fw:"固件",
i_built:"构建日期",i_hw:"硬件",i_temp:"温度",i_flash:"Flash",i_syslog:"Syslog", i_built:"构建日期",i_hw:"硬件",i_temp:"温度",i_flash:"Flash",i_syslog:"Syslog",
p_state:"状态",p_disabled:"已禁用",p_up:"已连接",p_txgb:"TX 正常 / 错误",p_rxgb:"RX 正常 / 错误", p_state:"状态",p_disabled:"已禁用",p_up:"已连接",
p_pkts:"个包",p_module:"模块",p_temp:"温度",p_vcc:"供电电压",p_txbias:"TX 偏置电流",p_txpower:"TX 光功率", p_pkts:"个包",p_module:"模块",p_temp:"温度",p_vcc:"供电电压",p_txbias:"TX 偏置电流",p_txpower:"TX 光功率",
p_rxpower:"RX 光功率",p_txfault:"TX 故障",p_txdis:"TX 禁用",p_rxlos:"RX 信号丢失",p_adv:"通告能力", p_rxpower:"RX 光功率",p_txfault:"TX 故障",p_txdis:"TX 禁用",p_rxlos:"RX 信号丢失",p_adv:"通告能力",
pt_title:"端口配置",pt_conn:"已学习设备",pt_speed:"速率",pt_mtu:"MTU", pt_title:"端口配置",pt_conn:"已学习设备",pt_speed:"速率",pt_mtu:"MTU",
@@ -292,14 +289,12 @@ v_empty:"尚未配置 VLAN。",v_mgmt:"管理 VLAN",v_mgmt_none:"untagged",v_edi
v_optional:"可选",v_setmgmt:"设为管理 VLAN",v_setmgmt_t:"将此 VLAN 设为管理 VLAN", v_optional:"可选",v_setmgmt:"设为管理 VLAN",v_setmgmt_t:"将此 VLAN 设为管理 VLAN",
v_member:"成员",v_pvid:"PVID", v_member:"成员",v_pvid:"PVID",
v_legend:"U = untagged 成员,T = tagged 成员,- = 非成员。PVID 将入方向的未标记流量归入此 VLAN。", v_legend:"U = untagged 成员,T = tagged 成员,- = 非成员。PVID 将入方向的未标记流量归入此 VLAN。",
v_ingress:"入方向过滤",v_ingress_h:"只写: 当前状态无法从交换机读取",
v_accept:"接受",v_ing_all:"全部",v_ing_apply:"应用入方向模式",v_del_t:"删除 VLAN", v_accept:"接受",v_ing_all:"全部",v_ing_apply:"应用入方向模式",v_del_t:"删除 VLAN",
v_del_q:"删除 VLAN {n}?",v_del_d:"端口的 PVID 在重新分配前保持不变。", v_del_q:"删除 VLAN {n}?",v_del_d:"端口的 PVID 在重新分配前保持不变。",
v_vid_err:"请输入 VLAN ID (1-4094)",v_name_err:"名称必须以字母开头 (字母、数字、_)", v_vid_err:"请输入 VLAN ID (1-4094)",v_name_err:"名称必须以字母开头 (字母、数字、_)",
v_loaded:"已读取 VLAN {n}",v_notfound:"VLAN {n} 不存在 (仍可新建)", v_loaded:"已读取 VLAN {n}",v_notfound:"VLAN {n} 不存在 (仍可新建)",
v_nomember:"请至少选择一个成员端口 (或改为删除该 VLAN)", v_nomember:"请至少选择一个成员端口 (或改为删除该 VLAN)",
v_ing_none:"请至少为一个端口选择入方向模式",v_vid_first:"请先输入 VLAN ID", v_vid_first:"请先输入 VLAN ID",v_mgmt_q:"将 VLAN {n} 设为管理 VLAN?",
v_mgmt_q:"将 VLAN {n} 设为管理 VLAN?",
v_mgmt_d:"交换机将开始为自身流量打上该 VLAN 标签。如果您所连接的端口不属于该 VLAN,本页面将无法访问,且只能通过控制台撤销此设置。", v_mgmt_d:"交换机将开始为自身流量打上该 VLAN 标签。如果您所连接的端口不属于该 VLAN,本页面将无法访问,且只能通过控制台撤销此设置。",
l2_title:"MAC 地址表",l2_filter:"筛选...",l2_flush:"清除学习条目",l2_static:"静态", l2_title:"MAC 地址表",l2_filter:"筛选...",l2_flush:"清除学习条目",l2_static:"静态",
l2_learned:"动态学习",l2_loading:"加载中...",l2_failed:"加载失败",l2_entries:"条", l2_learned:"动态学习",l2_loading:"加载中...",l2_failed:"加载失败",l2_entries:"条",
@@ -373,7 +368,7 @@ d_system:"Sistema",d_traffic:"Tráfico",
d_traffic_h:"paquetes/s, en vivo",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX erróneos",d_rxbad:"RX erróneos", d_traffic_h:"paquetes/s, en vivo",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX erróneos",d_rxbad:"RX erróneos",
i_host:"Nombre de host",i_ip:"Dirección IP",i_mask:"Máscara de red",i_gw:"Puerta de enlace",i_mac:"MAC",i_fw:"Firmware", i_host:"Nombre de host",i_ip:"Dirección IP",i_mask:"Máscara de red",i_gw:"Puerta de enlace",i_mac:"MAC",i_fw:"Firmware",
i_built:"Compilado",i_hw:"Hardware",i_flash:"Flash",i_syslog:"Syslog", i_built:"Compilado",i_hw:"Hardware",i_flash:"Flash",i_syslog:"Syslog",
p_state:"Estado",p_disabled:"deshabilitado",p_up:"activo",p_txgb:"TX correctos / erróneos",p_rxgb:"RX correctos / erróneos", p_state:"Estado",p_disabled:"deshabilitado",p_up:"activo",
p_pkts:"paquetes",p_module:"Módulo",p_temp:"Temperatura",p_vcc:"Vcc",p_txbias:"Polarización TX",p_txpower:"Potencia TX", p_pkts:"paquetes",p_module:"Módulo",p_temp:"Temperatura",p_vcc:"Vcc",p_txbias:"Polarización TX",p_txpower:"Potencia TX",
p_rxpower:"Potencia RX",p_txfault:"Fallo TX",p_txdis:"TX deshabilitado",p_rxlos:"Pérdida RX (LOS)",p_adv:"Anunciado", p_rxpower:"Potencia RX",p_txfault:"Fallo TX",p_txdis:"TX deshabilitado",p_rxlos:"Pérdida RX (LOS)",p_adv:"Anunciado",
pt_title:"Configuración de puerto",pt_conn:"Dispositivos aprendidos",pt_speed:"Velocidad",pt_mtu:"MTU", pt_title:"Configuración de puerto",pt_conn:"Dispositivos aprendidos",pt_speed:"Velocidad",pt_mtu:"MTU",
@@ -405,14 +400,12 @@ v_empty:"No hay VLANs configuradas.",v_mgmt:"VLAN de gestión",v_mgmt_none:"sin
v_optional:"opcional",v_setmgmt:"Establecer como VLAN de gestión",v_setmgmt_t:"Hacer esta la VLAN de gestión", v_optional:"opcional",v_setmgmt:"Establecer como VLAN de gestión",v_setmgmt_t:"Hacer esta la VLAN de gestión",
v_member:"Miembro",v_pvid:"PVID", v_member:"Miembro",v_pvid:"PVID",
v_legend:"U = miembro sin etiquetar, T = miembro etiquetado, - = no miembro. PVID asigna esta VLAN al tráfico entrante sin etiquetar.", v_legend:"U = miembro sin etiquetar, T = miembro etiquetado, - = no miembro. PVID asigna esta VLAN al tráfico entrante sin etiquetar.",
v_ingress:"Filtrado de entrada",v_ingress_h:"solo escritura: el estado no se puede leer desde el switch",
v_accept:"Aceptar",v_ing_all:"Todos",v_ing_apply:"Aplicar modos de entrada",v_del_t:"Eliminar VLAN", v_accept:"Aceptar",v_ing_all:"Todos",v_ing_apply:"Aplicar modos de entrada",v_del_t:"Eliminar VLAN",
v_del_q:"¿Eliminar VLAN {n}?",v_del_d:"Los puertos conservan su PVID hasta que se reasignen.", v_del_q:"¿Eliminar VLAN {n}?",v_del_d:"Los puertos conservan su PVID hasta que se reasignen.",
v_vid_err:"Introduce un ID de VLAN (1-4094)",v_name_err:"El nombre debe empezar por una letra (letras, dígitos, _)", v_vid_err:"Introduce un ID de VLAN (1-4094)",v_name_err:"El nombre debe empezar por una letra (letras, dígitos, _)",
v_loaded:"VLAN {n} cargada",v_notfound:"VLAN {n} no encontrada (aún se puede crear una nueva)", v_loaded:"VLAN {n} cargada",v_notfound:"VLAN {n} no encontrada (aún se puede crear una nueva)",
v_nomember:"Selecciona al menos un puerto miembro (o elimina la VLAN)", v_nomember:"Selecciona al menos un puerto miembro (o elimina la VLAN)",
v_ing_none:"Elige un modo de entrada para al menos un puerto",v_vid_first:"Introduce primero un ID de VLAN", v_vid_first:"Introduce primero un ID de VLAN",v_mgmt_q:"¿Establecer VLAN {n} como VLAN de gestión?",
v_mgmt_q:"¿Establecer VLAN {n} como VLAN de gestión?",
v_mgmt_d:"El switch empezará a etiquetar su propio tráfico con esa VLAN. Si el puerto por el que te conectas no la transporta, esta página dejará de ser accesible y el ajuste solo podrá deshacerse por la consola.", v_mgmt_d:"El switch empezará a etiquetar su propio tráfico con esa VLAN. Si el puerto por el que te conectas no la transporta, esta página dejará de ser accesible y el ajuste solo podrá deshacerse por la consola.",
l2_title:"Tabla de direcciones MAC",l2_filter:"filtrar...",l2_flush:"Vaciar entradas aprendidas",l2_static:"estática", l2_title:"Tabla de direcciones MAC",l2_filter:"filtrar...",l2_flush:"Vaciar entradas aprendidas",l2_static:"estática",
l2_learned:"aprendida",l2_loading:"cargando...",l2_failed:"error de carga",l2_entries:"entradas", l2_learned:"aprendida",l2_loading:"cargando...",l2_failed:"error de carga",l2_entries:"entradas",
@@ -472,7 +465,7 @@ fw_timeout:"el switch no ha vuelto tras 150 s: comprueba la alimentación / cons
fr:{ fr:{
nav_dash:"Tableau de bord",nav_ports:"Ports",nav_stp:"Arborescence",nav_stats:"Statistiques", nav_dash:"Tableau de bord",nav_ports:"Ports",nav_stp:"Arborescence",nav_stats:"Statistiques",
nav_vlan:"LANs virtuels",nav_l2:"Table MAC",nav_mirror:"Mise en mirroir",nav_lag:"Aggrégation",nav_eee:"Energie", nav_vlan:"LANs virtuels",nav_l2:"Table MAC",nav_mirror:"Mise en mirroir",nav_lag:"Aggrégation",nav_eee:"Energie",
nav_bw:"Bande passante",nav_system:"Système",nav_fw:"Micro logiciel", nav_bw:"Bande passante",nav_system:"Système",nav_fw:"Micro logiciel",nav_lldp:"Couche de Liaison",
hdr_dirty:"Changements non sauvegardés",hdr_dirty_t:"La configuration utilisée diffère de celle au démarrage", hdr_dirty:"Changements non sauvegardés",hdr_dirty_t:"La configuration utilisée diffère de celle au démarrage",
hdr_save:"Sauvegarder en flash",hdr_save_t:"Maintenir la configuration actuelle en flash", hdr_save:"Sauvegarder en flash",hdr_save_t:"Maintenir la configuration actuelle en flash",
th_auto:"Système",th_auto_sel:"Système (sélénisé)",sy_display:"Affichage",sy_theme:"Thème",sy_display_note:"Enregistré seulement dans ce navigateur.",th_light:"Clair",th_dark:"Sombre", th_auto:"Système",th_auto_sel:"Système (sélénisé)",sy_display:"Affichage",sy_theme:"Thème",sy_display_note:"Enregistré seulement dans ce navigateur.",th_light:"Clair",th_dark:"Sombre",
@@ -485,7 +478,7 @@ d_system:"Système",d_traffic:"Trafic",
d_traffic_h:"paquets/s (en direct)",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX KO",d_rxbad:"RX KO", d_traffic_h:"paquets/s (en direct)",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX KO",d_rxbad:"RX KO",
i_host:"Nom d'hote",i_ip:"Adresse IP",i_mask:"Masque réseau",i_gw:"Passerelle",i_mac:"Adresse MAC",i_fw:"Micro logiciel", i_host:"Nom d'hote",i_ip:"Adresse IP",i_mask:"Masque réseau",i_gw:"Passerelle",i_mac:"Adresse MAC",i_fw:"Micro logiciel",
i_built:"Compilé le",i_hw:"Matériel",i_temp:"Température",i_flash:"Taille Flash",i_syslog:"Journal système", i_built:"Compilé le",i_hw:"Matériel",i_temp:"Température",i_flash:"Taille Flash",i_syslog:"Journal système",
p_state:"Etat",p_disabled:"Inactif",p_up:"Actif",p_txgb:"TX bon / mauvais",p_rxgb:"RX bon / mauvais", p_state:"Etat",p_disabled:"Inactif",p_up:"Actif",
p_pkts:"paquets",p_module:"Module",p_temp:"Température",p_vcc:"Vcc",p_txbias:"TX biais",p_txpower:"TX puissance", p_pkts:"paquets",p_module:"Module",p_temp:"Température",p_vcc:"Vcc",p_txbias:"TX biais",p_txpower:"TX puissance",
p_rxpower:"RX puissance",p_txfault:"TX erreur",p_txdis:"TX désactivé",p_rxlos:"RX pertes de signaux",p_adv:"Capacités", p_rxpower:"RX puissance",p_txfault:"TX erreur",p_txdis:"TX désactivé",p_rxlos:"RX pertes de signaux",p_adv:"Capacités",
pt_title:"Configuration du port",pt_conn:"Appareils appris",pt_speed:"Vitesse",pt_mtu:"MTU", pt_title:"Configuration du port",pt_conn:"Appareils appris",pt_speed:"Vitesse",pt_mtu:"MTU",
@@ -509,6 +502,7 @@ stp_en_q:"Activer l'arbrescence?",
stp_en_d:"Les ports sont d'abord bloqués et cela prend deux fois le délai de retransmission pour atteindre l'état de retransmission; les ports en bordures recouvrent immédiatement leur état.", stp_en_d:"Les ports sont d'abord bloqués et cela prend deux fois le délai de retransmission pour atteindre l'état de retransmission; les ports en bordures recouvrent immédiatement leur état.",
stp_dis_q:"Désactiver l'arborescence ?",stp_dis_d:"Tous les ports vont être retransmis; la protection de boucles est perdue.", stp_dis_q:"Désactiver l'arborescence ?",stp_dis_d:"Tous les ports vont être retransmis; la protection de boucles est perdue.",
stp_cost_err:"Le coût du chemin doit être compris entre 0 et 200000000", stp_cost_err:"Le coût du chemin doit être compris entre 0 et 200000000",
lldp_enabled: "Activer LLDP",lldp_title:"Protocole de découverte de la couche de liason (LLDP)",lldp_portname:"Nom",lldp_enable:"Envoi de trames",
st_title:"Statiqtique du port",st_h:"cumul depuis le démarrage",st_txg:"TX OK",st_txb:"TX KO",st_rxg:"RX OK", st_title:"Statiqtique du port",st_h:"cumul depuis le démarrage",st_txg:"TX OK",st_txb:"TX KO",st_rxg:"RX OK",
st_rxb:"RX KO",st_details:"Détails",st_counters:"Compteur MIB",st_nonzero:"non-zero seulement", st_rxb:"RX KO",st_details:"Détails",st_counters:"Compteur MIB",st_nonzero:"non-zero seulement",
st_autoref:"rafraichissement automatique",st_counter:"Compteur",st_value:"Valeur",st_fail:"Erreur au chargement des compteurs", st_autoref:"rafraichissement automatique",st_counter:"Compteur",st_value:"Valeur",st_fail:"Erreur au chargement des compteurs",
@@ -517,14 +511,12 @@ v_empty:"Aucun VLANs configuré.",v_mgmt:"VLAN de management",v_mgmt_none:"déta
v_optional:"optionel",v_setmgmt:"Mette en VLAN de management",v_setmgmt_t:"Mettre celui-ci en VLAN de management", v_optional:"optionel",v_setmgmt:"Mette en VLAN de management",v_setmgmt_t:"Mettre celui-ci en VLAN de management",
v_member:"Membre",v_pvid:"PVID", v_member:"Membre",v_pvid:"PVID",
v_legend:"U = Membre détagué (UnTagged), T = membre tagué (Tagged), - = non membre. PVID (Port VLan ID) assigne ce VLAN au trafic entrant non tagué.", v_legend:"U = Membre détagué (UnTagged), T = membre tagué (Tagged), - = non membre. PVID (Port VLan ID) assigne ce VLAN au trafic entrant non tagué.",
v_ingress:"Filtrage entrant",v_ingress_h:"en ériture seule: cet état n'est pas lisible depuis le communtateur réseau",
v_accept:"Accepté",v_ing_all:"Tous",v_ing_apply:"Appliquer les filtes",v_del_t:"Supprimer le VLAN", v_accept:"Accepté",v_ing_all:"Tous",v_ing_apply:"Appliquer les filtes",v_del_t:"Supprimer le VLAN",
v_del_q:"Supprimer le VLAN {n}?",v_del_d:"Les ports conservent leur PVID tant qu'ils ne sont pas réassignés.", v_del_q:"Supprimer le VLAN {n}?",v_del_d:"Les ports conservent leur PVID tant qu'ils ne sont pas réassignés.",
v_vid_err:"Entrez un identifiant VLAN (1-4094)",v_name_err:"Le nom doit commencer par une lettre (lettre, chiffres, _)", v_vid_err:"Entrez un identifiant VLAN (1-4094)",v_name_err:"Le nom doit commencer par une lettre (lettre, chiffres, _)",
v_loaded:"VLAN chargés {n}",v_notfound:"VLAN {n} non trouvé (un nouveau peut toutefois être créé)", v_loaded:"VLAN chargés {n}",v_notfound:"VLAN {n} non trouvé (un nouveau peut toutefois être créé)",
v_nomember:"Selectionnez au moins un port membre (ou supprimez le VLAN)", v_nomember:"Selectionnez au moins un port membre (ou supprimez le VLAN)",
v_ing_none:"Sélectionnez le mode entrant pour au moins un port",v_vid_first:"Entrez un identifiant VLAN d'abord", v_vid_first:"Entrez un identifiant VLAN d'abord",v_mgmt_q:"Mettre ce VLAN {n} en tant que VLAN de management?",
v_mgmt_q:"Mettre ce VLAN {n} en tant que VLAN de management?",
v_mgmt_d:"Ce commutateur commencera par taguer son propre trafic avec ce VLAN. Si le port avec lequel vous êtes connecté ne le permet pas, cette page deviendra inaccessible et les paramètres ne pourront plus être changés que via la console.", v_mgmt_d:"Ce commutateur commencera par taguer son propre trafic avec ce VLAN. Si le port avec lequel vous êtes connecté ne le permet pas, cette page deviendra inaccessible et les paramètres ne pourront plus être changés que via la console.",
l2_title:"Table des adresses MAC",l2_filter:"filtre...",l2_flush:"Effacer les entrées apprises",l2_static:"static", l2_title:"Table des adresses MAC",l2_filter:"filtre...",l2_flush:"Effacer les entrées apprises",l2_static:"static",
l2_learned:"appris",l2_loading:"chargement...",l2_failed:"chargement non réussi",l2_entries:"entrées", l2_learned:"appris",l2_loading:"chargement...",l2_failed:"chargement non réussi",l2_entries:"entrées",
@@ -542,6 +534,9 @@ e_idle:"Inactif",e_na:"N/A",
bw_title:"Limitations de bande passante",bw_h:"Mbit/s, 0.016-10000",bw_in:"Limite entrante",bw_out:"Limite sortante", bw_title:"Limitations de bande passante",bw_h:"Mbit/s, 0.016-10000",bw_in:"Limite entrante",bw_out:"Limite sortante",
bw_exceed:"Lorsque dépassé",bw_fc:"Limiter",bw_drop:"Rejetter", bw_exceed:"Lorsque dépassé",bw_fc:"Limiter",bw_drop:"Rejetter",
bw_in_err:"La limite entrante doit être comprise entre 0.016 et 10000 Mbit/s",bw_out_err:"La limite sortante doit être comprise entre 0.016 et 10000 Mbit/s", bw_in_err:"La limite entrante doit être comprise entre 0.016 et 10000 Mbit/s",bw_out_err:"La limite sortante doit être comprise entre 0.016 et 10000 Mbit/s",
sc_title:"Contrôle des tempêtes",sc_h:"par port et par type de diffusion (générale ou groupée), vide = inactif",sc_bcast:"Générale (Broadcast)",sc_mcast:"Groupée (Multicast)",
sc_ucast:"Unicast inconnu",sc_umcast:"Multicast inconnu",sc_err:" La limite de tempête doit être comprise entre 1 à 1048575 pps ou 1 à 10000000 kbit/s",
sc_note:"Les trames de chacun des types ci-dessus dépassant les limites sont abandonnées lorsqu'elles arrivent sur le communtateur.",
sy_network:"Réseau",sy_dhcp:"Uiliser DHCP",sy_dhcp_t:"Aquérir l'adresse via le protocole DHCP",sy_services:"Services", sy_network:"Réseau",sy_dhcp:"Uiliser DHCP",sy_dhcp_t:"Aquérir l'adresse via le protocole DHCP",sy_services:"Services",
sy_igmp:"Surveillance IGMP",sy_sysip:"IP du serveur...",sy_server:"Serveur",sy_port:"Port", sy_igmp:"Surveillance IGMP",sy_sysip:"IP du serveur...",sy_server:"Serveur",sy_port:"Port",
sy_sesstmo:"Temps mort de session (s)",sy_sesstmo_err:"Le temps morts de session doit être compris en 1-65535 s", sy_sesstmo:"Temps mort de session (s)",sy_sesstmo_err:"Le temps morts de session doit être compris en 1-65535 s",
@@ -555,7 +550,14 @@ sy_ip_err:"Erreur d'IP / masque réseau / passerelle",sy_host_err:"Nom d'hôte :
sy_net_q:"Appliquer les paramètres réseau?",sy_net_d:"L'adresse IP de management va devenir {ip}: merci de réouvrir le page depuis cette adresse.", sy_net_q:"Appliquer les paramètres réseau?",sy_net_d:"L'adresse IP de management va devenir {ip}: merci de réouvrir le page depuis cette adresse.",
sy_ip_changed:"L'adresse IP a changée, reconnectez-vous à http://{ip}/",sy_dhcp_q:"Passer en DHCP?", sy_ip_changed:"L'adresse IP a changée, reconnectez-vous à http://{ip}/",sy_dhcp_q:"Passer en DHCP?",
sy_dhcp_d:"Le commutateur va demander une addresse via le DHCP. Vous devrez rechercher sa prochaine adresse IP pour vous reconnecter.", sy_dhcp_d:"Le commutateur va demander une addresse via le DHCP. Vous devrez rechercher sa prochaine adresse IP pour vous reconnecter.",
sy_sysip_err:"Adresse IP du serveur Syslog invalide",sy_sysport_err:"Le port Syslog doit être compris entre 1 et 65535", sy_sysip_err:"Adresse IP du serveur Syslog invalide",sy_dns1:"DNS principal",sy_dns2:"DNS secondaire",sy_dns_err:"Adresse IP du DNS invalide",sy_dns_dhcp:"émis par le DHCP: {ip}",
sy_time:"Temps",sy_ntp:"NTP",sy_ntpsrv:"Serveur NTP",sy_ntpsrv_p:"Nom d'hôte ou adresse IPv4",sy_ntpint:"intervalle de scrutation [min]",
sy_tz:"Fuseau horaire",sy_dst:"Heure d'été",sy_dst_eu:"Europe",
sy_dst_us:"Etats-Unis",sy_now:"Heure locale",
sy_sync:"Dernière synchronisation",sy_ntpaddr:"Adresse du serveur",sy_nosync:"Désynchronisé",sy_waiting:"En attente du serveur",
sy_ago:"depuis {n} minutes",sy_ntp_err:"Entrez le nom d'hôte ou l'adresse IPv4 du serveur NTP",sy_ntpint_err:"L'intervalle de scrutation doit être compris entre 1 et 1440",
sy_time_note:"Le commutateur n'a pas d'horloge sauvegardée par batterie: L'heure est donc perdue à chaque redémarrage et doit être récupérée grâce au serveur NTP une fois le réseau fonctionnel. Sauvegardez en mémoire flash pour enregistrer les paramètres.",
sy_sysport_err:"Le port Syslog doit être compris entre 1 et 65535",
sy_pw_len:"Mot de passe : 1 à 20 caractères",sy_pw_space:"Le mot de passe ne doit pas contenir d'espaces",sy_pw_match:"Les mots de passe ne correspondent pas", sy_pw_len:"Mot de passe : 1 à 20 caractères",sy_pw_space:"Le mot de passe ne doit pas contenir d'espaces",sy_pw_match:"Les mots de passe ne correspondent pas",
sy_pw_q:"Changer le mot de passe d'administration?",sy_pw_d:"Cela prend effet immédiatement dès la prochaine connection. Enregistrez en Flash pour rendre la modification persistante.", sy_pw_q:"Changer le mot de passe d'administration?",sy_pw_d:"Cela prend effet immédiatement dès la prochaine connection. Enregistrez en Flash pour rendre la modification persistante.",
sy_reboot_q:"Redémarrer le commutateur ?",sy_reboot_d:"Il y a des paramètres NON SAUVEGARDES qui vont être perdus. Sauvegardez-les au préalable en mémoire flash si vous souhaitez les conserver.", sy_reboot_q:"Redémarrer le commutateur ?",sy_reboot_d:"Il y a des paramètres NON SAUVEGARDES qui vont être perdus. Sauvegardez-les au préalable en mémoire flash si vous souhaitez les conserver.",
@@ -602,7 +604,7 @@ d_system:"System",d_traffic:"Datenverkehr",
d_traffic_h:"Pakete/s, live",d_txpps:"TX Pakete/s",d_rxpps:"RX Pakete/s",d_txbad:"TX fehlerhaft",d_rxbad:"RX fehlerhaft", d_traffic_h:"Pakete/s, live",d_txpps:"TX Pakete/s",d_rxpps:"RX Pakete/s",d_txbad:"TX fehlerhaft",d_rxbad:"RX fehlerhaft",
i_host:"Hostname",i_ip:"IP-Adresse",i_mask:"Netzmaske",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware", i_host:"Hostname",i_ip:"IP-Adresse",i_mask:"Netzmaske",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
i_built:"Erstellt",i_hw:"Hardware",i_temp:"Temperatur",i_flash:"Flash",i_syslog:"Syslog", i_built:"Erstellt",i_hw:"Hardware",i_temp:"Temperatur",i_flash:"Flash",i_syslog:"Syslog",
p_state:"Zustand",p_disabled:"deaktiviert",p_up:"verbunden",p_txgb:"TX gut / fehlerhaft",p_rxgb:"RX gut / fehlerhaft", p_state:"Zustand",p_disabled:"deaktiviert",p_up:"verbunden",
p_pkts:"Pakete",p_module:"Modul",p_temp:"Temperatur",p_vcc:"Vcc",p_txbias:"TX-Bias",p_txpower:"Sendeleistung", p_pkts:"Pakete",p_module:"Modul",p_temp:"Temperatur",p_vcc:"Vcc",p_txbias:"TX-Bias",p_txpower:"Sendeleistung",
p_rxpower:"Empfangsleistung",p_txfault:"TX-Fehler",p_txdis:"TX deaktiviert",p_rxlos:"RX LOS",p_adv:"Angeboten", p_rxpower:"Empfangsleistung",p_txfault:"TX-Fehler",p_txdis:"TX deaktiviert",p_rxlos:"RX LOS",p_adv:"Angeboten",
pt_title:"Port-Konfiguration",pt_conn:"Gelernte Geräte",pt_speed:"Geschwindigkeit",pt_mtu:"MTU", pt_title:"Port-Konfiguration",pt_conn:"Gelernte Geräte",pt_speed:"Geschwindigkeit",pt_mtu:"MTU",
@@ -633,17 +635,18 @@ st_autoref:"automatisch aktualisieren",st_counter:"Zähler",st_value:"Wert",st_f
v_title:"VLANs",v_vid:"VID",v_members:"Mitglieder",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID auf", v_title:"VLANs",v_vid:"VID",v_members:"Mitglieder",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID auf",
v_empty:"Keine VLANs konfiguriert.",v_mgmt:"Management-VLAN",v_mgmt_none:"untagged",v_editor:"VLAN-Editor", v_empty:"Keine VLANs konfiguriert.",v_mgmt:"Management-VLAN",v_mgmt_none:"untagged",v_editor:"VLAN-Editor",
v_optional:"optional",v_setmgmt:"Als Management-VLAN",v_setmgmt_t:"Dieses VLAN zum Management-VLAN machen", v_optional:"optional",v_setmgmt:"Als Management-VLAN",v_setmgmt_t:"Dieses VLAN zum Management-VLAN machen",
v_setmgmt_q:"{n} als Management-VLAN setzen?",v_add:"Neues VLAN hinzufügen",
v_mgmt_c:"WARNUNG: Du musst dieses VLAN erreichen können, um den Switch weiter verwalten zu können.",
v_member:"Mitglied",v_pvid:"PVID", v_member:"Mitglied",v_pvid:"PVID",
v_legend:"U = Mitglied untagged, T = Mitglied tagged, - = kein Mitglied. PVID ordnet eingehenden Verkehr ohne Tag diesem VLAN zu.", v_legend:"U = Mitglied untagged, T = Mitglied tagged, - = kein Mitglied. PVID ordnet eingehenden Verkehr ohne Tag diesem VLAN zu.",
v_ingress:"Ingress-Filter",v_ingress_h:"nur schreibbar: der Zustand lässt sich vom Switch nicht auslesen", v_accept:"Annehmen",v_ing_all:"Alle",v_ing_apply:"Ingress-Modi übernehmen",v_del_t:"VLAN löschen",v_rename:"Umbenennen",v_rename_t:"VLAN umbenennen",
v_accept:"Annehmen",v_ing_all:"Alle",v_ing_apply:"Ingress-Modi übernehmen",v_del_t:"VLAN löschen",
v_del_q:"VLAN {n} löschen?",v_del_d:"Die Ports behalten ihre PVID, bis sie neu zugewiesen wird.", v_del_q:"VLAN {n} löschen?",v_del_d:"Die Ports behalten ihre PVID, bis sie neu zugewiesen wird.",
v_vid_err:"VLAN-ID eingeben (1-4094)",v_name_err:"Der Name muss mit einem Buchstaben beginnen (Buchstaben, Ziffern, _)", v_vid_err:"VLAN-ID eingeben (1-4094)",v_name_err:"Der Name muss mit einem Buchstaben beginnen (Buchstaben, Ziffern, _)",
v_loaded:"VLAN {n} geladen",v_notfound:"VLAN {n} nicht gefunden (es kann neu angelegt werden)", v_loaded:"VLAN {n} geladen",v_notfound:"VLAN {n} nicht gefunden (es kann neu angelegt werden)",
v_nomember:"Mindestens einen Mitglieds-Port wählen (oder das VLAN stattdessen löschen)", v_nomember:"Mindestens einen Mitglieds-Port wählen (oder das VLAN stattdessen löschen)",
v_ing_none:"Für mindestens einen Port einen Ingress-Modus wählen",v_vid_first:"Zuerst eine VLAN-ID eingeben", v_vid_first:"Zuerst eine VLAN-ID eingeben",v_mgmt_q:"VLAN {n} als Management-VLAN setzen?",
v_mgmt_q:"VLAN {n} als Management-VLAN setzen?",
v_mgmt_d:"Der Switch versieht dann seinen eigenen Verkehr mit diesem VLAN-Tag. Führt der Port, über den du verbunden bist, dieses VLAN nicht, ist diese Seite nicht mehr erreichbar und die Einstellung lässt sich nur noch über die serielle Konsole zurücknehmen.", v_mgmt_d:"Der Switch versieht dann seinen eigenen Verkehr mit diesem VLAN-Tag. Führt der Port, über den du verbunden bist, dieses VLAN nicht, ist diese Seite nicht mehr erreichbar und die Einstellung lässt sich nur noch über die serielle Konsole zurücknehmen.",
v_mgmt_tt:"Das Management-VLAN lässt sich nicht löschen. Lege zuerst ein anderes VLAN als Management-VLAN fest.",
l2_title:"MAC-Adresstabelle",l2_filter:"filtern...",l2_flush:"Gelernte Einträge löschen",l2_static:"statisch", l2_title:"MAC-Adresstabelle",l2_filter:"filtern...",l2_flush:"Gelernte Einträge löschen",l2_static:"statisch",
l2_learned:"gelernt",l2_loading:"lädt...",l2_failed:"Laden fehlgeschlagen",l2_entries:"Einträge", l2_learned:"gelernt",l2_loading:"lädt...",l2_failed:"Laden fehlgeschlagen",l2_entries:"Einträge",
l2_del_t:"Eintrag löschen",l2_flush_q:"Alle gelernten MAC-Einträge löschen?", l2_del_t:"Eintrag löschen",l2_flush_q:"Alle gelernten MAC-Einträge löschen?",
@@ -800,34 +803,49 @@ function getJSON(p){return api(p).then(function(r){if(!r.ok)throw new Error(p+"
function getText(p){return api(p).then(function(r){if(!r.ok)throw new Error(p+" "+r.status);return r.body})} function getText(p){return api(p).then(function(r){if(!r.ok)throw new Error(p+" "+r.status);return r.body})}
var CONF_CMDS=[ var CONF_CMDS=[
/^ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^ip\s+dhcp$/, /^ip\s+(\d{1,3}\.){3}\d{1,3}$/,
/^gw\s+(\d{1,3}\.){3}\d{1,3}$/,/^netmask\s+(\d{1,3}\.){3}\d{1,3}$/, /^ip\s+dhcp$/,
/^syslog\s+(on|off)$/,/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^syslog\s+port\s+\d{1,5}$/, /^gw\s+(\d{1,3}\.){3}\d{1,3}$/,
/^netmask\s+(\d{1,3}\.){3}\d{1,3}$/,
/^syslog\s+(on|off)$/,
/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,
/^syslog\s+port\s+\d{1,5}$/,
/^session\s+\d{1,5}$/, /^session\s+\d{1,5}$/,
/^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/, /^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/,
/^ntp\s+(on|off)$/,/^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,/^ntp\s+interval\s+\d{1,4}$/, /^ntp\s+(on|off)$/,
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,/^ntp\s+dst\s+(off|eu|us)$/, /^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,
/^ntp\s+interval\s+\d{1,4}$/,
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,
/^ntp\s+dst\s+(off|eu|us)$/,
/^passwd\s+\S+$/,/^hostname\s+\S{1,23}$/, /^passwd\s+\S+$/,/^hostname\s+\S{1,23}$/,
/^vlan\s+\d{1,4}\s+d$/,/^vlan\s+\d{1,4}\s+mgmt$/, /^vlan\s+\d+\s+(d|mgmt)$/m,
/^vlan\s+\d{1,4}(\s+[a-zA-Z]\w*)?(\s+\d{1,2}t?)+$/, /^vlan\s+\d+(\s+[a-zA-Z]\w*)?(\s+\d+t?)*$/m,
/^pvid\s+\d{1,2}\s+\d{1,4}$/, /^pvid\s+\d+\s+\d+$/m,
/^ingress(\s+\d{1,2}[tua])+$/,/^ingress\s+[tua]$/, /^ingress(\s+\d+[tua])+$/,/^ingress\s+[tua]$/,
/^port\s+\d{1,2}\s+(10m|100m|1g|2g5|5g|10g|auto|on|off)(\s+(half|full))?$/, /^port\s+\d+\s+(10m|100m|1g|2g5|5g|10g|auto|on|off)(\s+(half|full))?$/,
/^port\s+\d{1,2}\s+name\s+\S+$/, /^port\s+\d+\s+name\s+\S+$/,
/^port\s+\d{1,2}\s+lldp\s+(permit|block)+$/, /^port\s+\d+\s+lldp\s+(permit|block)+$/,
/^eee\s+(on|off)(\s+\d{1,2})?(\s+(100m|1g|2g5))?$/, /^eee\s+(on|off)(\s+\d+)?(\s+(100m|1g|2g5))?$/,
/^mirror(\s+\d{1,2})(\s+\d{1,2}[tr]?)+$/,/^mirror\s+off$/, /^mirror(\s+\d+)(\s+\d+[tr]?)+$/,/^mirror\s+off$/,
/^lag\s+[1-4](\s+\d{1,2})+$/,/^lag\s+[1-4]\s+d$/,/^lag\s+[1-4]\s+lacp(\s+\d{1,2})+$/,/^lag\s+[1-4]\s+lacp\s+off$/,/^laghash\s+[1-4](\s+\w+)+$/, /^lag\s+[1-4](\s+\d+)+$/,/^lag\s+[1-4]\s+d$/,
/^isolate\s+\d{1,2}(\s+(off|\d{1,2}))+$/, /^lag\s+[1-4]\s+lacp(\s+\d+)+$/,
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d{1,2}$/, /^lag\s+[1-4]\s+lacp\s+off$/,
/^laghash\s+[1-4](\s+\w+)+$/,
/^isolate\s+\d+(\s+(off|\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+(port\s+\d{1,2}|lag\s+[1-4])\s+(on|off)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+edge\s+(on|off|auto)$/, /^stp\s+(port\s+\d+|lag\s+[1-4])\s+(on|off)$/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+cost\s+\d{1,9}$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+prio\s+\d{1,3}$/, /^stp\s+(port\s+\d+|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+guard\s+(none|bpdu|root)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+filter\s+(on|off)$/, /^stp\s+(port\s+\d+|lag\s+[1-4])\s+cost\s+\d+$/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/, /^stp\s+(port\s+\d+|lag\s+[1-4])\s+prio\s+\d+$/,
/^igmp\s+(on|off)$/,/^mtu\s+\d{1,2}\s+\d+$/,/^lldp\s+(on|off)$/, /^stp\s+(port\s+\d+|lag\s+[1-4])\s+guard\s+(none|bpdu|root)$/,
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/, /^stp\s+(port\s+\d+|lag\s+[1-4])\s+filter\s+(on|off)$/,
/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\s+(off|\d{1,8}\s+(pps|kbps))$/, /^stp\s+(port\s+\d=|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/,
/^igmp\s+(on|off)$/,
/^mtu\s+\d+\s+\d+$/,
/^lldp\s+(on|off)$/,
/^bw\s+(in|out)\s+\d+\s+\S+$/,
/^storm\s+\d+\s+(bcast|mcast|ucast|umcast)\s+(off|\d+\s+(pps|kbps))$/,
]; ];
function isConfCmd(line){ function isConfCmd(line){
for(var i=0;i<CONF_CMDS.length;i++)if(CONF_CMDS[i].test(line))return true; for(var i=0;i<CONF_CMDS.length;i++)if(CONF_CMDS[i].test(line))return true;
@@ -836,6 +854,7 @@ function isConfCmd(line){
function setDirty(d){ function setDirty(d){
S.dirty=d; S.dirty=d;
$("dirty").classList.toggle("show",d); $("dirty").classList.toggle("show",d);
$("saveBtn").classList.toggle("pri",d);
} }
function postCmd(cmd,quiet){ function postCmd(cmd,quiet){
return api("/cmd",{method:"POST",body:cmd}).then(function(r){ return api("/cmd",{method:"POST",body:cmd}).then(function(r){
@@ -850,7 +869,6 @@ function postCmds(list){
list.forEach(function(c){p=p.then(function(){return postCmd(c,true)})}); list.forEach(function(c){p=p.then(function(){return postCmd(c,true)})});
return p.then(function(){toast(t("t_cmds",{n:list.length}),"ok")}); return p.then(function(){toast(t("t_cmds",{n:list.length}),"ok")});
} }
function toast(msg,cls){ function toast(msg,cls){
var el=h("div",{class:"toast "+(cls||""),text:msg}); var el=h("div",{class:"toast "+(cls||""),text:msg});
$("toasts").appendChild(el); $("toasts").appendChild(el);
@@ -936,6 +954,7 @@ function byPort(a){return a.sort(function(x,y){return x.portNum-y.portNum})}
function pollStatus(){ function pollStatus(){
return getJSON("/status.json").then(function(s){ return getJSON("/status.json").then(function(s){
byPort(s); byPort(s);
S.ports=s;
var now=Date.now(); var now=Date.now();
if(!S.n){ if(!S.n){
S.n=s.length; S.n=s.length;
@@ -957,7 +976,7 @@ function pollStatus(){
}; };
}); });
} }
S.prev=s;S.prevT=now;S.ports=s; S.prev=s;S.prevT=now;
updateStrip(); updateStrip();
var hk=tabHooks[curTab]; var hk=tabHooks[curTab];
if(hk&&hk.status)hk.status(); if(hk&&hk.status)hk.status();
@@ -1024,8 +1043,10 @@ function portRows(p){
var rows=[[t("c_port"),String(p.portNum)],[t("c_type"),p.isSFP?"SFP":"RJ45"]]; var rows=[[t("c_port"),String(p.portNum)],[t("c_type"),p.isSFP?"SFP":"RJ45"]];
if(p.name)rows.push([t("c_name"),p.name]); if(p.name)rows.push([t("c_name"),p.name]);
rows.push([t("p_state"),!p.enabled?t("p_disabled"):(p.link>0?t("p_up")+" "+LINKS[p.link]:t("c_down"))]); rows.push([t("p_state"),!p.enabled?t("p_disabled"):(p.link>0?t("p_up")+" "+LINKS[p.link]:t("c_down"))]);
rows.push([t("p_txgb"),BigInt(p.txG)+" / "+BigInt(p.txB)+" "+t("p_pkts")]); rows.push([t("st_txg"),BigInt(p.txG)+" "+t("p_pkts")]);
rows.push([t("p_rxgb"),BigInt(p.rxG)+" / "+BigInt(p.rxB)+" "+t("p_pkts")]); rows.push([t("st_txb"),BigInt(p.txB)+" "+t("p_pkts")]);
rows.push([t("st_rxg"),BigInt(p.rxG)+" "+t("p_pkts")]);
rows.push([t("st_rxb"),BigInt(p.rxB)+" "+t("p_pkts")]);
if(p.isSFP){ if(p.isSFP){
if(p.sfp_vendor)rows.push([t("p_module"),[p.sfp_vendor,p.sfp_model,p.sfp_serial].filter(Boolean).join(" / ")]); if(p.sfp_vendor)rows.push([t("p_module"),[p.sfp_vendor,p.sfp_model,p.sfp_serial].filter(Boolean).join(" / ")]);
var ext=p.sfp_options&0x40; var ext=p.sfp_options&0x40;
@@ -1461,131 +1482,282 @@ function rangeStr(list){
} }
return parts.join(", "); return parts.join(", ");
} }
function vlanRefresh(){
var tb=$("vtable").tBodies[0]; function vlanRefresh() {
return getJSON("/vlanlist").then(function(d){ var tb = $("vtable").tBodies[0];
var vl=d.vlan||[]; tb.style="cursor:pointer;"
$("vmgmtcur").textContent=d.mgmt?String(d.mgmt):t("v_mgmt_none"); return getJSON("/vlanlist").then(function(d) {
$("vempty").style.display=vl.length?"none":""; var vl = d.vlan || [];
tb.innerHTML=""; $("vmgmtcur").dataset.id = d.mgmt;
var p=Promise.resolve(); $("vempty").style.display = vl.length ? "none" : "";
vl.forEach(function(v){ tb.innerHTML = "";
p=p.then(function(){return getJSON("/vlan.json?vid="+v.id).catch(function(){return null})}).then(function(d){ var p = Promise.resolve();
var tr=tb.insertRow(); vl.forEach(function(v) {
tr.insertCell().appendChild(h("a",{href:"#vlan",text:String(v.id),onclick:function(e){ p = p.then(function() {
e.preventDefault();$("vvid").value=v.id;vlanLoad(); return getJSON("/vlan.json?vid=" + v.id).catch(function() {
}})); return null
tr.insertCell().textContent=v.name||""; })
if(!d){for(var c=0;c<4;c++)tr.insertCell().textContent="?";} }).then(function(d) {
else{ var tr = tb.insertRow();
var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem; tr.onclick = function(e) {
var pv=parseInt(d.pvid,16)&0x3ff; e.preventDefault();
tr.insertCell().textContent=rangeStr(maskToPorts(mem)); vlanLoad(v.id);
tr.insertCell().textContent=rangeStr(maskToPorts(mem&~unt)); };
tr.insertCell().textContent=rangeStr(maskToPorts(unt)); var l = h("a", {
tr.insertCell().textContent=rangeStr(maskToPorts(pv)); href: "#vlan",
text: String(v.id) + ($("vmgmtcur").dataset.id == v.id ? " ("+t("v_mgmt")+")":""),
"data-id": v.id,
"data-name": v.name
});
tr.insertCell().appendChild(l);
tr.insertCell().textContent = v.name || "";
if (!d) {
for (var c = 0; c < 4; c++) tr.insertCell().textContent = "?";
} else {
var m = parseInt(d.members, 16),
mem = m & 0x3ff,
unt = ((m >> 10) & 0x3ff) & mem,
tag = mem & ~unt;
var pv = parseInt(d.pvid, 16) & 0x3ff;
tr.insertCell().textContent = rangeStr(maskToPorts(mem));
tr.insertCell().textContent = rangeStr(maskToPorts(tag));
tr.insertCell().textContent = rangeStr(maskToPorts(unt));
tr.insertCell().textContent = rangeStr(maskToPorts(pv));
l.dataset.t = maskToPorts(tag);
l.dataset.u = maskToPorts(unt);
} }
var del=tr.insertCell(); var act = tr.insertCell();
if(v.id!==1)del.appendChild(h("button",{class:"ctl",text:"\u2715",title:t("v_del_t"),onclick:function(){ act.onclick=function(e){e.stopPropagation()};
confirmModal(t("v_del_q",{n:v.id}),t("v_del_d"),function(){ act.appendChild(h("button", {
postCmd("vlan "+v.id+" d").then(vlanRefresh).catch(function(){}); class: "ctl",
}); text: t("v_rename"),
}})); title: t("v_rename_t"),
}).catch(function(){}); onclick: function() {
var body = h("div");
body.appendChild(h("p", null, [
h("label", {class: "row"}, [
h("span",{class:"lb", "data-i18n":"c_name"},[document.createTextNode(t("c_name"))]),
h("input", {id: "nvlrename", class: "in", size: "12", maxlength: "15", "data-i18n-p": "v_optional", value:(v.name||"")})
])
]));
modal("Rename VLAN "+v.id, body, [h("button", {
class: "ctl pri", text: t("v_rename_t"),
onclick: function(){
if(!validVlanName($("nvlrename").value)) return;
postCmd(vlan2str(v.id,$("nvlrename").value,mySplit(l.dataset.t),mySplit(l.dataset.u))).then(function(){closeModal();vlanRefresh()});
}
})]);
},
}));
if (v.id == $("vmgmtcur").dataset.id) {
act.appendChild(h("span",{class:"ctl", title:t("v_mgmt_tt"), class:"hint", style:"color: var(--fg2); font-size: 150%; padding-left: 10px;"},[document.createTextNode("\u{1F6C8}")]));
} else {
act.appendChild(h("button", {
class: "ctl",
text: "\u2715",
title: t("v_del_t"),
style: "margin-left: 5px;",
onclick: function() {
confirmModal(t("v_del_q", { n: v.id }), t("v_del_d"), function() {
postCmd("vlan " + v.id + " d").then(vlanRefresh).catch(function() {});
});
}
}));
}
}).catch(function() {});
}); });
return p; return p;
}); });
} }
function buildVlanEdit(){
var tb=$("vedit").tBodies[0]; function buildVlanEdit() {
if(tb.rows.length||!S.n)return; var tb = $("vedit").tBodies[0];
var hd=tb.insertRow();hd.insertCell().className="mut"; if (tb.rows.length || !S.n) return;
var rM=tb.insertRow();rM.insertCell().textContent=t("v_member"); var hd = tb.insertRow();
var rP=tb.insertRow();rP.insertCell().textContent=t("v_pvid"); hd.insertCell().className = "mut";
for(var p=1;p<=S.n;p++)(function(p){ var rN = tb.insertRow();
hd.insertCell().innerHTML='<b>'+p+'</b>'; rN.insertCell().textContent = t("c_name");
var seg=h("span",{class:"seg",id:"vm"+p}); var rM = tb.insertRow();
["-","U","T"].forEach(function(s,ix){ rM.insertCell().textContent = t("v_member");
seg.appendChild(h("button",{text:s,"data-v":ix,onclick:function(){ var rP = tb.insertRow();
seg.querySelectorAll("button").forEach(function(b){b.classList.remove("on")}); rP.insertCell().textContent = t("v_pvid");
this.classList.add("on"); for (var p = 1; p <= S.n; p++)(function(p) {
}})); hd.insertCell().innerHTML = '<b>' + p + '</b>';
var seg = h("span", { class: "seg", id: "vm" + p });
["-", "U", "T"].forEach(function(s, ix) {
seg.appendChild(h("button", {
text: s,
"data-v": ix,
onclick: function() {
seg.querySelectorAll("button").forEach(function(b) { b.classList.remove("on") });
this.classList.add("on");
}
}));
}); });
seg.children[0].classList.add("on"); seg.children[0].classList.add("on");
rN.insertCell().appendChild(document.createTextNode((S.ports.filter(x=>x.portNum==p)[0]).name));
rM.insertCell().appendChild(seg); rM.insertCell().appendChild(seg);
rP.insertCell().appendChild(h("input",{type:"checkbox",id:"vp"+p})); rP.insertCell().appendChild(h("span", { id: "vp" + p }));
})(p); })(p);
var it=$("ingress").tBodies[0];
var ih=it.insertRow();ih.insertCell().className="mut";
var ir=it.insertRow();ir.insertCell().textContent=t("v_accept");
for(var q=1;q<=S.n;q++)(function(q){
ih.insertCell().innerHTML='<b>'+q+'</b>';
var sel=h("select",{class:"in",id:"ing"+q});
[["","-"],["a",t("v_ing_all")],["u",t("v_untagged")],["t",t("v_tagged")]].forEach(function(o){
sel.appendChild(h("option",{value:o[0],text:o[1]}));
});
ir.insertCell().appendChild(sel);
})(q);
} }
function segVal(p){
return Number($("vm"+p).querySelector("button.on").getAttribute("data-v"));
}
function segSet(p,v){
$("vm"+p).querySelectorAll("button").forEach(function(b,i){b.classList.toggle("on",i===v)});
}
function vlanLoad(){
var vid=parseInt($("vvid").value,10);
if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;}
getJSON("/vlan.json?vid="+vid).then(function(d){
$("vname").value=d.name||"";
var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem;
var pv=parseInt(d.pvid,16)&0x3ff;
for(var p=1;p<=S.n;p++){
var bit=S.physToLog[p-1];
segSet(p,(mem>>bit)&1?(((unt>>bit)&1)?1:2):0);
$("vp"+p).checked=!!((pv>>bit)&1);
}
toast(t("v_loaded",{n:vid}),"ok");
}).catch(function(){toast(t("v_notfound",{n:vid}),"err")});
}
function vlanApply(){
var vid=parseInt($("vvid").value,10);
if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;}
var name=$("vname").value.trim();
if(name&&!/^[a-zA-Z]\w*$/.test(name)){toast(t("v_name_err"),"err");return;}
var cmd="vlan "+vid+(name?" "+name:""),members=0;
for(var p=1;p<=S.n;p++){
var v=segVal(p);
if(v===1){cmd+=" "+p;members++;}
else if(v===2){cmd+=" "+p+"t";members++;}
}
if(!members){toast(t("v_nomember"),"err");return;}
var cmds=[cmd];
for(var q=1;q<=S.n;q++)if($("vp"+q).checked)cmds.push("pvid "+q+" "+vid);
postCmds(cmds).then(vlanRefresh).catch(function(){});
}
function ingressApply(){
var cmd="ingress",any=false;
for(var p=1;p<=S.n;p++){
var v=$("ing"+p).value;
if(v){cmd+=" "+p+v;any=true;}
}
if(!any){toast(t("v_ing_none"),"err");return;}
postCmd(cmd).catch(function(){});
}
$("vload").addEventListener("click",vlanLoad);
$("vapply").addEventListener("click",vlanApply);
$("ingapply").addEventListener("click",ingressApply);
$("vmgmt").addEventListener("click",function(){
var vid=parseInt($("vvid").value,10);
if(!vid){toast(t("v_vid_first"),"err");return;}
confirmModal(t("v_mgmt_q",{n:vid}),t("v_mgmt_d"),
function(){postCmd("vlan "+vid+" mgmt").then(vlanRefresh).catch(function(){})});
});
tabHooks.vlan={
enter:function(){needPorts(function(){buildVlanEdit();vlanRefresh().catch(function(){})})},
};
function segVal(p) {
return Number($("vm" + p).querySelector("button.on").getAttribute("data-v"));
}
function segSet(p, v) {
$("vm" + p).querySelectorAll("button").forEach(function(b, i) { b.classList.toggle("on", i === v) });
}
function vlanLoad(vid) {
if (!vid || vid < 1 || vid > 4094) {
toast(t("v_vid_err"), "err");
return;
}
getJSON("/vlan.json?vid=" + vid).then(function(d) {
$("vtitle").innerText = "VLAN " + vid + (d.name ? " (" + (d.name) + ")" : "");
$("vtitle").dataset.id = vid;
$("vtitle").dataset.name = d.name;
var m = parseInt(d.members, 16),
mem = m & 0x3ff,
unt = ((m >> 10) & 0x3ff) & mem;
var pv = parseInt(d.pvid, 16) & 0x3ff;
for (var p = 1; p <= S.n; p++) {
var bit = S.physToLog[p - 1];
segSet(p, (mem >> bit) & 1 ? (((unt >> bit) & 1) ? 1 : 2) : 0);
$("vp" + p).innerText = (!!((pv >> bit) & 1))?"Y":"N";
}
$("vmgmtpromote").checked = ($("vmgmtcur").dataset.id == vid);
toast(t("v_loaded", {n: vid}), "ok");
for(const vlanRow of $("vtable").tBodies[0].rows) {
vlanRow.className=vlanRow.firstChild.firstChild.dataset.id == vid ? "sel":"";
}
}).catch(function() {toast(t("v_notfound", {n: vid}), "err")});
}
function vlan2str(i, n, t, u){
return ("vlan " + i + (n ? " " + n : "") + " " + t.map((x)=>x+"t").concat(u).join(" ")).trim();
}
function pvidcmd(i, p){
return "pvid " + p + " " + i;
}
function mySplit(a){
if(a.trim()=="") return [];
return a.trim().split(",");
}
function vlanApply() {
var vid = $("vtitle").dataset.id;
var vname = $("vtitle").dataset.name;
if (!vid || vid < 1 || vid > 4094) {
toast(t("v_vid_err"), "err");
return;
}
var top_cmds = [], tail_cmds = [], vlansPerPort=[], thisVlanInStructure={id:vid, tagged:[], untagged:[]};
for (var p = 1; p <= S.n; p++) {
var v = segVal(p);
vlansPerPort[p]={t:[],u:[]};
if (v === 1) {
thisVlanInStructure.untagged.push(p);
vlansPerPort[p].u.push(vid);
} else if (v === 2) {
thisVlanInStructure.tagged.push(p);
vlansPerPort[p].t.push(vid);
}
}
if (thisVlanInStructure.tagged.length + thisVlanInStructure.untagged.length == 0) {
toast(t("v_nomember"), "err");
return;
}
for(const vlanRow of $("vtable").tBodies[0].rows){
var rowData = vlanRow.firstChild.firstChild.dataset,
rowUntagged = mySplit(rowData.u),
rowTagged = mySplit(rowData.t);
if(rowData.id == vid) continue;
for(const newVlanPort of thisVlanInStructure.untagged) {
if(rowUntagged.includes(""+newVlanPort)) rowUntagged.splice(rowUntagged.indexOf(""+newVlanPort),1);
}
for(const rut of rowUntagged) {
vlansPerPort[rut].u.push(rowData.id);
}
for(const rt of rowTagged) {
vlansPerPort[rt].t.push(rowData.id)
}
top_cmds.push(vlan2str(rowData.id,rowData.name,rowTagged,rowUntagged));
}
tail_cmds.push(vlan2str(vid, vname, thisVlanInStructure.tagged, thisVlanInStructure.untagged));
for (p in vlansPerPort){/*park all ports with only tagged membership*/
if (vlansPerPort[p].u.length == 0 && vlansPerPort[p].t.length > 0) {
tail_cmds.push(pvidcmd(0, p));
}
}
for(const up of thisVlanInStructure.untagged){
tail_cmds.push(pvidcmd(vid,up));
}
if($("vmgmtpromote").checked){/*must be the last command*/
tail_cmds.push("vlan " + vid + " mgmt");
}
var cmd = top_cmds.concat(tail_cmds);
postCmd(cmd.join("\n"),true).then(function(){toast(t("t_cmds",{n:cmd.length}),"ok")}).then(vlanRefresh).catch(function() {}).then(function() {
vlanLoad(vid)
});
}
$("vapply").addEventListener("click", vlanApply);
$("vlan_add").addEventListener("click", function() {
var body = h("div");
body.appendChild(h("p", null, [
h("label", {class: "row"}, [
h("span",{class:"lb", "data-i18n":"v_vid"},[document.createTextNode(t("v_vid"))]),
h("input", {id: "nvlid", class: "in sm", type: "number", min: "1", max: "4094"})
])
]));
body.appendChild(h("p", null, [
h("label", {class: "row"}, [
h("span",{class:"lb", "data-i18n":"c_name"},[document.createTextNode(t("c_name"))]),
h("input", {id: "nvlname", class: "in", size: "12", maxlength: "15", "data-i18n-p": "v_optional"})
])
]));
modal("Add VLAN", body, [h("button", {
class: "ctl pri", text: t("v_add"),
onclick: addVlan
})]);
});
function validVlanName(n){
if (n && (!/^[a-zA-Z]\w*$/.test(n) || n == "d" || n == "mgmt")) {
toast(t("v_name_err"), "err");
return false;
}
return true;
}
function addVlan() {
var vid = parseInt($("nvlid").value, 10);
for (const r of $("vtable").tBodies[0].rows) {
if (r.firstChild.firstChild.dataset.id == vid) {
toast(t("v_vid_err"), "err");
return;
}
}
var name = $("nvlname").value;
if(!validVlanName(name)) return false;
var cmd = "vlan " + vid + (name ? " " + name : "");
postCmd(cmd).then(function() {
closeModal();
vlanRefresh().then(function(){vlanLoad(vid)});
}).catch(function() {});
}
tabHooks.vlan = {
enter: function() {
needPorts(function() {
buildVlanEdit();
vlanRefresh().catch(function() {}).then(function() {
VlanInit();
})
})
},
};
function VlanInit() {
vlanLoad($("vtable").tBodies[0].firstChild.firstChild.firstChild.dataset.id)
};
var l2Rows=[],l2SortCol="pport",l2SortDir=1,l2Gen=0; var l2Rows=[],l2SortCol="pport",l2SortDir=1,l2Gen=0;
function l2Load(){ function l2Load(){
var seen={},all=[],idx=0,guard=0,gen=++l2Gen; var seen={},all=[],idx=0,guard=0,gen=++l2Gen;
@@ -2243,14 +2415,14 @@ var CONF_OVERWRITE=[
/^ntp\s+server\b/,/^ntp\s+interval\b/,/^ntp\s+timezone\b/,/^ntp\s+dst\b/, /^ntp\s+server\b/,/^ntp\s+interval\b/,/^ntp\s+timezone\b/,/^ntp\s+dst\b/,
/^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/, /^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
/^pvid\s+\d{1,2}\b/, /^pvid\s+\d{1,2}\b/,
/^port\s+\d{1,2}(?!\s+name\b)/,/^port\s+\d{1,2}\s+name\b/, /^port\s+\d{1,2}(?!\s+(name|lldp)\b)/,/^port\s+\d{1,2}\s+name\b/,
/^mirror\b/, /^mirror\b/,
/^lag\s+\d\b/,/^laghash\s+\d\b/,/^isolate\s+\d{1,2}\b/, /^lag\s+\d\b/,/^laghash\s+\d\b/,/^isolate\s+\d{1,2}\b/,
/^stp\s+(prio|hello|maxage|fwd|txhold|version)\b/, /^stp\s+(prio|hello|maxage|fwd|txhold|version)\b/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/, /^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/,
/^igmp\b/,/^mtu\s+\d{1,2}\b/,/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\b/, /^igmp\b/,/^mtu\s+\d{1,2}\b/,/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\b/,
]; ];
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/]; var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(lldp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
function mergeConf(base,texts){ function mergeConf(base,texts){
var conf=base.slice(); var conf=base.slice();
function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})} function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})}
@@ -2268,6 +2440,7 @@ function mergeConf(base,texts){
} }
if((m=line.match(/^lag (\d) lacp off$/))){drop(new RegExp("^lag "+m[1]+" lacp( |$)"));return;} if((m=line.match(/^lag (\d) lacp off$/))){drop(new RegExp("^lag "+m[1]+" lacp( |$)"));return;}
if(line==="mirror off"){drop(/^mirror /);return;} if(line==="mirror off"){drop(/^mirror /);return;}
if((m=line.match(/^(port \d{1,2} lldp) (permit|block)$/))){drop(new RegExp("^"+m[1]+" (permit|block)$"));conf.push(line);return;}
if(!isConfCmd(line))return; if(!isConfCmd(line))return;
if((m=line.match(/^ingress (.+)$/))){ if((m=line.match(/^ingress (.+)$/))){
if(/^[tua]$/.test(m[1])){drop(/^ingress /);conf.push(line);return;} if(/^[tua]$/.test(m[1])){drop(/^ingress /);conf.push(line);return;}
@@ -2300,7 +2473,7 @@ function mergeConf(base,texts){
var key=line.match(rx)[0]; var key=line.match(rx)[0];
conf=conf.filter(function(item){ conf=conf.filter(function(item){
return!(item===key||(item.indexOf(key+" ")===0 return!(item===key||(item.indexOf(key+" ")===0
&&!/\smgmt$/.test(item)&&item.indexOf(key+" name ")!==0)); &&!/\smgmt$/.test(item)&&item.indexOf(key+" name ")!==0&&item.indexOf(key+" lldp ")!==0));
}); });
break; break;
} }
+26 -22
View File
@@ -33,7 +33,7 @@
html,body{height:100%} html,body{height:100%}
body{ body{
font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
background:var(--bg);color:var(--fg);display:flex; background:var(--bg);color:var(--fg);display:flex;overflow:hidden;
} }
button,input,select,textarea{font:inherit;color:var(--fg)} button,input,select,textarea{font:inherit;color:var(--fg)}
a{color:var(--ac);text-decoration:none} a{color:var(--ac);text-decoration:none}
@@ -72,8 +72,8 @@ select.ctl,button.ctl{
button.pri{background:var(--ac);color:var(--acfg);border-color:transparent;font-weight:600} button.pri{background:var(--ac);color:var(--acfg);border-color:transparent;font-weight:600}
button.pri:hover,button.ctl:hover{filter:brightness(1.07)} button.pri:hover,button.ctl:hover{filter:brightness(1.07)}
button:disabled{opacity:.45;cursor:default;filter:none} button:disabled{opacity:.45;cursor:default;filter:none}
main{padding:18px 20px 40px;max-width:1400px;width:100%} main{padding:18px 20px 40px;width:100%;overflow-y:auto;}
section.tab{display:none} section.tab{display:none;max-width:1400px;}
section.tab.act{display:block} section.tab.act{display:block}
.card{ .card{
background:var(--panel);border:1px solid var(--bd);border-radius:10px; background:var(--panel);border:1px solid var(--bd);border-radius:10px;
@@ -89,9 +89,11 @@ table.t th{
padding:6px 8px;border-bottom:1px solid var(--bd);white-space:nowrap; padding:6px 8px;border-bottom:1px solid var(--bd);white-space:nowrap;
} }
table.t th[data-sort]{cursor:pointer;user-select:none} table.t th[data-sort]{cursor:pointer;user-select:none}
table.t th.num{text-align:right}
table.t td{padding:6px 8px;border-bottom:1px solid var(--bd);vertical-align:middle} table.t td{padding:6px 8px;border-bottom:1px solid var(--bd);vertical-align:middle}
table.t tr:last-child td{border-bottom:0} table.t tr:last-child td{border-bottom:0}
table.t tr:hover td{background:var(--sel)} table.t tr:hover td{background:var(--sel)}
table.t tr.sel{background:var(--panel2); font-weight: bold;}
.num{font-variant-numeric:tabular-nums;text-align:right} .num{font-variant-numeric:tabular-nums;text-align:right}
.scrollx{overflow-x:auto} .scrollx{overflow-x:auto}
input.in,select.in,textarea.in{ input.in,select.in,textarea.in{
@@ -123,12 +125,20 @@ label.row span.lb{width:110px;flex:none;color:var(--fg2);font-size:13px}
.port .pn{font-weight:650;font-size:12px} .port .pn{font-weight:650;font-size:12px}
.port .ps{font-size:9px;color:var(--fg2);display:block;line-height:1.2} .port .ps{font-size:9px;color:var(--fg2);display:block;line-height:1.2}
.port .jack{ .port .jack{
display:block;width:16px;height:10px;margin:2px auto 1px;border-radius:2px; display:block;width:21px;height:12px;margin:4px auto 2px;border-radius:2px;
border:2px solid var(--fg2);border-top-width:4px;opacity:.55; border:2px solid var(--fg2);border-top-width:8px;opacity:.55;position:relative;
} }
.port.sfp .jack{width:24px;height:9px;border-top-width:3px} .port .jack:before{content:"";
position:absolute;bottom:100%;left:50%;transform:translateX(-50%);
width:6px;height:5px;margin-bottom:7px;border-radius: 1px 1px 0 0;
background-color:var(--fg2);
}
.port.sfp .jack{width:24px;height:16px;margin:0 auto 2px;border-top-width:5px}
.port.sfp .jack:before{width:10px;margin-bottom:2px;border-radius:0 0 1px 1px;background-color:var(--panel2)}
.port.up{border-color:var(--pc);background:color-mix(in srgb,var(--pc) 10%,var(--panel2))} .port.up{border-color:var(--pc);background:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
.port.up .jack{border-color:var(--pc);opacity:1} .port.up .jack{border-color:var(--pc);opacity:1}
.port.up .jack:before{background-color:var(--pc)}
.port.up.sfp .jack:before{background-color:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
.port.up .ps{color:var(--pc);font-weight:600} .port.up .ps{color:var(--pc);font-weight:600}
.port.dis{opacity:.45;border-style:dashed} .port.dis{opacity:.45;border-style:dashed}
.badge{font-size:11px;font-weight:600;border-radius:10px;padding:1px 8px;background:var(--panel2);color:var(--fg2)} .badge{font-size:11px;font-weight:600;border-radius:10px;padding:1px 8px;background:var(--panel2);color:var(--fg2)}
@@ -223,7 +233,7 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<span id="ttitle"></span> <span id="ttitle"></span>
<div id="strip"></div> <div id="strip"></div>
<span id="dirty" data-i18n="hdr_dirty" data-i18n-t="hdr_dirty_t">unsaved changes</span> <span id="dirty" data-i18n="hdr_dirty" data-i18n-t="hdr_dirty_t">unsaved changes</span>
<button class="ctl pri" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button> <button class="ctl" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
</header> </header>
<main> <main>
@@ -285,27 +295,21 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
</section> </section>
<section class="tab" id="tab-vlan"> <section class="tab" id="tab-vlan">
<div class="card"><h2><span data-i18n="v_title">VLANs</span> <span class="hint"><span data-i18n="v_mgmt">Management VLAN</span>: <span id="vmgmtcur">-</span></span></h2> <div class="card"><h2><span data-i18n="v_title">VLANs</span><span id="vmgmtcur"></span></h2>
<div class="scrollx"><table class="t" id="vtable"> <div class="scrollx"><table class="t" id="vtable">
<thead><tr><th data-i18n="v_vid">VID</th><th data-i18n="c_name">Name</th><th data-i18n="v_members">Members</th><th data-i18n="v_tagged">Tagged</th><th data-i18n="v_untagged">Untagged</th><th data-i18n="v_pvid_on">PVID on</th><th></th></tr></thead> <thead><tr><th data-i18n="v_vid">VID</th><th data-i18n="c_name">Name</th><th data-i18n="v_members">Members</th><th data-i18n="v_tagged">Tagged</th><th data-i18n="v_untagged">Untagged</th><th data-i18n="v_pvid_on">PVID on</th><th></th></tr></thead>
<tbody></tbody></table></div> <tbody></tbody></table></div>
<p class="small mut" id="vempty" style="display:none;margin-top:6px" data-i18n="v_empty">No VLANs configured.</p> <p class="small mut" id="vempty" style="display:none;margin-top:6px" data-i18n="v_empty">No VLANs configured.</p>
<button class="ctl pri" id="vlan_add" data-i18n="v_add">Add a new VLAN</button>
</div> </div>
<div class="card"><h2 data-i18n="v_editor">VLAN editor</h2> <div class="card"><h2><span data-i18n="v_editor">VLAN editor</span> <span class="hint" id="vtitle"></span></h2>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px"> <div><label class="row"><span class="lb" style="width:150px;" data-i18n="v_setmgmt_t"></span>
<label><span data-i18n="v_vid">VID</span> <input class="in sm" id="vvid" type="number" min="1" max="4094"></label> <span class="switch"><input type="checkbox" id="vmgmtpromote"><i></i></span>
<label><span data-i18n="c_name">Name</span> <input class="in" id="vname" size="12" maxlength="15" data-i18n-p="v_optional"></label> </label></div>
<button class="ctl" id="vload" data-i18n="c_load">Load</button>
<button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button>
<button class="ctl" id="vmgmt" data-i18n="v_setmgmt" data-i18n-t="v_setmgmt_t">Set as mgmt VLAN</button>
</div>
<div class="scrollx"><table class="t" id="vedit"><tbody></tbody></table></div> <div class="scrollx"><table class="t" id="vedit"><tbody></tbody></table></div>
<div style="margin-top:10px"><button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button></div>
<p class="small mut" style="margin-top:6px" data-i18n="v_legend">U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.</p> <p class="small mut" style="margin-top:6px" data-i18n="v_legend">U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.</p>
</div> </div>
<div class="card"><h2><span data-i18n="v_ingress">Ingress filtering</span> <span class="hint" data-i18n="v_ingress_h">write-only: the state is not readable from the switch</span></h2>
<div class="scrollx"><table class="t" id="ingress"><tbody></tbody></table></div>
<div style="margin-top:10px"><button class="ctl pri" id="ingapply" data-i18n="v_ing_apply">Apply ingress modes</button></div>
</div>
</section> </section>
<section class="tab" id="tab-l2"> <section class="tab" id="tab-l2">
@@ -437,8 +441,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<p class="small mut" style="margin-top:8px" data-i18n="sy_time_note">The switch has no battery backed clock.</p> <p class="small mut" style="margin-top:8px" data-i18n="sy_time_note">The switch has no battery backed clock.</p>
</div> </div>
<div class="card"><h2 data-i18n="sy_password">Admin password</h2> <div class="card"><h2 data-i18n="sy_password">Admin password</h2>
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" maxlength="20"></label> <label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" autocomplete="new-password" maxlength="20"></label>
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" maxlength="20"></label> <label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" autocomplete="new-password" maxlength="20"></label>
<button class="ctl pri" id="sy-pwapply" style="margin-top:8px" data-i18n="sy_pwapply">Change password</button> <button class="ctl pri" id="sy-pwapply" style="margin-top:8px" data-i18n="sy_pwapply">Change password</button>
<p class="small mut" style="margin-top:8px" data-i18n="sy_pw_note">Takes effect immediately; save to flash to keep it after reboot.</p> <p class="small mut" style="margin-top:8px" data-i18n="sy_pw_note">Takes effect immediately; save to flash to keep it after reboot.</p>
</div> </div>
+12 -48
View File
@@ -6,12 +6,12 @@
#include "cmd_parser.h" #include "cmd_parser.h"
#include "rtl837x_flash.h" #include "rtl837x_flash.h"
#include "uip.h" #include "uip.h"
#include "sessions.h"
#include "html_data.h" #include "html_data.h"
// #define DEBUG // #define DEBUG
#include "debug.h" #include "debug.h"
#define SESSION_ID_LENGTH 12
#define SESSION_TIMEOUT_DEFAULT 200 #define SESSION_TIMEOUT_DEFAULT 200
/* Seconds of inactivity after which a session cookie stops validating. /* Seconds of inactivity after which a session cookie stops validating.
@@ -75,10 +75,9 @@ __xdata uint8_t postbody_endpoint;
__xdata uint16_t postbody_start; __xdata uint16_t postbody_start;
__xdata char passwd[21]; __xdata char passwd[21];
__xdata char session_id[SESSION_ID_LENGTH + 1]; // Up to four logins at once, see sessions.c
__xdata uint8_t authenticated; __xdata uint8_t authenticated;
__xdata uint32_t now; __xdata uint32_t now;
__xdata uint32_t last_session_use;
#define TSTATE_NONE 0 #define TSTATE_NONE 0
#define TSTATE_TX 1 #define TSTATE_TX 1
@@ -109,6 +108,7 @@ void httpd_init(void) __banked
// handling one, and nothing has set it yet at init time. // handling one, and nothing has set it yet at init time.
for (uint8_t i = 0; i < UIP_CONNS; i++) for (uint8_t i = 0; i < UIP_CONNS; i++)
uip_conns[i].appstate.tstate = TSTATE_CLOSED; uip_conns[i].appstate.tstate = TSTATE_CLOSED;
session_init();
} }
@@ -343,38 +343,11 @@ __xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
read_reg_timer(&now); read_reg_timer(&now);
if (session && session_id[0]) { if (session)
if (now - last_session_use > session_timeout) { session_check();
dbg_string("Session expired\n");
} else {
if (is_word_x(session, session_id)) {
authenticated = 1;
last_session_use = now;
} else {
dbg_string("Invalid session cookie!\n");
}
}
}
return p; return p;
} }
/*
* Generate random HEX-chars at the buffer location.
*/
void gen_random_hex_chars(__xdata uint8_t * b, __xdata uint8_t bytes)
{
uint8_t i = 0;
while (bytes) {
if (!i)
get_random_32();
b[--bytes] = itohex(sfr_data[i]);
if (!bytes) { break; }
b[--bytes] = itohex(sfr_data[i] >> 4 | sfr_data[i] << 4);
i = (i + 1) & 0x3;
}
}
/* 0: body incomplete, 1: configuration stored, 2: malformed */ /* 0: body incomplete, 1: configuration stored, 2: malformed */
static uint8_t config_take(void) static uint8_t config_take(void)
{ {
@@ -448,15 +421,9 @@ static uint8_t config_take(void)
// unlike scan_header(), keeps no auth state, so it may run on every buffered segment // unlike scan_header(), keeps no auth state, so it may run on every buffered segment
static uint16_t preamble_payload_start(uint16_t n) static uint16_t preamble_payload_start(uint16_t n)
{ {
uint16_t pos; // Skip the HTTP header separator retained by handle_post().
uint16_t pos = 4;
for (pos = 0; pos + 24 <= n; pos++) {
if (strstart(&config_buf[pos], "application/octet-stream"))
break;
}
if (pos + 24 > n)
return 0;
pos += 24;
while (pos + 3 < n && !strstart(&config_buf[pos], "\r\n\r\n")) while (pos + 3 < n && !strstart(&config_buf[pos], "\r\n\r\n"))
pos++; pos++;
if (pos + 3 >= n) if (pos + 3 >= n)
@@ -537,9 +504,10 @@ uint8_t stream_upload(void)
memcpy(flash_buf + write_len, boundary, bindex); memcpy(flash_buf + write_len, boundary, bindex);
write_len += bindex; write_len += bindex;
bindex = 0; bindex = 0;
} else {
crc16_bank1(upload_settings.p + upload_settings.bptr);
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
} }
crc16_bank1(upload_settings.p + upload_settings.bptr);
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
if (write_len >= FLASH_PAGE_SIZE) { if (write_len >= FLASH_PAGE_SIZE) {
/* The staged image spans FIRMWARE_UPLOAD_START to twice that, /* The staged image spans FIRMWARE_UPLOAD_START to twice that,
* the span check_and_flash_update_image() reads back. Nothing * the span check_and_flash_update_image() reads back. Nothing
@@ -571,7 +539,6 @@ uint8_t stream_upload(void)
memcpy(flash_buf, flash_buf + FLASH_PAGE_SIZE, write_len); memcpy(flash_buf, flash_buf + FLASH_PAGE_SIZE, write_len);
} }
} }
bindex = 0;
} }
} while(1); } while(1);
} }
@@ -681,13 +648,10 @@ static void run_login_body(__xdata uint8_t *body)
{ {
if (strstart(body, "pwd=") && is_url_word_x(body + 4, passwd)) { if (strstart(body, "pwd=") && is_url_word_x(body + 4, passwd)) {
dbg_string("Password accepted!\n"); dbg_string("Password accepted!\n");
read_reg_timer(&last_session_use); read_reg_timer(&now);
gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
session_id[SESSION_ID_LENGTH] = NUL;
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \ slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
"Set-Cookie: session="); "Set-Cookie: session=");
for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++) session_new();
outbuf[slen++] = session_id[i];
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n"); slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
} else { } else {
dbg_string("Password invalid!\n"); dbg_string("Password invalid!\n");
+13 -3
View File
@@ -1,9 +1,14 @@
CODE_LOCATION=0x1000 CODE_LOCATION=0x1000
INSTALLER_ADDRESS=0x1100 INSTALLER_ADDRESS=0x1100
CC = sdcc SDCC_PREFIX = ../toolchain/sdcc-$(shell cat ../sdcc/* | cksum | cut -d' ' -f1)
ifeq ($(origin CC),default)
SDCC = $(SDCC_PREFIX)/bin/sdcc
CC = $(SDCC)
ASM = $(SDCC_PREFIX)/bin/sdas8051
endif
CC_FLAGS = -mmcs51 CC_FLAGS = -mmcs51
ASM = sdas8051 ASM ?= sdas8051
AFLAGS= -plosgff AFLAGS= -plosgff
BUILDDIR = output BUILDDIR = output
@@ -13,9 +18,14 @@ OBJS = ${SRCS:%.c=$(BUILDDIR)/%.rel}
all: create_build_dir $(BUILDDIR)/updatebuilder $(BUILDDIR)/rtlplayground_oem_upgrade.bin all: create_build_dir $(BUILDDIR)/updatebuilder $(BUILDDIR)/rtlplayground_oem_upgrade.bin
create_build_dir: create_build_dir: $(SDCC)
mkdir -p $(BUILDDIR) mkdir -p $(BUILDDIR)
ifneq ($(SDCC),)
$(SDCC):
sh ../sdcc/build.sh $(abspath $(SDCC_PREFIX))
endif
$(BUILDDIR)/updatebuilder: updatebuilder.c $(BUILDDIR)/updatebuilder: updatebuilder.c
gcc $^ -o $@ gcc $^ -o $@
+1 -1
View File
@@ -348,7 +348,7 @@ void installer(void)
{ {
CKCON = 0; // Initial Clock configuration CKCON = 0; // Initial Clock configuration
SFR_97 = 0; // HADDR? MAP_FLASH_PAGE_TO_XDATA = 0; // HADDR?
// Set in managed mode: // Set in managed mode:
SFR_b9 = 0x00; SFR_b9 = 0x00;
-4
View File
@@ -84,10 +84,6 @@ struct sfp_port
struct machine { struct machine {
char machine_name[30]; char machine_name[30];
uint8_t isRTL8373; uint8_t isRTL8373;
// Lowest logical port number
uint8_t min_port;
// Highest logical port number
uint8_t max_port;
uint8_t n_sfp; uint8_t n_sfp;
uint8_t n_10g; uint8_t n_10g;
uint8_t log_to_phys_port[9]; uint8_t log_to_phys_port[9];
+31
View File
@@ -0,0 +1,31 @@
#include "machine_defs.h"
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.mac_flash_offset = 0x1FC000,
.n_sfp = 1,
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
.sfp_port[0].pin_detect = GPIO38,
.sfp_port[0].pin_los = GPIO_NA,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO48_I2C_SCL1,
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
.led_mux = { 0x00, 0x01, 0x04, 0x05, 0x08, 0x09, 0x0c, 0x09, 0x0d, 0x10, 0x11, 0x0e, 0x14, 0x11, 0x12, 0x15, 0x15, 0x16, 0x18, 0x19, 0x1a, 0x19, 0x1d, 0x1e, 0x1c, 0x1d, 0x20, 0x21 },
.led_mux_custom = 1,
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
LEDS_DUPLEX,
LEDS_2G5 | LEDS_LINK | LEDS_ACT
},
{ LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT,
LEDS_10G | LEDS_LINK | LEDS_ACT,
LEDS_2G5 | LEDS_LINK,
LEDS_COL | LEDS_DUPLEX
},
},
};
@@ -0,0 +1,6 @@
#include "machine_init_defs.h"
void machine_custom_init(void) __banked
{
REG_SET(RTL837X_REG_LED_GLB_IO_EN, 0x7f24977f);
}
+1 -1
View File
@@ -21,7 +21,7 @@ __code const struct machine machine = {
.sfp_port[1].sds = 1, .sfp_port[1].sds = 1,
.sfp_port[1].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ), .sfp_port[1].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO_NA, .reset_pin = GPIO48_I2C_SCL1,
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_27 | LED_28_SYS | LED_29 }, .high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_27 | LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1}, .port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
.led_sets = { .led_sets = {
+1
View File
@@ -0,0 +1 @@
PCB_AFGW_SWTG218ANS_V_2_0_2.c
+7
View File
@@ -0,0 +1,7 @@
#ifndef _MACHINE_SWTG218ANS_V_2_0_2_H_
#define _MACHINE_SWTG218ANS_V_2_0_2_H_
#define MACHINE_MODEL "RealHD PSW8-25MG"
#define MACHINE_CHIPSET RTL8373
#endif
+1
View File
@@ -0,0 +1 @@
PCB_AFGW_SWTG218ANS_V_2_0_2_init.c
+1
View File
@@ -2,6 +2,7 @@
__code const struct machine machine = { __code const struct machine machine = {
MACHINE_COMMON_SETTING, MACHINE_COMMON_SETTING,
.mac_flash_offset = 0x1FC000,
.n_sfp = 1, .n_sfp = 1,
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9}, .log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8}, .phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
+41
View File
@@ -0,0 +1,41 @@
#include "machine_defs.h"
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.n_sfp = 2,
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
.is_sfp = {0, 0, 0, 1, 0, 0, 0, 0, 2},
// Left SFP port (5)
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
.sfp_port[0].pin_los = GPIO_NA,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
// Right SFP port (6)
.sfp_port[1].pin_detect = GPIO50_I2C_SCL2_UART1_TX,
.sfp_port[1].pin_los = GPIO_NA,
.sfp_port[1].pin_tx_disable = GPIO_NA,
.sfp_port[1].sds = 0,
.sfp_port[1].i2c = I2CBUS( GPIO41_I2C_SDA3_MDIO1, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO_NA,
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
/* On Ethernet: Left led is orange, right green
* 100M and 1G give right led green with activity
* 2.5GBit adds solid left orange LED
* LEDs for the SFP-ports are the same as for the Ethernet ports
*/
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
0,
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
LEDS_2G5 | LEDS_LINK | LEDS_ACT
},
{ LEDS_2G5 | LEDS_TWO_PAIR_1G | LEDS_1G | LEDS_500M | LEDS_100M | LEDS_10M |
LEDS_LINK | LEDS_ACT | LEDS_10G | LEDS_TWO_PAIR_5G | LEDS_5G | LEDS_TWO_PAIR_2G5,
LEDS_1G | LEDS_LINK,
LEDS_LINK,
LEDS_COL | LEDS_DUPLEX
}
}
};
+7
View File
@@ -0,0 +1,7 @@
#ifndef _MACHINE_ZOERAX_HG0402XG_V1_1_H_
#define _MACHINE_ZOERAX_HG0402XG_V1_1_H_
#define MACHINE_MODEL "ZoeRax HG0402XG V1.1"
#define MACHINE_CHIPSET RTL8372
#endif
+1 -3
View File
@@ -43,8 +43,6 @@
#define MACHINE_COMMON_SETTING \ #define MACHINE_COMMON_SETTING \
.machine_name = MACHINE_MODEL, \ .machine_name = MACHINE_MODEL, \
.isRTL8373 = MACHINE_CHIPSET == RTL8373, \ .isRTL8373 = MACHINE_CHIPSET == RTL8373
.min_port = MACHINE_MIN_PORT, \
.max_port = MACHINE_MAX_PORT
#endif #endif
+3
View File
@@ -19,6 +19,8 @@
#define MAC_ADDR_LEN 6 #define MAC_ADDR_LEN 6
#define PORT_BIT(p) ((uint16_t)1 << (p)) #define PORT_BIT(p) ((uint16_t)1 << (p))
#define NUL '\0' #define NUL '\0'
#define STR_(x) #x
#define STR(x) STR_(x)
// Define Port-masks for 9-port devices and 6-port devices // Define Port-masks for 9-port devices and 6-port devices
#define PMASK_9 0x1ff #define PMASK_9 0x1ff
@@ -154,6 +156,7 @@ void print_long(uint32_t a);
void print_short(uint16_t a); void print_short(uint16_t a);
void print_byte(uint8_t a); void print_byte(uint8_t a);
void itoa(uint8_t v); void itoa(uint8_t v);
void itoa_no_syslog(uint8_t v);
void itoa_short(uint16_t v); void itoa_short(uint16_t v);
void print_sfr_data(void); void print_sfr_data(void);
void print_phy_data(void); void print_phy_data(void);
+4 -2
View File
@@ -221,9 +221,11 @@ void rtl8372_init(void) __banked
} else if(machine.n_10g == 2) { } else if(machine.n_10g == 2) {
REG_SET(RTL837X_REG_SDS_MODES, 0x1ad); // Both 10g ports use SDS_QXGMII REG_SET(RTL837X_REG_SDS_MODES, 0x1ad); // Both 10g ports use SDS_QXGMII
} else { } else {
// Replace the reset value 0xbff (both SDS off) instead of ORing into it:
// RTL837X_REG_SEC_COUNTER only holds a written value while SDS 0 is on
reg_read_m(RTL837X_REG_SDS_MODES); reg_read_m(RTL837X_REG_SDS_MODES);
sfr_mask_data(1, 0, 0x03); sfr_mask_data(1, 0xff, 0x03);
sfr_mask_data(0, 0, 0xe2); sfr_mask_data(0, 0xff, 0xe2);
reg_write_m(RTL837X_REG_SDS_MODES); reg_write_m(RTL837X_REG_SDS_MODES);
} }
+2 -2
View File
@@ -271,7 +271,7 @@ void vlan_setup(void) __banked
wait_table_ready(); wait_table_ready();
// Set PVID 1 for every port. TODO: Skip unused ports! // Set PVID 1 for every port. TODO: Skip unused ports!
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT + 1; i++) { // Do this also for the CPU port (+1) for (uint8_t i = MACHINE_MIN_PORT; i <= CPU_PORT; i++) { // CPU MAC is independent of the last user port
uint16_t reg = RTL837x_PVID_BASE_REG + ((i >> 1) << 2); uint16_t reg = RTL837x_PVID_BASE_REG + ((i >> 1) << 2);
#ifdef DEBUG #ifdef DEBUG
print_byte(i); write_char(':'); write_char(' '); print_short(reg); write_char('='); print_byte(i); write_char(':'); write_char(' '); print_short(reg); write_char('=');
@@ -307,7 +307,7 @@ void vlan_setup(void) __banked
REG_SET(RTL837X_VLAN_L2_LRN_DIS_0, 0); REG_SET(RTL837X_VLAN_L2_LRN_DIS_0, 0);
REG_SET(RTL837X_VLAN_L2_LRN_DIS_1, 0); REG_SET(RTL837X_VLAN_L2_LRN_DIS_1, 0);
// Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members // Enable VLAN 1: user ports untagged, CPU MAC9 tagged (untag bit 19 clear)
REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0207ffff : 0x0207e3f8); // 02: Entry valid, 7...: membership REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0207ffff : 0x0207e3f8); // 02: Entry valid, 7...: membership
REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1 REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1
+1 -1
View File
@@ -55,7 +55,7 @@ __sfr __at(0xbb) SFR_BANK_RET;
__sfr __at(0x8e) CKCON; __sfr __at(0x8e) CKCON;
__sfr __at(0x97) SFR_97; // HADDR? __sfr __at(0x97) MAP_FLASH_PAGE_TO_XDATA; // 1 (hardware reset default): xdata 0x4000-0xbfff reads the flash image; 0: RAM
__sfr __at(0xb9) SFR_b9; __sfr __at(0xb9) SFR_b9;
__sfr __at(0xba) SFR_ba; __sfr __at(0xba) SFR_ba;
+23 -2
View File
@@ -305,6 +305,19 @@ void itoa(uint8_t v)
} }
void itoa_no_syslog(uint8_t v)
{
uint8_t t = (v / 100);
uint8_t print_zeros = t;
if (print_zeros)
write_char_no_syslog('0' + t);
t = (v / 10) % 10;
print_zeros |= t;
if (print_zeros)
write_char_no_syslog('0' + t);
write_char_no_syslog('0' + (v % 10));
}
/* Same as itoa(), one decade wider: enough for a port number. Kept separate /* Same as itoa(), one decade wider: enough for a port number. Kept separate
* rather than widening itoa() itself, because every existing caller passes a * rather than widening itoa() itself, because every existing caller passes a
* byte and would start paying for 16-bit divisions it does not need. */ * byte and would start paying for 16-bit divisions it does not need. */
@@ -1203,7 +1216,7 @@ static void handle_tick(void)
lacp_timers(); lacp_timers();
health_phase(HEALTH_PH_STP); health_phase(HEALTH_PH_STP);
lldp_tick(); lldp_tick();
health_phase(HEALTH_LLDP); health_phase(HEALTH_PH_LLDP);
cabletest_tick(); cabletest_tick();
} }
@@ -1554,6 +1567,15 @@ void check_and_flash_update_image(void)
} }
/* Called by the SDCC startup code before it copies XINIT to XISEG and clears XSEG.
* Hardware reset starts us with MAP_FLASH_PAGE_TO_XDATA = 1, which maps xdata
* 0x4000-0xbfff to the flash image (reads return flash, writes are lost), so it
* must be 0 by then. */
unsigned char __sdcc_external_startup(void)
{
MAP_FLASH_PAGE_TO_XDATA = 0;
return 0;
}
void main(void) void main(void)
{ {
@@ -1564,7 +1586,6 @@ void main(void)
sbuf_ptr = 0; sbuf_ptr = 0;
CKCON = 0; // Initial Clock configuration CKCON = 0; // Initial Clock configuration
SFR_97 = 0; // HADDR?
// Set in managed mode: // Set in managed mode:
SFR_b9 = 0x00; SFR_b9 = 0x00;
+15
View File
@@ -0,0 +1,15 @@
https://sourceforge.net/p/sdcc/bugs/4153/
Index: src/mcs51/ralloc.c
===================================================================
--- src/mcs51/ralloc.c (revision 17011)
+++ src/mcs51/ralloc.c (working copy)
@@ -1208,7 +1208,7 @@
int change = 0;
/* if the result has been spilt then cannot share */
- if (opsym->isspilt)
+ if (result->isspilt || opsym->isspilt)
return 0;
for (;;)
+24
View File
@@ -0,0 +1,24 @@
https://sourceforge.net/p/sdcc/bugs/4154/
Index: src/mcs51/ralloc.c
===================================================================
--- src/mcs51/ralloc.c (revision 17011)
+++ src/mcs51/ralloc.c (working copy)
@@ -641,6 +641,7 @@
spillThis (symbol * sym)
{
int i;
+ bool ownsRegs = bitVectBitValue (_G.regAssigned, sym->key);
/* if this is rematerializable or has a spillLocation
we are okay, else we need to create a spillLocation
for it */
@@ -658,7 +659,8 @@
{
if (sym->regs[i])
{
- freeReg (sym->regs[i]);
+ if (ownsRegs)
+ freeReg (sym->regs[i]);
sym->regs[i] = NULL;
}
}
+130
View File
@@ -0,0 +1,130 @@
https://sourceforge.net/p/sdcc/bugs/4070/
Index: sdas/asxxsrc/asexpr.c
===================================================================
--- sdas/asxxsrc/asexpr.c
+++ sdas/asxxsrc/asexpr.c
@@ -863,18 +863,59 @@
return((n & s_mask) ? (n | ~v_mask) : (n & v_mask));
}
-/*)Function void exprmasks(esp)
+/*)Function void exprmasks(n)
*
* int n T Line Bytes in Address
*
* The function exprmasks() configures the assembler
- * for 16, 24, or 32-Bit Data/Addresses.
+ * for 16, 24, or 32-Bit Data/Addresses. The arithmetic
+ * precision follows the address width unless a port
+ * calls valmasks() afterwards.
*
* local variables:
* none
*
* global variables:
* int a_bytes T Line Bytes in Address
+ *
+ * functions called:
+ * void valmasks() asexpr.c
+ *
+ * side effects:
+ * The address width and arithmetic precision
+ * parameters are set.
+ */
+
+void
+exprmasks(int n)
+{
+ switch(n) {
+ default:
+ n = 2;
+ case 2:
+ case 3:
+ case 4:
+ break;
+ }
+ a_bytes = n;
+ valmasks(n);
+}
+
+/*)Function void valmasks(n)
+ *
+ * int n Bytes of arithmetic precision
+ *
+ * The function valmasks() configures the 16, 24, or
+ * 32-Bit arithmetic precision independently of the
+ * T Line address width. A port whose addresses fit
+ * in 24 bits but whose symbols may hold 32-bit values
+ * (the 8051 __sfr32 address lists, for example) uses
+ * exprmasks(3) followed by valmasks(4).
+ *
+ * local variables:
+ * none
+ *
+ * global variables:
* a_uint a_mask Address mask
* a_uint s_mask Sign mask
* a_uint v_mask Value mask
@@ -885,16 +926,13 @@
* side effects:
* The arithmetic precision parameters are set.
*/
-
+
void
-exprmasks(int n)
+valmasks(int n)
{
- a_bytes = n;
-
#ifdef LONGINT
- switch(a_bytes) {
+ switch(n) {
default:
- a_bytes = 2;
case 2:
a_mask = (a_uint) 0x0000FFFFl;
s_mask = (a_uint) 0x00008000l;
@@ -914,9 +952,8 @@
break;
}
#else
- switch(a_bytes) {
+ switch(n) {
default:
- a_bytes = 2;
case 2:
a_mask = (a_uint) 0x0000FFFF;
s_mask = (a_uint) 0x00008000;
Index: sdas/asxxsrc/asxxxx.h
===================================================================
--- sdas/asxxsrc/asxxxx.h
+++ sdas/asxxsrc/asxxxx.h
@@ -1354,6 +1354,7 @@
extern int digit(int c, int r);
extern int is_digit(int c, int r);
extern void exprmasks(int n);
+extern void valmasks(int n);
extern void expr(struct expr *esp, int n);
extern void binop(int c, struct expr *esp, struct expr *re);
extern int is_abs(struct expr *esp);
Index: sdas/as8051/i51mch.c
===================================================================
--- sdas/as8051/i51mch.c
+++ sdas/as8051/i51mch.c
@@ -782,9 +782,12 @@
hilo = 1;
/*
- * Address Space
+ * Address Space: 24-bit addresses in the T lines,
+ * 32-bit arithmetic so that __sfr32 address lists
+ * like 0xa4a5a6a7 keep their most significant byte.
*/
- exprmasks(3);
+ exprmasks(3);
+ valmasks(4);
/*
* First time only:
Executable
+26
View File
@@ -0,0 +1,26 @@
#!/bin/sh
set -eu
V=4.6.0
SHA=5fd6a93e5997ce01756868fe35e441095cfb637894a80c262514a634094973b6
PREFIX=$1
D=$(cd "$(dirname "$0")" && pwd)
W=$(mktemp -d)
trap 'rm -rf "$W"' EXIT
curl -fL -o "$W/sdcc.tar.bz2" "https://downloads.sourceforge.net/project/sdcc/sdcc/$V/sdcc-src-$V.tar.bz2"
if command -v sha256sum >/dev/null; then
echo "$SHA $W/sdcc.tar.bz2" | sha256sum -c -
else
echo "$SHA $W/sdcc.tar.bz2" | shasum -a 256 -c -
fi
tar -C "$W" -xjf "$W/sdcc.tar.bz2"
cd "$W/sdcc-$V"
for p in "$D"/*.patch; do
patch -p0 < "$p"
done
off=""
for p in z80 z180 r2k r2ka r3ka r4k r5k r6k sm83 tlcs90 ez80 ez80_z80 z80n r800 ds390 ds400 pic14 pic16 hc08 s08 stm8 pdk13 pdk14 pdk15 mos6502 mos65c02 huc6280 f8 f8l; do
off="$off --disable-$p-port"
done
./configure --prefix="$PREFIX" $off --disable-sdcdb --disable-ucsim --disable-non-free
make -j"$(getconf _NPROCESSORS_ONLN)"
make install
+128
View File
@@ -0,0 +1,128 @@
/*
* Login sessions of the web interface.
*
* Up to SESSIONS logins at once, each with its own cookie and its own expiry.
* With a single one, logging in from a second browser (or a phone) ended the
* first browser's session at its next request.
*
* This sits in bank 3: the web server's own bank is full, so httpd.c only
* calls in here. It shares three things with httpd.c: the cookie value it
* parsed (session), the time read for the request (now) and the result
* (authenticated); the reply is written to outbuf at slen.
*/
#pragma codeseg BANK3
#pragma constseg BANK3
#include <stdint.h>
#include "rtl837x_common.h"
#include "sessions.h"
extern volatile __xdata uint8_t sfr_data[4];
extern __xdata uint16_t session_timeout;
extern __xdata uint32_t now;
extern __xdata uint8_t authenticated;
extern __xdata uint8_t * __xdata session;
extern __xdata uint8_t outbuf[];
extern __xdata uint16_t slen;
__xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1];
__xdata uint32_t last_session_use[SESSIONS];
static __xdata uint8_t ss_slot, ss_i, ss_oldest, ss_c;
/* The slot whose cookie the last request came with; only meaningful while
* authenticated is set, which only session_check() does */
static __xdata uint8_t ss_hit;
static __code const char ss_hex[] = "0123456789abcdef";
/* What may follow a cookie value: ';' separates cookies in a Cookie header */
static __code const char ss_end[] = " \t:?=\n\r;";
/* Leaf function, so the compiler keeps its 32-bit scratch in the overlay
* rather than in internal RAM. */
static uint8_t ss_stale(void)
{
if (now - last_session_use[ss_slot] > session_timeout)
return 1;
return 0;
}
/* Likewise a leaf: the 32-bit comparison needs no stack of its own this way */
static uint8_t ss_older(void)
{
if (last_session_use[ss_slot] < last_session_use[ss_oldest])
return 1;
return 0;
}
/* Whether the cookie value is the id of slot ss_slot */
static uint8_t ss_match(void)
{
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
if (session[ss_i] != session_id[ss_slot][ss_i])
return 0;
}
ss_c = session[SESSION_ID_LENGTH];
if (!ss_c)
return 1;
for (ss_i = 0; ss_end[ss_i]; ss_i++) {
if (ss_c == ss_end[ss_i])
return 1;
}
return 0;
}
void session_init(void) __banked
{
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++)
session_id[ss_slot][0] = NUL;
}
void session_check(void) __banked
{
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
if (!session_id[ss_slot][0])
continue;
if (ss_stale()) {
session_id[ss_slot][0] = NUL;
} else if (ss_match()) {
authenticated = 1;
ss_hit = ss_slot;
last_session_use[ss_slot] = now;
return;
}
}
}
void session_new(void) __banked
{
if (authenticated && session_id[ss_hit][0]) {
/* The login came with a cookie that is still valid: its slot gets the
* new id. A second slot for the same browser would leave the old one
* looking fresh and push out someone else at the next login. */
ss_slot = ss_hit;
} else {
/* A free or expired slot, else the least recently used */
ss_oldest = 0;
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
if (!session_id[ss_slot][0] || ss_stale())
break;
if (ss_older())
ss_oldest = ss_slot;
}
if (ss_slot == SESSIONS)
ss_slot = ss_oldest;
}
last_session_use[ss_slot] = now;
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
if (!(ss_i & 7))
get_random_32();
ss_c = sfr_data[(ss_i >> 1) & 3];
if (ss_i & 1)
ss_c >>= 4;
session_id[ss_slot][ss_i] = ss_hex[ss_c & 0xf];
outbuf[slen++] = ss_hex[ss_c & 0xf];
}
session_id[ss_slot][SESSION_ID_LENGTH] = NUL;
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef _SESSIONS_H_
#define _SESSIONS_H_
#include <stdint.h>
#define SESSIONS 4
#define SESSION_ID_LENGTH 12
extern __xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1];
extern __xdata uint32_t last_session_use[SESSIONS];
/* No session at boot */
void session_init(void) __banked;
/* Sets authenticated if the cookie in session belongs to a live session */
void session_check(void) __banked;
/* Starts a session at now and writes its id to outbuf at slen */
void session_new(void) __banked;
#endif
+11
View File
@@ -29,6 +29,9 @@ TESTS = $(BUILDDIR)/test_cmd_editor \
$(BUILDDIR)/test_page_json \ $(BUILDDIR)/test_page_json \
$(BUILDDIR)/test_httpd_tx \ $(BUILDDIR)/test_httpd_tx \
$(BUILDDIR)/test_tick_gate \ $(BUILDDIR)/test_tick_gate \
$(BUILDDIR)/test_cpu_vlan_full \
$(BUILDDIR)/test_cpu_vlan_restricted \
$(BUILDDIR)/test_cpu_vlan_8372 \
$(BUILDDIR)/test_nic_rx \ $(BUILDDIR)/test_nic_rx \
$(BUILDDIR)/test_lacp $(BUILDDIR)/test_lacp
@@ -69,6 +72,14 @@ $(BUILDDIR)/test_lacp: test_lacp.c $(SRCROOT)/rtl837x_lacp.c | $(BUILDDIR)
# this test carries the environment below them itself. # this test carries the environment below them itself.
$(BUILDDIR)/test_httpd_tx: test_httpd_tx.c $(SRCROOT)/httpd/httpd.c $(SRCROOT)/uip/uip.c | $(BUILDDIR) $(BUILDDIR)/test_httpd_tx: test_httpd_tx.c $(SRCROOT)/httpd/httpd.c $(SRCROOT)/uip/uip.c | $(BUILDDIR)
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS) $(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
# the login sessions httpd.c calls into
$(BUILDDIR)/test_httpd_tx: $(SRCROOT)/sessions.c
# CPU VLAN initialization with full and reduced user-port ranges.
$(BUILDDIR)/test_cpu_vlan_restricted: CFLAGS += -DMACHINE_MAX_PORT=7
$(BUILDDIR)/test_cpu_vlan_8372: CFLAGS += -DMACHINE_MAX_PORT=7 -DMACHINE_MIN_PORT=3 -DRTLP_TEST_IS8373=0
$(BUILDDIR)/test_cpu_vlan_full $(BUILDDIR)/test_cpu_vlan_restricted $(BUILDDIR)/test_cpu_vlan_8372: test_cpu_vlan.c $(TABLE_ENV) | $(BUILDDIR)
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
run: $(TESTS) run: $(TESTS)
@fail=0; for t in $(TESTS); do \ @fail=0; for t in $(TESTS); do \
+4 -3
View File
@@ -57,15 +57,16 @@ const uint8_t hex_chars[] = "0123456789abcdef";
uint8_t *hex = (uint8_t *)hex_chars; uint8_t *hex = (uint8_t *)hex_chars;
/* ---- a 9-port RTL8373 whose logical ports map to themselves ---- */ /* ---- a 9-port RTL8373 whose logical ports map to themselves ---- */
#ifndef RTLP_TEST_IS8373
#define RTLP_TEST_IS8373 1
#endif
const struct machine machine = { const struct machine machine = {
.machine_name = "HOSTTEST", .machine_name = "HOSTTEST",
.min_port = 0,
.max_port = 8,
.n_sfp = 0, .n_sfp = 0,
.log_to_phys_port = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, .log_to_phys_port = { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
.phys_to_log_port = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, .phys_to_log_port = { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
}; };
struct machine_runtime machine_detected = { .isRTL8373 = 1 }; struct machine_runtime machine_detected = { .isRTL8373 = RTLP_TEST_IS8373 };
/* ---- firmware state the modules read or write ---- */ /* ---- firmware state the modules read or write ---- */
uint8_t outbuf[TCP_OUTBUF_SIZE]; uint8_t outbuf[TCP_OUTBUF_SIZE];
+24
View File
@@ -0,0 +1,24 @@
#include <stdint.h>
#include <stdio.h>
#include "rtl837x_common.h"
#include "rtl837x_port.h"
#include "machine.h"
#include "support.h"
#include "hw_mock.h"
extern const struct machine machine;
extern struct machine_runtime machine_detected;
int main(void) {
hw_reset();out_reset();
hw_reg_set(RTL837X_VLAN_PORT_EGR_TAG,0xa50fffff);
hw_reg_set(RTL837x_PVID_BASE_REG+16,0x5a123456);
vlan_setup();
uint32_t untouched=(1u<<(MACHINE_MIN_PORT*2))-1;
CHECK(hw_reg_get(RTL837X_VLAN_PORT_EGR_TAG)==(0xa5000000|untouched),"CPU egress set, unrelated bits preserved");
CHECK(hw_reg_get(RTL837x_PVID_BASE_REG+16)==0x5a001001,"CPU PVID initialized explicitly");
uint16_t active=((1u<<(MACHINE_MAX_PORT+1))-1)&~((1u<<MACHINE_MIN_PORT)-1);
CHECK(hw_reg_get(RTL837X_VLAN_PORT_IGR_FLTR)==(active|PMASK_CPU),"CPU ingress included, excluded user ports rejected");
CHECK(hw_vlan_word(1)==(machine_detected.isRTL8373?0x0207ffff:0x0207e3f8),"existing family VLAN membership preserved");
CHECK(hw_vlan_word(2)==0,"no unrelated VLAN changed");
printf("CPU VLAN min=%u max=%u RTL8373=%u: %d checks, %d failed\n",MACHINE_MIN_PORT,MACHINE_MAX_PORT,machine_detected.isRTL8373,tests_run,tests_failed);
return tests_failed?1:0;
}
+273 -11
View File
@@ -1,11 +1,10 @@
/* /*
* Host-side bench for the httpd transmit path. * Host-side bench for the httpd transmit and firmware upload paths.
* *
* Compiles the UNMODIFIED httpd/httpd.c and uip/uip.c and drives them through a * Compiles the UNMODIFIED httpd/httpd.c and uip/uip.c and drives them through a
* real handshake and a real GET from a client that moves its receive window. * real handshake, GETs with moving receive windows, and multipart POSTs.
* Everything below the two modules - flash, console, JSON pages - is mocked * Everything below the two modules - flash, console, JSON pages - is mocked
* here, so what the bench observes is the byte stream the firmware would put on * here, so the bench observes the wire response and the bytes staged to flash.
* the wire.
* *
* The file served out of the simulated flash carries a position-dependent * The file served out of the simulated flash carries a position-dependent
* pattern, so a duplicated or skipped range shows up as a mismatch at a known * pattern, so a duplicated or skipped range shows up as a mismatch at a known
@@ -82,6 +81,9 @@ static uint8_t written_config[CONFIG_LEN];
static uint16_t written_config_len; static uint16_t written_config_len;
static uint32_t config_erase_addr, config_write_addr; static uint32_t config_erase_addr, config_write_addr;
static int config_erases, config_writes; static int config_erases, config_writes;
static uint8_t flash_written[1024];
static size_t flash_written_len;
static int flash_write_bad;
const char * const mime_strings[] = { "text/html", "image/svg+xml", "image/x-icon", const char * const mime_strings[] = { "text/html", "image/svg+xml", "image/x-icon",
"image/png", "text/javascript", "text/css", "text/plain" }; "image/png", "text/javascript", "text/css", "text/plain" };
@@ -107,11 +109,21 @@ void flash_sector_erase(void)
void flash_write_bytes(uint8_t *ptr) void flash_write_bytes(uint8_t *ptr)
{ {
config_writes++; if (flash_region.addr < FIRMWARE_UPLOAD_START) {
config_write_addr = flash_region.addr; config_writes++;
written_config_len = flash_region.len; config_write_addr = flash_region.addr;
if (written_config_len <= sizeof(written_config)) written_config_len = flash_region.len;
memcpy(written_config, ptr, written_config_len); if (written_config_len <= sizeof(written_config))
memcpy(written_config, ptr, written_config_len);
return;
}
if (flash_region.addr != FIRMWARE_UPLOAD_START + flash_written_len ||
flash_region.len > sizeof(flash_written) - flash_written_len) {
flash_write_bad = 1;
return;
}
memcpy(flash_written + flash_written_len, ptr, flash_region.len);
flash_written_len += flash_region.len;
} }
const char *get_flash_size_str(void) { return "512 kB"; } const char *get_flash_size_str(void) { return "512 kB"; }
void crc16_bank1(uint8_t *v) { (void)v; } void crc16_bank1(uint8_t *v) { (void)v; }
@@ -120,7 +132,9 @@ void reset_chip(void) { reset_calls++; }
void delay(uint16_t t) { (void)t; } void delay(uint16_t t) { (void)t; }
void write_char(char c) { (void)c; } void write_char(char c) { (void)c; }
void write_char_no_syslog(char c) { (void)c; } void write_char_no_syslog(char c) { (void)c; }
void itoa_no_syslog(uint8_t v) { (void)v; }
void print_string(const char *p) { (void)p; } void print_string(const char *p) { (void)p; }
void print_string_no_syslog(const char *p) { (void)p; }
void print_string_newline_no_syslog(const char *p) { (void)p; } void print_string_newline_no_syslog(const char *p) { (void)p; }
void set_sys_led_state(uint8_t state) { (void)state; } void set_sys_led_state(uint8_t state) { (void)state; }
void cmd_parser(void) { } void cmd_parser(void) { }
@@ -130,7 +144,8 @@ void clear_command_history(void) { }
void udp_callbacks(void) { } void udp_callbacks(void) { }
void tcpip_output(void) { } void tcpip_output(void) { }
void get_random_32(void) { } void get_random_32(void) { }
void read_reg_timer(uint32_t *tmr) { *tmr = 0; } static uint32_t fake_now;
void read_reg_timer(uint32_t *tmr) { *tmr = fake_now; }
/* uip-conf.h asks uIP not to define the packet buffer: on the switch it lives /* uip-conf.h asks uIP not to define the packet buffer: on the switch it lives
* at a fixed XDATA address, so the bench provides the storage itself. */ * at a fixed XDATA address, so the bench provides the storage itself. */
@@ -179,7 +194,9 @@ void send_lldp(void) {}
extern uint8_t authenticated; extern uint8_t authenticated;
extern char session_id[]; extern char session_id[];
extern uint32_t last_session_use[];
extern char passwd[21]; extern char passwd[21];
extern uint16_t session_timeout;
/* uip.c defines the listen table but no header declares it; the bench reads it /* uip.c defines the listen table but no header declares it; the bench reads it
* to confirm the port httpd_init() asked for is the one uIP is watching. */ * to confirm the port httpd_init() asked for is the one uIP is watching. */
@@ -535,7 +552,7 @@ static void scenario_bad_l4_checksum(void)
/* Private HTTP lifecycle values; drive the real callback, not a model. */ /* Private HTTP lifecycle values; drive the real callback, not a model. */
enum { TSTATE_NONE = 0, TSTATE_TX = 1 }; enum { TSTATE_NONE = 0, TSTATE_TX = 1, TSTATE_POST = 4 };
extern uint16_t slen, cont_len, sent_len, bindex, crc_final, write_len; extern uint16_t slen, cont_len, sent_len, bindex, crc_final, write_len;
extern uint8_t boundary[], verify_crc, outbuf[]; extern uint8_t boundary[], verify_crc, outbuf[];
extern struct { uint8_t *p; uint16_t bptr, plen; } upload_settings; extern struct { uint8_t *p; uint16_t bptr, plen; } upload_settings;
@@ -863,6 +880,77 @@ static void scenario_split_get_header_terminator(void)
"fragmented GET terminator: CRLFCRLF may span receive events"); "fragmented GET terminator: CRLFCRLF may span receive events");
} }
/* The sessions of the login tests below: four in use, slot 1 the least recently used */
#define ID_STRIDE 13 /* SESSION_ID_LENGTH + 1 */
static const char *slot_id(int i)
{
return session_id + i * ID_STRIDE;
}
static void fill_sessions(void)
{
static const char *const ids[4] = { "0123456789ab", "aaaaaaaaaaaa", "bbbbbbbbbbbb", "cccccccccccc" };
static const uint32_t used[4] = { 50, 10, 60, 70 };
fake_now = 100;
for (int i = 0; i < 4; i++) {
strcpy(session_id + i * ID_STRIDE, ids[i]);
last_session_use[i] = used[i];
}
}
static int others_kept(int first, int second, int third)
{
static const char *const ids[4] = { "0123456789ab", "aaaaaaaaaaaa", "bbbbbbbbbbbb", "cccccccccccc" };
return !strcmp(slot_id(first), ids[first]) && !strcmp(slot_id(second), ids[second]) &&
!strcmp(slot_id(third), ids[third]);
}
static void scenario_login_with_valid_cookie_reuses_its_slot(void)
{
static const char post[] =
"POST /login HTTP/1.1\r\n"
"Cookie: session=0123456789ab\r\n"
"Content-Length: 8\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"\r\n"
"pwd=1234";
login_session_start();
fill_sessions();
client_send(TCP_ACK | TCP_PSH, post, (int)strlen(post));
CHECK(stream_contains("Location: index.html\r\n"),
"login with a valid cookie: accepted");
CHECK(strcmp(slot_id(0), "0123456789ab") != 0,
"login with a valid cookie: its own slot gets the new id");
CHECK(others_kept(1, 2, 3),
"login with a valid cookie: nobody else is pushed out");
fake_now = 0;
}
static void scenario_login_without_cookie_replaces_least_recently_used(void)
{
static const char post[] =
"POST /login HTTP/1.1\r\n"
"Content-Length: 8\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"\r\n"
"pwd=1234";
login_session_start();
fill_sessions();
client_send(TCP_ACK | TCP_PSH, post, (int)strlen(post));
CHECK(stream_contains("Location: index.html\r\n"),
"login without a cookie: accepted");
CHECK(strcmp(slot_id(1), "aaaaaaaaaaaa") != 0,
"login without a cookie: the least recently used slot is taken");
CHECK(others_kept(0, 2, 3),
"login without a cookie: the other three stay");
fake_now = 0;
}
static void scenario_fragmented_get_without_cookie(void) static void scenario_fragmented_get_without_cookie(void)
{ {
static const char first[] = static const char first[] =
@@ -1012,6 +1100,171 @@ static void scenario_config_upload(void)
"multipart/config: every split writes the exact configuration and NUL to its sector"); "multipart/config: every split writes the exact configuration and NUL to its sector");
} }
/* Requiring octet-stream would leave alternate or absent MIME types buffered
* as headers forever instead of staging the binary and checking its CRC. */
static void scenario_upload(const char *name, const char *part_type, int split)
{
static const uint8_t payload[] = { 0x00, 0xff, 'R', '\r', '\n', '-', 0x80, 0x00 };
static const uint8_t staged[] = { 0x00, 0xff, 'R', '\r', '\n', '-', 0x80, 0x00, 0x00 };
static const char closing[] = "\r\n--upload-boundary--\r\n";
char preamble[256], request[512];
uint32_t saved_flash_size = flash_size;
int preamble_len, header_len, request_len;
printf("\n-- upload: %s --\n", name);
session_start(MSS_FULL);
strcpy(session_id, "0123456789ab");
session_timeout = 200;
last_session_use[0] = 0; /* read_reg_timer() returns zero in this bench. */
flash_size = 0x100000;
flash_written_len = 0;
flash_write_bad = 0;
reset_calls = 0;
preamble_len = snprintf(preamble, sizeof(preamble),
"--upload-boundary\r\n"
"Content-Disposition: form-data; name=\"file\"; filename=\"firmware.bin\"\r\n"
"%s\r\n", part_type);
header_len = snprintf(request, sizeof(request),
"POST /upload HTTP/1.1\r\nHost: sw\r\n"
"Cookie: session=0123456789ab\r\n"
"Content-Type: multipart/form-data; boundary=upload-boundary\r\n"
"Content-Length: %zu\r\n\r\n%s",
(size_t)preamble_len + sizeof(payload) + sizeof(closing) - 1, preamble);
memcpy(request + header_len, payload, sizeof(payload));
memcpy(request + header_len + sizeof(payload), closing, sizeof(closing) - 1);
request_len = header_len + sizeof(payload) + sizeof(closing) - 1;
if (split) {
/* Cut the part's CRLFCRLF between its final CR and LF. */
client_send(TCP_ACK | TCP_PSH, request, header_len - 1);
CHECK(stream_len == 0 && flash_written_len == 0,
"upload: incomplete part headers stage nothing and await more data");
client_send(TCP_ACK | TCP_PSH, request + header_len - 1,
request_len - header_len + 1);
} else {
client_send(TCP_ACK | TCP_PSH, request, request_len);
}
drain(MSS_FULL);
CHECK(!flash_write_bad && flash_written_len == sizeof(staged) &&
!memcmp(flash_written, staged, sizeof(staged)),
"upload: exact binary payload plus zero terminator staged at image address");
CHECK(stream_len >= 12 && !memcmp(stream, "HTTP/1.1 400", 12),
"upload: invalid firmware is rejected with HTTP 400");
CHECK(!uip_conn->appstate.fw_reset_pending && reset_calls == 0,
"upload: deliberately invalid CRC never schedules or triggers reset");
flash_size = saved_flash_size;
}
extern uint32_t uptr;
#define UPLOAD_LEN 600
#define UPLOAD_BOUNDARY "\r\n--XyZ"
#define UPLOAD_TRAILER UPLOAD_BOUNDARY "--\r\n"
static int upload_in_two(uint8_t *body, uint16_t body_len, uint16_t split)
{
uptr = FIRMWARE_UPLOAD_START;
write_len = 0;
bindex = 0;
verify_crc = 0;
flash_written_len = 0;
flash_write_bad = 0;
strcpy((char *)boundary, UPLOAD_BOUNDARY);
uip_conn = &uip_conns[0];
uip_conn->appstate.tstate = TSTATE_POST;
upload_settings.p = body;
upload_settings.bptr = 0;
upload_settings.plen = split;
stream_upload();
if (uip_conn->appstate.tstate == TSTATE_POST) {
upload_settings.p = body + split;
upload_settings.bptr = 0;
upload_settings.plen = body_len - split;
stream_upload();
}
return uip_conn->appstate.tstate == TSTATE_NONE
&& uptr == FIRMWARE_UPLOAD_START + UPLOAD_LEN && !flash_write_bad
&& flash_written_len == UPLOAD_LEN + 1 && !flash_written[UPLOAD_LEN];
}
static void scenario_upload_cr_before_boundary(void)
{
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
uint16_t body_len, split;
int completed = 0, intact = 0, splits = 0;
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
body[i] = pattern(i);
body[UPLOAD_LEN - 2] = 'x';
body[UPLOAD_LEN - 1] = '\r';
body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
for (split = 0; split <= body_len; split++) {
splits++;
if (upload_in_two(body, body_len, split)) {
completed++;
if (!memcmp(flash_written, body, UPLOAD_LEN))
intact++;
}
}
if (verbose)
printf(" %d of %d splits complete, %d intact\n", completed, splits, intact);
CHECK(completed == splits,
"upload: an image ending in CR still finds the closing boundary");
CHECK(intact == splits,
"upload: the CR and the bytes before it reach the flash unchanged");
}
static int upload_whole_and_split(uint8_t *body, uint16_t body_len)
{
return upload_in_two(body, body_len, body_len) && !memcmp(flash_written, body, UPLOAD_LEN)
&& upload_in_two(body, body_len, UPLOAD_LEN) && !memcmp(flash_written, body, UPLOAD_LEN);
}
static void scenario_upload_last_byte(void)
{
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
uint16_t body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
int bad = -1;
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
body[i] = pattern(i);
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
for (int last = 0; last < 256 && bad < 0; last++) {
body[UPLOAD_LEN - 1] = last;
if (!upload_whole_and_split(body, body_len))
bad = last;
}
if (bad >= 0)
printf(" last byte 0x%02x loses the closing boundary\n", bad);
CHECK(bad < 0, "upload: the part ends after the payload whatever its last byte");
body[UPLOAD_LEN - 1] = '\r';
body[UPLOAD_LEN - 2] = '\r';
CHECK(upload_whole_and_split(body, body_len), "upload: an even run of CR before the boundary");
body[UPLOAD_LEN - 3] = '\r';
CHECK(upload_whole_and_split(body, body_len), "upload: an odd run of CR before the boundary");
}
static void scenario_upload_broken_matches(void)
{
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
uint16_t body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
memset(body, '\r', UPLOAD_LEN);
CHECK(upload_whole_and_split(body, body_len),
"upload: a run of CR longer than a flash page is staged intact");
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
body[i] = "\r\n-"[i % 3];
CHECK(upload_whole_and_split(body, body_len),
"upload: repeated partial delimiters are staged as payload");
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc > 1 && !strcmp(argv[1], "-v")) if (argc > 1 && !strcmp(argv[1], "-v"))
@@ -1040,6 +1293,15 @@ int main(int argc, char **argv)
scenario_oversized_get_header(); scenario_oversized_get_header();
scenario_split_method_names(); scenario_split_method_names();
scenario_config_upload(); scenario_config_upload();
scenario_upload("octet-stream control", "Content-Type: application/octet-stream\r\n", 0);
scenario_upload("MacBinary, split part terminator", "Content-Type: application/macbinary\r\n", 1);
scenario_upload("no part Content-Type", "", 0);
scenario_upload_cr_before_boundary();
scenario_upload_last_byte();
scenario_upload_broken_matches();
scenario_login_with_valid_cookie_reuses_its_slot();
scenario_login_without_cookie_replaces_least_recently_used();
printf("\n%s (%d failure%s)\n", printf("\n%s (%d failure%s)\n",
failures ? "BENCH: FAILURES" : "BENCH: ALL PASS", failures ? "BENCH: FAILURES" : "BENCH: ALL PASS",
+5 -2
View File
@@ -14,8 +14,11 @@
uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE]; uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
uint16_t uip_len; uint16_t uip_len;
struct uip_eth_addr uip_ethaddr = { .addr = {0x02,0x11,0x22,0x33,0x44,0x55} }; struct uip_eth_addr uip_ethaddr = { .addr = {0x02,0x11,0x22,0x33,0x44,0x55} };
const struct machine machine = { .min_port = 0, .max_port = 7, const struct machine machine = {
.log_to_phys_port = {5, 1, 2, 3, 4, 6, 7, 8, 9} }; .log_to_phys_port = {5, 1, 2, 3, 4, 6, 7, 8, 9} };
/* Logical ports of the mock machine above */
#define TEST_MIN_PORT 0
#define TEST_MAX_PORT 7
struct machine_runtime machine_detected = { .isRTL8373 = 1 }; struct machine_runtime machine_detected = { .isRTL8373 = 1 };
uint8_t lacpEnabled; uint8_t lacpEnabled;
uint16_t management_vlan; /* mocked; slow-protocol path zeroes it per-frame */ uint16_t management_vlan; /* mocked; slow-protocol path zeroes it per-frame */
@@ -216,7 +219,7 @@ int main(int argc, char **argv)
lacp_lag_set(0, 0x00ff); lacp_lag_set(0, 0x00ff);
step(8); step(8);
int all_tx = 1, sane = 1; int all_tx = 1, sane = 1;
for (int i = machine.min_port; i <= machine.max_port; i++) { for (int i = TEST_MIN_PORT; i <= TEST_MAX_PORT; i++) {
if (!tx_count[i]) all_tx = 0; if (!tx_count[i]) all_tx = 0;
if (last_tx[i].ethertype != HTONS(0x8809) || last_tx[i].subtype != 1 if (last_tx[i].ethertype != HTONS(0x8809) || last_tx[i].subtype != 1
|| last_tx[i].dst[5] != 0x02 || last_tx[i].dst[5] != 0x02
+19 -1
View File
@@ -98,6 +98,7 @@
#include "../rtl837x_regs.h" #include "../rtl837x_regs.h"
extern __xdata uint8_t rx_headers[16]; extern __xdata uint8_t rx_headers[16];
extern volatile __xdata uint32_t ticks;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Variable definitions. */ /* Variable definitions. */
@@ -243,6 +244,9 @@ __xdata struct uip_stats uip_stat;
#if UIP_LOGGING == 1 #if UIP_LOGGING == 1
#define UIP_LOG(m) print_string_newline_no_syslog(m); #define UIP_LOG(m) print_string_newline_no_syslog(m);
#define FRAGDROP_WINDOW (5 * SYS_TICK_HZ)
static __xdata u16_t fragdrop_mark;
static __xdata u8_t fragdrop_count;
#else #else
#define UIP_LOG(m) #define UIP_LOG(m)
#endif /* UIP_LOGGING == 1 */ #endif /* UIP_LOGGING == 1 */
@@ -710,6 +714,17 @@ uip_process(u8_t flag) __banked
/* Check if we were invoked because of the perodic timer fireing. */ /* Check if we were invoked because of the perodic timer fireing. */
} else if(flag == UIP_TIMER) { } else if(flag == UIP_TIMER) {
// print_string("T1\n"); // print_string("T1\n");
#if UIP_LOGGING == 1
if(fragdrop_count && (u16_t)ticks - fragdrop_mark >= FRAGDROP_WINDOW) {
print_string_no_syslog("ip: fragments dropped: ");
itoa_no_syslog(fragdrop_count);
if(fragdrop_count == 0xff)
write_char_no_syslog('+');
write_char_no_syslog('\n');
fragdrop_mark = (u16_t)ticks;
fragdrop_count = 0;
}
#endif /* UIP_LOGGING == 1 */
#if UIP_IDLE_TIMEOUT #if UIP_IDLE_TIMEOUT
if(uip_connr == uip_conns) { if(uip_connr == uip_conns) {
uip_idle_age = 0; uip_idle_age = 0;
@@ -915,7 +930,10 @@ uip_process(u8_t flag) __banked
#else /* UIP_REASSEMBLY */ #else /* UIP_REASSEMBLY */
UIP_STAT(++uip_stat.ip.drop); UIP_STAT(++uip_stat.ip.drop);
UIP_STAT(++uip_stat.ip.fragerr); UIP_STAT(++uip_stat.ip.fragerr);
UIP_LOG("ip: fragment dropped."); #if UIP_LOGGING == 1
if(fragdrop_count != 0xff)
++fragdrop_count;
#endif /* UIP_LOGGING == 1 */
goto drop; goto drop;
#endif /* UIP_REASSEMBLY */ #endif /* UIP_REASSEMBLY */
} }