Subject: [PATCH] etun: Cleanup device factory. From: Eric W. Biederman Date: 1137622288 -0700 - Don't use a spinlock and an external list use the network device list. - Make newif and delif sysfs module parameters for allocating and freeing new devices. - Report the other device of a pair as partnter_ifindex not rx_ifindex - Check for CAP_NET_ADMIN before allowing use of the device factory. --- drivers/net/Kconfig | 1 drivers/net/etun.c | 161 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 117 insertions(+), 45 deletions(-) 0e0d4603cf1434272ae3ac1eb585a0d67ee114d4 diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 4975a1f..2444655 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -107,6 +107,7 @@ config TUN config ETUN tristate "Universal ETUN device driver support" + depends on SYSFS ---help--- ETUN provices a pair of network devices that can be used for configuring interesting topolgies. What one devices transmits diff --git a/drivers/net/etun.c b/drivers/net/etun.c index da5ea8d..5de158d 100644 --- a/drivers/net/etun.c +++ b/drivers/net/etun.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -56,16 +57,12 @@ * For checksums both the transmitter and the receiver must * agree before the are actually disabled. */ -#define ETUN_MUST_CHECKSUM 1 - -static DEFINE_SPINLOCK(etun_lock); -static LIST_HEAD(etun_list); #define ETUN_NUM_STATS 1 static struct { const char string[ETH_GSTRING_LEN]; } ethtool_stats_keys[ETUN_NUM_STATS] = { - { "rx_ifindex" }, + { "partner_ifindex" }, }; struct etun_info { @@ -162,7 +159,7 @@ static void etun_get_ethtool_stats(struc struct ethtool_stats *stats, u64 *data) { struct etun_info *info = dev->priv; - + data[0] = info->rx_dev->ifindex; } @@ -244,26 +241,39 @@ static void etun_set_multicast_list(stru /* Nothing sane I can do here */ return; } + static int etun_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { return -EOPNOTSUPP; } -static struct net_device *etun_alloc(void) +/* Only allow letters and numbers in an etun device name */ +static int is_valid_name(const char *name) +{ + const char *ptr; + for (ptr = name; *ptr; ptr++) { + if (!isalnum(*ptr)) + return 0; + } + return 1; +} + +static struct net_device *etun_alloc(const char *name) { struct net_device *dev; struct etun_info *info; int err; - dev = alloc_netdev(sizeof(struct etun_info), "etun%d", ether_setup); + + if (!name || !is_valid_name(name)) + return ERR_PTR(-EINVAL); + + dev = alloc_netdev(sizeof(struct etun_info), name, ether_setup); if (!dev) return ERR_PTR(-ENOMEM); - info = netdev_priv(dev); + + info = dev->priv; info->dev = dev; - spin_lock(&etun_lock); - list_add(&info->list, &etun_list); - spin_unlock(&etun_lock); - random_ether_addr(dev->dev_addr); dev->tx_queue_len = 0; /* A queue is silly for a loopback device */ dev->hard_start_xmit = etun_xmit; @@ -277,58 +287,114 @@ static struct net_device *etun_alloc(voi | NETIF_F_LLTX; dev->flags = IFF_BROADCAST | IFF_MULTICAST |IFF_PROMISC; dev->ethtool_ops = &etun_ethtool_ops; - dev->priv = info; dev->destructor = free_netdev; netif_carrier_off(dev); err = register_netdev(dev); if (err) { - spin_lock(&etun_lock); - list_del(&info->list); - spin_unlock(&etun_lock); - free_netdev(dev); dev = ERR_PTR(err); + goto out; } + out: return dev; } -static int etun_alloc_pair(struct net_device **dev) +static int etun_alloc_pair(const char *name0, const char *name1) { - struct net_device *etun0, *etun1; + struct net_device *dev0, *dev1; struct etun_info *info0, *info1; - etun0 = etun_alloc(); - if (IS_ERR(etun0)) { - return PTR_ERR(etun0); + dev0 = etun_alloc(name0); + if (IS_ERR(dev0)) { + return PTR_ERR(dev0); } - info0 = etun0->priv; + info0 = dev0->priv; - etun1 = etun_alloc(); - if (IS_ERR(etun1)) { - unregister_netdev(etun0); - return PTR_ERR(etun1); + dev1 = etun_alloc(name1); + if (IS_ERR(dev1)) { + unregister_netdev(dev0); + return PTR_ERR(dev1); } - info1 = etun1->priv; + info1 = dev1->priv; - info0->rx_dev = etun1; - info1->rx_dev = etun0; + dev_hold(dev0); + dev_hold(dev1); + info0->rx_dev = dev1; + info1->rx_dev = dev0; - dev[0] = etun0; - dev[1] = etun1; return 0; } -static int etun_new(const char *val, struct kernel_param *kp) +static int etun_unregister_pair(struct net_device *dev0) { - struct net_device *pair[2]; - return etun_alloc_pair(pair); + struct etun_info *info0, *info1; + struct net_device *dev1; + + ASSERT_RTNL(); + + if (!dev0) + return -ENODEV; + + info0 = dev0->priv; + dev1 = info0->rx_dev; + info1 = dev1->priv; + + /* Drop the cross device references */ + dev_put(dev0); + dev_put(dev1); + + unregister_netdevice(dev0); + unregister_netdevice(dev1); + return 0; } -static int etun_new_get(char *buffer, struct kernel_param *kp) +static int etun_noget(char *buffer, struct kernel_param *kp) { return 0; } +static int etun_newif(const char *val, struct kernel_param *kp) +{ + char name0[IFNAMSIZ], name1[IFNAMSIZ]; + const char *mid; + int len; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Split the string into 2 names */ + mid = strchr(val, ','); + if (!mid) + return -EINVAL; + + /* Get the first device name */ + len = mid - val; + if (len > sizeof(name0) - 1) + len = sizeof(name0) - 1; + strncpy(name0, val, len); + name0[len] = '\0'; + + /* And the second device name */ + len = sizeof(name1) - 1; + strncpy(name1, mid + 1, len); + name1[len] = '\0'; + + return etun_alloc_pair(name0, name1); +} + +static int etun_delif(const char *val, struct kernel_param *kp) +{ + struct net_device *dev; + int err; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + rtnl_lock(); + err = -ENODEV; + dev = __dev_get_by_name(current->host, val); + err = etun_unregister_pair(dev); + rtnl_unlock(); + return err; +} + static int __init etun_init(void) { printk(KERN_INFO "etun: %s, %s\n", DRV_DESCRIPTION, DRV_VERSION); @@ -339,16 +405,21 @@ static int __init etun_init(void) static void etun_cleanup(void) { - struct etun_info *info, *tmp; - spin_lock(&etun_lock); - list_for_each_entry_safe(info, tmp, &etun_list, list) { - list_del(&info->list); - unregister_netdev(info->dev); - } - spin_unlock(&etun_lock); + struct net_device *dev; + /* Slow and simple N^2 device free algorithm. */ + rtnl_lock(); + do { + for (dev = dev_base; dev; dev = dev->next) { + if (dev->open == etun_open) + break; + } + etun_unregister_pair(dev); + } while(dev); + rtnl_unlock(); } -module_param_call(new, etun_new, etun_new_get, NULL, S_IWUSR); +module_param_call(newif, etun_newif, etun_noget, NULL, S_IWUSR); +module_param_call(delif, etun_delif, etun_noget, NULL, S_IWUSR); module_init(etun_init); module_exit(etun_cleanup); MODULE_DESCRIPTION(DRV_DESCRIPTION); -- 1.0.GIT