summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--quicktun.c44
1 files changed, 39 insertions, 5 deletions
diff --git a/quicktun.c b/quicktun.c
index 4c5c585..50429a4 100644
--- a/quicktun.c
+++ b/quicktun.c
@@ -4,6 +4,7 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/netdevice.h>
+#include <linux/rculist.h>
#include <linux/udp.h>
#include <net/dst.h>
#include <net/genetlink.h>
@@ -23,6 +24,9 @@
#define MAX_MTU 65535
+static LIST_HEAD(quicktun_list);
+
+
struct quicktun_struct {
struct net_device *dev;
unsigned long flags;
@@ -34,6 +38,12 @@ struct quicktun_struct {
};
+struct quicktun_list_entry {
+ struct list_head list;
+ struct quicktun_struct *quicktun;
+};
+
+
static netdev_tx_t quicktun_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct quicktun_struct *tun = netdev_priv(dev);
@@ -265,6 +275,7 @@ static int quicktun_cmd_create_device(struct sk_buff *skb, struct genl_info *inf
struct quicktun_struct *tun;
struct net *net = genl_info_net(info);
struct net_device *dev;
+ struct quicktun_list_entry *entry;
char *name = "qt%d";
int err;
@@ -286,16 +297,20 @@ static int quicktun_cmd_create_device(struct sk_buff *skb, struct genl_info *inf
if (info->attrs[QUICKTUN_A_IFNAME])
name = nla_data(info->attrs[QUICKTUN_A_IFNAME]);
+ entry = kmalloc(sizeof(struct quicktun_struct), GFP_KERNEL);
dev = alloc_netdev(sizeof(struct quicktun_struct), name, quicktun_setup);
- if (!dev)
- return -ENOMEM;
+ if (!entry || !dev) {
+ err = -ENOMEM;
+ goto err_free;
+ }
dev_net_set(dev, net);
tun = netdev_priv(dev);
tun->dev = dev;
tun->flags = flags;
+ entry->quicktun = tun;
quicktun_net_init(dev);
@@ -321,7 +336,7 @@ static int quicktun_cmd_create_device(struct sk_buff *skb, struct genl_info *inf
err = quicktun_socket_init(dev);
if (err < 0)
- goto err_free_dev;
+ goto err_free;
rtnl_lock();
@@ -335,6 +350,8 @@ static int quicktun_cmd_create_device(struct sk_buff *skb, struct genl_info *inf
if (err < 0)
goto err_free_sk;
+ list_add_tail_rcu(&entry->list, &quicktun_list);
+
rtnl_unlock();
return 0;
@@ -343,8 +360,9 @@ static int quicktun_cmd_create_device(struct sk_buff *skb, struct genl_info *inf
rtnl_unlock();
sock_release(tun->sock);
- err_free_dev:
- free_netdev(dev);
+ err_free:
+ if (dev) free_netdev(dev);
+ if (entry) kfree(entry);
return err;
}
@@ -354,6 +372,7 @@ static int quicktun_cmd_delete_device(struct sk_buff *skb, struct genl_info *inf
struct net *net = genl_info_net(info);
struct net_device *dev;
struct quicktun_struct *tun;
+ struct quicktun_list_entry *entry = NULL;
struct socket *sock;
char *name;
@@ -381,12 +400,27 @@ static int quicktun_cmd_delete_device(struct sk_buff *skb, struct genl_info *inf
tun = netdev_priv(dev);
sock = tun->sock;
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(entry, &quicktun_list, list) {
+ if (entry->quicktun->dev == dev) {
+ break;
+ }
+ }
+
+ list_del_rcu(&entry->list);
+
+ rcu_read_unlock();
+
unregister_netdevice(dev);
rtnl_unlock();
sock_release(sock);
+ synchronize_rcu();
+ kfree(entry);
+
return 0;
err_unlock: