diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 2d6339680b4..dc3fee5dfbb 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -807,6 +807,22 @@ suffix. + + TriggerLimitIntervalSec= + TriggerLimitIntervalBurst= + + Configures a limit on how often this socket unit my be activated within a specific time + interval. The TriggerLimitIntervalSec= may be used to configure the length of the time + interval in the usual time units us, ms, s, + min, h, … and defaults to 5s (See + systemd.time7 for details on + the various time units available). The TriggerLimitBurst= setting takes an integer value and + specifies the numer of permitted activations per time interval, and defaults to 2500 (thus by default + permitting 2500 activations per 5s). Set either to 0 to disable any form of trigger rate limiting. If the limit + is hit, the socket unit is placed into a failure mode, and will not be connectible anymore until + restarted. Note that this limit is enforced before the service activation is enqueued. + + Check diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index d33e494f6ba..bb09a515f88 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -149,6 +149,8 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitIntervalSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 5568b4696fe..32bb62fa63c 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -297,6 +297,8 @@ Socket.RemoveOnStop, config_parse_bool, 0, Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks) Socket.FileDescriptorName, config_parse_fdname, 0, 0 Socket.Service, config_parse_socket_service, 0, 0 +Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval) +Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst) m4_ifdef(`HAVE_SMACK', `Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in) diff --git a/src/core/main.c b/src/core/main.c index 75c5ff81f23..9fcd3fe1bdc 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -289,6 +289,7 @@ static int parse_crash_chvt(const char *value) { } static int set_machine_id(const char *m) { + assert(m); if (sd_id128_from_string(m, &arg_machine_id) < 0) return -EINVAL; diff --git a/src/core/socket.c b/src/core/socket.c index a9fff9c2593..42260d87292 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -99,6 +99,8 @@ static void socket_init(Unit *u) { s->exec_context.std_error = u->manager->default_std_error; s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; + + RATELIMIT_INIT(s->trigger_limit, 5*USEC_PER_SEC, 2500); } static void socket_unwatch_control_pid(Socket *s) { @@ -1887,6 +1889,9 @@ static void socket_enter_running(Socket *s, int cfd) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; + /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or + * close it. */ + assert(s); /* We don't take connections anymore if we are supposed to @@ -1896,7 +1901,7 @@ static void socket_enter_running(Socket *s, int cfd) { log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled."); if (cfd >= 0) - safe_close(cfd); + cfd = safe_close(cfd); else { /* Flush all sockets by closing and reopening them */ socket_close_fds(s); @@ -1918,6 +1923,13 @@ static void socket_enter_running(Socket *s, int cfd) { return; } + if (!ratelimit_test(&s->trigger_limit)) { + safe_close(cfd); + log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation."); + socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT); + return; + } + if (cfd < 0) { Iterator i; Unit *other; @@ -1949,7 +1961,7 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections); + log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); safe_close(cfd); return; } @@ -1965,6 +1977,7 @@ static void socket_enter_running(Socket *s, int cfd) { /* ENOTCONN is legitimate if TCP RST was received. * This connection is over, but the socket unit lives on. */ + log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); safe_close(cfd); return; } @@ -1993,7 +2006,7 @@ static void socket_enter_running(Socket *s, int cfd) { if (r < 0) goto fail; - cfd = -1; + cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); @@ -2806,6 +2819,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { [SOCKET_FAILURE_EXIT_CODE] = "exit-code", [SOCKET_FAILURE_SIGNAL] = "signal", [SOCKET_FAILURE_CORE_DUMP] = "core-dump", + [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit" }; diff --git a/src/core/socket.h b/src/core/socket.h index b537b026a7b..2a4b1bb6741 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -52,6 +52,7 @@ typedef enum SocketResult { SOCKET_FAILURE_EXIT_CODE, SOCKET_FAILURE_SIGNAL, SOCKET_FAILURE_CORE_DUMP, + SOCKET_FAILURE_TRIGGER_LIMIT_HIT, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT, _SOCKET_RESULT_MAX, _SOCKET_RESULT_INVALID = -1 @@ -156,6 +157,8 @@ struct Socket { bool reset_cpu_usage:1; char *fdname; + + RateLimit trigger_limit; }; /* Called from the service code when collecting fds */