summaryrefslogtreecommitdiff
path: root/vlandev.c
diff options
context:
space:
mode:
authorGioacchino Mazzurco <gio@eigenlab.org>2014-06-10 19:29:13 +0200
committerFelix Fietkau <nbd@openwrt.org>2014-06-11 12:22:23 +0200
commit59217785704fca27d2c7a19e279d27c384a452cd (patch)
tree5de87f928553d8b56c1681151026c4b14f0f17c4 /vlandev.c
parentf8981d9933683e1a8314fa86ac610f8e9a22661c (diff)
downloadnetifd-59217785704fca27d2c7a19e279d27c384a452cd.tar.gz
Add vlan 802.1q/802.1ad support as netifd devices
At moment netifd supports just 802.1q vlan, you can configure them using a concise but "hacky" syntax using an interface config section, with this patch netifd acquire the capability of configuring 802.1ad and 802.1q vlan using config device sections, so you can define a vlan device plus interface with something like this: config device 'test' option type '8021ad' option name 'test' option ifname 'eth0' option vid '1000' config interface 'testif' option ifname 'test' option proto 'none' option auto '1' old syntax for 802.1q keeps working so no retrocompatibility problems, to keep retrocompatibility means also that user must not use name/ifname like eth0.2 for devices declared with the new style because this would trigger the "old style" when interface config section is parsed Signed-off-by: Gioacchino Mazzurco <gmazzurco89@gmail.com>
Diffstat (limited to 'vlandev.c')
-rw-r--r--vlandev.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/vlandev.c b/vlandev.c
new file mode 100644
index 0000000..36a5c63
--- /dev/null
+++ b/vlandev.c
@@ -0,0 +1,255 @@
+/*
+ * netifd - network interface daemon
+ * Copyright (C) 2014 Gioacchino Mazzurco <gio@eigenlab.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <string.h>
+
+#include "netifd.h"
+#include "device.h"
+#include "interface.h"
+#include "system.h"
+
+enum {
+ VLANDEV_ATTR_TYPE,
+ VLANDEV_ATTR_IFNAME,
+ VLANDEV_ATTR_VID,
+ __VLANDEV_ATTR_MAX
+};
+
+static const struct blobmsg_policy vlandev_attrs[__VLANDEV_ATTR_MAX] = {
+ [VLANDEV_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
+ [VLANDEV_ATTR_IFNAME] = { "ifname", BLOBMSG_TYPE_STRING },
+ [VLANDEV_ATTR_VID] = { "vid", BLOBMSG_TYPE_INT32 },
+};
+
+static const struct uci_blob_param_list vlandev_attr_list = {
+ .n_params = __VLANDEV_ATTR_MAX,
+ .params = vlandev_attrs,
+
+ .n_next = 1,
+ .next = { &device_attr_list },
+};
+
+struct vlandev_device {
+ struct device dev;
+ struct device_user parent;
+
+ device_state_cb set_state;
+
+ struct blob_attr *config_data;
+ struct blob_attr *ifname;
+ struct vlandev_config config;
+};
+
+static void
+vlandev_base_cb(struct device_user *dev, enum device_event ev)
+{
+ struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, parent);
+
+ switch (ev) {
+ case DEV_EVENT_ADD:
+ device_set_present(&mvdev->dev, true);
+ break;
+ case DEV_EVENT_REMOVE:
+ device_set_present(&mvdev->dev, false);
+ break;
+ case DEV_EVENT_LINK_UP:
+ device_set_link(&mvdev->dev, true);
+ break;
+ case DEV_EVENT_LINK_DOWN:
+ device_set_link(&mvdev->dev, false);
+ break;
+ default:
+ return;
+ }
+}
+
+static int
+vlandev_set_down(struct vlandev_device *mvdev)
+{
+ mvdev->set_state(&mvdev->dev, false);
+ system_vlandev_del(&mvdev->dev);
+ device_release(&mvdev->parent);
+
+ return 0;
+}
+
+static int
+vlandev_set_up(struct vlandev_device *mvdev)
+{
+ int ret;
+
+ ret = device_claim(&mvdev->parent);
+ if (ret < 0)
+ return ret;
+
+ ret = system_vlandev_add(&mvdev->dev, mvdev->parent.dev, &mvdev->config);
+ if (ret < 0)
+ goto release;
+
+ ret = mvdev->set_state(&mvdev->dev, true);
+ if (ret)
+ goto delete;
+
+ return 0;
+
+delete:
+ system_vlandev_del(&mvdev->dev);
+release:
+ device_release(&mvdev->parent);
+ return ret;
+}
+
+static int
+vlandev_set_state(struct device *dev, bool up)
+{
+ struct vlandev_device *mvdev;
+
+ D(SYSTEM, "vlandev_set_state(%s, %u)\n", dev->ifname, up);
+
+ mvdev = container_of(dev, struct vlandev_device, dev);
+ if (up)
+ return vlandev_set_up(mvdev);
+ else
+ return vlandev_set_down(mvdev);
+}
+
+static void
+vlandev_free(struct device *dev)
+{
+ struct vlandev_device *mvdev;
+
+ mvdev = container_of(dev, struct vlandev_device, dev);
+ device_remove_user(&mvdev->parent);
+ free(mvdev);
+}
+
+static void
+vlandev_dump_info(struct device *dev, struct blob_buf *b)
+{
+ struct vlandev_device *mvdev;
+
+ mvdev = container_of(dev, struct vlandev_device, dev);
+ blobmsg_add_string(b, "parent", mvdev->parent.dev->ifname);
+ system_if_dump_info(dev, b);
+}
+
+static void
+vlandev_config_init(struct device *dev)
+{
+ struct vlandev_device *mvdev;
+ struct device *basedev = NULL;
+
+ mvdev = container_of(dev, struct vlandev_device, dev);
+ if (mvdev->ifname)
+ basedev = device_get(blobmsg_data(mvdev->ifname), true);
+
+ device_add_user(&mvdev->parent, basedev);
+}
+
+static void
+vlandev_apply_settings(struct vlandev_device *mvdev, struct blob_attr **tb)
+{
+ struct vlandev_config *cfg = &mvdev->config;
+ struct blob_attr *cur;
+
+ cfg->proto = VLAN_PROTO_8021Q;
+ cfg->vid = 1;
+
+ if ((cur = tb[VLANDEV_ATTR_TYPE]))
+ {
+ if(!strcmp(blobmsg_data(cur), "8021ad"))
+ cfg->proto = VLAN_PROTO_8021AD;
+ }
+
+ if ((cur = tb[VLANDEV_ATTR_VID]))
+ cfg->vid = (uint16_t) blobmsg_get_u32(cur);
+}
+
+static enum dev_change_type
+vlandev_reload(struct device *dev, struct blob_attr *attr)
+{
+ struct blob_attr *tb_dev[__DEV_ATTR_MAX];
+ struct blob_attr *tb_mv[__VLANDEV_ATTR_MAX];
+ enum dev_change_type ret = DEV_CONFIG_APPLIED;
+ struct vlandev_device *mvdev;
+
+ mvdev = container_of(dev, struct vlandev_device, dev);
+
+ blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
+ blob_data(attr), blob_len(attr));
+ blobmsg_parse(vlandev_attrs, __VLANDEV_ATTR_MAX, tb_mv,
+ blob_data(attr), blob_len(attr));
+
+ device_init_settings(dev, tb_dev);
+ vlandev_apply_settings(mvdev, tb_mv);
+ mvdev->ifname = tb_mv[VLANDEV_ATTR_IFNAME];
+
+ if (mvdev->config_data) {
+ struct blob_attr *otb_dev[__DEV_ATTR_MAX];
+ struct blob_attr *otb_mv[__VLANDEV_ATTR_MAX];
+
+ blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev,
+ blob_data(mvdev->config_data), blob_len(mvdev->config_data));
+
+ if (uci_blob_diff(tb_dev, otb_dev, &device_attr_list, NULL))
+ ret = DEV_CONFIG_RESTART;
+
+ blobmsg_parse(vlandev_attrs, __VLANDEV_ATTR_MAX, otb_mv,
+ blob_data(mvdev->config_data), blob_len(mvdev->config_data));
+
+ if (uci_blob_diff(tb_mv, otb_mv, &vlandev_attr_list, NULL))
+ ret = DEV_CONFIG_RESTART;
+
+ vlandev_config_init(dev);
+ }
+
+ mvdev->config_data = attr;
+ return ret;
+}
+
+static struct device *
+vlandev_create(const char *name, struct blob_attr *attr)
+{
+ struct vlandev_device *mvdev;
+ struct device *dev = NULL;
+
+ mvdev = calloc(1, sizeof(*mvdev));
+ if (!mvdev)
+ return NULL;
+
+ dev = &mvdev->dev;
+ device_init(dev, &vlandev_device_type, name);
+ dev->config_pending = true;
+
+ mvdev->set_state = dev->set_state;
+ dev->set_state = vlandev_set_state;
+
+ dev->hotplug_ops = NULL;
+ mvdev->parent.cb = vlandev_base_cb;
+
+ vlandev_reload(dev, attr);
+
+ return dev;
+}
+
+const struct device_type vlandev_device_type = {
+ .name = "VLANDEV",
+ .config_params = &vlandev_attr_list,
+
+ .create = vlandev_create,
+ .config_init = vlandev_config_init,
+ .reload = vlandev_reload,
+ .free = vlandev_free,
+ .dump_info = vlandev_dump_info,
+};