From c31984e3d007a370e7428b095b3510efafa69139 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 13 Jun 2023 10:03:44 +0200 Subject: [PATCH 1/3] socket-util: Allow specifying socket type in vsock address --- src/basic/socket-util.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 9040ff7c53..d82d9fe78a 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1522,13 +1522,20 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { _cleanup_free_ char *n = NULL; char *e, *cid_start; unsigned port, cid; - int r; + int type, r; assert(ret_address); assert(s); - cid_start = startswith(s, "vsock:"); - if (!cid_start) + if ((cid_start = startswith(s, "vsock:"))) + type = 0; + else if ((cid_start = startswith(s, "vsock-dgram:"))) + type = SOCK_DGRAM; + else if ((cid_start = startswith(s, "vsock-seqpacket:"))) + type = SOCK_SEQPACKET; + else if ((cid_start = startswith(s, "vsock-stream:"))) + type = SOCK_STREAM; + else return -EPROTO; e = strchr(cid_start, ':'); @@ -1557,6 +1564,7 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { .svm_family = AF_VSOCK, .svm_port = port, }, + .type = type, .size = sizeof(struct sockaddr_vm), }; From 56d02f29695ab6d0c1a500d8621c754c93236d98 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 12 Jun 2023 18:21:08 +0200 Subject: [PATCH 2/3] sd-daemon: Use socket type from vsock address if set If a socket type is explicitly provided in the vsock address, let's make sure we try only that socket type. --- src/libsystemd/sd-daemon/sd-daemon.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index c4472d204e..7d31ae89ce 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -466,7 +466,7 @@ static int pid_notify_with_fds_internal( struct cmsghdr *cmsg = NULL; const char *e; bool send_ucred; - int r; + int type, r; if (!state) return -EINVAL; @@ -491,30 +491,33 @@ static int pid_notify_with_fds_internal( if (address.sockaddr.vm.svm_family == AF_VSOCK && address.sockaddr.vm.svm_cid == VMADDR_CID_ANY) return -EINVAL; + type = address.type == 0 ? SOCK_DGRAM : address.type; + /* At the time of writing QEMU does not yet support AF_VSOCK + SOCK_DGRAM and returns * ENODEV. Fallback to SOCK_SEQPACKET in that case. */ - fd = socket(address.sockaddr.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC, 0); + fd = socket(address.sockaddr.sa.sa_family, type|SOCK_CLOEXEC, 0); if (fd < 0) { - if (!(ERRNO_IS_NOT_SUPPORTED(errno) || errno == ENODEV) || address.sockaddr.sa.sa_family != AF_VSOCK) - return log_debug_errno(errno, "Failed to open datagram notify socket to '%s': %m", e); + if (!(ERRNO_IS_NOT_SUPPORTED(errno) || errno == ENODEV) || address.sockaddr.sa.sa_family != AF_VSOCK || address.type > 0) + return log_debug_errno(errno, "Failed to open %s notify socket to '%s': %m", socket_address_type_to_string(type), e); - fd = socket(address.sockaddr.sa.sa_family, SOCK_SEQPACKET|SOCK_CLOEXEC, 0); + type = SOCK_SEQPACKET; + fd = socket(address.sockaddr.sa.sa_family, type|SOCK_CLOEXEC, 0); if (fd < 0) - return log_debug_errno(errno, "Failed to open sequential packet socket to '%s': %m", e); + return log_debug_errno(errno, "Failed to open %s socket to '%s': %m", socket_address_type_to_string(type), e); + } + if (address.sockaddr.sa.sa_family == AF_VSOCK) { r = vsock_bind_privileged_port(fd); if (r < 0 && !ERRNO_IS_PRIVILEGE(r)) return log_debug_errno(r, "Failed to bind socket to privileged port: %m"); + } + if (IN_SET(type, SOCK_STREAM, SOCK_SEQPACKET)) { if (connect(fd, &address.sockaddr.sa, address.size) < 0) return log_debug_errno(errno, "Failed to connect socket to '%s': %m", e); msghdr.msg_name = NULL; msghdr.msg_namelen = 0; - } else if (address.sockaddr.sa.sa_family == AF_VSOCK) { - r = vsock_bind_privileged_port(fd); - if (r < 0 && !ERRNO_IS_PRIVILEGE(r)) - return log_debug_errno(r, "Failed to bind socket to privileged port: %m"); } (void) fd_inc_sndbuf(fd, SNDBUF_SIZE); From 7f78d3431b4ea06a46258c32669092f94c06a7c9 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 13 Jun 2023 10:26:00 +0200 Subject: [PATCH 3/3] sd-daemon: Add vsock fallback to SOCK_STREAM SOCK_SEQPACKET is only supported on recent kernels. Let's add a fallback to SOCK_STREAM if it is not supported. To accomodate SOCK_STREAM, we also modify pid_notify_with_fds_internal() to do sendmsg() in a loop as with SOCK_STREAM, sendmsg() is not guaranteed to write all data in a single syscall or fail otherwise. --- src/libsystemd/sd-daemon/sd-daemon.c | 40 +++++++++++++++++++--------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 7d31ae89ce..5711e5e18a 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -466,6 +466,7 @@ static int pid_notify_with_fds_internal( struct cmsghdr *cmsg = NULL; const char *e; bool send_ucred; + ssize_t n; int type, r; if (!state) @@ -502,6 +503,10 @@ static int pid_notify_with_fds_internal( type = SOCK_SEQPACKET; fd = socket(address.sockaddr.sa.sa_family, type|SOCK_CLOEXEC, 0); + if (fd < 0 && ERRNO_IS_NOT_SUPPORTED(errno)) { + type = SOCK_STREAM; + fd = socket(address.sockaddr.sa.sa_family, type|SOCK_CLOEXEC, 0); + } if (fd < 0) return log_debug_errno(errno, "Failed to open %s socket to '%s': %m", socket_address_type_to_string(type), e); } @@ -563,21 +568,32 @@ static int pid_notify_with_fds_internal( } } - /* First try with fake ucred data, as requested */ - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) - return 1; + do { + /* First try with fake ucred data, as requested */ + n = sendmsg(fd, &msghdr, MSG_NOSIGNAL); + if (n < 0) { + if (!send_ucred) + return log_debug_errno(errno, "Failed to send notify message to '%s': %m", e); - /* If that failed, try with our own ucred instead */ - if (send_ucred) { - msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); - if (msghdr.msg_controllen == 0) + /* If that failed, try with our own ucred instead */ + msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); + if (msghdr.msg_controllen == 0) + msghdr.msg_control = NULL; + + n = 0; + send_ucred = false; + } else { + /* Unless we're using SOCK_STREAM, we expect to write all the contents immediately. */ + if (type != SOCK_STREAM && (size_t) n < IOVEC_TOTAL_SIZE(msghdr.msg_iov, msghdr.msg_iovlen)) + return -EIO; + + /* Make sure we only send fds and ucred once, even if we're using SOCK_STREAM. */ msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + } while (!IOVEC_INCREMENT(msghdr.msg_iov, msghdr.msg_iovlen, n)); - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) - return 1; - } - - return log_debug_errno(errno, "Failed to send notify message to '%s': %m", e); + return 1; } _public_ int sd_pid_notify_with_fds(