]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-device: check netlink netns matches host netns before using monitor
authorLennart Poettering <lennart@poettering.net>
Tue, 26 May 2020 21:27:20 +0000 (23:27 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 29 May 2020 06:28:10 +0000 (15:28 +0900)
Tracking down #15931 confused the hell out of me, since running homed in
gdb from the command line worked fine, but doing so as a service failed.
Let's make this more debuggable and check if we live in the host netns
when allocating a new udev monitor.

This is just debug stuff, so that if things don't work, a quick debug
run will reveal what is going on.

That said, while we are at it, also fix unexpected closing of passed in
fd when failing.

src/basic/missing_socket.h
src/libsystemd/sd-device/device-monitor.c

index 276be366c3d742ddbee34a15a53f141410a7e061..8c3fec1e350a53bf40d0eb67e41e41ed89f29422 100644 (file)
@@ -62,3 +62,8 @@ struct sockaddr_vm {
 #ifndef IP_TRANSPARENT
 #define IP_TRANSPARENT 19
 #endif
+
+/* linux/sockios.h */
+#ifndef SIOCGSKNS
+#define SIOCGSKNS 0x894C
+#endif
index 20a422c77cf347add270c7d417b486ab2ee62c70..ee8005833dee58354bd79541c68cd1b100b6a16c 100644 (file)
@@ -3,6 +3,8 @@
 #include <errno.h>
 #include <linux/filter.h>
 #include <linux/netlink.h>
+#include <linux/sockios.h>
+#include <sys/ioctl.h>
 #include <unistd.h>
 
 #include "sd-device.h"
 #include "device-monitor-private.h"
 #include "device-private.h"
 #include "device-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "hashmap.h"
 #include "io-util.h"
+#include "missing_socket.h"
 #include "mountpoint-util.h"
 #include "set.h"
 #include "socket-util.h"
@@ -163,12 +167,54 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group,
 
         if (fd >= 0) {
                 r = monitor_set_nl_address(m);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
+                if (r < 0) {
+                        log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
+                        goto fail;
+                }
+        }
+
+        if (DEBUG_LOGGING) {
+                _cleanup_close_ int netns = -1;
+
+                /* So here's the thing: only AF_NETLINK sockets from the main network namespace will get
+                 * hardware events. Let's check if ours is from there, and if not generate a debug message,
+                 * since we cannot possibly work correctly otherwise. This is just a safety check to make
+                 * things easier to debug. */
+
+                netns = ioctl(m->sock, SIOCGSKNS);
+                if (netns < 0)
+                        log_debug_errno(errno, "sd-device-monitor: Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns: %m");
+                else {
+                        struct stat a, b;
+
+                        if (fstat(netns, &a) < 0) {
+                                r = log_debug_errno(errno, "sd-device-monitor: Failed to stat netns of udev netlink socket: %m");
+                                goto fail;
+                        }
+
+                        if (stat("/proc/1/ns/net", &b) < 0) {
+                                if (ERRNO_IS_PRIVILEGE(errno))
+                                        /* If we can't access PID1's netns info due to permissions, it's fine, this is a
+                                         * safety check only after all. */
+                                        log_debug_errno(errno, "sd-device-monitor: No permission to stat PID1's netns, unable to determine if we are in host netns: %m");
+                                else
+                                        log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns: %m");
+
+                        } else if (a.st_dev != b.st_dev || a.st_ino != b.st_ino)
+                                log_debug("sd-device-monitor: Netlink socket we listen on is not from host netns, we won't see device events.");
+                }
         }
 
         *ret = TAKE_PTR(m);
         return 0;
+
+fail:
+        /* Let's unset the socket fd in the monitor object before we destroy it so that the fd passed in is
+         * not closed on failure. */
+        if (fd >= 0)
+                m->sock = -1;
+
+        return r;
 }
 
 _public_ int sd_device_monitor_new(sd_device_monitor **ret) {