From 3023b0cc73520cf7b14e1bfa9075072098b6a753 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 7 Aug 2020 14:19:06 +0200 Subject: [PATCH] bridge: add support for defining port member vlans via hotplug ops Signed-off-by: Felix Fietkau --- bridge.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++- config.c | 1 + device.h | 4 +- interface.c | 10 +++-- interface.h | 2 +- ubus.c | 5 ++- wireless.c | 4 +- 7 files changed, 120 insertions(+), 11 deletions(-) diff --git a/bridge.c b/bridge.c index 14d4972..92eea9f 100644 --- a/bridge.c +++ b/bridge.c @@ -122,6 +122,11 @@ struct bridge_member { char name[]; }; +struct bridge_vlan_hotplug_port { + struct list_head list; + struct bridge_vlan_port port; +}; + static void bridge_reset_primary(struct bridge_state *bst) { @@ -153,6 +158,7 @@ bridge_reset_primary(struct bridge_state *bst) static struct bridge_vlan_port * bridge_find_vlan_member_port(struct bridge_member *bm, struct bridge_vlan *vlan) { + struct bridge_vlan_hotplug_port *port; const char *ifname = bm->dev.dev->ifname; int i; @@ -163,6 +169,13 @@ bridge_find_vlan_member_port(struct bridge_member *bm, struct bridge_vlan *vlan) return &vlan->ports[i]; } + list_for_each_entry(port, &vlan->hotplug_ports, list) { + if (strcmp(port->port.ifname, ifname) != 0) + continue; + + return &port->port; + } + return NULL; } @@ -415,9 +428,25 @@ bridge_remove_member(struct bridge_member *bm) static void bridge_free_member(struct bridge_member *bm) { + struct bridge_state *bst = bm->bst; struct device *dev = bm->dev.dev; + const char *ifname = dev->ifname; + struct bridge_vlan *vlan; bridge_remove_member(bm); + + vlist_for_each_element(&bst->dev.vlans, vlan, node) { + struct bridge_vlan_hotplug_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &vlan->hotplug_ports, list) { + if (strcmp(port->port.ifname, ifname) != 0) + continue; + + list_del(&port->list); + free(port); + } + } + device_remove_user(&bm->dev); /* @@ -616,11 +645,66 @@ bridge_add_member(struct bridge_state *bst, const char *name) bridge_create_member(bst, name, dev, false); } +static void +bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *vlans, const char *ifname) +{ + struct bridge_vlan *vlan; + struct blob_attr *cur; + int rem; + + if (!vlans) + return; + + blobmsg_for_each_attr(cur, vlans, rem) { + struct bridge_vlan_hotplug_port *port; + uint16_t flags = BRVLAN_F_UNTAGGED; + char *name_buf; + unsigned int vid; + char *end; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + vid = strtoul(blobmsg_get_string(cur), &end, 0); + if (!vid || vid > 4095) + continue; + + vlan = vlist_find(&bst->dev.vlans, &vid, vlan, node); + if (!vlan) + continue; + + if (end && *end) { + if (*end != ':') + continue; + + for (end++; *end; end++) { + switch (*end) { + case 't': + flags &= ~BRVLAN_F_UNTAGGED; + break; + case '*': + flags |= BRVLAN_F_PVID; + break; + } + } + } + + port = calloc_a(sizeof(*port), &name_buf, strlen(ifname) + 1); + if (!port) + continue; + + port->port.flags = flags; + port->port.ifname = strcpy(name_buf, ifname); + list_add_tail(&port->list, &vlan->hotplug_ports); + } +} + static int -bridge_hotplug_add(struct device *dev, struct device *member) +bridge_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) { struct bridge_state *bst = container_of(dev, struct bridge_state, dev); + bridge_hotplug_create_member_vlans(bst, vlan, member->ifname); bridge_create_member(bst, member->ifname, member, true); return 0; @@ -899,6 +983,20 @@ bridge_vlan_equal(struct bridge_vlan *v1, struct bridge_vlan *v2) return true; } +static void +bridge_vlan_free(struct bridge_vlan *vlan) +{ + struct bridge_vlan_hotplug_port *port, *tmp; + + if (!vlan) + return; + + list_for_each_entry_safe(port, tmp, &vlan->hotplug_ports, list) + free(port); + + free(vlan); +} + static void bridge_vlan_update(struct vlist_tree *tree, struct vlist_node *node_new, struct vlist_node *node_old) @@ -920,13 +1018,16 @@ bridge_vlan_update(struct vlist_tree *tree, struct vlist_node *node_new, if (node_old) bridge_set_vlan_state(bst, vlan_old, false); + if (node_old && node_new) + list_splice_init(&vlan_old->hotplug_ports, &vlan_new->hotplug_ports); + if (node_new) bridge_set_vlan_state(bst, vlan_new, true); bst->dev.config_pending = true; out: - free(vlan_old); + bridge_vlan_free(vlan_old); } static struct device * diff --git a/config.c b/config.c index 526a20f..3546787 100644 --- a/config.c +++ b/config.c @@ -323,6 +323,7 @@ config_parse_vlan(struct device *dev, struct uci_section *s) vlan->n_ports = n_ports; vlan->ports = port = (struct bridge_vlan_port *)&vlan[1]; + INIT_LIST_HEAD(&vlan->hotplug_ports); name_buf = (char *)&port[n_ports]; blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_PORTS], rem) { diff --git a/device.h b/device.h index b25d267..617a272 100644 --- a/device.h +++ b/device.h @@ -227,7 +227,7 @@ struct device { struct device_hotplug_ops { int (*prepare)(struct device *dev); - int (*add)(struct device *main, struct device *member); + int (*add)(struct device *main, struct device *member, struct blob_attr *vlan); int (*del)(struct device *main, struct device *member); }; @@ -248,6 +248,8 @@ struct bridge_vlan { struct bridge_vlan_port *ports; int n_ports; + struct list_head hotplug_ports; + uint16_t vid; bool local; }; diff --git a/interface.c b/interface.c index 782174f..c53c091 100644 --- a/interface.c +++ b/interface.c @@ -1038,7 +1038,8 @@ interface_remove_link(struct interface *iface, struct device *dev) } static int -interface_add_link(struct interface *iface, struct device *dev, bool link_ext) +interface_add_link(struct interface *iface, struct device *dev, + struct blob_attr *vlan, bool link_ext) { struct device *mdev = iface->main_dev.dev; @@ -1050,7 +1051,7 @@ interface_add_link(struct interface *iface, struct device *dev, bool link_ext) if (mdev) { if (mdev->hotplug_ops) - return mdev->hotplug_ops->add(mdev, dev); + return mdev->hotplug_ops->add(mdev, dev, vlan); else return UBUS_STATUS_NOT_SUPPORTED; } @@ -1064,7 +1065,8 @@ interface_add_link(struct interface *iface, struct device *dev, bool link_ext) } int -interface_handle_link(struct interface *iface, const char *name, bool add, bool link_ext) +interface_handle_link(struct interface *iface, const char *name, + struct blob_attr *vlan, bool add, bool link_ext) { struct device *dev; int ret; @@ -1081,7 +1083,7 @@ interface_handle_link(struct interface *iface, const char *name, bool add, bool interface_set_device_config(iface, dev); device_set_present(dev, true); - ret = interface_add_link(iface, dev, link_ext); + ret = interface_add_link(iface, dev, vlan, link_ext); } else { ret = interface_remove_link(iface, dev); } diff --git a/interface.h b/interface.h index 3d58aa3..9c136b6 100644 --- a/interface.h +++ b/interface.h @@ -196,7 +196,7 @@ void interface_set_l3_dev(struct interface *iface, struct device *dev); void interface_add_user(struct interface_user *dep, struct interface *iface); void interface_remove_user(struct interface_user *dep); -int interface_handle_link(struct interface *iface, const char *name, bool add, bool link_ext); +int interface_handle_link(struct interface *iface, const char *name, struct blob_attr *vlan, bool add, bool link_ext); void interface_add_error(struct interface *iface, const char *subsystem, const char *code, const char **data, int n_data); diff --git a/ubus.c b/ubus.c index 15c826b..4f16764 100644 --- a/ubus.c +++ b/ubus.c @@ -903,12 +903,14 @@ netifd_handle_dump(struct ubus_context *ctx, struct ubus_object *obj, enum { DEV_LINK_NAME, DEV_LINK_EXT, + DEV_LINK_VLAN, __DEV_LINK_MAX, }; static const struct blobmsg_policy dev_link_policy[__DEV_LINK_MAX] = { [DEV_LINK_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, [DEV_LINK_EXT] = { .name = "link-ext", .type = BLOBMSG_TYPE_BOOL }, + [DEV_LINK_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_ARRAY }, }; static int @@ -933,7 +935,8 @@ netifd_iface_handle_device(struct ubus_context *ctx, struct ubus_object *obj, if (cur) link_ext = blobmsg_get_bool(cur); - return interface_handle_link(iface, blobmsg_data(tb[DEV_LINK_NAME]), add, link_ext); + return interface_handle_link(iface, blobmsg_data(tb[DEV_LINK_NAME]), + tb[DEV_LINK_VLAN], add, link_ext); } diff --git a/wireless.c b/wireless.c index efb7992..6bf6b58 100644 --- a/wireless.c +++ b/wireless.c @@ -319,7 +319,7 @@ static void wireless_interface_handle_link(struct wireless_interface *vif, bool if (!iface) continue; - interface_handle_link(iface, vif->ifname, up, true); + interface_handle_link(iface, vif->ifname, NULL, up, true); } } @@ -349,7 +349,7 @@ static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up) if (!iface) continue; - interface_handle_link(iface, vlan->ifname, up, true); + interface_handle_link(iface, vlan->ifname, NULL, up, true); } } -- 2.30.2