From c758bea7222662e6a43626ea66623f1efabbf32c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Oct 2020 16:19:10 +0100 Subject: [PATCH 1/7] man: add references to fifo(7) and mq_overview(7) man pages --- man/systemd.socket.xml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 079cdf0052..a1e4284763 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -253,11 +253,10 @@ ListenFIFO= - Specifies a file system FIFO to listen on. - This expects an absolute file system path as argument. - Behavior otherwise is very similar to the - ListenDatagram= directive - above. + Specifies a file system FIFO (see fifo7 for + details) to listen on. This expects an absolute file system path as argument. Behavior otherwise is + very similar to the ListenDatagram= directive above. @@ -285,12 +284,12 @@ ListenMessageQueue= - Specifies a POSIX message queue name to listen - on. This expects a valid message queue name (i.e. beginning - with /). Behavior otherwise is very similar to the - ListenFIFO= directive above. On Linux - message queue descriptors are actually file descriptors and - can be inherited between processes. + Specifies a POSIX message queue name to listen on (see mq_overview7 + for details). This expects a valid message queue name (i.e. beginning with + /). Behavior otherwise is very similar to the ListenFIFO= + directive above. On Linux message queue descriptors are actually file descriptors and can be + inherited between processes. From 2d6d4136cdad5cde1f7fa8b54e427383645ab5fe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Oct 2020 16:19:24 +0100 Subject: [PATCH 2/7] socket-util: make socket_set_recvpktinfo control PACKET_AUXDATA sockopt on AF_PACKET Just for the sake of completness. --- man/systemd.socket.xml | 9 +++++---- src/basic/socket-util.c | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index a1e4284763..8ec662c3ac 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -697,10 +697,11 @@ PassPacketInfo= Takes a boolean value. This controls the IP_PKTINFO, - IPV6_RECVPKTINFO and NETLINK_PKTINFO socket options, which - enable reception of additional per-packet metadata as ancillary message, on - AF_INET, AF_INET6 and AF_UNIX sockets. - Defaults to . + IPV6_RECVPKTINFO, NETLINK_PKTINFO or + PACKET_AUXDATA socket options, which enable reception of additional per-packet + metadata as ancillary message, on AF_INET, AF_INET6, + AF_UNIX and AF_PACKET sockets. Defaults to + . diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index f2e1148e87..6f57b2a3c4 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1240,6 +1240,9 @@ int socket_set_recvpktinfo(int fd, int af, bool b) { case AF_NETLINK: return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); + case AF_PACKET: + return setsockopt_int(fd, SOL_PACKET, PACKET_AUXDATA, b); + default: return -EAFNOSUPPORT; } From 3b719003c386c8e93f31eb98b8cc5bcd6941822f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Oct 2020 16:31:07 +0100 Subject: [PATCH 3/7] journald: remove pointless conditionalization Let's not have #ifdeffery both in the consumers and the providers of the selinux glue code. Unless the code is particularly complex, let's do the ifdeffery only in the provider of the selinux glue code, and let's keep the consumers simple and just invoke it. --- src/journal/journald-native.c | 2 -- src/journal/journald-syslog.c | 7 ++----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index f2b867da3d..fed81161e0 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -483,13 +483,11 @@ int server_open_native_socket(Server *s, const char *native_socket) { if (r < 0) return log_error_errno(r, "SO_PASSCRED failed: %m"); -#if HAVE_SELINUX if (mac_selinux_use()) { r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_PASSSEC, true); if (r < 0) log_warning_errno(r, "SO_PASSSEC failed: %m"); } -#endif r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, true); if (r < 0) diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 0da7dcfcf5..91ea41fa78 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -70,9 +70,8 @@ static void forward_syslog_iovec( msghdr.msg_controllen = cmsg->cmsg_len; } - /* Forward the syslog message we received via /dev/log to - * /run/systemd/syslog. Unfortunately we currently can't set - * the SO_TIMESTAMP auxiliary data, and hence we don't. */ + /* Forward the syslog message we received via /dev/log to /run/systemd/syslog. Unfortunately we + * currently can't set the SO_TIMESTAMP auxiliary data, and hence we don't. */ if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) return; @@ -484,13 +483,11 @@ int server_open_syslog_socket(Server *s, const char *syslog_socket) { if (r < 0) return log_error_errno(r, "SO_PASSCRED failed: %m"); -#if HAVE_SELINUX if (mac_selinux_use()) { r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, true); if (r < 0) log_warning_errno(r, "SO_PASSSEC failed: %m"); } -#endif r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, true); if (r < 0) From a2b06dbe15871f41deb107ee30a784e8a1ceb951 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Oct 2020 17:38:55 +0100 Subject: [PATCH 4/7] conf-parser: add a flavour of DEFINE_CONFIG_PARSE_ENUM() that allows specifiying the precie from_string() function to call --- src/shared/conf-parser.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 2514dcbf48..d2391d43bb 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -193,7 +193,7 @@ typedef enum Disabled { return 0; \ } -#define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \ +#define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type, msg) \ CONFIG_PARSER_PROTOTYPE(function) { \ type *i = data, x; \ \ @@ -202,7 +202,7 @@ typedef enum Disabled { assert(rvalue); \ assert(data); \ \ - x = name##_from_string(rvalue); \ + x = from_string(rvalue); \ if (x < 0) { \ log_syntax(unit, LOG_WARNING, filename, line, 0, \ msg ", ignoring: %s", rvalue); \ @@ -213,6 +213,9 @@ typedef enum Disabled { return 0; \ } +#define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \ + DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type, msg) + #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \ CONFIG_PARSER_PROTOTYPE(function) { \ type *i = data, x; \ From 9b1915256cb33940a72a7c39444ebac7f906603b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Oct 2020 17:39:14 +0100 Subject: [PATCH 5/7] core: add Timestamping= option for socket units This adds a way to control SO_TIMESTAMP/SO_TIMESTAMPNS socket options for sockets PID 1 binds to. This is useful in journald so that we get proper timestamps even for ingress log messages that are submitted before journald is running. We recently turned on packet info metadata from PID 1 for these sockets, but the timestamping info was still missing. Let's correct that. --- src/core/dbus-socket.c | 6 ++++ src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/load-fragment.c | 1 + src/core/load-fragment.h | 1 + src/core/socket.c | 46 +++++++++++++++++++++++++++ src/core/socket.h | 13 ++++++++ src/shared/bus-unit-util.c | 3 +- 7 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 07d030adb3..a5d15d80cd 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -20,6 +20,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, socket_result, SocketResult); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); static BUS_DEFINE_PROPERTY_GET(property_get_fdname, "s", Socket, socket_fdname); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_timestamping, socket_timestamping, SocketTimestamping); static int property_get_listen( sd_bus *bus, @@ -106,6 +107,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PassPacketInfo", "b", bus_property_get_bool, offsetof(Socket, pass_pktinfo), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Timestamping", "s", property_get_timestamping, offsetof(Socket, timestamping), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), @@ -159,6 +161,7 @@ static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(fdname, fdname_is_valid); static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(ifname, ifname_valid); static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(ip_tos, "i", int32_t, int, "%" PRIi32, ip_tos_to_string_alloc); static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%" PRIi32, socket_protocol_to_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(socket_timestamping, SocketTimestamping, socket_timestamping_from_string_harder); static int bus_socket_set_transient_property( Socket *s, @@ -210,6 +213,9 @@ static int bus_socket_set_transient_property( if (streq(name, "PassPacketInfo")) return bus_set_transient_bool(u, name, &s->pass_pktinfo, message, flags, error); + if (streq(name, "Timestamping")) + return bus_set_transient_socket_timestamping(u, name, &s->timestamping, message, flags, error); + if (streq(name, "ReusePort")) return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index b5ccf62ae0..063d8ba6b6 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -419,6 +419,7 @@ Socket.Broadcast, config_parse_bool, Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec) Socket.PassPacketInfo, config_parse_bool, 0, offsetof(Socket, pass_pktinfo) +Socket.Timestamping, config_parse_socket_timestamping, 0, offsetof(Socket, timestamping) Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port) Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 60c9a5f03a..0ab64e1708 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -140,6 +140,7 @@ DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag"); DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type"); DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format"); +DEFINE_CONFIG_PARSE_ENUM_FULL(config_parse_socket_timestamping, socket_timestamping_from_string_harder, SocketTimestamping, "Failed to parse timestamping precision"); int config_parse_unit_deps( const char *unit, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index fa4c1fb1a0..47036145c8 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -136,6 +136,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_crash_chvt); CONFIG_PARSER_PROTOTYPE(config_parse_timeout_abort); CONFIG_PARSER_PROTOTYPE(config_parse_swap_priority); CONFIG_PARSER_PROTOTYPE(config_parse_mount_images); +CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/core/socket.c b/src/core/socket.c index f71fa34300..f045eed5b5 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -629,6 +629,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, socket_fdname(s), prefix, yes_no(s->selinux_context_from_net)); + if (s->timestamping != SOCKET_TIMESTAMPING_OFF) + fprintf(f, + "%sTimestamping: %s\n", + prefix, socket_timestamping_to_string(s->timestamping)); + if (s->control_pid > 0) fprintf(f, "%sControl PID: "PID_FMT"\n", @@ -1051,6 +1056,14 @@ static void socket_apply_socket_options(Socket *s, SocketPort *p, int fd) { log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m"); } + if (s->timestamping != SOCKET_TIMESTAMPING_OFF) { + r = setsockopt_int(fd, SOL_SOCKET, + s->timestamping == SOCKET_TIMESTAMPING_NS ? SO_TIMESTAMPNS : SO_TIMESTAMP, + true); + if (r < 0) + log_unit_warning_errno(UNIT(s), r, "Failed to enable timestamping socket option, ignoring: %m"); + } + if (s->priority >= 0) { r = setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY, s->priority); if (r < 0) @@ -3409,6 +3422,39 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult); +static const char* const socket_timestamping_table[_SOCKET_TIMESTAMPING_MAX] = { + [SOCKET_TIMESTAMPING_OFF] = "off", + [SOCKET_TIMESTAMPING_US] = "us", + [SOCKET_TIMESTAMPING_NS] = "ns", +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_timestamping, SocketTimestamping); + +SocketTimestamping socket_timestamping_from_string_harder(const char *p) { + SocketTimestamping t; + int r; + + if (!p) + return _SOCKET_TIMESTAMPING_INVALID; + + t = socket_timestamping_from_string(p); + if (t >= 0) + return t; + + /* Let's alternatively support the various other aliases parse_time() accepts for ns and µs here, + * too. */ + if (streq(p, "nsec")) + return SOCKET_TIMESTAMPING_NS; + if (STR_IN_SET(p, "usec", "µs")) + return SOCKET_TIMESTAMPING_US; + + r = parse_boolean(p); + if (r < 0) + return _SOCKET_TIMESTAMPING_INVALID; + + return r ? SOCKET_TIMESTAMPING_NS : SOCKET_TIMESTAMPING_OFF; /* If boolean yes, default to ns accuracy */ +} + const UnitVTable socket_vtable = { .object_size = sizeof(Socket), .exec_context_offset = offsetof(Socket, exec_context), diff --git a/src/core/socket.h b/src/core/socket.h index cf475e2638..90839de9fa 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -58,6 +58,14 @@ typedef struct SocketPort { LIST_FIELDS(struct SocketPort, port); } SocketPort; +typedef enum SocketTimestamping { + SOCKET_TIMESTAMPING_OFF, + SOCKET_TIMESTAMPING_US, /* SO_TIMESTAMP */ + SOCKET_TIMESTAMPING_NS, /* SO_TIMESTAMPNS */ + _SOCKET_TIMESTAMPING_MAX, + _SOCKET_TIMESTAMPING_INVALID = -1, +} SocketTimestamping; + struct Socket { Unit meta; @@ -123,6 +131,7 @@ struct Socket { bool pass_cred; bool pass_sec; bool pass_pktinfo; + SocketTimestamping timestamping; /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ SocketAddressBindIPv6Only bind_ipv6_only; @@ -182,4 +191,8 @@ SocketResult socket_result_from_string(const char *s) _pure_; const char* socket_port_type_to_string(SocketPort *p) _pure_; SocketType socket_port_type_from_string(const char *p) _pure_; +const char* socket_timestamping_to_string(SocketTimestamping p) _const_; +SocketTimestamping socket_timestamping_from_string(const char *p) _pure_; +SocketTimestamping socket_timestamping_from_string_harder(const char *p) _pure_; + DEFINE_CAST(SOCKET, Socket); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 89e0c5bb95..79c2e0cf19 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2037,7 +2037,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons "BindIPv6Only", "FileDescriptorName", "SocketUser", - "SocketGroup")) + "SocketGroup", + "Timestamping")) return bus_append_string(m, field, eq); if (streq(field, "Symlinks")) From 95923d7e13ebd41aa09f9367cd3666529fccd214 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Oct 2020 17:40:55 +0100 Subject: [PATCH 6/7] man: document the new Timestamping= option --- man/org.freedesktop.systemd1.xml | 6 ++++++ man/systemd.socket.xml | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 3c0e5b6eb1..c0e0f385b4 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -3968,6 +3968,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b PassPacketInfo = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Timestamping = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b RemoveOnStop = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(ss) Listen = [...]; @@ -4523,6 +4525,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -5065,6 +5069,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 8ec662c3ac..015337ff2f 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -704,6 +704,15 @@ . + + Timestamping= + Takes one of off, us (alias: + usec, µs) or ns (alias: + nsec). This controls the SO_TIMESTAMP or + SO_TIMESTAMPNS socket options, and enables whether ingress network traffic shall + carry timestamping metadata. Defaults to . + + TCPCongestion= Takes a string value. Controls the TCP congestion algorithm used by this From 02229dff2b371f3a6235fe18ea72e972ddbc90a9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Oct 2020 17:41:06 +0100 Subject: [PATCH 7/7] units: turn on timestamping for journald sockets To make things simple and robust when debugging journald, we'll leave the SO_TIMESTAMP invocations in the C code in place, even if they are now typically redundant, given that the sockets are already passed into the process with SO_TIMESTAMP turned on now. --- units/systemd-journald-dev-log.socket | 7 ++++--- units/systemd-journald.socket | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/units/systemd-journald-dev-log.socket b/units/systemd-journald-dev-log.socket index 80ad6ac845..7cec55440d 100644 --- a/units/systemd-journald-dev-log.socket +++ b/units/systemd-journald-dev-log.socket @@ -19,12 +19,13 @@ Before=sockets.target IgnoreOnIsolate=yes [Socket] -Service=systemd-journald.service ListenDatagram=/run/systemd/journal/dev-log -Symlinks=/dev/log -SocketMode=0666 PassCredentials=yes PassSecurity=yes +Service=systemd-journald.service +SocketMode=0666 +Symlinks=/dev/log +Timestamping=us # Increase both the send and receive buffer, so that things don't # block early. Note that journald internally uses the this socket both diff --git a/units/systemd-journald.socket b/units/systemd-journald.socket index c95ae5ab64..95d5f21964 100644 --- a/units/systemd-journald.socket +++ b/units/systemd-journald.socket @@ -19,10 +19,11 @@ Before=sockets.target IgnoreOnIsolate=yes [Socket] -ListenStream=/run/systemd/journal/stdout ListenDatagram=/run/systemd/journal/socket -SocketMode=0666 +ListenStream=/run/systemd/journal/stdout PassCredentials=yes PassSecurity=yes ReceiveBuffer=8M Service=systemd-journald.service +SocketMode=0666 +Timestamping=us