Browse Source

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 <nbd@nbd.name>
Felix Fietkau 2 years ago
parent
commit
50381d0a29
6 changed files with 80 additions and 27 deletions
  1. 50 4
      bridge.c
  2. 1 1
      device.h
  3. 1 1
      extdev.c
  4. 4 3
      interface.c
  5. 12 9
      vlan.c
  6. 12 9
      vlandev.c

+ 50 - 4
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;
 }

+ 1 - 1
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 {

+ 1 - 1
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;

+ 4 - 3
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:

+ 12 - 9
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

+ 12 - 9
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