From 1ebee4d6e9f50cd6279a2d5597e063db5a390f7a Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Fri, 28 Aug 2026 01:53:29 +0100 Subject: [PATCH 01/13] Implementing basic LLDP server, should send port_id message to each port once every 30 seconds. * Additionally fixes the on-ward passing of LLDP frames through the switch The ports on the case are marked 1-8 so this should reflect that rather than being 0-based We can send the hostname as well, this can be dynamically allocated Let's send the model as the system description as well --- Makefile | 1 + cmd_parser.c | 10 +++ cmd_parser.h | 1 + rtl837x_lldp.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ rtl837x_lldp.h | 36 ++++++++++ rtlplayground.c | 12 ++++ 6 files changed, 241 insertions(+) create mode 100644 rtl837x_lldp.c create mode 100644 rtl837x_lldp.h diff --git a/Makefile b/Makefile index bb914904..15d3f10c 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,7 @@ SRCS += \ rtl837x_pins.c\ rtl837x_port.c \ rtl837x_stp.c \ + rtl837x_lldp.c \ rtl837x_storm.c SRCS += \ httpd/httpd.c \ diff --git a/cmd_parser.c b/cmd_parser.c index c493c9e9..de7e1d49 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -16,6 +16,7 @@ #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" @@ -32,6 +33,7 @@ extern __code const struct machine machine; extern __xdata bool stp_enabled; extern __code const uint8_t log_to_phys_port[9]; +extern __xdata uint8_t lldp_enabled; extern volatile __xdata uint32_t ticks; extern volatile __xdata uint8_t sfr_data[4]; @@ -2024,6 +2026,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"); diff --git a/cmd_parser.h b/cmd_parser.h index f96c72ae..2359eb16 100644 --- a/cmd_parser.h +++ b/cmd_parser.h @@ -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++ diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c new file mode 100644 index 00000000..24e774c6 --- /dev/null +++ b/rtl837x_lldp.c @@ -0,0 +1,181 @@ +#pragma codeseg BANK2 +#pragma constseg BANK2 + +#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 uint8_t lldp_enabled; +extern __code const struct machine machine; + +uint8_t lldp_seconds; + +void lldp_init(void) __banked +{ + lldp_seconds = 0; +} + +void lldp_tick(void) __banked +{ + lldp_seconds++; + + if (lldp_seconds < LLDP_TX_INTERVAL_SEC) + return; + + lldp_seconds = 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 +{ + uint8_t *p; + uint16_t len = 0; + uint8_t port_position; + + lldp_set_addresses(); + lldp_set_rtl_wrapper(); + + p = LLDP_O->payload; + + //Chassis ID + p[len++] = LLDP_TYPE(LLDP_CHASSIS_ID_TLV_TYPE); + p[len++] = LLDP_CHASSIS_ID_TLV_LENGTH; + p[len++] = LLDP_CHASSIS_ID_TLV_SUBTYPE; + + for (uint8_t i = 0; i < LLDP_MAC_ADDR_LEN; i++) + p[len + i] = uip_ethaddr.addr[i]; + + len += LLDP_MAC_ADDR_LEN; + + //Port ID + p[len++] = LLDP_TYPE(LLDP_PORT_ID_TLV_TYPE); + p[len++] = LLDP_PORT_ID_TLV_LENGTH; + p[len++] = LLDP_PORT_ID_TLV_SUBTYPE; + port_position = len; + p[len++] = '0'; // filled in per port below + + //TTL + p[len++] = LLDP_TYPE(LLDP_TTL_TLV_TYPE); + p[len++] = LLDP_TTL_TLV_LENGTH; + p[len++] = 0x00; //padding + p[len++] = LLDP_TTL_TTL_SECONDS; + + lldp_sysname(p, &len); + lldp_sysdesc(p, &len); + + // End of LLDPDU TLV + p[len++] = 0x00; //padding + p[len++] = 0x00; //padding + + while (len < LLDP_MIN_ETHERNET_PAYLOAD_LENGTH) + p[len++] = 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 len + */ + uip_len = LLDP_MAC_ADDR_LEN + LLDP_MAC_ADDR_LEN + sizeof(struct rtl_tag) + LLDP_ETHERTYPE_LENGTH + len; + + for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { + + LLDP_O->payload[port_position] = '1' + 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 + LLDP_O->dst.addr[0] = 0x01; + LLDP_O->dst.addr[1] = 0x80; + LLDP_O->dst.addr[2] = 0xc2; + LLDP_O->dst.addr[3] = 0x00; + LLDP_O->dst.addr[4] = 0x00; + LLDP_O->dst.addr[5] = 0x0e; + + for (uint8_t i = 0; i < LLDP_MAC_ADDR_LEN; i++) + LLDP_O->src.addr[i] = uip_ethaddr.addr[i]; +} + +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); +} + +void lldp_sysname(uint8_t *p, uint16_t *len) __banked +{ + uint16_t hostname_length = 0; + __xdata char *hp = hostname; + + p[(*len)++] = LLDP_TYPE(LLDP_SYSNAME_TLV_TYPE); + p[(*len)++] = 0; + + while (*hp) + { + hostname_length++; + p[(*len)++] = *hp++; + } + p[(*len)-hostname_length-1] = hostname_length; + +} + +void lldp_sysdesc(uint8_t *p, uint16_t *len) __banked +{ + uint16_t machine_name_length = 0; + const char *mp = machine.machine_name; + + p[(*len)++] = LLDP_TYPE(LLDP_SYSDESC_TLV_TYPE); + p[(*len)++] = 0; + + while (*mp) + { + machine_name_length++; + p[(*len)++] = *mp++; + } + p[(*len)-machine_name_length-1] = machine_name_length; +} \ No newline at end of file diff --git a/rtl837x_lldp.h b/rtl837x_lldp.h new file mode 100644 index 00000000..038d005d --- /dev/null +++ b/rtl837x_lldp.h @@ -0,0 +1,36 @@ +#ifndef _LLDP_H_ +#define _LLDP_H_ + +#include + +#define LLDP_MIN_ETHERNET_PAYLOAD_LENGTH 46 +#define LLDP_ETHERTYPE 0x88cc +#define LLDP_ETHERTYPE_LENGTH 2 +#define LLDP_MAX_FRAME 256 +#define LLDP_MAC_ADDR_LEN 6 +#define LLDP_TX_INTERVAL_SEC 30 + +#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; +void lldp_sysname(uint8_t *p, uint16_t *len) __banked; +void lldp_sysdesc(uint8_t *p, uint16_t *len) __banked; + +#endif diff --git a/rtlplayground.c b/rtlplayground.c index 916984f1..738b6ab2 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -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" @@ -127,6 +128,7 @@ __xdata uint8_t tx_seq; __xdata bool stp_enabled; __xdata uint8_t igmpEnabled; +__xdata uint8_t lldp_enabled; __xdata char hostname[24]; /* device hostname, default set at boot, see rtl837x_common.h */ __code const uint16_t bit_mask[16] = { @@ -1066,6 +1068,13 @@ void handle_rx(void) if (uip_len) { tcpip_output(); } + } else if (lldp_enabled && uip_buf[0] == 0x01 && uip_buf[1] == 0x80 && uip_buf[2] == 0xc2 + && uip_buf[3] == 0x00 && uip_buf[4] == 0x00 && uip_buf[5] == 0x0e && uip_buf[12] == 0x88 + && uip_buf[13] == 0xcc) { //LLDP Mac + EtherType check + // LLDP packets shouldn't be passed elsewhere + if(uip_len){ + print_string("LLDP received\n"); + } } else if (ETH_IN->ether_type == HTONS(0x0806)) { // ARP uip_arp_arpin(); if (uip_len) { @@ -1286,6 +1295,8 @@ static void handle_tick(void) } } health_phase(HEALTH_PH_STP); + lldp_tick(); + health_phase(HEALTH_LLDP); } @@ -1794,6 +1805,7 @@ void main(void) uip_init(); uip_arp_init(); httpd_init(); + lldp_init(); management_vlan = 1; // Default management VLAN is 1 From 735672ccf1ff8e1f2de0113969fbb833db901b48 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Sun, 13 Sep 2026 12:16:30 +0100 Subject: [PATCH 02/13] Moving to Bank3 --- cmd_parser.c | 2 +- rtl837x_lldp.c | 8 ++++---- rtlplayground.c | 5 +---- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cmd_parser.c b/cmd_parser.c index de7e1d49..8c21d9f8 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -33,7 +33,7 @@ extern __code const struct machine machine; extern __xdata bool stp_enabled; extern __code const uint8_t log_to_phys_port[9]; -extern __xdata uint8_t lldp_enabled; +extern __xdata bool lldp_enabled; extern volatile __xdata uint32_t ticks; extern volatile __xdata uint8_t sfr_data[4]; diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c index 24e774c6..032d4746 100644 --- a/rtl837x_lldp.c +++ b/rtl837x_lldp.c @@ -1,5 +1,5 @@ -#pragma codeseg BANK2 -#pragma constseg BANK2 +#pragma codeseg BANK3 +#pragma constseg BANK3 #include "rtl837x_common.h" #include "rtl837x_lldp.h" @@ -10,7 +10,7 @@ #include "uip/uip.h" #include "machine.h" -extern __xdata uint8_t lldp_enabled; +extern __xdata bool lldp_enabled; extern __code const struct machine machine; uint8_t lldp_seconds; @@ -178,4 +178,4 @@ void lldp_sysdesc(uint8_t *p, uint16_t *len) __banked p[(*len)++] = *mp++; } p[(*len)-machine_name_length-1] = machine_name_length; -} \ No newline at end of file +} diff --git a/rtlplayground.c b/rtlplayground.c index 738b6ab2..4fb22009 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -128,7 +128,7 @@ __xdata uint8_t tx_seq; __xdata bool stp_enabled; __xdata uint8_t igmpEnabled; -__xdata uint8_t lldp_enabled; +__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] = { @@ -1072,9 +1072,6 @@ void handle_rx(void) && uip_buf[3] == 0x00 && uip_buf[4] == 0x00 && uip_buf[5] == 0x0e && uip_buf[12] == 0x88 && uip_buf[13] == 0xcc) { //LLDP Mac + EtherType check // LLDP packets shouldn't be passed elsewhere - if(uip_len){ - print_string("LLDP received\n"); - } } else if (ETH_IN->ether_type == HTONS(0x0806)) { // ARP uip_arp_arpin(); if (uip_len) { From 3eacc9c8a5f102cda0d1156647a03dd871d5f86d Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Sun, 13 Sep 2026 20:41:48 +0100 Subject: [PATCH 03/13] LLPD_MAC_ADDR_LEN -> MAC_ADDR_LEN and memcopy instead of manually setting bytes --- rtl837x_lldp.c | 17 +++++------------ rtl837x_lldp.h | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c index 032d4746..163bf598 100644 --- a/rtl837x_lldp.c +++ b/rtl837x_lldp.c @@ -69,10 +69,10 @@ void lldp_send(void) __banked __reentrant p[len++] = LLDP_CHASSIS_ID_TLV_LENGTH; p[len++] = LLDP_CHASSIS_ID_TLV_SUBTYPE; - for (uint8_t i = 0; i < LLDP_MAC_ADDR_LEN; i++) + for (uint8_t i = 0; i < MAC_ADDR_LEN; i++) p[len + i] = uip_ethaddr.addr[i]; - len += LLDP_MAC_ADDR_LEN; + len += MAC_ADDR_LEN; //Port ID p[len++] = LLDP_TYPE(LLDP_PORT_ID_TLV_TYPE); @@ -108,7 +108,7 @@ void lldp_send(void) __banked __reentrant * EtherType (2 bytes) * payload len */ - uip_len = LLDP_MAC_ADDR_LEN + LLDP_MAC_ADDR_LEN + sizeof(struct rtl_tag) + LLDP_ETHERTYPE_LENGTH + len; + uip_len = MAC_ADDR_LEN + MAC_ADDR_LEN + sizeof(struct rtl_tag) + LLDP_ETHERTYPE_LENGTH + len; for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { @@ -121,15 +121,8 @@ void lldp_send(void) __banked __reentrant void lldp_set_addresses(void) __banked { // LLDP destination multicast addr: 01:80:c2:00:00:0e - LLDP_O->dst.addr[0] = 0x01; - LLDP_O->dst.addr[1] = 0x80; - LLDP_O->dst.addr[2] = 0xc2; - LLDP_O->dst.addr[3] = 0x00; - LLDP_O->dst.addr[4] = 0x00; - LLDP_O->dst.addr[5] = 0x0e; - - for (uint8_t i = 0; i < LLDP_MAC_ADDR_LEN; i++) - LLDP_O->src.addr[i] = uip_ethaddr.addr[i]; + 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 { diff --git a/rtl837x_lldp.h b/rtl837x_lldp.h index 038d005d..519cfdb8 100644 --- a/rtl837x_lldp.h +++ b/rtl837x_lldp.h @@ -7,7 +7,7 @@ #define LLDP_ETHERTYPE 0x88cc #define LLDP_ETHERTYPE_LENGTH 2 #define LLDP_MAX_FRAME 256 -#define LLDP_MAC_ADDR_LEN 6 +#define MAC_ADDR_LEN 6 #define LLDP_TX_INTERVAL_SEC 30 #define LLDP_PORT_ID_TLV_TYPE 2 From d5124df89ecc25213df7e5937ebc980956a5d900 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Sun, 13 Sep 2026 20:57:03 +0100 Subject: [PATCH 04/13] avoid SRAM --- rtl837x_lldp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c index 163bf598..dc1e6b55 100644 --- a/rtl837x_lldp.c +++ b/rtl837x_lldp.c @@ -13,7 +13,7 @@ extern __xdata bool lldp_enabled; extern __code const struct machine machine; -uint8_t lldp_seconds; +__xdata uint8_t lldp_seconds; void lldp_init(void) __banked { From ccf541a06bc59f8c22e1b33caa11ea0a4e586634 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Sun, 13 Sep 2026 21:12:32 +0100 Subject: [PATCH 05/13] Refactor and golfing from https://github.com/logicog/RTLPlayground/pull/398#issuecomment-5654087271 I've tweaked it a little to keep the port_position in as I think that is clearer than a random "12" in the loop later on --- rtl837x_lldp.c | 90 +++++++++++++++++++++----------------------------- rtl837x_lldp.h | 4 +-- 2 files changed, 39 insertions(+), 55 deletions(-) diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c index dc1e6b55..e426fdef 100644 --- a/rtl837x_lldp.c +++ b/rtl837x_lldp.c @@ -55,9 +55,8 @@ struct lldp_pkt { void lldp_send(void) __banked __reentrant { - uint8_t *p; - uint16_t len = 0; - uint8_t port_position; + __xdata uint8_t *p; + uint8_t port_position; lldp_set_addresses(); lldp_set_rtl_wrapper(); @@ -65,37 +64,35 @@ void lldp_send(void) __banked __reentrant p = LLDP_O->payload; //Chassis ID - p[len++] = LLDP_TYPE(LLDP_CHASSIS_ID_TLV_TYPE); - p[len++] = LLDP_CHASSIS_ID_TLV_LENGTH; - p[len++] = LLDP_CHASSIS_ID_TLV_SUBTYPE; + *p++ = LLDP_TYPE(LLDP_CHASSIS_ID_TLV_TYPE); + *p++ = LLDP_CHASSIS_ID_TLV_LENGTH; + *p++ = LLDP_CHASSIS_ID_TLV_SUBTYPE; - for (uint8_t i = 0; i < MAC_ADDR_LEN; i++) - p[len + i] = uip_ethaddr.addr[i]; - - len += MAC_ADDR_LEN; + memcpy(p, uip_ethaddr.addr, MAC_ADDR_LEN); + p += MAC_ADDR_LEN; //Port ID - p[len++] = LLDP_TYPE(LLDP_PORT_ID_TLV_TYPE); - p[len++] = LLDP_PORT_ID_TLV_LENGTH; - p[len++] = LLDP_PORT_ID_TLV_SUBTYPE; - port_position = len; - p[len++] = '0'; // filled in per port below + *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[len++] = LLDP_TYPE(LLDP_TTL_TLV_TYPE); - p[len++] = LLDP_TTL_TLV_LENGTH; - p[len++] = 0x00; //padding - p[len++] = LLDP_TTL_TTL_SECONDS; + *p++ = LLDP_TYPE(LLDP_TTL_TLV_TYPE); + *p++ = LLDP_TTL_TLV_LENGTH; + *p++ = 0x00; //padding + *p++ = LLDP_TTL_TTL_SECONDS; - lldp_sysname(p, &len); - lldp_sysdesc(p, &len); + p += lldp_sysname(p); + p += lldp_sysdesc(p); // End of LLDPDU TLV - p[len++] = 0x00; //padding - p[len++] = 0x00; //padding + *p++ = 0x00; //padding + *p++ = 0x00; //padding - while (len < LLDP_MIN_ETHERNET_PAYLOAD_LENGTH) - p[len++] = 0x00; + while (p < (LLDP_O->payload + LLDP_MIN_ETHERNET_PAYLOAD_LENGTH)) + *p++ = 0x00; /* * uip_len is the Ethernet frame length excluding FCS. @@ -106,13 +103,13 @@ void lldp_send(void) __banked __reentrant * src mac * rtl_tag sizeof(struct rtl_tag) * EtherType (2 bytes) - * payload len + * payload (how far we've moved p pointer) */ - uip_len = MAC_ADDR_LEN + MAC_ADDR_LEN + sizeof(struct rtl_tag) + LLDP_ETHERTYPE_LENGTH + len; + 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++) { - LLDP_O->payload[port_position] = '1' + port; + LLDP_O->payload[port_position] = '0' + machine.log_to_phys_port[port]; LLDP_O->rtl_tag.pmask = HTONS((uint16_t)1 << port); tcpip_output(); @@ -140,35 +137,22 @@ void lldp_set_rtl_wrapper(void) __banked { LLDP_O->ether_type = HTONS(LLDP_ETHERTYPE); } -void lldp_sysname(uint8_t *p, uint16_t *len) __banked +uint8_t lldp_sysname(__xdata uint8_t *p) __banked { - uint16_t hostname_length = 0; - __xdata char *hp = hostname; - - p[(*len)++] = LLDP_TYPE(LLDP_SYSNAME_TLV_TYPE); - p[(*len)++] = 0; - - while (*hp) - { - hostname_length++; - p[(*len)++] = *hp++; - } - p[(*len)-hostname_length-1] = hostname_length; + 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; } -void lldp_sysdesc(uint8_t *p, uint16_t *len) __banked +uint8_t lldp_sysdesc(__xdata uint8_t *p) __banked { - uint16_t machine_name_length = 0; - const char *mp = machine.machine_name; + uint16_t machine_name_length = strlen(machine.machine_name); - p[(*len)++] = LLDP_TYPE(LLDP_SYSDESC_TLV_TYPE); - p[(*len)++] = 0; - - while (*mp) - { - machine_name_length++; - p[(*len)++] = *mp++; - } - p[(*len)-machine_name_length-1] = machine_name_length; + *p++ = LLDP_TYPE(LLDP_SYSDESC_TLV_TYPE); + *p++ = machine_name_length; + memcpyc(p, machine.machine_name, machine_name_length); + return machine_name_length + 2; } diff --git a/rtl837x_lldp.h b/rtl837x_lldp.h index 519cfdb8..b441d8c4 100644 --- a/rtl837x_lldp.h +++ b/rtl837x_lldp.h @@ -30,7 +30,7 @@ void lldp_tick(void) __banked; void lldp_send(void) __banked; void lldp_set_addresses(void) __banked; void lldp_set_rtl_wrapper(void) __banked; -void lldp_sysname(uint8_t *p, uint16_t *len) __banked; -void lldp_sysdesc(uint8_t *p, uint16_t *len) __banked; +uint8_t lldp_sysname(__xdata uint8_t *p) __banked; +uint8_t lldp_sysdesc(__xdata uint8_t *p) __banked; #endif From cedc795b827b426b6c1e0296681342cbc89af977 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Sat, 12 Sep 2026 19:31:57 +0100 Subject: [PATCH 06/13] Adding UI options to enable/disable LLDP --- html/app.js | 11 +++++++++-- html/index.html | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/html/app.js b/html/app.js index 494a8463..9bbafcc9 100644 --- a/html/app.js +++ b/html/app.js @@ -77,7 +77,7 @@ sc_title:"Storm control",sc_h:"per port and traffic type, empty = off",sc_bcast: sc_ucast:"Unknown unicast",sc_umcast:"Unknown multicast",sc_err:"Storm limit must be 1-1048575 pps or 1-10000000 kbit/s", sc_note:"Frames of a type above its limit are dropped where they enter the switch.", sy_network:"Network",sy_dhcp:"Use DHCP",sy_dhcp_t:"Request address via DHCP",sy_services:"Services", -sy_igmp:"IGMP snooping",sy_sysip:"server IP",sy_server:"Server",sy_port:"Port", +sy_igmp:"IGMP snooping",sy_sysip:"server IP",sy_server:"Server",sy_port:"Port",sy_lldp:"LLDP", sy_sesstmo:"Session timeout (s)",sy_sesstmo_err:"Session timeout must be 1-65535 s", sy_services_note:"Service state reflects the startup config; runtime state is not readable.", sy_password:"Admin password",sy_newpw:"New password",sy_repeat:"Repeat",sy_pwapply:"Change password", @@ -1995,15 +1995,18 @@ function sysLoad(){ cfgReload(); } function cfgParseKnown(txt){ - var igmp=false,syslog=false; + var igmp=false,lldp=false,syslog=false; txt.split(/\r?\n/).forEach(function(l){ l=l.trim(); if(/^igmp on$/.test(l))igmp=true; if(/^igmp off$/.test(l))igmp=false; + if(/^lldp on$/.test(l))lldp=true; + if(/^lldp off$/.test(l))lldp=false; if(/^syslog on$/.test(l))syslog=true; if(/^syslog off$/.test(l))syslog=false; }); $("sy-igmp").checked=igmp; + $("sy-lldp").checked=lldp; $("sy-syslog").checked=syslog; } $("sy-apply").addEventListener("click",function(){ @@ -2031,6 +2034,10 @@ $("sy-igmp").addEventListener("change",function(){ var el=this; postCmd("igmp "+(el.checked?"on":"off")).catch(function(){el.checked=!el.checked}); }); +$("sy-lldp").addEventListener("change",function(){ + var el=this; + postCmd("lldp "+(el.checked?"on":"off")).catch(function(){el.checked=!el.checked}); +}); $("sy-syslog").addEventListener("change",function(){ var cmds=[],el=this; if(el.checked){ diff --git a/html/index.html b/html/index.html index 54ab11d2..48e430ac 100644 --- a/html/index.html +++ b/html/index.html @@ -386,6 +386,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}

Services

+
From 4f1a2f04d8a844051b6a5944488e247146947b9f Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Tue, 15 Sep 2026 15:00:23 +0100 Subject: [PATCH 07/13] Allowing commands like `port 1 lldp block` and `port 2 lldp permit` so we are able to enable/disable lldp transmission on a specific port. --- cmd_parser.c | 9 +++++++++ rtl837x_common.h | 1 + rtl837x_lldp.c | 9 ++++++--- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cmd_parser.c b/cmd_parser.c index 8c21d9f8..9c52e1f9 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -881,6 +881,7 @@ void parse_port(void) cmd_error("\nUsage:" \ "\nport [show|on|off]" \ "\nport [10m|100m|1g|2g5|duplex] [half|full]" \ + "\nport lldp [block|permit]" \ "\nport name [custom port name]\n"); return; } @@ -966,6 +967,14 @@ void parse_port(void) else phy_settings.duplex = PHY_DUPLEX_HALF; phy_set_duplex(); + } else if (cmd_compare(2, "lldp")) { + if (cmd_compare(3, "block")){ + lldp_port_status &= ~(1 << phy_settings.port); + } else if (cmd_compare(3, "permit")) { + lldp_port_status |= 1 << phy_settings.port; + } else { + print_string ("Unknown port lldp [block|permit] command\n"); + } } else { cmd_error("Unknown port command\n"); } diff --git a/rtl837x_common.h b/rtl837x_common.h index 9b601730..8fa68477 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -131,6 +131,7 @@ struct flash_region_t { }; extern __xdata char port_names[9][PORT_NAME_SIZE]; +extern __xdata uint16_t lldp_port_status; extern __xdata bool stp_enabled; diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c index e426fdef..57f27150 100644 --- a/rtl837x_lldp.c +++ b/rtl837x_lldp.c @@ -14,6 +14,7 @@ extern __xdata bool lldp_enabled; extern __code const struct machine machine; __xdata uint8_t lldp_seconds; +__xdata uint16_t lldp_port_status = 0xffff; void lldp_init(void) __banked { @@ -109,10 +110,12 @@ void lldp_send(void) __banked __reentrant for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { - LLDP_O->payload[port_position] = '0' + machine.log_to_phys_port[port]; - LLDP_O->rtl_tag.pmask = HTONS((uint16_t)1 << port); + if ((1 << machine.log_to_phys_port[port]) & lldp_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(); + tcpip_output(); + } } } From 58fd1a3cccc4e13445feac9b2591f40699b51d4b Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Tue, 15 Sep 2026 15:43:16 +0100 Subject: [PATCH 08/13] Adding lldp.json to the simulator Moving the global on/off onto the LLDP page Building a table of LLDP status from the S ports. Later on, if we wish to implement an lldp receiver we can display the remote details here Adding support to the cmd parser for the lldp commands --- cmd_parser.c | 4 ++-- html/app.js | 54 ++++++++++++++++++++++++++++++++++++++--------- html/index.html | 13 ++++++++++-- httpd/httpd.c | 2 ++ httpd/page_impl.c | 20 +++++++++++++++++- httpd/page_impl.h | 1 + rtl837x_common.h | 2 +- rtl837x_lldp.c | 6 +++--- tools/httpd_sim.c | 18 +++++++++++++++- 9 files changed, 100 insertions(+), 20 deletions(-) diff --git a/cmd_parser.c b/cmd_parser.c index 9c52e1f9..8262463b 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -969,9 +969,9 @@ void parse_port(void) phy_set_duplex(); } else if (cmd_compare(2, "lldp")) { if (cmd_compare(3, "block")){ - lldp_port_status &= ~(1 << phy_settings.port); + lldp_physical_port_status &= ~(1 << phy_settings.port); } else if (cmd_compare(3, "permit")) { - lldp_port_status |= 1 << phy_settings.port; + lldp_physical_port_status |= 1 << phy_settings.port; } else { print_string ("Unknown port lldp [block|permit] command\n"); } diff --git a/html/app.js b/html/app.js index 9bbafcc9..d9c9a1e8 100644 --- a/html/app.js +++ b/html/app.js @@ -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", @@ -77,7 +78,7 @@ sc_title:"Storm control",sc_h:"per port and traffic type, empty = off",sc_bcast: sc_ucast:"Unknown unicast",sc_umcast:"Unknown multicast",sc_err:"Storm limit must be 1-1048575 pps or 1-10000000 kbit/s", sc_note:"Frames of a type above its limit are dropped where they enter the switch.", sy_network:"Network",sy_dhcp:"Use DHCP",sy_dhcp_t:"Request address via DHCP",sy_services:"Services", -sy_igmp:"IGMP snooping",sy_sysip:"server IP",sy_server:"Server",sy_port:"Port",sy_lldp:"LLDP", +sy_igmp:"IGMP snooping",sy_sysip:"server IP",sy_server:"Server",sy_port:"Port", sy_sesstmo:"Session timeout (s)",sy_sesstmo_err:"Session timeout must be 1-65535 s", sy_services_note:"Service state reflects the startup config; runtime state is not readable.", sy_password:"Admin password",sy_newpw:"New password",sy_repeat:"Repeat",sy_pwapply:"Change password", @@ -788,6 +789,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+)+$/, @@ -798,7 +800,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))$/, ]; @@ -860,6 +862,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:"M7 12h6"}, {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"}, @@ -1835,6 +1838,44 @@ 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=""; + if(tb.rows.length!==S.n){ + tb.innerHTML=""; + for(var i=0;i +
+

LLDP

+ +
+ +
Port NumberNameSend Frames
+
+
+

Port isolation

A frame entering a port may only leave through the ports ticked in its row.

@@ -386,8 +397,6 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}

Services

-
diff --git a/httpd/httpd.c b/httpd/httpd.c index 770eaa39..aec2eaf1 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -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")) { diff --git a/httpd/page_impl.c b/httpd/page_impl.c index fe5ccced..f9c55b83 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -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_physical_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,6 +736,22 @@ 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, ",\"port_status\": ["); + + for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { + bool_to_html((1 << machine.log_to_phys_port[port]) & lldp_physical_port_status); + slen += strtox(outbuf + slen, ","); + } + + slen += strtox(outbuf + slen, "]}"); +} diff --git a/httpd/page_impl.h b/httpd/page_impl.h index d6690dc6..fd51c8c6 100644 --- a/httpd/page_impl.h +++ b/httpd/page_impl.h @@ -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. diff --git a/rtl837x_common.h b/rtl837x_common.h index 8fa68477..74425fbc 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -131,7 +131,7 @@ struct flash_region_t { }; extern __xdata char port_names[9][PORT_NAME_SIZE]; -extern __xdata uint16_t lldp_port_status; +extern __xdata uint16_t lldp_physical_port_status; extern __xdata bool stp_enabled; diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c index 57f27150..3e484cbe 100644 --- a/rtl837x_lldp.c +++ b/rtl837x_lldp.c @@ -14,7 +14,7 @@ extern __xdata bool lldp_enabled; extern __code const struct machine machine; __xdata uint8_t lldp_seconds; -__xdata uint16_t lldp_port_status = 0xffff; +__xdata uint16_t lldp_physical_port_status = 0xffff; void lldp_init(void) __banked { @@ -110,9 +110,9 @@ void lldp_send(void) __banked __reentrant for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { - if ((1 << machine.log_to_phys_port[port]) & lldp_port_status) { + if ((1 << machine.log_to_phys_port[port]) & lldp_physical_port_status) { LLDP_O->payload[port_position] = '0' + machine.log_to_phys_port[port]; - LLDP_O->rtl_tag.pmask = HTONS((uint16_t)1 << port); + LLDP_O->rtl_tag.pmask = HTONS((uint16_t)1 << machine.log_to_phys_port[port]); tcpip_output(); } diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index 76c088c3..efa367c6 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -454,6 +454,16 @@ void send_mtu(int s) json_object_put(mtus); } +void send_lldp(int s) +{ + char *header = "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n\r\n"; + + char *body = "{\"on\": true, \"port_status\":[true,true,false,true,true,true,true,false,false]}"; + + write(s, header, strlen(header)); + write(s, body, strlen(body)); +} void send_cmd_log(int s) { @@ -468,7 +478,6 @@ void send_cmd_log(int s) } } - void send_config(int s) { char *header = "HTTP/1.1 200 OK\r\n" @@ -701,6 +710,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) From 72c2625121769bd52f6f64aa4891c33d73e8dea2 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Tue, 15 Sep 2026 21:21:23 +0100 Subject: [PATCH 09/13] Icon from https://github.com/logicog/RTLPlayground/pull/429#issuecomment-5687458129 --- html/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/app.js b/html/app.js index d9c9a1e8..3da29b5a 100644 --- a/html/app.js +++ b/html/app.js @@ -862,7 +862,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:"M7 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"}, From 2dfe531fe00d9904930ba69f0ea7c9cedddb2789 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Fri, 25 Sep 2026 19:34:47 +0100 Subject: [PATCH 10/13] Fixing missing parts to make the tests pass boot_to_html outputs 1 and 0 not true and false, making the sim match the real implementation. lldp.json has a hanging , in the array; so overwriting it --- httpd/page_impl.c | 6 ++---- test/env_tables.c | 3 +++ test/test_httpd_tx.c | 1 + tools/httpd_sim.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/httpd/page_impl.c b/httpd/page_impl.c index f9c55b83..0d054be9 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -743,18 +743,16 @@ void send_lldp(void) slen += strtox(outbuf + slen, "{\"on\":"); bool_to_html(lldp_enabled); - slen += strtox(outbuf + slen, ",\"port_status\": ["); + slen += strtox(outbuf + slen, ",\"port_status\":["); for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { bool_to_html((1 << machine.log_to_phys_port[port]) & lldp_physical_port_status); slen += strtox(outbuf + slen, ","); } - + slen--; //overwrite the last comma slen += strtox(outbuf + slen, "]}"); } - - void send_eee(void) { dbg_string("send_eee called\nsending EEE status\n"); diff --git a/test/env_tables.c b/test/env_tables.c index 274a67d5..3b246948 100644 --- a/test/env_tables.c +++ b/test/env_tables.c @@ -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_physical_port_status; + uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask; struct uip_eth_addr uip_ethaddr = { .addr = { 0x02, 0x11, 0x22, 0x33, 0x44, 0x55 } }; diff --git a/test/test_httpd_tx.c b/test/test_httpd_tx.c index c7ccf09c..4df05472 100644 --- a/test/test_httpd_tx.c +++ b/test/test_httpd_tx.c @@ -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; diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index efa367c6..53a4d403 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -459,7 +459,7 @@ void send_lldp(int s) char *header = "HTTP/1.1 200 OK\r\n" "Content-Type: application/json; charset=UTF-8\r\n\r\n"; - char *body = "{\"on\": true, \"port_status\":[true,true,false,true,true,true,true,false,false]}"; + char *body = "{\"on\": 1,\"port_status\":[1,1,0,1,1,1,1,0,0]}"; write(s, header, strlen(header)); write(s, body, strlen(body)); From 9b2bd7f1b82efc113471ead52a861af973d0dff5 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Thu, 1 Oct 2026 11:37:13 +0100 Subject: [PATCH 11/13] The tick rate has been changed now so we'll have to approximate timing with ticks Fixing up the log_to_phys_port assignment 2000 is about every 10 seconds, so scaling up to once every 30ish Removing the attempt to block lldp frames, This does not work however the hard work has been done for us in the #514 so we can borrow that work later on --- cmd_parser.c | 4 ++-- html/app.js | 4 +--- httpd/page_impl.c | 4 ++-- rtl837x_common.h | 2 +- rtl837x_lldp.c | 16 ++++++++-------- rtl837x_lldp.h | 2 +- rtlplayground.c | 4 ---- test/env_tables.c | 2 +- 8 files changed, 16 insertions(+), 22 deletions(-) diff --git a/cmd_parser.c b/cmd_parser.c index 8262463b..3cb7724c 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -969,9 +969,9 @@ void parse_port(void) phy_set_duplex(); } else if (cmd_compare(2, "lldp")) { if (cmd_compare(3, "block")){ - lldp_physical_port_status &= ~(1 << phy_settings.port); + lldp_logical_port_status &= ~(1 << phy_settings.port); } else if (cmd_compare(3, "permit")) { - lldp_physical_port_status |= 1 << phy_settings.port; + lldp_logical_port_status |= 1 << phy_settings.port; } else { print_string ("Unknown port lldp [block|permit] command\n"); } diff --git a/html/app.js b/html/app.js index 3da29b5a..00af6434 100644 --- a/html/app.js +++ b/html/app.js @@ -1872,10 +1872,8 @@ $("lldp_en").addEventListener("change",function(){ var el=this; postCmd("lldp "+(el.checked?"on":"off")).catch(function(){el.checked=!el.checked}); }); - tabHooks.lldp={enter:needPorts(lldpLoad)}; - function eeeFlags(bits){ var b=parseInt(bits,2); return["100M","1G","2.5G"].map(function(s,i){ @@ -2036,7 +2034,7 @@ function sysLoad(){ cfgReload(); } function cfgParseKnown(txt){ - var igmp=false,lldp=false,syslog=false; + var igmp=false,syslog=false; txt.split(/\r?\n/).forEach(function(l){ l=l.trim(); if(/^igmp on$/.test(l))igmp=true; diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 0d054be9..9a818dcf 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -49,7 +49,7 @@ 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_physical_port_status; +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"; @@ -746,7 +746,7 @@ void send_lldp(void) slen += strtox(outbuf + slen, ",\"port_status\":["); for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { - bool_to_html((1 << machine.log_to_phys_port[port]) & lldp_physical_port_status); + bool_to_html((1 << port) & lldp_logical_port_status); slen += strtox(outbuf + slen, ","); } slen--; //overwrite the last comma diff --git a/rtl837x_common.h b/rtl837x_common.h index 74425fbc..a36d41fc 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -131,7 +131,7 @@ struct flash_region_t { }; extern __xdata char port_names[9][PORT_NAME_SIZE]; -extern __xdata uint16_t lldp_physical_port_status; +extern __xdata uint16_t lldp_logical_port_status; extern __xdata bool stp_enabled; diff --git a/rtl837x_lldp.c b/rtl837x_lldp.c index 3e484cbe..84208433 100644 --- a/rtl837x_lldp.c +++ b/rtl837x_lldp.c @@ -13,22 +13,22 @@ extern __xdata bool lldp_enabled; extern __code const struct machine machine; -__xdata uint8_t lldp_seconds; -__xdata uint16_t lldp_physical_port_status = 0xffff; +__xdata uint16_t lldp_ticks; +__xdata uint16_t lldp_logical_port_status = 0xffff; void lldp_init(void) __banked { - lldp_seconds = 0; + lldp_ticks = 0; } void lldp_tick(void) __banked { - lldp_seconds++; + lldp_ticks++; - if (lldp_seconds < LLDP_TX_INTERVAL_SEC) + if (lldp_ticks < LLDP_TX_INTERVAL_TICKS) return; - lldp_seconds = 0; + lldp_ticks = 0; if(lldp_enabled == 1) lldp_send(); @@ -110,9 +110,9 @@ void lldp_send(void) __banked __reentrant for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { - if ((1 << machine.log_to_phys_port[port]) & lldp_physical_port_status) { + 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 << machine.log_to_phys_port[port]); + LLDP_O->rtl_tag.pmask = HTONS((uint16_t)1 << port); tcpip_output(); } diff --git a/rtl837x_lldp.h b/rtl837x_lldp.h index b441d8c4..298775c6 100644 --- a/rtl837x_lldp.h +++ b/rtl837x_lldp.h @@ -8,7 +8,7 @@ #define LLDP_ETHERTYPE_LENGTH 2 #define LLDP_MAX_FRAME 256 #define MAC_ADDR_LEN 6 -#define LLDP_TX_INTERVAL_SEC 30 +#define LLDP_TX_INTERVAL_TICKS 6000 #define LLDP_PORT_ID_TLV_TYPE 2 #define LLDP_PORT_ID_TLV_LENGTH 0x02 diff --git a/rtlplayground.c b/rtlplayground.c index 4fb22009..43ff83f0 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -1068,10 +1068,6 @@ void handle_rx(void) if (uip_len) { tcpip_output(); } - } else if (lldp_enabled && uip_buf[0] == 0x01 && uip_buf[1] == 0x80 && uip_buf[2] == 0xc2 - && uip_buf[3] == 0x00 && uip_buf[4] == 0x00 && uip_buf[5] == 0x0e && uip_buf[12] == 0x88 - && uip_buf[13] == 0xcc) { //LLDP Mac + EtherType check - // LLDP packets shouldn't be passed elsewhere } else if (ETH_IN->ether_type == HTONS(0x0806)) { // ARP uip_arp_arpin(); if (uip_len) { diff --git a/test/env_tables.c b/test/env_tables.c index 3b246948..41694b72 100644 --- a/test/env_tables.c +++ b/test/env_tables.c @@ -85,7 +85,7 @@ struct flash_region_t flash_region; struct syslog_state syslog_state; bool stp_enabled; bool lldp_enabled; -uint16_t lldp_physical_port_status; +uint16_t lldp_logical_port_status; uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask; From dda873a633498aa4c27737714b49943c09cc14a6 Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Tue, 6 Oct 2026 07:58:48 +0100 Subject: [PATCH 12/13] Moving lldp cmd parsing to be above the is_sfp check to take precedence Needed to increase buffer size for this to send app.js The logical port status is what is returned in the array, for non-sequential systems we need to map through physToLog rather than just subtracting 1 physToLog is 1 based! not 0; also fixing up typecasts Reimplementing using an array of objects to represent ports. The previous approach of a bitmask of logical ports, was just too simple on devices such as the SWGT024_V2_0_MANAGED which has gaps in the port assignments. --- cmd_parser.c | 16 ++++++++-------- html/app.js | 26 ++++++++------------------ httpd/page_impl.c | 30 +++++++++++++++++++++++------- tools/httpd_sim.c | 14 +++++++++++++- tools/httpd_sim.h | 2 +- 5 files changed, 53 insertions(+), 35 deletions(-) diff --git a/cmd_parser.c b/cmd_parser.c index 3cb7724c..8cfc7d4b 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -914,6 +914,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 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")) { @@ -967,14 +975,6 @@ void parse_port(void) else phy_settings.duplex = PHY_DUPLEX_HALF; phy_set_duplex(); - } else if (cmd_compare(2, "lldp")) { - if (cmd_compare(3, "block")){ - lldp_logical_port_status &= ~(1 << phy_settings.port); - } else if (cmd_compare(3, "permit")) { - lldp_logical_port_status |= 1 << phy_settings.port; - } else { - print_string ("Unknown port lldp [block|permit] command\n"); - } } else { cmd_error("Unknown port command\n"); } diff --git a/html/app.js b/html/app.js index 00af6434..d5391274 100644 --- a/html/app.js +++ b/html/app.js @@ -1843,36 +1843,26 @@ function lldpLoad(){ return getJSON("/lldp.json").then(function(s){ $("lldp_en").checked=s.on; var tb=$("lldp_table").tBodies[0];tb.innerHTML=""; - if(tb.rows.length!==S.n){ - tb.innerHTML=""; - for(var i=0;i> i)); + slen += strtox(outbuf + slen, "\"}"); + + if (i < machine.max_port) + char_to_html(','); + else + char_to_html(']'); + } + char_to_html('}'); } void send_eee(void) diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index 53a4d403..fba9ffae 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -459,7 +459,19 @@ void send_lldp(int s) char *header = "HTTP/1.1 200 OK\r\n" "Content-Type: application/json; charset=UTF-8\r\n\r\n"; - char *body = "{\"on\": 1,\"port_status\":[1,1,0,1,1,1,1,0,0]}"; + //portNum represents physical ports + char *body = "{\"on\": 1,\"portStatus\":[\ + {\"portNum\":1,\"logPort\":1,\"name\":\"PC\",\"enabled\":1},\ + {\"portNum\":2,\"logPort\":2,\"name\":\"\",\"enabled\":1},\ + {\"portNum\":3,\"logPort\":3,\"name\":\"\",\"enabled\":1},\ + {\"portNum\":4,\"logPort\":4,\"name\":\"\",\"enabled\":0},\ + {\"portNum\":5,\"logPort\":5,\"name\":\"\",\"enabled\":1},\ + {\"portNum\":6,\"logPort\":6,\"name\":\"TV\",\"enabled\":1},\ + {\"portNum\":7,\"logPort\":7,\"name\":\"\",\"enabled\":0},\ + {\"portNum\":8,\"logPort\":8,\"name\":\"Router\",\"enabled\":1},\ + {\"portNum\":9,\"logPort\":9,\"name\":\"\",\"enabled\":1},\ + {\"portNum\":10,\"logPort\":10,\"name\":\"Uplink\",\"enabled\":1}\ + ]}"; write(s, header, strlen(header)); write(s, body, strlen(body)); diff --git a/tools/httpd_sim.h b/tools/httpd_sim.h index 49c078aa..df59ff19 100644 --- a/tools/httpd_sim.h +++ b/tools/httpd_sim.h @@ -3,7 +3,7 @@ #include -#define BUFFER_SIZE 131072 +#define BUFFER_SIZE 262144 struct Server { int domain; From eff3f42ae1d6556adf2174faac6088e1290bb5aa Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Fri, 9 Oct 2026 12:43:15 +0100 Subject: [PATCH 13/13] Rewriting sim implementation to be data driven The value for enabled should be an int, not a string --- html/app.js | 2 +- httpd/page_impl.c | 5 ++--- tools/httpd_sim.c | 36 +++++++++++++++++++++--------------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/html/app.js b/html/app.js index d5391274..3e95e0e7 100644 --- a/html/app.js +++ b/html/app.js @@ -1852,7 +1852,7 @@ function lldpLoad(){ postCmd("port "+p.portNum+" lldp "+(this.checked?"permit":"block")) .then(function(){setTimeout(lldpLoad,300)}).catch(function(){}); }}),h("i")]); - sw.firstChild.checked=p.enabled; + sw.firstChild.checked=p.permitted; tr.insertCell().appendChild(sw); }); }); diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 11110ef1..1a51580f 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -755,11 +755,10 @@ void send_lldp(void) 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, "\""); - slen += strtox(outbuf + slen, ",\"enabled\":\""); + slen += strtox(outbuf + slen, "\",\"permitted\":"); bool_to_html(1 & (lldp_logical_port_status >> i)); - slen += strtox(outbuf + slen, "\"}"); + slen += strtox(outbuf + slen, "}"); if (i < machine.max_port) char_to_html(','); diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index fba9ffae..5fea029f 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -447,7 +447,7 @@ 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)); @@ -456,25 +456,31 @@ void send_mtu(int s) 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"; - //portNum represents physical ports - char *body = "{\"on\": 1,\"portStatus\":[\ - {\"portNum\":1,\"logPort\":1,\"name\":\"PC\",\"enabled\":1},\ - {\"portNum\":2,\"logPort\":2,\"name\":\"\",\"enabled\":1},\ - {\"portNum\":3,\"logPort\":3,\"name\":\"\",\"enabled\":1},\ - {\"portNum\":4,\"logPort\":4,\"name\":\"\",\"enabled\":0},\ - {\"portNum\":5,\"logPort\":5,\"name\":\"\",\"enabled\":1},\ - {\"portNum\":6,\"logPort\":6,\"name\":\"TV\",\"enabled\":1},\ - {\"portNum\":7,\"logPort\":7,\"name\":\"\",\"enabled\":0},\ - {\"portNum\":8,\"logPort\":8,\"name\":\"Router\",\"enabled\":1},\ - {\"portNum\":9,\"logPort\":9,\"name\":\"\",\"enabled\":1},\ - {\"portNum\":10,\"logPort\":10,\"name\":\"Uplink\",\"enabled\":1}\ - ]}"; + 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)); - write(s, body, strlen(body)); + + 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)