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");
* 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);
#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"
}
}
+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 = {};
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");
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;
}
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;
.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,
.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,
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);
#include "strv.h"
#include "tc.h"
#include "tmpfile-util.h"
+#include "tuntap.h"
#include "udev-util.h"
#include "util.h"
#include "vrf.h"
#include <linux/nexthop.h>
#include <linux/nl80211.h>
-#include "sd-daemon.h"
#include "sd-netlink.h"
#include "alloc-util.h"
#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"
#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. */
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) {
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
}
int manager_setup(Manager *m) {
+ _cleanup_close_ int rtnl_fd = -1;
int r;
assert(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;
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);
if (r < 0)
return r;
+ manager_clear_unmanaged_tuntap_fds(m);
+
r = network_load(m, &m->networks);
if (r < 0)
return r;
FirewallContext *fw_ctx;
OrderedSet *request_queue;
+
+ Hashmap *tuntap_fds_by_name;
};
int manager_new(Manager **ret, bool test_mode);
DeviceAllow=char-* rw
ExecStart=!!{{ROOTLIBEXECDIR}}/systemd-networkd
ExecReload=networkctl reload
+FileDescriptorStoreMax=512
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes