Merge branch 'logicog:main' into main

This commit is contained in:
dobodu authored and GitHub committed 2026-10-11 21:38:57 +02:00
commit 8790f0dca4
24 files changed
+765 -15

No files matched your search

+3
View File
@@ -69,6 +69,8 @@ SRCS = \
rtlplayground.c \
boot.c \
sfp.c \
dns.c \
dns_cli.c \
syslog.c \
udp_apps.c
@@ -84,6 +86,7 @@ SRCS += \
rtl837x_pins.c\
rtl837x_port.c \
rtl837x_stp.c \
rtl837x_lldp.c \
rtl837x_storm.c
SRCS += \
httpd/httpd.c \
+22
View File
@@ -16,10 +16,12 @@
#include "rtl837x_stp.h"
#include "rtl837x_igmp.h"
#include "rtl837x_bandwidth.h"
#include "rtl837x_lldp.h"
#include "rtl837x_storm.h"
#include "sfp.h"
#include "dhcp.h"
#include "syslog.h"
#include "dns.h"
#include "uip/uip.h"
#include "version.h"
@@ -33,6 +35,7 @@
extern __code const struct machine machine;
extern __xdata bool stp_enabled;
extern __code const uint8_t log_to_phys_port[9];
extern __xdata bool lldp_enabled;
extern volatile __xdata uint32_t ticks;
extern volatile __xdata uint8_t sfr_data[4];
@@ -880,6 +883,7 @@ void parse_port(void)
cmd_error("\nUsage:" \
"\nport <port> [show|on|off]" \
"\nport <port> [10m|100m|1g|2g5|duplex] [half|full]" \
"\nport <port> lldp [block|permit]" \
"\nport <port> name [custom port name]\n");
return;
}
@@ -912,6 +916,14 @@ void parse_port(void)
print_string("\nName set to: \"");
print_string_x(port_names[phy_settings.port]);
print_string("\"\n");
} else if (cmd_compare(2, "lldp")) {
if (cmd_compare(3, "block")){
lldp_logical_port_status &= ~((uint16_t)1 << phy_settings.port);
} else if (cmd_compare(3, "permit")) {
lldp_logical_port_status |= (uint16_t)1 << phy_settings.port;
} else {
print_string ("Unknown port <port> lldp [block|permit] command\n");
}
} else if (machine.is_sfp[phy_settings.port]) {
print_string(" is SFP no PHY information available.\n");
} else if (cmd_compare(2, "10m")) {
@@ -1855,6 +1867,8 @@ void cmd_parser(void) __banked
parse_mtu();
} else if (cmd_compare(0, "syslog")) {
parse_syslog();
} else if (cmd_compare(0, "dns")) {
dns_parse();
} else if (cmd_compare(0, "ip")) {
if (cmd_compare(1, "dhcp")) {
dhcp_start();
@@ -2027,6 +2041,14 @@ void cmd_parser(void) __banked
parse_ingress();
} else if (cmd_compare(0, "session")) {
parse_session();
} else if (cmd_compare(0, "lldp")) {
if (cmd_compare(1, "on")) {
print_string("LLDP enabled\n");
lldp_enabled = 1;
} else {
print_string("LLDP disabled\n");
lldp_enabled = 0;
}
}
else {
cmd_error("Unknown command\n");
+1
View File
@@ -20,6 +20,7 @@ extern __xdata uint8_t err_status;
#define HEALTH_PH_STP 4
#define HEALTH_PH_CMD 5
#define HEALTH_PHASES 6
#define HEALTH_LLDP 7
extern __xdata uint16_t health_rx_frames;
#define health_rx_frame() health_rx_frames++
+238
View File
@@ -0,0 +1,238 @@
/*
* DNS resolver for the RTL837x platform
* This code is in the Public Domain
*/
#include <stdint.h>
#include "rtl837x_common.h"
#include "cmd_parser.h"
#include "uip/uip.h"
#include "dhcp.h"
#include "dns.h"
#pragma codeseg BANK3
#pragma constseg BANK3
#define DNS_PORT 53
#define DNS_TRIES 6
#define DNS_TIMEOUT (2 * SYS_TICK_HZ)
extern __xdata struct dhcp_state dhcp_state;
extern volatile __xdata uint32_t ticks;
__xdata struct dns_state dns_state;
__xdata uip_ipaddr_t dns_ip;
uint8_t * __xdata dns_cand[3];
static uint8_t dns_used(__xdata uint8_t *a) __reentrant
{
return a[0] | a[1] | a[2] | a[3];
}
static uint8_t dns_name_is_ip(void) __reentrant
{
__xdata char *s = dns_state.name;
uint8_t i, n;
uint16_t v;
for (i = 0; i < 4; i++) {
v = 0;
n = 0;
while (*s >= '0' && *s <= '9') {
v = v * 10 + (*s++ - '0');
if (++n > 3 || v > 255)
return 0;
}
if (!n)
return 0;
dns_state.addr[i] = v;
if (i < 3 && *s++ != '.')
return 0;
}
return !*s;
}
static void dns_next(void) __reentrant
{
uint8_t k = 0;
if (dns_state.conn) {
uip_udp_remove(dns_state.conn);
dns_state.conn = 0;
}
if (dns_used(dns_state.server[0]))
dns_cand[k++] = dns_state.server[0];
if (dns_used(dns_state.server[1]))
dns_cand[k++] = dns_state.server[1];
if (dns_used(dhcp_state.dns))
dns_cand[k++] = dhcp_state.dns;
if (!k || dns_state.tries >= DNS_TRIES) {
dns_state.status = DNS_FAILED;
if (dns_state.verbose)
print_string(k ? "DNS: no answer\n" : "DNS: no server\n");
return;
}
k = dns_state.tries % k;
uip_ipaddr(dns_ip, dns_cand[k][0], dns_cand[k][1], dns_cand[k][2], dns_cand[k][3]);
dns_state.conn = uip_udp_new(&dns_ip, HTONS(DNS_PORT));
if (!dns_state.conn) {
dns_state.status = DNS_FAILED;
print_string("DNS: no free UDP connection\n");
return;
}
dns_state.send = 1;
}
static void dns_query(void) __reentrant
{
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
__xdata char *n = dns_state.name;
__xdata uint8_t *label;
uint16_t len = 12;
dns_state.id++;
p[0] = dns_state.id >> 8;
p[1] = dns_state.id;
p[2] = 0x01;
p[3] = 0x00;
p[4] = 0;
p[5] = 1;
p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
while (*n) {
label = p + len++;
*label = 0;
while (*n && *n != '.') {
p[len++] = *n++;
(*label)++;
}
if (*n)
n++;
}
p[len++] = 0;
p[len++] = 0;
p[len++] = 1;
p[len++] = 0;
p[len++] = 1;
uip_udp_send(len);
}
static uint16_t dns_skip_name(__xdata uint8_t *p, uint16_t i, uint16_t len) __reentrant
{
while (i < len) {
if (!p[i])
return i + 1;
if ((p[i] & 0xc0) == 0xc0)
return i + 2;
i += p[i] + 1;
}
return len;
}
static void dns_answer(void) __reentrant
{
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
uint16_t len = uip_datalen();
uint16_t i, an;
if (dns_state.status != DNS_PENDING || len < 12 || !(p[2] & 0x80)
|| p[0] != (uint8_t)(dns_state.id >> 8) || p[1] != (uint8_t)dns_state.id)
return;
an = ((uint16_t)p[6] << 8) | p[7];
i = dns_skip_name(p, 12, len) + 4;
while (!(p[3] & 0x0f) && an-- && i + 10 <= len) {
i = dns_skip_name(p, i, len);
if (i + 10 > len)
break;
if (!p[i] && p[i + 1] == 1 && !p[i + 8] && p[i + 9] == 4 && i + 14 <= len) {
memcpy(dns_state.addr, p + i + 10, 4);
dns_state.status = DNS_DONE;
uip_udp_remove(dns_state.conn);
dns_state.conn = 0;
if (dns_state.verbose) {
print_string("DNS: ");
print_string_x(dns_state.name);
print_string(" is ");
print_ip(dns_state.addr);
write_char('\n');
}
return;
}
i += 10 + (((uint16_t)p[i + 8] << 8) | p[i + 9]);
}
dns_state.tries = DNS_TRIES;
dns_next();
}
void dns_init(void) __banked
{
memset(dns_state.server, 0, sizeof(dns_state.server));
dns_state.name[0] = 0;
dns_state.status = DNS_IDLE;
dns_state.conn = 0;
}
void dns_lookup(void) __banked
{
if (dns_name_is_ip()) {
dns_state.status = DNS_DONE;
return;
}
dns_state.tries = 0;
dns_state.status = DNS_PENDING;
dns_next();
}
void dns_show(void) __banked
{
print_string("DNS servers ");
print_ip(dns_state.server[0]);
write_char(' ');
print_ip(dns_state.server[1]);
print_string(", from DHCP ");
print_ip(dhcp_state.dns);
write_char('\n');
if (!dns_state.name[0])
return;
print_string("Last lookup ");
print_string_x(dns_state.name);
print_string(": ");
if (dns_state.status == DNS_DONE)
print_ip(dns_state.addr);
else if (dns_state.status == DNS_PENDING)
print_string("pending");
else
print_string("failed");
write_char('\n');
}
void dns_callback(uint16_t lport) __banked __reentrant
{
if (!dns_state.conn || lport != dns_state.conn->lport)
return;
if (uip_newdata()) {
dns_answer();
return;
}
if (dns_state.status != DNS_PENDING)
return;
if (dns_state.send) {
dns_state.send = 0;
dns_state.sent = ticks;
dns_query();
return;
}
if (ticks - dns_state.sent >= DNS_TIMEOUT) {
dns_state.tries++;
dns_next();
}
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef _DNS_H_
#define _DNS_H_
#include <stdint.h>
#define DNS_NAME_LEN 48
#define DNS_IDLE 0
#define DNS_PENDING 1
#define DNS_DONE 2
#define DNS_FAILED 3
struct uip_udp_conn;
struct dns_state {
uint8_t server[2][4]; /* configured servers, 0.0.0.0 = unused */
char name[DNS_NAME_LEN]; /* name of the current or last lookup */
uint8_t addr[4]; /* address found by the last lookup */
uint8_t status; /* DNS_* */
uint8_t verbose; /* print the result of the lookup */
uint8_t tries; /* queries sent for the current lookup */
uint8_t send; /* a query is due on the connection */
uint16_t id;
uint32_t sent; /* ticks when the last query went out */
struct uip_udp_conn *conn;
};
extern __xdata struct dns_state dns_state;
void dns_init(void) __banked;
void dns_lookup(void) __banked;
void dns_show(void) __banked;
void dns_callback(uint16_t lport) __banked __reentrant;
void dns_parse(void) __banked __reentrant;
#endif
+63
View File
@@ -0,0 +1,63 @@
/*
* Command line interface of the DNS resolver for the RTL837x platform
* This code is in the Public Domain
*/
#include <stdint.h>
#include "rtl837x_common.h"
#include "cmd_parser.h"
#include "dns.h"
#pragma codeseg BANK2
#pragma constseg BANK2
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
extern __xdata uint8_t cmd_words_len;
extern __xdata uint8_t cmd_words_b[15];
extern __xdata uint8_t ip[4];
uint8_t cmd_compare(uint8_t start, __code const uint8_t * cmd);
uint8_t parse_ip(uint8_t idx);
void dns_parse(void) __banked __reentrant
{
__xdata uint8_t *s;
uint8_t n;
if (cmd_words_len < 2) {
dns_show();
return;
}
if (cmd_compare(1, "server") && cmd_words_len >= 3 && parse_ip(cmd_words_b[2])) {
memcpy(dns_state.server[0], ip, 4);
memset(dns_state.server[1], 0, 4);
if (cmd_words_len >= 4) {
if (!parse_ip(cmd_words_b[3]))
goto err;
memcpy(dns_state.server[1], ip, 4);
}
return;
}
if (cmd_compare(1, "lookup") && cmd_words_len >= 3) {
if (dns_state.status == DNS_PENDING) {
print_string("DNS: a lookup is already running\n");
return;
}
s = &cmd_buffer[cmd_words_b[2]];
for (n = 0; s[n] && s[n] != ' '; n++) {
if (n == DNS_NAME_LEN - 1)
goto err;
dns_state.name[n] = s[n];
}
dns_state.name[n] = 0;
dns_state.verbose = 1;
dns_lookup();
if (dns_state.status == DNS_DONE) {
print_ip(dns_state.addr);
write_char('\n');
}
return;
}
err:
print_string("Error: dns [server <ip> [<ip>]|lookup <name>]\n");
}
+23
View File
@@ -0,0 +1,23 @@
# DNS
The switch can resolve host names to IPv4 addresses, for the NTP client and
for anything else that is given a name instead of an address.
```
dns server <ip> [<ip>] # one or two servers, 0.0.0.0 removes them
dns lookup <name> # resolve a name and print the address
dns # show the servers and the last lookup
```
The answer to a lookup arrives after the command has returned, so it is
printed on the serial console and in the syslog. In the console of the web
interface, run `dns` afterwards to see it.
Without a configured server the resolver uses the one handed out by DHCP.
Only address records are looked up; a query goes to the servers in turn,
every two seconds, and gives up after six tries. A name that already is a
dotted address is taken as it is, without a query.
The DNS servers are set in the Network card of the System page as well.
Settings made there or on the console are kept only once the configuration is
saved.
+75 -9
View File
@@ -3,7 +3,7 @@ var LANG={
en:{
nav_dash:"Dashboard",nav_ports:"Ports",nav_stp:"Spanning tree",nav_stats:"Statistics",
nav_vlan:"VLANs",nav_l2:"MAC table",nav_mirror:"Mirroring",nav_isolate:"Isolation",nav_lag:"LAG",nav_eee:"EEE",
nav_bw:"Bandwidth",nav_system:"System",nav_fw:"Firmware",
nav_bw:"Bandwidth",nav_system:"System",nav_fw:"Firmware",nav_lldp:"LLDP",
hdr_dirty:"unsaved changes",hdr_dirty_t:"Running config differs from startup config",
hdr_save:"Save to flash",hdr_save_t:"Persist running configuration to flash",
th_auto:"System",th_auto_sel:"System (Selenized)",sy_display:"Display",sy_theme:"Theme",sy_display_note:"Stored in this browser only.",th_light:"Light",th_dark:"Dark",
@@ -40,6 +40,7 @@ stp_en_q:"Enable spanning tree?",
stp_en_d:"Ports start blocked and take up to twice the forward delay to reach forwarding; edge ports recover immediately.",
stp_dis_q:"Disable spanning tree?",stp_dis_d:"All ports go straight to forwarding; loop protection is lost.",
stp_cost_err:"Path cost must be 0-200000000",
lldp_enabled: "LLDP Enabled",lldp_title:"Service Status",lldp_portname:"Name",lldp_enable:"Send Frames",
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_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed to load counters",
@@ -89,7 +90,7 @@ sy_ip_err:"Invalid IP / netmask / gateway",sy_host_err:"Hostname: 1-23 printable
sy_net_q:"Apply network settings?",sy_net_d:"The management IP changes to {ip}: this page will need to be reopened there.",
sy_ip_changed:"IP changed, reconnect at http://{ip}/",sy_dhcp_q:"Switch to DHCP?",
sy_dhcp_d:"The switch requests an address via DHCP. You must find its new IP to reconnect.",
sy_sysip_err:"Invalid syslog server IP",sy_sysport_err:"Syslog port must be 1-65535",
sy_sysip_err:"Invalid syslog server IP",sy_dns1:"DNS server",sy_dns2:"Second DNS",sy_dns_err:"Invalid DNS server IP",sy_dns_dhcp:"from DHCP: {ip}",sy_sysport_err:"Syslog port must be 1-65535",
sy_pw_len:"Password: 1-20 characters",sy_pw_space:"Password cannot contain spaces",sy_pw_match:"Passwords do not match",
sy_pw_q:"Change admin password?",sy_pw_d:"Takes effect immediately for new logins. Save to flash to persist.",
sy_reboot_q:"Reboot the switch?",sy_reboot_d:"There are UNSAVED changes and they will be lost. Save to flash first if you want to keep them.",
@@ -108,6 +109,7 @@ fw_intro:"Upload an RTLPlayground image (512 KiB .bin). The image is staged to f
fw_upload:"Upload",fw_checking:"{f}: {n} bytes. Checking...",fw_size_err:"size is {n}, expected 524288 (512 KiB)",
fw_magic_err:"missing bank header / LJMP magic: not a firmware image",fw_crc_err:"CRC16 check failed: corrupt image",
fw_valid:"valid RTLPlayground image (size, magic and CRC16 OK)",fw_q:"Upload firmware?",
fw_other:"This image was not built for this switch ({m}): its SFP, LED and button pins may differ. Upload it only if you are sure.",
fw_d:"On a good checksum the switch resets itself and applies the image during boot (the startup config is preserved). Do not power it off until it comes back.",
fw_finishing:"finishing flash write... {s} s",fw_uploading:"uploading... {p}% / {s} s",
fw_verified:"checksum verified, the switch is rebooting...",
@@ -678,6 +680,7 @@ fw_intro:"Ein RTLPlayground-Image hochladen (.bin, 512 KiB). Das Image wird im F
fw_upload:"Hochladen",fw_checking:"{f}: {n} Bytes. Wird geprüft...",fw_size_err:"Grösse ist {n}, erwartet 524288 (512 KiB)",
fw_magic_err:"Bank-Header / LJMP-Kennung fehlt: kein Firmware-Image",fw_crc_err:"CRC16-Prüfung fehlgeschlagen: Image beschädigt",
fw_valid:"gültiges RTLPlayground-Image (Grösse, Kennung und CRC16 in Ordnung)",fw_q:"Firmware hochladen?",
fw_other:"Dieses Image wurde nicht für diesen Switch ({m}) gebaut: SFP-, LED- und Tasten-Pins können abweichen. Nur hochladen, wenn du sicher bist.",
fw_d:"Ist die Prüfsumme korrekt, startet der Switch neu und übernimmt das Image beim Hochfahren (die Startkonfiguration bleibt erhalten). Nicht vom Strom trennen, bis er wieder erreichbar ist.",
fw_finishing:"Flash wird fertig beschrieben... {s} s",fw_uploading:"wird hochgeladen... {p}% / {s} s",
fw_verified:"Prüfsumme bestätigt, der Switch startet neu...",
@@ -784,6 +787,7 @@ var CONF_CMDS=[
/^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}$/,
/^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/,
/^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{1,4}(\s+[a-zA-Z]\w*)?(\s+\d{1,2}t?)+$/,
@@ -791,6 +795,7 @@ var CONF_CMDS=[
/^ingress(\s+\d{1,2}[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{1,2}\s+name\s+\S+$/,
/^port\s+\d{1,2}\s+lldp\s+(permit|block)+$/,
/^eee\s+(on|off)(\s+\d{1,2})?(\s+(100m|1g|2g5))?$/,
/^mirror(\s+\d{1,2})(\s+\d{1,2}[tr]?)+$/,/^mirror\s+off$/,
/^lag\s+[1-4](\s+\d{1,2})+$/,/^lag\s+[1-4]\s+d$/,/^laghash\s+[1-4](\s+\w+)+$/,
@@ -801,7 +806,7 @@ var CONF_CMDS=[
/^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{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{1,2}|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/,
/^igmp\s+(on|off)$/,/^mtu\s+\d{1,2}\s+\d+$/,
/^igmp\s+(on|off)$/,/^mtu\s+\d{1,2}\s+\d+$/,/^lldp\s+(on|off)$/,
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/,
/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\s+(off|\d{1,8}\s+(pps|kbps))$/,
];
@@ -863,6 +868,7 @@ var TABS=[
{id:"mirror",icon:"M12 3v18M7 8l-4 4 4 4M17 8l4 4-4 4"},
{id:"isolate",icon:"M4 4h6v6H4zM14 14h6v6h-6zM14 4h6v6h-6zM4 14h6v6H4z"},
{id:"lag", icon:"M7 8a4 4 0 100 8h3M17 8a4 4 0 110 8h-3M9 12h6"},
{id:"lldp", icon:"M11 12a1 1 0 002 0 1 1 0 00-2 0M8.5 8.5a5 5 0 000 7M15.5 8.5a5 5 0 010 7M5.6 5.6a9 9 0 000 12.8M18.4 5.6a9 9 0 010 12.8"},
{id:"eee", icon:"M13 2L4 14h6l-1 8 9-12h-6z"},
{id:"bw", icon:"M4 18a8 8 0 0116 0M12 18l4-6"},
{id:"system",icon:"M12 8a4 4 0 100 8 4 4 0 000-8zM4 12h2M18 12h2M12 4v2M12 18v2M6 6l1.5 1.5M16.5 16.5L18 18M18 6l-1.5 1.5M7.5 16.5L6 18"},
@@ -1051,7 +1057,11 @@ function renderInfo(){
if(v==null||v==="")return;
tb.appendChild(h("tr",null,[h("td",{class:"mut",text:t(r[0])}),h("td",{class:"mono",text:String(v)})]));
});
if(S.info.hostname)$("brandname").textContent=$("brandname").title=S.info.hostname;
if(S.info.hostname){
$("brandname").textContent=$("brandname").title=document.title=S.info.hostname;
// Remembered per switch (each has its own origin) for the login page tab
try{localStorage.setItem("rtl_host",S.info.hostname);}catch(e){}
}
if(S.info.sw_ver)$("fver").textContent="RTLPlayground "+S.info.sw_ver;
}
function pollInfo(){
@@ -1838,6 +1848,32 @@ function lagApply(g){
}
tabHooks.lag={enter:function(){needPorts(function(){buildLag();lagLoad().catch(function(){})})}};
function lldpLoad(){
return getJSON("/lldp.json").then(function(s){
$("lldp_en").checked=s.on;
var tb=$("lldp_table").tBodies[0];tb.innerHTML="";
byPort(s.portStatus).forEach(function(p){
var tr=tb.insertRow();
tr.insertCell().textContent=p.portNum;
tr.insertCell().textContent=p.name;
var sw=h("label",{class:"switch"},[
h("input",{type:"checkbox",onchange:function(){
postCmd("port "+p.portNum+" lldp "+(this.checked?"permit":"block"))
.then(function(){setTimeout(lldpLoad,300)}).catch(function(){});
}}),h("i")]);
sw.firstChild.checked=p.permitted;
tr.insertCell().appendChild(sw);
});
});
}
$("lldp_en").addEventListener("change",function(){
var el=this;
postCmd("lldp "+(el.checked?"on":"off")).catch(function(){el.checked=!el.checked});
});
tabHooks.lldp={enter:lldpLoad};
function eeeFlags(bits){
var b=parseInt(bits,2);
return["100M","1G","2.5G"].map(function(s,i){
@@ -1996,6 +2032,16 @@ function sysLoad(){
if(S.info.session_timeout)$("sy-sesstmo").value=S.info.session_timeout;
}).catch(function(){});
cfgReload();
dnsLoad();
}
function dnsLoad(){
return api("/cmd",{method:"POST",body:"dns"}).then(function(r){
var m=r.body.match(/DNS servers ([\d.]+) ([\d.]+), from DHCP ([\d.]+)/);
if(!m)return;
function v(x){return x==="0.0.0.0"?"":x}
$("sy-dns1").value=v(m[1]);$("sy-dns2").value=v(m[2]);
$("sy-dnsdhcp").textContent=m[3]==="0.0.0.0"?"":t("sy_dns_dhcp",{ip:m[3]});
}).catch(function(){});
}
function cfgParseKnown(txt){
var igmp=false,syslog=false;
@@ -2012,13 +2058,15 @@ function cfgParseKnown(txt){
$("sy-apply").addEventListener("click",function(){
var ip=$("sy-ip").value.trim(),mask=$("sy-mask").value.trim(),gw=$("sy-gw").value.trim();
if(!okIp(ip)||!okIp(mask)||!okIp(gw)){toast(t("sy_ip_err"),"err");return;}
var d1=$("sy-dns1").value.trim(),d2=$("sy-dns2").value.trim();
if((d1&&!okIp(d1))||(d2&&!okIp(d2))){toast(t("sy_dns_err"),"err");return;}
var cmds=[];
var hn=$("sy-host").value.trim();
if(hn&&hn!==S.info.hostname){
if(!/^[\x21-\x7e]{1,23}$/.test(hn)||/["\\]/.test(hn)){toast(t("sy_host_err"),"err");return;}
cmds.push("hostname "+hn);
}
cmds.push("ip "+ip+"\nnetmask "+mask+"\ngw "+gw);
cmds.push("ip "+ip+"\nnetmask "+mask+"\ngw "+gw+"\ndns server "+(d1||"0.0.0.0")+(d2?" "+d2:""));
var changingIp=ip!==S.info.ip_address;
confirmModal(t("sy_net_q"),changingIp?t("sy_net_d",{ip:ip}):"",function(){
postCmds(cmds).then(function(){
@@ -2114,7 +2162,7 @@ tabHooks.system={enter:sysLoad};
var CONF_OVERWRITE=[
/^ip\b/,/^gw\b/,/^netmask\b/,/^hostname\b/,
/^syslog\s+ip\b/,/^syslog\s+port\b/,/^passwd\b/,/^session\b/,
/^syslog\s+ip\b/,/^syslog\s+port\b/,/^dns\s+server\b/,/^passwd\b/,/^session\b/,
/^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
/^pvid\s+\d{1,2}\b/,
/^port\s+\d{1,2}(?!\s+name\b)/,/^port\s+\d{1,2}\s+name\b/,
@@ -2234,10 +2282,26 @@ $("saveBtn").addEventListener("click",function(){
});
});
var fwBuf=null;
var fwBuf=null,fwOther=false;
// Every image carries its machine name as a NUL-terminated string, the same
// one the running switch reports as hw_ver: an image without it was built
// for another board (SFP, LED and button pins differ between machines).
function fwForThisSwitch(u){
var name=S.info.hw_ver;
if(!name)return true;
var n=[];
for(var i=0;i<name.length;i++)n.push(name.charCodeAt(i)&0xff);
n.push(0);
outer:for(var p=0;p<=u.length-n.length;p++){
for(var k=0;k<n.length;k++)if(u[p+k]!==n[k])continue outer;
return true;
}
return false;
}
$("fwfile").addEventListener("change",function(){
var f=this.files[0];
fwBuf=null;
fwOther=false;
$("fwup").disabled=true;
$("fwinfo").textContent="";
if(!f)return;
@@ -2260,13 +2324,15 @@ $("fwfile").addEventListener("change",function(){
return;
}
fwBuf=f;
info.innerHTML='<span style="color:var(--ok)">\u2713 '+esc(t("fw_valid"))+"</span>";
fwOther=!fwForThisSwitch(u);
info.innerHTML='<span style="color:var(--ok)">\u2713 '+esc(t("fw_valid"))+"</span>"+
(fwOther?'<br><span style="color:var(--warn)">\u26a0 '+esc(t("fw_other",{m:S.info.hw_ver}))+"</span>":"");
$("fwup").disabled=false;
});
});
$("fwup").addEventListener("click",function(){
if(!fwBuf)return;
confirmModal(t("fw_q"),t("fw_d"),function(){
confirmModal(t("fw_q"),(fwOther?t("fw_other",{m:S.info.hw_ver})+" ":"")+t("fw_d"),function(){
var form=new FormData();
form.append("uploadedfile",fwBuf,fwBuf.name);
var xhr=new XMLHttpRequest();
+14
View File
@@ -331,6 +331,17 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
</div>
</section>
<section class="tab" id="tab-lldp">
<div class="card"><h2 data-i18n="lldp_title">LLDP</h2>
<label class="row"><span class="lb" data-i18n="lldp_enabled">Enabled</span>
<span class="switch"><input type="checkbox" id="lldp_en"><i></i></span>
</label>
<div class="scrollx"><table class="t" id="lldp_table">
<thead><tr><th data-i18n="c_port">Port Number</th><th data-i18n="lldp_portname">Name</th><th data-i18n="lldp_enable">Send Frames</th></tr></thead>
<tbody></tbody></table></div>
</div>
</section>
<section class="tab" id="tab-isolate">
<div class="card"><h2 data-i18n="iso_title">Port isolation</h2>
<p class="small mut" style="margin-bottom:10px" data-i18n="iso_intro">A frame entering a port may only leave through the ports ticked in its row.</p>
@@ -378,6 +389,9 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<label class="row"><span class="lb" data-i18n="i_ip">IP address</span><input class="in" id="sy-ip" size="15"></label>
<label class="row"><span class="lb" data-i18n="i_mask">Netmask</span><input class="in" id="sy-mask" size="15"></label>
<label class="row"><span class="lb" data-i18n="i_gw">Gateway</span><input class="in" id="sy-gw" size="15"></label>
<label class="row"><span class="lb" data-i18n="sy_dns1">DNS server</span><input class="in" id="sy-dns1" size="15"></label>
<label class="row"><span class="lb" data-i18n="sy_dns2">Second DNS</span><input class="in" id="sy-dns2" size="15"></label>
<p class="small mut" id="sy-dnsdhcp"></p>
<div style="display:flex;gap:10px;margin-top:12px">
<button class="ctl pri" id="sy-apply" data-i18n="c_apply">Apply</button>
<button class="ctl" id="sy-dhcp" data-i18n="sy_dhcp" data-i18n-t="sy_dhcp_t">Use DHCP</button>
+6 -2
View File
@@ -92,8 +92,12 @@ button:hover{filter:brightness(1.1)}
try{l=localStorage.getItem("rtl_lang");}catch(e){}
if(!L[l])l=(navigator.language||"en").slice(0,2);
var s=L[l]||L.en;
document.title=s[0];
document.getElementById("hn").textContent=s[0];
// Name the tab after the switch: the hostname seen at the last login here,
// otherwise the address, so several open switches can be told apart
var host=null;
try{host=localStorage.getItem("rtl_host");}catch(e){}
document.title=(host||location.hostname)+" – "+s[0];
document.getElementById("hn").textContent=host||s[0];
document.getElementById("sub").textContent=s[1];
document.getElementById("lp").textContent=s[2];
document.getElementById("lb").textContent=s[3];
+2
View File
@@ -1034,6 +1034,8 @@ void httpd_appcall(void)
send_lag();
} else if (is_word(q, "/stp.json")) {
send_stp();
} else if (is_word(q, "/lldp.json")) {
send_lldp();
} else if (is_word(q, "/vlanlist")) {
send_vlanlist();
} else if (is_word(q, "/config")) {
+32 -1
View File
@@ -49,6 +49,9 @@ extern __xdata char sfp_module_model[2][17];
extern __xdata char sfp_module_serial[2][17];
extern __xdata uint8_t sfp_options[2];
extern __xdata uint16_t lldp_logical_port_status;
extern __xdata bool lldp_enabled;
__code const uint8_t * __code const HTTP_RESPONCE_JSON = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n";
__code const uint8_t * __code const HTTP_RESPONCE_TXT = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\n";
@@ -619,7 +622,6 @@ static void bridge_to_html(void)
byte_to_html(pi_mac[i]);
}
void send_stp(void)
{
uint8_t i, j, st, dsg;
@@ -734,8 +736,37 @@ void send_stp(void)
slen += strtox(outbuf + slen, "]}");
}
void send_lldp(void)
{
dbg_string("send_lldp called\n");
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
slen += strtox(outbuf + slen, "{\"on\":");
bool_to_html(lldp_enabled);
slen += strtox(outbuf + slen, ",\"portStatus\":[");
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
slen += strtox(outbuf + slen, "{\"portNum\":");
itoa_html(machine.log_to_phys_port[i]);
slen += strtox(outbuf + slen, ",\"logPort\":");
itoa_html(i);
slen += strtox(outbuf + slen, ",\"name\":\"");
for (uint8_t j = 0; j < PORT_NAME_SIZE && port_names[i][j]; j++) {
json_char_to_html(port_names[i][j]);
}
slen += strtox(outbuf + slen, "\",\"permitted\":");
bool_to_html(1 & (lldp_logical_port_status >> i));
slen += strtox(outbuf + slen, "}");
if (i < machine.max_port)
char_to_html(',');
else
char_to_html(']');
}
char_to_html('}');
}
void send_eee(void)
{
+1
View File
@@ -23,6 +23,7 @@ void send_config(void);
void send_cmd_log(void);
void send_lag(void);
void send_stp(void);
void send_lldp(void);
void send_vlanlist(void);
/* Convert only the lower nibble to ascii HEX char.
+1
View File
@@ -131,6 +131,7 @@ struct flash_region_t {
};
extern __xdata char port_names[9][PORT_NAME_SIZE];
extern __xdata uint16_t lldp_logical_port_status;
extern __xdata bool stp_enabled;
+1
View File
@@ -269,6 +269,7 @@ void flash_read_security(void)
// Set slow read mode
SFR_FLASH_CMD_R = CMD_READ_SECURITY_REGS; // read security register
SFR_FLASH_DUMMYCYCLES = 8;
// Transfer 4 bytes (command + 3byte address)
SFR_FLASH_TCONF = 4;
+161
View File
@@ -0,0 +1,161 @@
#pragma codeseg BANK3
#pragma constseg BANK3
#include "rtl837x_common.h"
#include "rtl837x_lldp.h"
#include "rtl837x_sfr.h"
#include "rtl837x_common.h"
#include "dhcp.h"
#include "uip.h"
#include "uip/uip.h"
#include "machine.h"
extern __xdata bool lldp_enabled;
extern __code const struct machine machine;
__xdata uint16_t lldp_ticks;
__xdata uint16_t lldp_logical_port_status = 0xffff;
void lldp_init(void) __banked
{
lldp_ticks = 0;
}
void lldp_tick(void) __banked
{
lldp_ticks++;
if (lldp_ticks < LLDP_TX_INTERVAL_TICKS)
return;
lldp_ticks = 0;
if(lldp_enabled == 1)
lldp_send();
}
//Outgoing LLDP packet.
struct lldp_pkt {
struct uip_eth_addr dst;
struct uip_eth_addr src;
struct rtl_tag rtl_tag;
uint16_t ether_type;
uint8_t payload[64];
};
#define LLDP_O ((__xdata struct lldp_pkt *)&uip_buf[RTL_FRAME_DESC_SIZE])
// The type is 7 bits and the length is 9 bits
// we can represent this as the 7 most significant
// bits of the type bit and leave the least significant
// bit as 0 always as we're not going to need 2^9 for
// the length at any point
#define LLDP_TYPE(value) (value << 1)
void lldp_send(void) __banked __reentrant
{
__xdata uint8_t *p;
uint8_t port_position;
lldp_set_addresses();
lldp_set_rtl_wrapper();
p = LLDP_O->payload;
//Chassis ID
*p++ = LLDP_TYPE(LLDP_CHASSIS_ID_TLV_TYPE);
*p++ = LLDP_CHASSIS_ID_TLV_LENGTH;
*p++ = LLDP_CHASSIS_ID_TLV_SUBTYPE;
memcpy(p, uip_ethaddr.addr, MAC_ADDR_LEN);
p += MAC_ADDR_LEN;
//Port ID
*p++ = LLDP_TYPE(LLDP_PORT_ID_TLV_TYPE);
*p++ = LLDP_PORT_ID_TLV_LENGTH;
*p++ = LLDP_PORT_ID_TLV_SUBTYPE;
port_position = (p-LLDP_O->payload);
*p++ = '0'; // filled in per port below
//TTL
*p++ = LLDP_TYPE(LLDP_TTL_TLV_TYPE);
*p++ = LLDP_TTL_TLV_LENGTH;
*p++ = 0x00; //padding
*p++ = LLDP_TTL_TTL_SECONDS;
p += lldp_sysname(p);
p += lldp_sysdesc(p);
// End of LLDPDU TLV
*p++ = 0x00; //padding
*p++ = 0x00; //padding
while (p < (LLDP_O->payload + LLDP_MIN_ETHERNET_PAYLOAD_LENGTH))
*p++ = 0x00;
/*
* uip_len is the Ethernet frame length excluding FCS.
*
* The frame consists of:
*
* dst mac
* src mac
* rtl_tag sizeof(struct rtl_tag)
* EtherType (2 bytes)
* payload (how far we've moved p pointer)
*/
uip_len = MAC_ADDR_LEN + MAC_ADDR_LEN + sizeof(struct rtl_tag) + LLDP_ETHERTYPE_LENGTH + (p - LLDP_O->payload);
for (uint8_t port = machine.min_port; port <= machine.max_port; port++) {
if ((1 << port) & lldp_logical_port_status) {
LLDP_O->payload[port_position] = '0' + machine.log_to_phys_port[port];
LLDP_O->rtl_tag.pmask = HTONS((uint16_t)1 << port);
tcpip_output();
}
}
}
void lldp_set_addresses(void) __banked {
// LLDP destination multicast addr: 01:80:c2:00:00:0e
memcpyc(LLDP_O->dst.addr, "\x01\x80\xc2\x00\x00\x0e", MAC_ADDR_LEN);
memcpy(LLDP_O->src.addr, uip_ethaddr.addr, MAC_ADDR_LEN);
}
void lldp_set_rtl_wrapper(void) __banked {
/*
* This is the RTL CPU tag, not the Ethernet EtherType.
* port 0 -> 0x0001
* port 1 -> 0x0002
* port 2 -> 0x0004
*/
LLDP_O->rtl_tag.tag = HTONS(RTL_FRAME_TAG_ID);
LLDP_O->rtl_tag.version = RTL_FRAME_TAG_VERSION;
LLDP_O->rtl_tag.reason = 0x00;
LLDP_O->rtl_tag.flags = HTONS(RTL_TAG_LEARN_DIS);
LLDP_O->ether_type = HTONS(LLDP_ETHERTYPE);
}
uint8_t lldp_sysname(__xdata uint8_t *p) __banked
{
uint8_t hostname_length = strlen_x(hostname);
*p++ = LLDP_TYPE(LLDP_SYSNAME_TLV_TYPE);
*p++ = hostname_length;
memcpy(p, hostname, hostname_length);
return hostname_length + 2;
}
uint8_t lldp_sysdesc(__xdata uint8_t *p) __banked
{
uint16_t machine_name_length = strlen(machine.machine_name);
*p++ = LLDP_TYPE(LLDP_SYSDESC_TLV_TYPE);
*p++ = machine_name_length;
memcpyc(p, machine.machine_name, machine_name_length);
return machine_name_length + 2;
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef _LLDP_H_
#define _LLDP_H_
#include <stdint.h>
#define LLDP_MIN_ETHERNET_PAYLOAD_LENGTH 46
#define LLDP_ETHERTYPE 0x88cc
#define LLDP_ETHERTYPE_LENGTH 2
#define LLDP_MAX_FRAME 256
#define MAC_ADDR_LEN 6
#define LLDP_TX_INTERVAL_TICKS 6000
#define LLDP_PORT_ID_TLV_TYPE 2
#define LLDP_PORT_ID_TLV_LENGTH 0x02
#define LLDP_PORT_ID_TLV_SUBTYPE 0x07
#define LLDP_TTL_TLV_TYPE 3
#define LLDP_TTL_TLV_LENGTH 2
#define LLDP_TTL_TTL_SECONDS 120
#define LLDP_CHASSIS_ID_TLV_TYPE 1
#define LLDP_CHASSIS_ID_TLV_LENGTH 7
#define LLDP_CHASSIS_ID_TLV_SUBTYPE 4
#define LLDP_SYSNAME_TLV_TYPE 5
#define LLDP_SYSDESC_TLV_TYPE 6
void lldp_init(void) __banked;
void lldp_tick(void) __banked;
void lldp_send(void) __banked;
void lldp_set_addresses(void) __banked;
void lldp_set_rtl_wrapper(void) __banked;
uint8_t lldp_sysname(__xdata uint8_t *p) __banked;
uint8_t lldp_sysdesc(__xdata uint8_t *p) __banked;
#endif
+7
View File
@@ -16,6 +16,7 @@
#include "rtl837x_leds.h"
#include "rtl837x_bandwidth.h"
#include "rtl837x_init.h"
#include "rtl837x_lldp.h"
#include "dhcp.h"
#include "cmd_parser.h"
#include "cmd_editor.h"
@@ -25,6 +26,7 @@
#include "machine.h"
#include "phy.h"
#include "syslog.h"
#include "dns.h"
#include "httpd/page_impl.h"
#include "boot.h"
#include "sfp.h"
@@ -127,6 +129,7 @@ __xdata uint8_t tx_seq;
__xdata bool stp_enabled;
__xdata uint8_t igmpEnabled;
__xdata bool lldp_enabled;
__xdata char hostname[24]; /* device hostname, default set at boot, see rtl837x_common.h */
__code const uint16_t bit_mask[16] = {
@@ -1286,6 +1289,8 @@ static void handle_tick(void)
}
}
health_phase(HEALTH_PH_STP);
lldp_tick();
health_phase(HEALTH_LLDP);
}
@@ -1763,6 +1768,7 @@ void main(void)
check_and_flash_update_image();
syslog_init();
dns_init();
#ifdef DEBUG
// This register seems to work on the RTL8373 only if also the SDS
@@ -1792,6 +1798,7 @@ void main(void)
uip_init();
uip_arp_init();
httpd_init();
lldp_init();
management_vlan = 1; // Default management VLAN is 1
+3
View File
@@ -84,6 +84,9 @@ char port_names[9][PORT_NAME_SIZE];
struct flash_region_t flash_region;
struct syslog_state syslog_state;
bool stp_enabled;
bool lldp_enabled;
uint16_t lldp_logical_port_status;
uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask;
struct uip_eth_addr uip_ethaddr = { .addr = { 0x02, 0x11, 0x22, 0x33, 0x44, 0x55 } };
+1
View File
@@ -152,6 +152,7 @@ void send_lag(void) { }
void send_stp(void) { }
void send_stp_counters(void) { }
void send_vlanlist(void) { }
void send_lldp(void) {}
extern uint8_t authenticated;
+36 -2
View File
@@ -447,13 +447,41 @@ void send_mtu(int s)
json_object_object_add(v, "mtu", json_object_new_string(mtu));
json_object_array_add(mtus, v);
}
write(s, header, strlen(header));
write(s, header, strlen(header));
jstring = json_object_to_json_string_ext(mtus, JSON_C_TO_STRING_PLAIN);
write(s, jstring, strlen(jstring));
json_object_put(mtus);
}
void send_lldp(int s)
{
struct json_object *c, *portStatuses, *p;
const char *jstring;
char *header = "HTTP/1.1 200 OK\r\n"
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
c = json_object_new_object();
json_object_object_add(c, "on", json_object_new_int(1));
portStatuses = json_object_new_array_ext(PORTS);
for (int i = 1; i <= PORTS; i++) {
p = json_object_new_object();
json_object_object_add(p, "portNum", json_object_new_int(i));
json_object_object_add(p, "logPort", json_object_new_int(i-1));
json_object_object_add(p, "name", json_object_new_string(""));
json_object_object_add(p, "permitted", json_object_new_int(1));
json_object_array_add(portStatuses, p);
}
json_object_object_add(c, "portStatus", portStatuses);
write(s, header, strlen(header));
jstring = json_object_to_json_string_ext(c, JSON_C_TO_STRING_PLAIN);
write(s, jstring, strlen(jstring));
json_object_put(c);
}
void send_cmd_log(int s)
{
@@ -468,7 +496,6 @@ void send_cmd_log(int s)
}
}
void send_config(int s)
{
char *header = "HTTP/1.1 200 OK\r\n"
@@ -701,6 +728,13 @@ void launch(struct Server *server)
else
send_vlan(new_socket, vlan);
goto done;
} else if (!strncmp(&buffer[4], "/lldp.json", 10)) {
printf("LLDP request");
if (!authenticated)
send_unauthorized(new_socket);
else
send_lldp(new_socket);
goto done;
} else if (!strncmp(&buffer[4], "/cmd_log", 8)) {
printf("Request cmd_log\n");
if (!authenticated)
+1 -1
View File
@@ -3,7 +3,7 @@
#include <netinet/in.h>
#define BUFFER_SIZE 131072
#define BUFFER_SIZE 262144
struct Server {
int domain;
+1
View File
@@ -6,4 +6,5 @@ void udp_callbacks(void)
{
dhcp_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
syslog_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
dns_callback(uip_udp_conn->lport);
}
+1
View File
@@ -3,6 +3,7 @@
#include "dhcp.h"
#include "syslog.h"
#include "dns.h"
void udp_callbacks(void);