summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2013-10-12 23:02:49 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2013-10-12 23:02:49 +0200
commit13c3b2edaa9384a0baf6d2acbc7d740166dce388 (patch)
treedb890e0de097f33cac0e20b8beb8e1719a16fa89
parentd66f59187743d8724b53a1f5cdd587ea2939e786 (diff)
downloadmodfastd-13c3b2edaa9384a0baf6d2acbc7d740166dce388.tar
modfastd-13c3b2edaa9384a0baf6d2acbc7d740166dce388.zip
Interface registration
-rw-r--r--fastd.c193
-rw-r--r--fastd.h6
2 files changed, 191 insertions, 8 deletions
diff --git a/fastd.c b/fastd.c
index 6eefd72..9c3894c 100644
--- a/fastd.c
+++ b/fastd.c
@@ -43,31 +43,71 @@
#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/rculist.h>
#include <net/genetlink.h>
+#include <net/route.h>
#include "fastd.h"
+static LIST_HEAD(fastd_list);
+static struct workqueue_struct *fastd_workqueue;
+
+
+struct fastd_struct {
+ struct list_head list;
+
+ struct net_device *dev;
+ u32 owner;
+
+ u16 mode;
+ unsigned long flags;
+
+ struct work_struct destroy_work;
+};
+
+
static struct genl_family fastd_nl_family = {
.id = GENL_ID_GENERATE,
+ .hdrsize = 0,
.name = DRV_NAME,
.version = 1,
- .hdrsize = 0,
.maxattr = FASTD_A_MAX,
+ .netnsok = true,
+ .parallel_ops = true,
};
-static int fastd_nl_event(struct notifier_block *this,
- unsigned long event, void *ptr)
+static int fastd_nl_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
{
struct netlink_notify *n = ptr;
+ struct fastd_struct *entry;
if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
return NOTIFY_DONE;
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(entry, &fastd_list, list) {
+ if (entry->owner == n->portid) {
+ /* allow registration of a new interface for this portid */
+ ACCESS_ONCE(entry->owner) = 0;
+
+ queue_work(fastd_workqueue, &entry->destroy_work);
+ }
+ }
+
+ rcu_read_unlock();
+
return NOTIFY_DONE;
}
@@ -76,16 +116,146 @@ static struct notifier_block nl_notifier = {
};
+static const struct net_device_ops fastd_netdev_ops_eth = {
+};
+
+static const struct net_device_ops fastd_netdev_ops_ip = {
+};
+
+
+static void fastd_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct fastd_struct *fastd = netdev_priv(dev);
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->fw_version, "N/A");
+
+ switch (fastd->mode) {
+ case FASTD_MODE_ETH:
+ strcpy(info->bus_info, "ethernet");
+ break;
+ case FASTD_MODE_IP:
+ strcpy(info->bus_info, "ip");
+ }
+}
+
+static u32 fastd_ethtool_get_link(struct net_device *dev)
+{
+ return 1;
+}
+
+
+static const struct ethtool_ops fastd_ethtool_ops = {
+ .get_drvinfo = fastd_ethtool_get_drvinfo,
+ .get_link = fastd_ethtool_get_link,
+};
+
+
+static void fastd_netdev_setup(struct net_device *dev)
+{
+ dev->ethtool_ops = &fastd_ethtool_ops;
+ dev->destructor = free_netdev;
+}
+
+static void fastd_netdev_init(struct net_device *dev)
+{
+ struct fastd_struct *fastd = netdev_priv(dev);
+
+ switch (fastd->mode) {
+ case FASTD_MODE_ETH:
+ dev->netdev_ops = &fastd_netdev_ops_eth;
+
+ eth_hw_addr_random(dev);
+ ether_setup(dev);
+
+ dev->hard_header_len = ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct udphdr) + ETH_HLEN;
+
+ break;
+
+ case FASTD_MODE_IP:
+ dev->netdev_ops = &fastd_netdev_ops_eth;
+
+ dev->addr_len = 0;
+ dev->hard_header_len = ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct udphdr);
+ dev->mtu = 1500;
+
+ dev->type = ARPHRD_NONE;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ }
+}
+
+static void fastd_destroy_work(struct work_struct *work)
+{
+ struct fastd_struct *fastd = container_of(work, struct fastd_struct,
+ destroy_work);
+
+ rtnl_lock();
+
+ list_del_rcu(&fastd->list);
+ unregister_netdevice(fastd->dev);
+
+ rtnl_unlock();
+}
+
static int fastd_cmd_create(struct sk_buff *skb, struct genl_info *info)
{
- if (!capable(CAP_NET_ADMIN))
+ struct net *net = genl_info_net(info);
+ struct net_device *dev;
+ struct fastd_struct *fastd;
+ u16 mode;
+ const char *name = "fastd%d";
+ int err;
+
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
- return -EINVAL;
+ if (!info->attrs[FASTD_A_MODE])
+ return -EINVAL;
+
+ mode = nla_get_u16(info->attrs[FASTD_A_MODE]);
+ if (mode != FASTD_MODE_ETH && mode != FASTD_MODE_IP)
+ return -EINVAL;
+
+ if (info->attrs[FASTD_A_IFNAME])
+ name = nla_data(info->attrs[FASTD_A_IFNAME]);
+
+ dev = alloc_netdev(sizeof(struct fastd_struct), name, fastd_netdev_setup);
+ if (!dev)
+ return -ENOMEM;
+
+ dev_net_set(dev, net);
+
+ fastd = netdev_priv(dev);
+ fastd->dev = dev;
+ fastd->owner = info->snd_portid;
+ fastd->mode = mode;
+ INIT_WORK(&fastd->destroy_work, fastd_destroy_work);
+
+ fastd_netdev_init(dev);
+
+ rtnl_lock();
+ err = register_netdevice(dev);
+ if (err < 0) {
+ rtnl_unlock();
+ goto err_free_dev;
+ }
+
+ list_add_tail_rcu(&fastd->list, &fastd_list);
+
+ rtnl_unlock();
+
+ return 0;
+
+err_free_dev:
+ free_netdev(dev);
+ return err;
}
static struct nla_policy fastd_nl_policy[__FASTD_A_MAX] = {
+ [FASTD_A_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+ [FASTD_A_MODE] = { .type = NLA_U16 },
};
static struct genl_ops fastd_nl_ops[] = {
@@ -101,9 +271,14 @@ static int __init fastd_init(void)
{
int ret = 0;
+ fastd_workqueue = alloc_workqueue("fastd", 0, 0);
+ if (!fastd_workqueue)
+ return -ENOMEM;
+
netlink_register_notifier(&nl_notifier);
- ret = genl_register_family_with_ops(&fastd_nl_family, fastd_nl_ops, ARRAY_SIZE(fastd_nl_ops));
+ ret = genl_register_family_with_ops(&fastd_nl_family, fastd_nl_ops,
+ ARRAY_SIZE(fastd_nl_ops));
if (ret)
goto unregister_notifier;
@@ -111,8 +286,9 @@ static int __init fastd_init(void)
return 0;
- unregister_notifier:
+unregister_notifier:
netlink_unregister_notifier(&nl_notifier);
+ destroy_workqueue(fastd_workqueue);
return ret;
}
@@ -121,6 +297,7 @@ static void fastd_cleanup(void)
{
genl_unregister_family(&fastd_nl_family);
netlink_unregister_notifier(&nl_notifier);
+ destroy_workqueue(fastd_workqueue);
}
@@ -129,4 +306,4 @@ module_exit(fastd_cleanup);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS_GENL_FAMILY("fastd");
+MODULE_ALIAS_GENL_FAMILY(DRV_NAME);
diff --git a/fastd.h b/fastd.h
index 0e110a9..026846a 100644
--- a/fastd.h
+++ b/fastd.h
@@ -39,6 +39,10 @@
#ifndef __LINUX_FASTD_H
#define __LINUX_FASTD_H
+#define FASTD_MODE_ETH 0x0001
+#define FASTD_MODE_IP 0x0002
+
+
enum {
FASTD_CMD_UNSPEC,
FASTD_CMD_CREATE,
@@ -47,6 +51,8 @@ enum {
enum {
FASTD_A_UNSPEC,
+ FASTD_A_IFNAME,
+ FASTD_A_MODE,
__FASTD_A_MAX
};