X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=vlandev.c;h=f2440a75220f68d240a6425f99be4638a2b57b4c;hb=327da9895327bc56b23413ee91a6e6b6e0e4329d;hp=7b2038e93019b5f89207591c64c88b3721fffbc5;hpb=5a4eb870afc69e9fa93fdccb8a42f75e8e357f4e;p=project%2Fnetifd.git diff --git a/vlandev.c b/vlandev.c index 7b2038e..f2440a7 100644 --- a/vlandev.c +++ b/vlandev.c @@ -13,23 +13,27 @@ */ #include +#include #include "netifd.h" #include "device.h" #include "interface.h" #include "system.h" +#include "utils.h" enum { - VLANDEV_ATTR_TYPE, VLANDEV_ATTR_IFNAME, VLANDEV_ATTR_VID, + VLANDEV_ATTR_INGRESS_QOS_MAPPING, + VLANDEV_ATTR_EGRESS_QOS_MAPPING, __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 }, + [VLANDEV_ATTR_VID] = { "vid", BLOBMSG_TYPE_STRING }, + [VLANDEV_ATTR_INGRESS_QOS_MAPPING] = { "ingress_qos_mapping", BLOBMSG_TYPE_ARRAY }, + [VLANDEV_ATTR_EGRESS_QOS_MAPPING] = { "egress_qos_mapping", BLOBMSG_TYPE_ARRAY }, }; static const struct uci_blob_param_list vlandev_attr_list = { @@ -40,6 +44,9 @@ static const struct uci_blob_param_list vlandev_attr_list = { .next = { &device_attr_list }, }; +static struct device_type vlan8021q_device_type; +static struct blob_buf b; + struct vlandev_device { struct device dev; struct device_user parent; @@ -48,9 +55,72 @@ struct vlandev_device { struct blob_attr *config_data; struct blob_attr *ifname; + struct blob_attr *vid; + struct vlandev_config config; }; +static int +vlandev_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) +{ + struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev); + void *a; + + dev = mvdev->parent.dev; + if (!dev || !dev->hotplug_ops) + return UBUS_STATUS_NOT_SUPPORTED; + + blob_buf_init(&b, 0); + a = blobmsg_open_array(&b, "vlans"); + blobmsg_printf(&b, NULL, "%d", mvdev->config.vid); + blobmsg_close_array(&b, a); + + return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); +} + +static int +vlandev_hotplug_del(struct device *dev, struct device *member) +{ + struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev); + + dev = mvdev->parent.dev; + if (!dev || !dev->hotplug_ops) + return UBUS_STATUS_NOT_SUPPORTED; + + return dev->hotplug_ops->del(dev, member); +} + +static int +vlandev_hotplug_prepare(struct device *dev, struct device **bridge_dev) +{ + struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev); + + dev = mvdev->parent.dev; + if (!dev || !dev->hotplug_ops) + return UBUS_STATUS_NOT_SUPPORTED; + + return dev->hotplug_ops->prepare(dev, bridge_dev); +} + +static void vlandev_hotplug_check(struct vlandev_device *mvdev) +{ + static const struct device_hotplug_ops hotplug_ops = { + .prepare = vlandev_hotplug_prepare, + .add = vlandev_hotplug_add, + .del = vlandev_hotplug_del + }; + struct device *dev = mvdev->parent.dev; + + if (!dev || !dev->hotplug_ops || avl_is_empty(&dev->vlans.avl) || + mvdev->dev.type != &vlan8021q_device_type) { + mvdev->dev.hotplug_ops = NULL; + return; + } + + mvdev->dev.hotplug_ops = &hotplug_ops; +} + + static void vlandev_base_cb(struct device_user *dev, enum device_event ev) { @@ -63,11 +133,8 @@ vlandev_base_cb(struct device_user *dev, enum device_event ev) 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); + case DEV_EVENT_UPDATE_IFNAME: + vlandev_hotplug_check(mvdev); break; default: return; @@ -131,9 +198,31 @@ vlandev_free(struct device *dev) mvdev = container_of(dev, struct vlandev_device, dev); device_remove_user(&mvdev->parent); + free(mvdev->config_data); + vlist_simple_flush_all(&mvdev->config.ingress_qos_mapping_list); + vlist_simple_flush_all(&mvdev->config.egress_qos_mapping_list); free(mvdev); } +static void vlandev_qos_mapping_dump(struct blob_buf *b, const char *name, const struct vlist_simple_tree *qos_mapping_li) +{ + const struct vlan_qos_mapping *elem; + void *a, *t; + + a = blobmsg_open_array(b, name); + + vlist_simple_for_each_element(qos_mapping_li, elem, node) { + t = blobmsg_open_table(b, NULL); + + blobmsg_add_u32(b, "from", elem->from); + blobmsg_add_u32(b, "to", elem->to); + + blobmsg_close_table(b, t); + } + + blobmsg_close_array(b, a); +} + static void vlandev_dump_info(struct device *dev, struct blob_buf *b) { @@ -142,6 +231,31 @@ vlandev_dump_info(struct device *dev, struct blob_buf *b) mvdev = container_of(dev, struct vlandev_device, dev); blobmsg_add_string(b, "parent", mvdev->parent.dev->ifname); system_if_dump_info(dev, b); + blobmsg_add_u32(b, "vid", mvdev->config.vid); + vlandev_qos_mapping_dump(b, "ingress_qos_mapping", &mvdev->config.ingress_qos_mapping_list); + vlandev_qos_mapping_dump(b, "egress_qos_mapping", &mvdev->config.egress_qos_mapping_list); +} + +static uint16_t +vlandev_get_vid(struct device *dev, const char *id_str) +{ + unsigned long id; + uint16_t *alias_id; + char *err; + + id = strtoul(id_str, &err, 10); + if (err && *err) { + if (!dev) + return 1; + + alias_id = kvlist_get(&dev->vlan_aliases, id_str); + if (!alias_id) + return 1; + + id = *alias_id; + } + + return (uint16_t)id; } static void @@ -154,7 +268,39 @@ vlandev_config_init(struct device *dev) if (mvdev->ifname) basedev = device_get(blobmsg_data(mvdev->ifname), true); + if (mvdev->vid) + mvdev->config.vid = vlandev_get_vid(basedev, blobmsg_get_string(mvdev->vid)); + else + mvdev->config.vid = 1; + device_add_user(&mvdev->parent, basedev); + vlandev_hotplug_check(mvdev); +} + +static void vlandev_qos_mapping_list_apply(struct vlist_simple_tree *qos_mapping_li, struct blob_attr *list) +{ + struct blob_attr *cur; + struct vlan_qos_mapping *qos_mapping; + int rem, rc; + + blobmsg_for_each_attr(cur, list, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (!blobmsg_check_attr(cur, false)) + continue; + + qos_mapping = calloc(1, sizeof(*qos_mapping)); + if (!qos_mapping) + continue; + + rc = sscanf(blobmsg_data(cur), "%" PRIu32 ":%" PRIu32, &qos_mapping->from, &qos_mapping->to); + if (rc != 2) { + free(qos_mapping); + continue; + } + vlist_simple_add(qos_mapping_li, &qos_mapping->node); + } } static void @@ -163,17 +309,20 @@ 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; + cfg->proto = (mvdev->dev.type == &vlan8021q_device_type) ? + VLAN_PROTO_8021Q : VLAN_PROTO_8021AD; - if ((cur = tb[VLANDEV_ATTR_TYPE])) - { - if(!strcmp(blobmsg_data(cur), "8021ad")) - cfg->proto = VLAN_PROTO_8021AD; - } + vlist_simple_update(&cfg->ingress_qos_mapping_list); + vlist_simple_update(&cfg->egress_qos_mapping_list); - if ((cur = tb[VLANDEV_ATTR_VID])) - cfg->vid = (uint16_t) blobmsg_get_u32(cur); + if ((cur = tb[VLANDEV_ATTR_INGRESS_QOS_MAPPING])) + vlandev_qos_mapping_list_apply(&cfg->ingress_qos_mapping_list, cur); + + if ((cur = tb[VLANDEV_ATTR_EGRESS_QOS_MAPPING])) + vlandev_qos_mapping_list_apply(&cfg->egress_qos_mapping_list, cur); + + vlist_simple_flush(&cfg->ingress_qos_mapping_list); + vlist_simple_flush(&cfg->egress_qos_mapping_list); } static enum dev_change_type @@ -185,6 +334,7 @@ vlandev_reload(struct device *dev, struct blob_attr *attr) struct vlandev_device *mvdev; mvdev = container_of(dev, struct vlandev_device, dev); + attr = blob_memdup(attr); blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev, blob_data(attr), blob_len(attr)); @@ -194,6 +344,7 @@ vlandev_reload(struct device *dev, struct blob_attr *attr) device_init_settings(dev, tb_dev); vlandev_apply_settings(mvdev, tb_mv); mvdev->ifname = tb_mv[VLANDEV_ATTR_IFNAME]; + mvdev->vid = tb_mv[VLANDEV_ATTR_VID]; if (mvdev->config_data) { struct blob_attr *otb_dev[__DEV_ATTR_MAX]; @@ -214,12 +365,14 @@ vlandev_reload(struct device *dev, struct blob_attr *attr) vlandev_config_init(dev); } + free(mvdev->config_data); mvdev->config_data = attr; return ret; } static struct device * -vlandev_create(const char *name, struct blob_attr *attr) +vlandev_create(const char *name, struct device_type *devtype, + struct blob_attr *attr) { struct vlandev_device *mvdev; struct device *dev = NULL; @@ -228,8 +381,19 @@ vlandev_create(const char *name, struct blob_attr *attr) if (!mvdev) return NULL; + vlist_simple_init(&mvdev->config.ingress_qos_mapping_list, + struct vlan_qos_mapping, node); + vlist_simple_init(&mvdev->config.egress_qos_mapping_list, + struct vlan_qos_mapping, node); + dev = &mvdev->dev; - device_init(dev, &vlandev_device_type, name); + + if (device_init(dev, devtype, name) < 0) { + device_cleanup(dev); + free(mvdev); + return NULL; + } + dev->config_pending = true; mvdev->set_state = dev->set_state; @@ -243,14 +407,28 @@ vlandev_create(const char *name, struct blob_attr *attr) return dev; } -const struct device_type vlandev_device_type = { - .name = "VLANDEV", +static struct device_type vlan8021ad_device_type = { + .name = "8021ad", .config_params = &vlandev_attr_list, - .keep_link_status = true, + .create = vlandev_create, + .config_init = vlandev_config_init, + .reload = vlandev_reload, + .free = vlandev_free, + .dump_info = vlandev_dump_info, +}; +static struct device_type vlan8021q_device_type = { + .name = "8021q", + .config_params = &vlandev_attr_list, .create = vlandev_create, .config_init = vlandev_config_init, .reload = vlandev_reload, .free = vlandev_free, .dump_info = vlandev_dump_info, }; + +static void __init vlandev_device_type_init(void) +{ + device_type_add(&vlan8021ad_device_type); + device_type_add(&vlan8021q_device_type); +}