diff options
Diffstat (limited to 'quicktun.c')
-rw-r--r-- | quicktun.c | 44 |
1 files changed, 39 insertions, 5 deletions
@@ -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: |