diff --git a/cmd_parser.c b/cmd_parser.c index 21ff7a8..4e8107e 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -1524,6 +1524,29 @@ void cmd_parser(void) __banked igmp_show(); else igmp_setup(); // Reverts to default with IP-MC being flooded + } else if (cmd_compare(0, "hostname")) { + /* "hostname" alone reports the current name; "hostname " + * sets it, sanitized to JSON-safe printable ASCII. A name with + * spaces would tokenize into several words - reject it instead + * of silently keeping the first one. */ + if (cmd_words_len == 1) { + print_string_x(hostname); + write_char('\n'); + } else if (cmd_words_len == 2) { + __xdata uint8_t *hp = &cmd_buffer[cmd_words_b[1]]; + __xdata char *dst = hostname; + for (uint8_t hn = 0; hn < sizeof(hostname) - 1; hn++) { + uint8_t c = *hp++; + if (c == '\0' || c == '\r' || c == '\n') + break; + if (c < 0x20 || c > 0x7e || c == '"' || c == '\\') + c = '.'; + *dst++ = c; + } + *dst = '\0'; + } else { + print_string("Error: hostname [name] - the name must not contain spaces\n"); + } } else if (cmd_compare(0, "stp")) { if (cmd_compare(1, "on")) { print_string("STP enabled\n"); diff --git a/html/config.js b/html/config.js index 6efa283..8af3fba 100644 --- a/html/config.js +++ b/html/config.js @@ -25,6 +25,7 @@ const conf_cmds = [ /^igmp\s+(on|off)$/, /^mtu\s+\d{1,2}\s+\d+$/, /^bw\s+(in|out)\s+\d{1,2}\s+\S+$/, + /^hostname\s+.{1,23}$/, ]; const conf_overwrite = [ /^ip\b/, @@ -49,6 +50,7 @@ const conf_overwrite = [ /^igmp\b/, /^mtu\s+\d{1,2}\b/, /^bw\s+(in|out)\s+\d{1,2}\b/, + /^hostname\b/, ]; function parseConf(s){ diff --git a/html/i18n.js b/html/i18n.js index bab49dc..a8d43cb 100644 --- a/html/i18n.js +++ b/html/i18n.js @@ -138,6 +138,9 @@ var LANG = { sys_tab_console: 'Console', sys_heading: 'System Settings', sys_ip: 'IP address:', + sys_model: 'Model:', + sys_hostname: 'Hostname:', + sys_apply: 'Apply', sys_netmask: 'Netmask:', sys_gateway: 'Gateway:', sys_language: 'Language:', @@ -317,6 +320,9 @@ var LANG = { sys_tab_console: 'コンソール', sys_heading: 'システム設定', sys_ip: 'IP アドレス:', + sys_model: 'モデル:', + sys_hostname: 'ホスト名:', + sys_apply: '適用', sys_netmask: 'ネットマスク:', sys_gateway: 'ゲートウェイ:', sys_language: '言語:', @@ -496,6 +502,9 @@ var LANG = { sys_tab_console: '控制台', sys_heading: '系统设置', sys_ip: 'IP 地址:', + sys_model: '型号:', + sys_hostname: '主机名:', + sys_apply: '应用', sys_netmask: '子网掩码:', sys_gateway: '网关:', sys_language: '语言:', diff --git a/html/system.html b/html/system.html index d1c908a..4b147c1 100644 --- a/html/system.html +++ b/html/system.html @@ -25,6 +25,15 @@

System Settings

+
+
+
+
+
+
+
+
+
diff --git a/html/system.js b/html/system.js index 091ff61..0add17f 100644 --- a/html/system.js +++ b/html/system.js @@ -48,6 +48,14 @@ async function cmdSub() { } +async function hostSub() { + const h = document.getElementById("hostname").value; + try { await fetch('/cmd', { method: 'POST', body: "hostname " + h }); } + catch(err) { console.error(`Error: ${err}`); } + fetchIP(); +} + + async function sendConfig(c) { if (isSaving) return; isSaving = true; @@ -123,6 +131,8 @@ function fetchIP() { document.getElementById("ip").value=s.ip_address; document.getElementById("netmask").value=s.ip_netmask; document.getElementById("gw").value=s.ip_gateway; + document.getElementById("hostname").value=s.hostname; + document.getElementById("model").textContent=s.hw_ver; clearInterval(systemInterval); // Fetch and populate the config textbox fetchConfig().then((configText) => { diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 5a3433d..cb9125b 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -252,6 +252,12 @@ void send_basic_info(void) byte_to_html(uip_ethaddr.addr[3]); char_to_html(':'); byte_to_html(uip_ethaddr.addr[4]); char_to_html(':'); byte_to_html(uip_ethaddr.addr[5]); + slen += strtox(outbuf + slen, "\",\"hostname\":\""); + { + __xdata char *hp = hostname; /* sanitized on ingest, emit verbatim */ + while (*hp) + char_to_html(*hp++); + } slen += strtox(outbuf + slen, "\",\"sw_ver\":\""); slen += strtox(outbuf + slen, VERSION_SW); slen += strtox(outbuf + slen, "\",\"build_date\":\""); diff --git a/rtl837x_common.h b/rtl837x_common.h index 9aa9af4..66df88a 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -115,6 +115,11 @@ struct flash_region_t { extern __xdata char port_names[9][PORT_NAME_SIZE]; +/* System hostname (device identity). Set via `hostname ` and the System + * Settings page, reported in /information.json. Other modules (e.g. LLDP, which + * advertises it as the System Name TLV) read it from here. */ +extern __xdata char hostname[24]; + extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2]; extern __xdata struct uip_eth_addr uip_ethaddr; diff --git a/rtlplayground.c b/rtlplayground.c index 27a6e3f..ce73963 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -25,6 +25,7 @@ #include "machine.h" #include "phy.h" #include "syslog.h" +#include "httpd/page_impl.h" extern __code const struct machine machine; extern __xdata uint32_t flash_size; @@ -120,6 +121,7 @@ __xdata uint16_t management_vlan; __xdata uint8_t tx_seq; __xdata uint8_t stpEnabled; +__xdata char hostname[24]; /* device hostname, default set at boot, see rtl837x_common.h */ __code uint16_t bit_mask[16] = { 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, @@ -2004,6 +2006,34 @@ void check_and_flash_update_image(void) } } +/* Give the switch a name carrying the tail of its MAC, so several of them on + * one network are distinguishable out of the box. Called after the startup + * config has been replayed and returns at once if that config already set a + * name, so a configured switch does no work for it (suggested in review). + * + * Written without a loop on purpose. Locals - counters and pointers alike - + * land in the 8051's internal-RAM overlay, and on an image with LACP and STP + * both enabled that overlay is exhausted: a loop here makes the linker fail + * with "Could not get 8 consecutive bytes in internal RAM for area OSEG". + * Moving the code into its own function does not help; the overlay is shared + * across the whole image. Hoisting the locals to xdata does not help either, + * because itohex() is inline and brings its own frame. */ +void set_hostname_default(void) +{ + if (hostname[0] != '\0') + return; + + strcpy((__xdata uint8_t *)hostname, "RTLPlayground-"); + hostname[14] = hex[uip_ethaddr.addr[3] >> 4]; + hostname[15] = hex[uip_ethaddr.addr[3] & 0xf]; + hostname[16] = hex[uip_ethaddr.addr[4] >> 4]; + hostname[17] = hex[uip_ethaddr.addr[4] & 0xf]; + hostname[18] = hex[uip_ethaddr.addr[5] >> 4]; + hostname[19] = hex[uip_ethaddr.addr[5] & 0xf]; + hostname[20] = '\0'; +} + + void main(void) { ticks = 0; @@ -2168,6 +2198,8 @@ void main(void) early_boot_handle_button(); execute_config(); + /* After the config: a name from it wins, otherwise derive one. */ + set_hostname_default(); print_cmd_prompt(); idle_ready = 1;