]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
netlink: port to recvmsg_safe()
authorLennart Poettering <lennart@poettering.net>
Thu, 23 Apr 2020 17:47:38 +0000 (19:47 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 12 May 2020 08:47:06 +0000 (10:47 +0200)
This also makes sure the control buffer is properly aligned. This
matters, as otherwise the control buffer might not be aligned and the
cmsg buffer counting might be off. The incorrect alignment is becoming
visible by using recvmsg_safe() as we suddenly notice the MSG_CTRUNC bit
set because of this.

That said, apparently this isn't enough to make this work on all
kernels. Since I couldn't figure this out, we now add 1K to the buffer
to be sure. We do this once already, also for a pktinfo structure
(though an IPv4/IPv6) one. I am puzzled by this, but this shouldn't
matter much. it works locally just fine, except for those ubuntu CI
kernels...

While we are at it, make some other changes too, to simplify and
modernize the function.

src/libsystemd/sd-netlink/netlink-socket.c
src/resolve/resolved-manager.h

index 8eff8d9924433479c2f2cbac3f3d7de29b6f3bd9..71b3d1e2f1f4affebe4a85308655fb89b07cd635 100644 (file)
 #include "socket-util.h"
 #include "util.h"
 
+/* For some reason we need some extra cmsg space on some kernels. It's not clear why, and one of those days
+ * we need to track this down. See: https://github.com/systemd/systemd/pull/15457 */
+#define EXTRA_CMSG_SPACE 1024
+
 int socket_open(int family) {
         int fd;
 
@@ -240,30 +244,27 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
 
 static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_group, bool peek) {
         union sockaddr_union sender;
-        uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo)) + EXTRA_CMSG_SPACE) control;
         struct msghdr msg = {
                 .msg_iov = iov,
                 .msg_iovlen = 1,
                 .msg_name = &sender,
                 .msg_namelen = sizeof(sender),
-                .msg_control = cmsg_buffer,
-                .msg_controllen = sizeof(cmsg_buffer),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
         ssize_t n;
 
         assert(fd >= 0);
         assert(iov);
 
-        n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
-        if (n < 0) {
-                /* no data */
-                if (errno == ENOBUFS)
-                        log_debug("rtnl: kernel receive buffer overrun");
-                else if (errno == EAGAIN)
-                        log_debug("rtnl: no data in socket");
-
-                return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
-        }
+        n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
+        if (n == -ENOBUFS)
+                return log_debug_errno(n, "rtnl: kernel receive buffer overrun");
+        if (IN_SET(n, -EAGAIN, -EINTR))
+                return 0;
+        if (n < 0)
+                return (int) n;
 
         if (sender.nl.nl_pid != 0) {
                 /* not from the kernel, ignore */
@@ -271,20 +272,20 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_gr
 
                 if (peek) {
                         /* drop the message */
-                        n = recvmsg(fd, &msg, 0);
+                        n = recvmsg_safe(fd, &msg, 0);
                         if (n < 0)
-                                return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
+                                return (int) n;
                 }
 
                 return 0;
         }
 
         if (ret_mcast_group) {
-                struct cmsghdr *cmsg;
+                struct nl_pktinfo *pi;
 
-                cmsg = cmsg_find(&msg, SOL_NETLINK, NETLINK_PKTINFO, CMSG_LEN(sizeof(struct nl_pktinfo)));
-                if (cmsg)
-                        *ret_mcast_group = ((struct nl_pktinfo*) CMSG_DATA(cmsg))->group;
+                pi = CMSG_FIND_DATA(&msg, SOL_NETLINK, NETLINK_PKTINFO, struct nl_pktinfo);
+                if (pi)
+                        *ret_mcast_group = pi->group;
                 else
                         *ret_mcast_group = 0;
         }
index 8e029b86d901b1d90f2ccc0141901e37fd4624c4..cbad1dce6058df10cf3e7fb9d22416d784d67617 100644 (file)
@@ -165,6 +165,7 @@ void manager_verify_all(Manager *m);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
+/* For some reason we need some extra cmsg space on some kernels/archs. One of those days we ned to figure out why */
 #define EXTRA_CMSG_SPACE 1024
 
 int manager_is_own_hostname(Manager *m, const char *name);