From: Yu Watanabe Date: Sat, 13 Aug 2022 08:18:55 +0000 (+0900) Subject: network/tuntap: save tun or tap file descriptor in fd store X-Git-Tag: v252-rc1~432^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af7a86b8a6b6510264b7ac0ae6a1e1d37d510ef5;p=thirdparty%2Fsystemd.git network/tuntap: save tun or tap file descriptor in fd store --- diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 464f47f2cf7..212df3daa02 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -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"); diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 1c7dc0f7e55..49eadbb7a44 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -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); diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c index f48ebf268a9..39ea7c1d739 100644 --- a/src/network/netdev/tuntap.c +++ b/src/network/netdev/tuntap.c @@ -10,7 +10,11 @@ #include #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, diff --git a/src/network/netdev/tuntap.h b/src/network/netdev/tuntap.h index 52f2da9fabe..88e0ce5f970 100644 --- a/src/network/netdev/tuntap.h +++ b/src/network/netdev/tuntap.h @@ -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); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 20eb43eb094..06e98222513 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -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" diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 9b77f536c8e..52c62d72978 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -8,7 +8,6 @@ #include #include -#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; diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 2191d0d9557..e16f2b854b7 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -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); diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in index 95dd2665b28..d15129e7f0a 100644 --- a/units/systemd-networkd.service.in +++ b/units/systemd-networkd.service.in @@ -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