From 50381d0a2998f6c0fc4823f0c2aa4206063d549e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 4 Jun 2021 09:05:31 +0200 Subject: bridge: allow adding/removing VLANs to configured member ports via hotplug This is useful for a dynamic VLAN setup, where extra tags need to be created on the trunking port on demand Signed-off-by: Felix Fietkau --- bridge.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++---- device.h | 2 +- extdev.c | 2 +- interface.c | 7 ++++--- vlan.c | 21 ++++++++++++--------- vlandev.c | 21 ++++++++++++--------- 6 files changed, 80 insertions(+), 27 deletions(-) diff --git a/bridge.c b/bridge.c index 04a9abf..ecb9b03 100644 --- a/bridge.c +++ b/bridge.c @@ -706,8 +706,21 @@ bridge_hotplug_get_vlan(struct bridge_state *bst, unsigned int vid) return vlan; } +static struct bridge_vlan_hotplug_port * +bridge_hotplug_get_vlan_port(struct bridge_vlan *vlan, const char *ifname) +{ + struct bridge_vlan_hotplug_port *port; + + list_for_each_entry(port, &vlan->hotplug_ports, list) + if (!strcmp(port->port.ifname, ifname)) + return port; + + return NULL; +} + static void -bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *vlans, const char *ifname) +bridge_hotplug_set_member_vlans(struct bridge_state *bst, struct blob_attr *vlans, + const char *ifname, struct bridge_member *bm, bool add) { struct bridge_vlan *vlan; struct blob_attr *cur; @@ -750,6 +763,27 @@ bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *v } } + port = bridge_hotplug_get_vlan_port(vlan, ifname); + if (!add) { + if (!port) + continue; + + __bridge_set_member_vlan(bm, vlan, &port->port, false); + list_del(&port->list); + free(port); + continue; + } + + if (port) { + if (port->port.flags == flags) + continue; + + __bridge_set_member_vlan(bm, vlan, &port->port, false); + port->port.flags = flags; + __bridge_set_member_vlan(bm, vlan, &port->port, true); + continue; + } + port = calloc_a(sizeof(*port), &name_buf, strlen(ifname) + 1); if (!port) continue; @@ -757,6 +791,11 @@ bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *v port->port.flags = flags; port->port.ifname = strcpy(name_buf, ifname); list_add_tail(&port->list, &vlan->hotplug_ports); + + if (!bm) + continue; + + __bridge_set_member_vlan(bm, vlan, &port->port, true); } } @@ -764,15 +803,18 @@ static int bridge_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) { struct bridge_state *bst = container_of(dev, struct bridge_state, dev); + struct bridge_member *bm; - bridge_hotplug_create_member_vlans(bst, vlan, member->ifname); - bridge_create_member(bst, member->ifname, member, true); + bm = vlist_find(&bst->members, member->ifname, bm, node); + bridge_hotplug_set_member_vlans(bst, vlan, member->ifname, bm, true); + if (!bm) + bridge_create_member(bst, member->ifname, member, true); return 0; } static int -bridge_hotplug_del(struct device *dev, struct device *member) +bridge_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan) { struct bridge_state *bst = container_of(dev, struct bridge_state, dev); struct bridge_member *bm; @@ -781,6 +823,10 @@ bridge_hotplug_del(struct device *dev, struct device *member) if (!bm) return UBUS_STATUS_NOT_FOUND; + bridge_hotplug_set_member_vlans(bst, vlan, member->ifname, bm, false); + if (!bm->dev.hotplug) + return 0; + vlist_delete(&bst->members, &bm->node); return 0; } diff --git a/device.h b/device.h index ed07791..c6fc02b 100644 --- a/device.h +++ b/device.h @@ -253,7 +253,7 @@ struct device { struct device_hotplug_ops { int (*prepare)(struct device *dev, struct device **bridge_dev); int (*add)(struct device *main, struct device *member, struct blob_attr *vlan); - int (*del)(struct device *main, struct device *member); + int (*del)(struct device *main, struct device *member, struct blob_attr *vlan); }; enum bridge_vlan_flags { diff --git a/extdev.c b/extdev.c index 95ed480..977d9c2 100644 --- a/extdev.c +++ b/extdev.c @@ -643,7 +643,7 @@ extdev_hotplug_add(struct device *ebr_dev, struct device *ebm_dev, struct blob_a } static int -extdev_hotplug_remove(struct device *dev, struct device *member) +extdev_hotplug_remove(struct device *dev, struct device *member, struct blob_attr *vlan) { struct extdev_bridge *ebr; struct extdev_bridge_member *ubm; diff --git a/interface.c b/interface.c index 1d1a5f8..2391e12 100644 --- a/interface.c +++ b/interface.c @@ -1034,12 +1034,13 @@ interface_set_main_dev(struct interface *iface, struct device *dev) } static int -interface_remove_link(struct interface *iface, struct device *dev) +interface_remove_link(struct interface *iface, struct device *dev, + struct blob_attr *vlan) { struct device *mdev = iface->main_dev.dev; if (mdev && mdev->hotplug_ops) - return mdev->hotplug_ops->del(mdev, dev); + return mdev->hotplug_ops->del(mdev, dev, vlan); if (dev == iface->ext_dev.dev) device_remove_user(&iface->ext_dev); @@ -1103,7 +1104,7 @@ interface_handle_link(struct interface *iface, const char *name, ret = interface_add_link(iface, dev, vlan, link_ext); } else { - ret = interface_remove_link(iface, dev); + ret = interface_remove_link(iface, dev, vlan); } out: diff --git a/vlan.c b/vlan.c index 82b2d66..459c907 100644 --- a/vlan.c +++ b/vlan.c @@ -39,7 +39,7 @@ static void free_vlan_if(struct device *iface) } static int -vlan_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) +__vlan_hotplug_op(struct device *dev, struct device *member, struct blob_attr *vlan, bool add) { struct vlan_device *vldev = container_of(dev, struct vlan_device, dev); void *a; @@ -53,19 +53,22 @@ vlan_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vl blobmsg_printf(&b, NULL, "%d", vldev->id); blobmsg_close_array(&b, a); - return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); + if (add) + return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); + else + return dev->hotplug_ops->del(dev, member, blobmsg_data(b.head)); } static int -vlan_hotplug_del(struct device *dev, struct device *member) +vlan_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) { - struct vlan_device *vldev = container_of(dev, struct vlan_device, dev); - - dev = vldev->dep.dev; - if (!dev || !dev->hotplug_ops) - return UBUS_STATUS_NOT_SUPPORTED; + return __vlan_hotplug_op(dev, member, vlan, true); +} - return dev->hotplug_ops->del(dev, member); +static int +vlan_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan) +{ + return __vlan_hotplug_op(dev, member, vlan, false); } static int diff --git a/vlandev.c b/vlandev.c index f2440a7..31b82b1 100644 --- a/vlandev.c +++ b/vlandev.c @@ -61,7 +61,7 @@ struct vlandev_device { }; static int -vlandev_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) +__vlandev_hotplug_op(struct device *dev, struct device *member, struct blob_attr *vlan, bool add) { struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev); void *a; @@ -75,19 +75,22 @@ vlandev_hotplug_add(struct device *dev, struct device *member, struct blob_attr blobmsg_printf(&b, NULL, "%d", mvdev->config.vid); blobmsg_close_array(&b, a); - return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); + if (add) + return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); + else + return dev->hotplug_ops->del(dev, member, blobmsg_data(b.head)); } static int -vlandev_hotplug_del(struct device *dev, struct device *member) +vlandev_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) { - 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 __vlandev_hotplug_op(dev, member, vlan, true); +} - return dev->hotplug_ops->del(dev, member); +static int +vlandev_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan) +{ + return __vlandev_hotplug_op(dev, member, vlan, false); } static int -- cgit v1.2.1