summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2020-08-07 14:19:06 +0200
committerFelix Fietkau <nbd@nbd.name>2020-10-28 04:49:11 +0100
commit3023b0cc73520cf7b14e1bfa9075072098b6a753 (patch)
tree497f337b16ada29e941c984dba1e50a799dca62b
parent24ce1eab4910869576406bafd0489daf0d3e6e28 (diff)
bridge: add support for defining port member vlans via hotplug ops
Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--bridge.c105
-rw-r--r--config.c1
-rw-r--r--device.h4
-rw-r--r--interface.c10
-rw-r--r--interface.h2
-rw-r--r--ubus.c5
-rw-r--r--wireless.c4
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;
@@ -900,6 +984,20 @@ bridge_vlan_equal(struct bridge_vlan *v1, struct bridge_vlan *v2)
}
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);
}
}