]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/tuntap: save tun or tap file descriptor in fd store
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 13 Aug 2022 08:18:55 +0000 (17:18 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 16 Aug 2022 12:57:35 +0000 (21:57 +0900)
src/network/netdev/netdev.c
src/network/netdev/netdev.h
src/network/netdev/tuntap.c
src/network/netdev/tuntap.h
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-manager.h
units/systemd-networkd.service.in

index 464f47f2cf78da81d4131ade6057a9c746c6ab3b..212df3daa02871e53942f0c4a08dad819f10d886 100644 (file)
@@ -237,6 +237,9 @@ void netdev_drop(NetDev *netdev) {
                 return;
         }
 
+        if (NETDEV_VTABLE(netdev) && NETDEV_VTABLE(netdev)->drop)
+                NETDEV_VTABLE(netdev)->drop(netdev);
+
         netdev->state = NETDEV_STATE_LINGER;
 
         log_netdev_debug(netdev, "netdev removed");
index 1c7dc0f7e55d452c2426140ca73b710216a27f34..49eadbb7a442e1c02ff1388d9fd1982abf65ab51 100644 (file)
@@ -142,6 +142,9 @@ typedef struct NetDevVTable {
          * to be set != 0. */
         void (*init)(NetDev *n);
 
+        /* This is called when the interface is removed. */
+        void (*drop)(NetDev *n);
+
         /* This should free all kind-specific variables. It should be
          * idempotent. */
         void (*done)(NetDev *n);
index f48ebf268a99dae84b2e5539ddaa16b58fef88ca..39ea7c1d739e6c1579134caf9980d4cc2b9d9cf1 100644 (file)
 #include <linux/if_tun.h>
 
 #include "alloc-util.h"
+#include "daemon-util.h"
 #include "fd-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "socket-util.h"
 #include "tuntap.h"
 #include "user-util.h"
 
@@ -29,6 +33,73 @@ static TunTap* TUNTAP(NetDev *netdev) {
         }
 }
 
+static void *close_fd_ptr(void *p) {
+        safe_close(PTR_TO_FD(p));
+        return NULL;
+}
+
+DEFINE_PRIVATE_HASH_OPS_FULL(named_fd_hash_ops, char, string_hash_func, string_compare_func, free, void, close_fd_ptr);
+
+int manager_add_tuntap_fd(Manager *m, int fd, const char *name) {
+        _cleanup_free_ char *tuntap_name = NULL;
+        const char *p;
+        int r;
+
+        assert(m);
+        assert(fd >= 0);
+        assert(name);
+
+        p = startswith(name, "tuntap-");
+        if (!p)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received unknown fd (%s).", name);
+
+        if (!ifname_valid(p))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received tuntap fd with invalid name (%s).", p);
+
+        tuntap_name = strdup(p);
+        if (!tuntap_name)
+                return log_oom_debug();
+
+        r = hashmap_ensure_put(&m->tuntap_fds_by_name, &named_fd_hash_ops, tuntap_name, FD_TO_PTR(fd));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to store tuntap fd: %m");
+
+        TAKE_PTR(tuntap_name);
+        return 0;
+}
+
+void manager_clear_unmanaged_tuntap_fds(Manager *m) {
+        char *name;
+        void *p;
+
+        assert(m);
+
+        while ((p = hashmap_steal_first_key_and_value(m->tuntap_fds_by_name, (void**) &name))) {
+                close_and_notify_warn(PTR_TO_FD(p), name);
+                name = mfree(name);
+        }
+}
+
+static int tuntap_take_fd(NetDev *netdev) {
+        _cleanup_free_ char *name = NULL;
+        void *p;
+        int r;
+
+        assert(netdev);
+        assert(netdev->manager);
+
+        r = link_get_by_name(netdev->manager, netdev->ifname, NULL);
+        if (r < 0)
+                return r;
+
+        p = hashmap_remove2(netdev->manager->tuntap_fds_by_name, netdev->ifname, (void**) &name);
+        if (!p)
+                return -ENOENT;
+
+        log_netdev_debug(netdev, "Found file descriptor in fd store.");
+        return PTR_TO_FD(p);
+}
+
 static int netdev_create_tuntap(NetDev *netdev) {
         _cleanup_close_ int fd = -1;
         struct ifreq ifr = {};
@@ -39,7 +110,11 @@ static int netdev_create_tuntap(NetDev *netdev) {
         t = TUNTAP(netdev);
         assert(t);
 
-        fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
+        fd = TAKE_FD(t->fd);
+        if (fd < 0)
+                fd = tuntap_take_fd(netdev);
+        if (fd < 0)
+                fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
         if (fd < 0)
                 return log_netdev_error_errno(netdev, errno,  "Failed to open " TUN_DEV ": %m");
 
@@ -90,8 +165,10 @@ static int netdev_create_tuntap(NetDev *netdev) {
         if (ioctl(fd, TUNSETPERSIST, 1) < 0)
                 return log_netdev_error_errno(netdev, errno, "TUNSETPERSIST failed: %m");
 
-        if (t->keep_fd)
+        if (t->keep_fd) {
                 t->fd = TAKE_FD(fd);
+                (void) notify_push_fdf(t->fd, "tuntap-%s", netdev->ifname);
+        }
 
         return 0;
 }
@@ -106,6 +183,16 @@ static void tuntap_init(NetDev *netdev) {
         t->fd = -1;
 }
 
+static void tuntap_drop(NetDev *netdev) {
+        TunTap *t;
+
+        assert(netdev);
+        t = TUNTAP(netdev);
+        assert(t);
+
+        t->fd = close_and_notify_warn(t->fd, netdev->ifname);
+}
+
 static void tuntap_done(NetDev *netdev) {
         TunTap *t;
 
@@ -141,6 +228,7 @@ const NetDevVTable tun_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tun\0",
         .config_verify = tuntap_verify,
         .init = tuntap_init,
+        .drop = tuntap_drop,
         .done = tuntap_done,
         .create = netdev_create_tuntap,
         .create_type = NETDEV_CREATE_INDEPENDENT,
@@ -152,6 +240,7 @@ const NetDevVTable tap_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tap\0",
         .config_verify = tuntap_verify,
         .init = tuntap_init,
+        .drop = tuntap_drop,
         .done = tuntap_done,
         .create = netdev_create_tuntap,
         .create_type = NETDEV_CREATE_INDEPENDENT,
index 52f2da9fabe623839b1f97fed36c9f41ff9c8951..88e0ce5f97047c1f0185d3eda36411f13ea487d6 100644 (file)
@@ -21,3 +21,6 @@ DEFINE_NETDEV_CAST(TUN, TunTap);
 DEFINE_NETDEV_CAST(TAP, TunTap);
 extern const NetDevVTable tun_vtable;
 extern const NetDevVTable tap_vtable;
+
+int manager_add_tuntap_fd(Manager *m, int fd, const char *name);
+void manager_clear_unmanaged_tuntap_fds(Manager *m);
index 20eb43eb0949c35a83b46828f7d68d16d192d80b..06e9822251385d8e9521b808dbc57a9bb6338cee 100644 (file)
@@ -64,6 +64,7 @@
 #include "strv.h"
 #include "tc.h"
 #include "tmpfile-util.h"
+#include "tuntap.h"
 #include "udev-util.h"
 #include "util.h"
 #include "vrf.h"
index 9b77f536c8e9f3b4224571e615f3e8f8b78c9a78..52c62d72978c9cfa99f51cf9af0075245f90abb7 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/nexthop.h>
 #include <linux/nl80211.h>
 
-#include "sd-daemon.h"
 #include "sd-netlink.h"
 
 #include "alloc-util.h"
@@ -18,6 +17,7 @@
 #include "bus-polkit.h"
 #include "bus-util.h"
 #include "conf-parser.h"
+#include "daemon-util.h"
 #include "def.h"
 #include "device-private.h"
 #include "device-util.h"
@@ -58,6 +58,7 @@
 #include "sysctl-util.h"
 #include "tclass.h"
 #include "tmpfile-util.h"
+#include "tuntap.h"
 #include "udev-util.h"
 
 /* use 128 MB for receive socket kernel queue. */
@@ -243,22 +244,45 @@ static int manager_connect_udev(Manager *m) {
         return 0;
 }
 
-static int systemd_netlink_fd(void) {
-        int n, fd, rtnl_fd = -EINVAL;
+static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) {
+        _cleanup_strv_free_ char **names = NULL;
+        int n, rtnl_fd = -1;
 
-        n = sd_listen_fds(true);
-        if (n <= 0)
+        assert(m);
+        assert(ret_rtnl_fd);
+
+        n = sd_listen_fds_with_names(/* unset_environment = */ true, &names);
+        if (n < 0)
+                return n;
+
+        if (strv_length(names) != (size_t) n)
                 return -EINVAL;
 
-        for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++)
+        for (int i = 0; i < n; i++) {
+                int fd = i + SD_LISTEN_FDS_START;
+
                 if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
-                        if (rtnl_fd >= 0)
-                                return -EINVAL;
+                        if (rtnl_fd >= 0) {
+                                log_debug("Received multiple netlink socket, ignoring.");
+                                safe_close(fd);
+                                continue;
+                        }
 
                         rtnl_fd = fd;
+                        continue;
                 }
 
-        return rtnl_fd;
+                if (manager_add_tuntap_fd(m, fd, names[i]) >= 0)
+                        continue;
+
+                if (m->test_mode)
+                        safe_close(fd);
+                else
+                        close_and_notify_warn(fd, names[i]);
+        }
+
+        *ret_rtnl_fd = rtnl_fd;
+        return 0;
 }
 
 static int manager_connect_genl(Manager *m) {
@@ -325,18 +349,21 @@ static int manager_setup_rtnl_filter(Manager *manager) {
         return sd_netlink_attach_filter(manager->rtnl, ELEMENTSOF(filter), filter);
 }
 
-static int manager_connect_rtnl(Manager *m) {
-        int fd, r;
+static int manager_connect_rtnl(Manager *m, int fd) {
+        _unused_ _cleanup_close_ int fd_close = fd;
+        int r;
 
         assert(m);
 
-        fd = systemd_netlink_fd();
+        /* This takes input fd. */
+
         if (fd < 0)
                 r = sd_netlink_open(&m->rtnl);
         else
                 r = sd_netlink_open_fd(&m->rtnl, fd);
         if (r < 0)
                 return r;
+        TAKE_FD(fd_close);
 
         /* Bump receiver buffer, but only if we are not called via socket activation, as in that
          * case systemd sets the receive buffer size for us, and the value in the .socket unit
@@ -487,6 +514,7 @@ static int manager_set_keep_configuration(Manager *m) {
 }
 
 int manager_setup(Manager *m) {
+        _cleanup_close_ int rtnl_fd = -1;
         int r;
 
         assert(m);
@@ -510,7 +538,11 @@ int manager_setup(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = manager_connect_rtnl(m);
+        r = manager_listen_fds(m, &rtnl_fd);
+        if (r < 0)
+                return r;
+
+        r = manager_connect_rtnl(m, TAKE_FD(rtnl_fd));
         if (r < 0)
                 return r;
 
@@ -600,6 +632,8 @@ Manager* manager_free(Manager *m) {
 
         m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
 
+        m->tuntap_fds_by_name = hashmap_free(m->tuntap_fds_by_name);
+
         m->wiphy_by_name = hashmap_free(m->wiphy_by_name);
         m->wiphy_by_index = hashmap_free_with_destructor(m->wiphy_by_index, wiphy_free);
 
@@ -678,6 +712,8 @@ int manager_load_config(Manager *m) {
         if (r < 0)
                 return r;
 
+        manager_clear_unmanaged_tuntap_fds(m);
+
         r = network_load(m, &m->networks);
         if (r < 0)
                 return r;
index 2191d0d9557340a7dbea70309b162d598a270fda..e16f2b854b76ff9829cce2f65dbb145089f15c18 100644 (file)
@@ -100,6 +100,8 @@ struct Manager {
         FirewallContext *fw_ctx;
 
         OrderedSet *request_queue;
+
+        Hashmap *tuntap_fds_by_name;
 };
 
 int manager_new(Manager **ret, bool test_mode);
index 95dd2665b28132713635e713897e8eb8ae62ba36..d15129e7f0ace9d4f15b791846fd1c150a9dcb6f 100644 (file)
@@ -25,6 +25,7 @@ CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_N
 DeviceAllow=char-* rw
 ExecStart=!!{{ROOTLIBEXECDIR}}/systemd-networkd
 ExecReload=networkctl reload
+FileDescriptorStoreMax=512
 LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes