syslog: make the destination port configurable

The port was frozen at 514 in the call that opens the connection, so a
collector listening anywhere else was simply out of reach, and plenty do,
because 514 is privileged and containerised collectors usually get a high
port instead. Working around it meant putting a proxy in front, which then
rewrites the source address and hides which device actually sent the line.

Add `syslog port <n>`. The default stays 514, so nothing changes for an
existing configuration. Setting it tears the connection down and builds a
new one, the same dance the address already does, because uip_udp_new()
freezes the port into the connection.

The value is parsed here rather than through atoi_short(), which reports
"no digits" but not overflow: 65540 would quietly wrap to 4 and we would
open a connection to a port nobody asked for. Checking before each multiply
keeps it in 16-bit arithmetic. Zero then covers all three ways to be wrong -
no digits, past 65535, or an explicit "0", which is not a port either.

Whitelist the new command in config.js as well, next to `syslog ip`, or the
line is dropped without a word when the configuration is saved and the
setting quietly fails to survive a reboot. The dedup entry has to precede
the general `^syslog\b` one, otherwise it would deduplicate against
`syslog on`.

itoa_short() is new: itoa() only handles a byte and print_short() prints
hex, so there was no way to show a port as a decimal number. Kept separate
from itoa() rather than widening it, because every existing caller passes a
byte and would start paying for 16-bit divisions it does not need.

The scratch variables for the parser live in xdata. Locals there would take
internal RAM, and this image has none left: data ends at 0x77, the overlay
occupies 0x78-0x7f and the stack starts at 0x80. Measured: no module's DSEG
grew.
This commit is contained in:
d00f committed 2026-08-30 22:38:31 +02:00
1 parent 9d4e531306
commit cb43b3cbd6
6 files changed
+101 -4

No files matched your search

+65 -2
View File
@@ -65,6 +65,13 @@ __xdata char save_cmd;
__xdata uint8_t ip[4];
/* Scratch for parse_syslog()'s port parser, in xdata: locals here would take
* internal RAM the linker has none of. */
__xdata uint16_t syslog_port_scratch;
__xdata uint8_t syslog_port_idx;
__xdata uint8_t syslog_port_digit;
__xdata uint8_t syslog_port_was_on;
// These variables combined create a Fixed-capacity vector/bounded buffer.
// `N_WORDS`: The total number of command arguments that can be tracked.
// `cmd_words_len` stores the number of arguments found inside `cmd_buffer`
@@ -1411,6 +1418,7 @@ void parse_syslog(void)
if (syslog_state.enabled) {
print_string("enabled, sending to ");
print_ip(syslog_state.server_ip);
write_char(':'); itoa_short(syslog_state.server_port);
write_char('\n');
} else {
print_string("disabled\n");
@@ -1439,11 +1447,66 @@ void parse_syslog(void)
} else {
print_string("Invalid IP address\n");
}
} else if (cmd_compare(1, "port")) {
if (cmd_words_len < 3) { // no additional argument -> print current port
print_string("Current syslog port: ");
itoa_short(syslog_state.server_port); write_char('\n');
return;
}
/* Parsed here rather than with atoi_short(), which reports "no
* digits" but not overflow - 65540 would quietly wrap to 4 and we
* would open a connection to a port nobody asked for. Checking
* before each multiply keeps this in 16-bit arithmetic. */
syslog_port_scratch = 0;
syslog_port_idx = cmd_words_b[2];
for (;;) {
/* Plain comparisons instead of isnumber() in the loop
* condition, and nested ifs instead of || and &&: either form
* makes SDCC park a boolean in the bit area, and an image
* carrying LACP and STP as well is one bit short of needing a
* third byte of it - which would push everything up and stop
* the link. */
syslog_port_digit = cmd_buffer[syslog_port_idx];
if (syslog_port_digit < '0')
break;
if (syslog_port_digit > '9')
break;
syslog_port_digit -= '0';
if (syslog_port_scratch > 6553) {
syslog_port_scratch = 0; /* too large - reject below */
break;
}
if (syslog_port_scratch == 6553) {
if (syslog_port_digit > 5) {
syslog_port_scratch = 0;
break;
}
}
syslog_port_scratch = syslog_port_scratch * 10 + syslog_port_digit;
syslog_port_idx++;
}
/* Zero covers all three ways to be wrong: no digits at all, a value
* past 65535, and an explicit "0", which is not a port either. */
if (!syslog_port_scratch) {
print_string("Invalid port\n");
return;
}
/* The port is frozen into the connection by uip_udp_new(), so
* changing it has to tear the connection down and build a new one -
* same dance as setting the address above. */
syslog_port_was_on = syslog_state.enabled;
if (syslog_port_was_on)
syslog_stop();
print_string("Setting new syslog port.\n");
syslog_state.server_port = syslog_port_scratch;
if (syslog_port_was_on)
syslog_start();
}
else
{
print_string("Error: syslog [on|off|ip [ip-address]]\n");
print_string(" on/off enables or disables syslog, ip sets the syslog server IP address\n");
print_string("Error: syslog [on|off|ip [ip-address]|port [number]]\n");
print_string(" on/off enables or disables syslog, ip sets the syslog server IP address,\n");
print_string(" port sets the destination UDP port (default 514)\n");
}
}