bridge: allow adding/removing VLANs to configured member ports via hotplug
authorFelix Fietkau <nbd@nbd.name>
Fri, 4 Jun 2021 07:05:31 +0000 (09:05 +0200)
committerFelix Fietkau <nbd@nbd.name>
Fri, 4 Jun 2021 07:05:33 +0000 (09:05 +0200)
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 <nbd@nbd.name>
bridge.c
device.h
extdev.c
interface.c
vlan.c
vlandev.c

index 04a9abfb735f9d75aeeac160813166224522e6ee..ecb9b0373cb45a0f1a6f3286aac51277b50ab02d 100644 (file)
--- 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;
 }
index ed07791ba458d56632259d1e932dc4bc8fedf48b..c6fc02b90f523f30fde5870f805570ceacf07991 100644 (file)
--- 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 {
index 95ed48063dfc4912401f02af4eb409a75dca0924..977d9c2fc7f9b818cf184939fcebaeb03a028a5e 100644 (file)
--- 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;
index 1d1a5f8cb8db260f3e350e5d5e870f26694230a0..2391e121badf56540679309c01bff5b86091a8af 100644 (file)
@@ -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 82b2d66821a2c9800c2a7718bec5dee483ca047b..459c9077a085cf15a73399a3d543674fcc6f7935 100644 (file)
--- 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
index f2440a75220f68d240a6425f99be4638a2b57b4c..31b82b18335a26ef90d2cbf5f161d457a5fa043d 100644 (file)
--- 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