summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2021-06-04 09:05:31 +0200
committerFelix Fietkau <nbd@nbd.name>2021-06-04 09:05:33 +0200
commit50381d0a2998f6c0fc4823f0c2aa4206063d549e (patch)
treee22f15cc925923c12a741a41e4fa289270c3a547
parent7f199050f395d5295a3f665bcd2eb8cc755d08aa (diff)
downloadnetifd-50381d0a2998f6c0fc4823f0c2aa4206063d549e.tar.gz
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>
-rw-r--r--bridge.c54
-rw-r--r--device.h2
-rw-r--r--extdev.c2
-rw-r--r--interface.c7
-rw-r--r--vlan.c21
-rw-r--r--vlandev.c21
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