chiark / gitweb /
nspawn: check with udev before we take possession of an interface
[elogind.git] / src / nspawn / nspawn.c
index 160b50b3ed83c8042c025da6bd46246ab11d7800..abcee3fb98162fae7c7553a0e1e79f1e2d2bf9ee 100644 (file)
@@ -73,6 +73,7 @@
 #include "env-util.h"
 #include "def.h"
 #include "rtnl-util.h"
+#include "udev-util.h"
 
 typedef enum LinkJournal {
         LINK_NO,
@@ -216,6 +217,7 @@ static int parse_argv(int argc, char *argv[]) {
         };
 
         int c, r;
+        uint64_t plus = 0, minus = 0;
 
         assert(argc >= 0);
         assert(argv);
@@ -325,9 +327,9 @@ static int parse_argv(int argc, char *argv[]) {
 
                                 if (streq(t, "all")) {
                                         if (c == ARG_CAPABILITY)
-                                                arg_retain = (uint64_t) -1;
+                                                plus = (uint64_t) -1;
                                         else
-                                                arg_retain = 0;
+                                                minus = (uint64_t) -1;
                                 } else {
                                         if (cap_from_name(t, &cap) < 0) {
                                                 log_error("Failed to parse capability %s.", t);
@@ -335,9 +337,9 @@ static int parse_argv(int argc, char *argv[]) {
                                         }
 
                                         if (c == ARG_CAPABILITY)
-                                                arg_retain |= 1ULL << (uint64_t) cap;
+                                                plus |= 1ULL << (uint64_t) cap;
                                         else
-                                                arg_retain &= ~(1ULL << (uint64_t) cap);
+                                                minus |= 1ULL << (uint64_t) cap;
                                 }
                         }
 
@@ -460,6 +462,8 @@ static int parse_argv(int argc, char *argv[]) {
                 return -EINVAL;
         }
 
+        arg_retain = (arg_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
+
         return 1;
 }
 
@@ -1252,7 +1256,8 @@ static int reset_audit_loginuid(void) {
 }
 
 static int move_network_interfaces(pid_t pid) {
-        _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
+        _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
+        _cleanup_udev_unref_ struct udev *udev = NULL;
         char **i;
         int r;
 
@@ -1262,23 +1267,43 @@ static int move_network_interfaces(pid_t pid) {
         if (strv_isempty(arg_network_interfaces))
                 return 0;
 
-        r = sd_rtnl_open(NETLINK_ROUTE, &rtnl);
+        r = sd_rtnl_open(0, &rtnl);
         if (r < 0) {
                 log_error("Failed to connect to netlink: %s", strerror(-r));
                 return r;
         }
 
+        udev = udev_new();
+        if (!udev) {
+                log_error("Failed to connect to udev.");
+                return -ENOMEM;
+        }
+
         STRV_FOREACH(i, arg_network_interfaces) {
-                _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
-                unsigned ifi;
+                _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+                _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+                char ifi_str[2 + DECIMAL_STR_MAX(int)];
+                int ifi;
 
-                ifi = if_nametoindex(*i);
-                if (ifi == 0) {
+                ifi = (int) if_nametoindex(*i);
+                if (ifi <= 0) {
                         log_error("Failed to resolve interface %s: %m", *i);
                         return -errno;
                 }
 
-                r = sd_rtnl_message_link_new(RTM_NEWLINK, ifi, &m);
+                sprintf(ifi_str, "n%i", ifi);
+                d = udev_device_new_from_device_id(udev, ifi_str);
+                if (!d) {
+                        log_error("Failed to get udev device for interface %s: %m", *i);
+                        return -errno;
+                }
+
+                if (udev_device_get_is_initialized(d) <= 0) {
+                        log_error("Network interface %s is not initialized yet.", *i);
+                        return -EBUSY;
+                }
+
+                r = sd_rtnl_message_new_link(RTM_NEWLINK, ifi, &m);
                 if (r < 0) {
                         log_error("Failed to allocate netlink message: %s", strerror(-r));
                         return r;
@@ -1292,7 +1317,7 @@ static int move_network_interfaces(pid_t pid) {
 
                 r = sd_rtnl_call(rtnl, m, 0, NULL);
                 if (r < 0) {
-                        log_error("Failed to move interface to namespace: %s", strerror(-r));
+                        log_error("Failed to move interface %s to namespace: %s", *i, strerror(-r));
                         return r;
                 }
         }