summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2017-10-25 19:45:16 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2017-10-31 17:25:23 +0100
commit424ff7b8ed77bf556e16fca94e72483daf846caa (patch)
treede01b8134cab4ddddb7261925779db6222ec19e4
parent0d474166163835b53e6ac265a08314756327493f (diff)
device: only take reference if netns is different
If we take two references, the namespace and the device are never freed in the usual manner. We should thus only take a reference to another namespace when it is a different namespace from our own. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--src/compat/compat.h5
-rw-r--r--src/device.c59
-rw-r--r--src/device.h1
3 files changed, 56 insertions, 9 deletions
diff --git a/src/compat/compat.h b/src/compat/compat.h
index 34353ea..cbb00e5 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -497,6 +497,11 @@ static inline int cpu_has_xfeatures(u64 xfeatures_needed, const char **feature_n
#endif
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+struct _____dummy_container { char dev; };
+#define netdev_notifier_info net_device *)data); __attribute((unused)) char _____dummy = ((struct _____dummy_container
+#endif
+
/* https://lkml.org/lkml/2017/6/23/790 */
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <linux/ip.h>
diff --git a/src/device.c b/src/device.c
index 593a91c..2bdbfd9 100644
--- a/src/device.c
+++ b/src/device.c
@@ -65,7 +65,7 @@ static int open(struct net_device *dev)
}
#ifdef CONFIG_PM_SLEEP
-static int suspending_clear_noise_peers(struct notifier_block *nb, unsigned long action, void *data)
+static int pm_notification(struct notifier_block *nb, unsigned long action, void *data)
{
struct wireguard_device *wg;
struct wireguard_peer *peer;
@@ -88,7 +88,7 @@ static int suspending_clear_noise_peers(struct notifier_block *nb, unsigned long
rcu_barrier_bh();
return 0;
}
-static struct notifier_block clear_peers_on_suspend = { .notifier_call = suspending_clear_noise_peers };
+static struct notifier_block pm_notifier = { .notifier_call = pm_notification };
#endif
static int stop(struct net_device *dev)
@@ -223,7 +223,8 @@ static void destruct(struct net_device *dev)
mutex_unlock(&wg->device_update_lock);
free_percpu(dev->tstats);
free_percpu(wg->incoming_handshakes_worker);
- put_net(wg->creating_net);
+ if (wg->have_creating_net_ref)
+ put_net(wg->creating_net);
pr_debug("%s: Interface deleted\n", dev->name);
free_netdev(dev);
@@ -264,7 +265,7 @@ static int newlink(struct net *src_net, struct net_device *dev, struct nlattr *t
int ret = -ENOMEM;
struct wireguard_device *wg = netdev_priv(dev);
- wg->creating_net = get_net(src_net);
+ wg->creating_net = src_net;
init_rwsem(&wg->static_identity.lock);
mutex_init(&wg->socket_update_lock);
mutex_init(&wg->device_update_lock);
@@ -337,7 +338,6 @@ error_3:
error_2:
free_percpu(dev->tstats);
error_1:
- put_net(src_net);
return ret;
}
@@ -348,22 +348,63 @@ static struct rtnl_link_ops link_ops __read_mostly = {
.newlink = newlink,
};
+static int netdevice_notification(struct notifier_block *nb, unsigned long action, void *data)
+{
+ struct net_device *dev = ((struct netdev_notifier_info *)data)->dev;
+ struct wireguard_device *wg = netdev_priv(dev);
+
+ ASSERT_RTNL();
+
+ if (action != NETDEV_REGISTER || dev->netdev_ops != &netdev_ops)
+ return 0;
+
+ if (dev_net(dev) == wg->creating_net && wg->have_creating_net_ref) {
+ put_net(wg->creating_net);
+ wg->have_creating_net_ref = false;
+ } else if (dev_net(dev) != wg->creating_net && !wg->have_creating_net_ref) {
+ wg->have_creating_net_ref = true;
+ get_net(wg->creating_net);
+ }
+ return 0;
+}
+
+static struct notifier_block netdevice_notifier = { .notifier_call = netdevice_notification };
+
int __init device_init(void)
{
-#ifdef CONFIG_PM_SLEEP
- int ret = register_pm_notifier(&clear_peers_on_suspend);
+ int ret;
+#ifdef CONFIG_PM_SLEEP
+ ret = register_pm_notifier(&pm_notifier);
if (ret)
return ret;
#endif
- return rtnl_link_register(&link_ops);
+
+ ret = register_netdevice_notifier(&netdevice_notifier);
+ if (ret)
+ goto error_pm;
+
+ ret = rtnl_link_register(&link_ops);
+ if (ret)
+ goto error_netdevice;
+
+ return 0;
+
+error_netdevice:
+ unregister_netdevice_notifier(&netdevice_notifier);
+error_pm:
+#ifdef CONFIG_PM_SLEEP
+ unregister_pm_notifier(&pm_notifier);
+#endif
+ return ret;
}
void device_uninit(void)
{
rtnl_link_unregister(&link_ops);
+ unregister_netdevice_notifier(&netdevice_notifier);
#ifdef CONFIG_PM_SLEEP
- unregister_pm_notifier(&clear_peers_on_suspend);
+ unregister_pm_notifier(&pm_notifier);
#endif
rcu_barrier_bh();
}
diff --git a/src/device.h b/src/device.h
index a74f58b..6ec3cb2 100644
--- a/src/device.h
+++ b/src/device.h
@@ -52,6 +52,7 @@ struct wireguard_device {
unsigned int num_peers, device_update_gen;
u32 fwmark;
u16 incoming_port;
+ bool have_creating_net_ref;
};
int device_init(void);