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.
This commit is contained in:
Lennart Poettering 2020-10-26 17:39:14 +01:00
parent a2b06dbe15
commit 9b1915256c
7 changed files with 70 additions and 1 deletions

View file

@ -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);

View file

@ -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)

View file

@ -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,

View file

@ -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);

View file

@ -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),

View file

@ -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);

View file

@ -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"))