From 99d52ab51fc02942f951891fbafcaf7ca5ad1dd0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2025 09:06:52 +0200 Subject: [PATCH 1/6] man: don't claim REMOTE_PORT= was initialized to the protocol for AF_RAW sockets This claim has no basis in reality, not even in the original commit where the sentence was added this was true, see 3b1c524154c876aecebc98787975cc2943100210. Hence removing. --- man/systemd.socket.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index f20c5fc2ef..acec1f1047 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -426,8 +426,7 @@ For IPv4 and IPv6 connections, the $REMOTE_ADDR environment variable will contain the remote IP address, and $REMOTE_PORT will contain the remote port. This - is the same as the format used by CGI. For SOCK_RAW, the port is the IP - protocol. + is the same as the format used by CGI. For AF_UNIX socket connections, the $REMOTE_ADDR environment variable will contain either the remote socket's file system path starting with a slash From 3bdcd994cd707bcf34664edaa21afff6025d15d2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2025 09:44:02 +0200 Subject: [PATCH 2/6] man: correct version information when $REMOTE_ADDR/$REMOTE_PORT where added This was in commit 3b1c524154c876aecebc98787975cc2943100210, i.e. in the v220 cycle. --- man/systemd.exec.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 62c59308ef..829149f561 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -4409,7 +4409,7 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX in case of an unnamed socket. $REMOTE_PORT is not set for AF_UNIX sockets. - + From 272017d32aff38bd236822fb2618e8888e4c57c8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2025 09:15:10 +0200 Subject: [PATCH 3/6] socket-util: add trivial socket_get_cookie() helper --- src/basic/socket-util.c | 15 +++++++++++++++ src/basic/socket-util.h | 2 ++ src/libsystemd/sd-netlink/test-netlink.c | 4 +--- src/shared/socket-netlink.c | 10 ++++------ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index c5499d6e48..919884ba47 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1959,3 +1959,18 @@ finalize: return 0; } + +int socket_get_cookie(int fd, uint64_t *ret) { + assert(fd >= 0); + + uint64_t cookie = 0; + socklen_t cookie_len = sizeof(cookie); + if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0) + return -errno; + + assert(cookie_len == sizeof(cookie)); + if (ret) + *ret = cookie; + + return 0; +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 830b48e435..dea93dbde8 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -354,3 +354,5 @@ int socket_address_equal_unix(const char *a, const char *b); int vsock_get_local_cid(unsigned *ret); int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups); + +int socket_get_cookie(int fd, uint64_t *ret); diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index 274719c296..252bb46b6e 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -717,9 +717,7 @@ TEST(sock_diag_unix) { ASSERT_OK_ERRNO(fstat(unix_fd, &st)); uint64_t cookie; - socklen_t cookie_len = sizeof(cookie); - ASSERT_OK_ERRNO(getsockopt(unix_fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len)); - ASSERT_EQ(cookie_len, sizeof(cookie)); + ASSERT_OK(socket_get_cookie(unix_fd, &cookie)); _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; ASSERT_OK(sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN)); diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index 1ecb2284fe..a810e1c1f3 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -504,12 +504,10 @@ int af_unix_get_qlen(int fd, uint32_t *ret) { if (r < 0) return r; - uint64_t cookie = 0; - socklen_t cookie_len = sizeof(cookie); - if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0) - return -errno; - - assert(cookie_len == sizeof(cookie)); + uint64_t cookie; + r = socket_get_cookie(fd, &cookie); + if (r < 0) + return r; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; r = sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN); From bfb1f9e2c9763b6989f9785ccd6f505dc1eddf97 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2025 09:15:46 +0200 Subject: [PATCH 4/6] core: pass the socket cookie to invoked per-connection service instances as $SO_COOKIE env var The socket cookie is just too useful for identifying connections, let's emphasize this a bit and pass it as environment variable. --- man/systemd.exec.xml | 11 +++++++++++ man/systemd.socket.xml | 12 +++++++++--- src/core/service.c | 10 +++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 829149f561..a63700afb3 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -4412,6 +4412,17 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX + + $SO_COOKIE + + If this is a unit started via per-connection socket activation (i.e. via a socket + unit with Accept=yes), this environment variable contains the Linux socket + cookie, formatted as decimal integer. The socket cookie can otherwise be acquired via getsockopt7. + + + + $TRIGGER_UNIT $TRIGGER_PATH diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index acec1f1047..39bd0dd626 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -425,13 +425,19 @@ above for a more detailed discussion of the naming rules of triggered services. For IPv4 and IPv6 connections, the $REMOTE_ADDR environment variable will - contain the remote IP address, and $REMOTE_PORT will contain the remote port. This - is the same as the format used by CGI. + contain the remote IP address, and $REMOTE_PORT will contain the remote port + number. These two variables correspond to those defined by the CGI interface for web services (see + RFC 3875). For AF_UNIX socket connections, the $REMOTE_ADDR environment variable will contain either the remote socket's file system path starting with a slash (/) or its address in the abstract namespace starting with an at symbol - (@). If the socket is unnamed, $REMOTE_ADDR will not be set. + (@). If the socket is unnamed, $REMOTE_ADDR will not be + set. + + The $SO_COOKIE environment variable is set to the Linux socket cookie, + formatted as decimal integer. The socket cookie can otherwise be acquired via getsockopt7. It is recommended to set CollectMode=inactive-or-failed for service instances activated via Accept=yes, to ensure that failed connection services are diff --git a/src/core/service.c b/src/core/service.c index 141c85745a..46e8ff6239 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1749,7 +1749,7 @@ static int service_spawn_internal( if (r < 0) return r; - our_env = new0(char*, 15); + our_env = new0(char*, 16); if (!our_env) return -ENOMEM; @@ -1821,6 +1821,14 @@ static int service_spawn_internal( our_env[n_env++] = t; } } + + uint64_t cookie; + if (socket_get_cookie(s->socket_fd, &cookie) >= 0) { + char *t; + if (asprintf(&t, "SO_COOKIE=%" PRIu64, cookie) < 0) + return -ENOMEM; + our_env[n_env++] = t; + } } Service *env_source = NULL; From 9be6e18444f9f0e9d88c0878b31d64013afc0749 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2025 09:16:22 +0200 Subject: [PATCH 5/6] core: name socket unit instances after the socket cookie On linux sockets have a really good, unique identifier, the SO_COOKIE, which is used by sockdiag and BPF and elsewhere. Let's expose this more prominently in the service name of per-connection services invoked via Accept=yes sockets. This is really nice, because together with our $SO_COOKIE env var we can now match up services, process execution contexts, and "ss" outputs, BPF rules, and more. --- src/core/socket.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/core/socket.c b/src/core/socket.c index af67ac3d06..1ce6bc1ba6 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -830,7 +830,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { cgroup_context_dump(UNIT(s), f, prefix); } -static int instance_from_socket(int fd, unsigned nr, char **ret) { +static int instance_from_socket( + int fd, + unsigned nr, + char **ret) { + union sockaddr_union local, remote; socklen_t l; int r; @@ -846,6 +850,11 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { if (getpeername(fd, &remote.sa, &l) < 0) return -errno; + uint64_t cookie; + r = socket_get_cookie(fd, &cookie); + if (r < 0) + return r; + char *s; switch (local.sa.sa_family) { @@ -856,8 +865,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { b = be32toh(remote.in.sin_addr.s_addr); if (asprintf(&s, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", + "%u-%" PRIu64 "-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", nr, + cookie, a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, be16toh(local.in.sin_port), b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF, @@ -879,8 +889,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { *b = remote.in6.sin6_addr.s6_addr+12; if (asprintf(&s, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", + "%u-%" PRIu64 "-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", nr, + cookie, a[0], a[1], a[2], a[3], be16toh(local.in6.sin6_port), b[0], b[1], b[2], b[3], @@ -888,8 +899,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { return -ENOMEM; } else { if (asprintf(&s, - "%u-%s:%u-%s:%u", + "%u-%" PRIu64 "-%s:%u-%s:%u", nr, + cookie, IN6_ADDR_TO_STRING(&local.in6.sin6_addr), be16toh(local.in6.sin6_port), IN6_ADDR_TO_STRING(&remote.in6.sin6_addr), @@ -909,19 +921,20 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { uint64_t pidfd_id; if (pidfd >= 0 && pidfd_get_inode_id(pidfd, &pidfd_id) >= 0) - r = asprintf(&s, "%u-" PID_FMT "_%" PRIu64 "-" UID_FMT, - nr, ucred.pid, pidfd_id, ucred.uid); + r = asprintf(&s, "%u-%" PRIu64 "-" PID_FMT "_%" PRIu64 "-" UID_FMT, + nr, cookie, ucred.pid, pidfd_id, ucred.uid); else - r = asprintf(&s, "%u-" PID_FMT "-" UID_FMT, - nr, ucred.pid, ucred.uid); + r = asprintf(&s, "%u-%" PRIu64 "-" PID_FMT "-" UID_FMT, + nr, cookie, ucred.pid, ucred.uid); if (r < 0) return -ENOMEM; } else if (r == -ENODATA) { /* This handles the case where somebody is connecting from another pid/uid namespace * (e.g. from outside of our container). */ if (asprintf(&s, - "%u-unknown", - nr) < 0) + "%u-%" PRIu64 "-unknown", + nr, + cookie) < 0) return -ENOMEM; } else return r; @@ -931,8 +944,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) { case AF_VSOCK: if (asprintf(&s, - "%u-%u:%u-%u:%u", + "%u-%" PRIu64 "-%u:%u-%u:%u", nr, + cookie, local.vm.svm_cid, local.vm.svm_port, remote.vm.svm_cid, remote.vm.svm_port) < 0) return -ENOMEM; From 1619918da7e5c363aa4e0b0fb05da5777f3ff138 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2025 09:53:30 +0200 Subject: [PATCH 6/6] update TODO --- TODO | 3 --- 1 file changed, 3 deletions(-) diff --git a/TODO b/TODO index f2d515ad78..3b3fe2e323 100644 --- a/TODO +++ b/TODO @@ -125,9 +125,6 @@ Deprecations and removals: Features: -* in pid1, name Accept=yes service instances after the socket cookie. Also, set - $SO_COOKIE env var or so for executed services. - * loginctl: show argv[] of "leader" process in tabular list-sessions output * loginctl: show "service identifier" in tabular list-sessions output, to make