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 079cdf0052..015337ff2f 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. @@ -698,10 +697,20 @@ 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 + . + + + + 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 . 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; } 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/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) 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")) 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; \ 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