summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau2023-06-23 08:17:45 +0000
committerFelix Fietkau2023-06-23 08:20:42 +0000
commit1571e18e4a69da146652d88dd89d38e3d746abc3 (patch)
tree3095243dbfc304a88938cb1a21c79ad2df473f1f
parent40fad91eb5be5959783d7bb06dcfcfb56bbbb9bd (diff)
downloadnetifd-1571e18e4a69da146652d88dd89d38e3d746abc3.tar.gz
bridge: add support for configuring extra tagged vlans on member devices
This can be used to allow trunking dynamically allocated VLANs into a specific member port. In order to use this, add a device section for the member port with the 'vlan' array option, which can contain vlan numbers or ranges. Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--bridge.c20
-rw-r--r--device.c43
-rw-r--r--device.h5
-rw-r--r--system-dummy.c6
-rw-r--r--system-linux.c13
-rw-r--r--system.h2
6 files changed, 79 insertions, 10 deletions
diff --git a/bridge.c b/bridge.c
index c8cfca4..0338f14 100644
--- a/bridge.c
+++ b/bridge.c
@@ -206,7 +206,7 @@ __bridge_set_member_vlan(struct bridge_member *bm, struct bridge_vlan *vlan,
if (bm->pvid == vlan->vid)
flags |= BRVLAN_F_PVID;
- system_bridge_vlan(port->ifname, vlan->vid, add, flags);
+ system_bridge_vlan(port->ifname, vlan->vid, -1, add, flags);
}
static void
@@ -233,7 +233,7 @@ bridge_set_local_vlan(struct bridge_state *bst, struct bridge_vlan *vlan, bool a
if (!vlan->local && add)
return;
- system_bridge_vlan(bst->dev.ifname, vlan->vid, add, BRVLAN_F_SELF);
+ system_bridge_vlan(bst->dev.ifname, vlan->vid, -1, add, BRVLAN_F_SELF);
}
static void
@@ -361,7 +361,7 @@ bridge_enable_interface(struct bridge_state *bst)
if (bst->has_vlans) {
/* delete default VLAN 1 */
- system_bridge_vlan(bst->dev.ifname, 1, false, BRVLAN_F_SELF);
+ system_bridge_vlan(bst->dev.ifname, 1, -1, false, BRVLAN_F_SELF);
bridge_set_local_vlans(bst, true);
}
@@ -394,6 +394,17 @@ bridge_disable_interface(struct bridge_state *bst)
bst->active = false;
}
+static void
+bridge_member_add_extra_vlans(struct bridge_member *bm)
+{
+ struct device *dev = bm->dev.dev;
+ int i;
+
+ for (i = 0; i < dev->n_extra_vlan; i++)
+ system_bridge_vlan(dev->ifname, dev->extra_vlan[i].start,
+ dev->extra_vlan[i].end, true, 0);
+}
+
static int
bridge_enable_member(struct bridge_member *bm)
{
@@ -435,8 +446,9 @@ bridge_enable_member(struct bridge_member *bm)
bm->active = true;
if (bst->has_vlans) {
/* delete default VLAN 1 */
- system_bridge_vlan(bm->dev.dev->ifname, 1, false, 0);
+ system_bridge_vlan(bm->dev.dev->ifname, 1, -1, false, 0);
+ bridge_member_add_extra_vlans(bm);
vlist_for_each_element(&bst->dev.vlans, vlan, node)
bridge_set_member_vlan(bm, vlan, true);
}
diff --git a/device.c b/device.c
index 1e89219..e98c77e 100644
--- a/device.c
+++ b/device.c
@@ -63,6 +63,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = {
[DEV_ATTR_AUTH] = { .name = "auth", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_SPEED] = { .name = "speed", .type = BLOBMSG_TYPE_INT32 },
[DEV_ATTR_DUPLEX] = { .name = "duplex", .type = BLOBMSG_TYPE_BOOL },
+ [DEV_ATTR_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_ARRAY },
};
const struct uci_blob_param_list device_attr_list = {
@@ -283,6 +284,45 @@ device_merge_settings(struct device *dev, struct device_settings *n)
n->flags = s->flags | os->flags | os->valid_flags;
}
+static void
+device_add_extra_vlan(struct device *dev, const char *val)
+{
+ unsigned long cur_start, cur_end;
+ char *sep;
+
+ cur_start = strtoul(val, &sep, 0);
+ cur_end = cur_start;
+
+ if (*sep == '-')
+ cur_end = strtoul(sep + 1, &sep, 0);
+ if (*sep || cur_end < cur_start)
+ return;
+
+ dev->extra_vlan[dev->n_extra_vlan].start = cur_start;
+ dev->extra_vlan[dev->n_extra_vlan].end = cur_end;
+ dev->n_extra_vlan++;
+}
+
+static void
+device_set_extra_vlans(struct device *dev, struct blob_attr *data)
+{
+ struct blob_attr *cur;
+ int n_vlans;
+ size_t rem;
+
+ dev->n_extra_vlan = 0;
+ if (!data)
+ return;
+
+ n_vlans = blobmsg_check_array(data, BLOBMSG_TYPE_STRING);
+ if (n_vlans < 1)
+ return;
+
+ dev->extra_vlan = realloc(dev->extra_vlan, n_vlans * sizeof(*dev->extra_vlan));
+ blobmsg_for_each_attr(cur, data, rem)
+ device_add_extra_vlan(dev, blobmsg_get_string(cur));
+}
+
void
device_init_settings(struct device *dev, struct blob_attr **tb)
{
@@ -463,7 +503,7 @@ device_init_settings(struct device *dev, struct blob_attr **tb)
s->duplex = blobmsg_get_bool(cur);
s->flags |= DEV_OPT_DUPLEX;
}
-
+ device_set_extra_vlans(dev, tb[DEV_ATTR_VLAN]);
device_set_disabled(dev, disabled);
}
@@ -891,6 +931,7 @@ device_free(struct device *dev)
__devlock++;
free(dev->config);
device_cleanup(dev);
+ free(dev->extra_vlan);
dev->type->free(dev);
__devlock--;
}
diff --git a/device.h b/device.h
index 6751628..c3e38ac 100644
--- a/device.h
+++ b/device.h
@@ -62,6 +62,7 @@ enum {
DEV_ATTR_AUTH,
DEV_ATTR_SPEED,
DEV_ATTR_DUPLEX,
+ DEV_ATTR_VLAN,
__DEV_ATTR_MAX,
};
@@ -248,6 +249,10 @@ struct device {
bool bpdu_filter;
struct interface *config_iface;
+ struct {
+ uint16_t start, end;
+ } *extra_vlan;
+ int n_extra_vlan;
/* set interface up or down */
device_state_cb set_state;
diff --git a/system-dummy.c b/system-dummy.c
index b13bc87..84382b3 100644
--- a/system-dummy.c
+++ b/system-dummy.c
@@ -55,12 +55,12 @@ int system_bridge_delif(struct device *bridge, struct device *dev)
return 0;
}
-int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags)
+int system_bridge_vlan(const char *iface, uint16_t vid, int16_t vid_end, bool add, unsigned int vflags)
{
- D(SYSTEM, "brctl vlan %s %s %s vid=%d pvid=%d untag=%d\n",
+ D(SYSTEM, "brctl vlan %s %s %s vid=%d vid_end=%d pvid=%d untag=%d\n",
add ? "add" : "remove",
(vflags & BRVLAN_F_SELF) ? "self" : "master",
- iface, vid,
+ iface, vid, vid_end,
!!(vflags & BRVLAN_F_PVID),
!!(vflags & BRVLAN_F_UNTAGGED));
return 0;
diff --git a/system-linux.c b/system-linux.c
index cc15537..0760e73 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -985,7 +985,7 @@ int system_bridge_delif(struct device *bridge, struct device *dev)
return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL);
}
-int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags)
+int system_bridge_vlan(const char *iface, uint16_t vid, int16_t vid_end, bool add, unsigned int vflags)
{
struct bridge_vlan_info vinfo = { .vid = vid, };
unsigned short flags = 0;
@@ -1020,7 +1020,18 @@ int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int v
if (flags)
nla_put_u16(nlm, IFLA_BRIDGE_FLAGS, flags);
+ if (vid_end > vid)
+ vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
+
nla_put(nlm, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
+
+ if (vid_end > vid) {
+ vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
+ vinfo.vid = vid_end;
+ nla_put(nlm, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
+ }
+
nla_nest_end(nlm, afspec);
return system_rtnl_call(nlm);
diff --git a/system.h b/system.h
index 1f7037d..19aafa4 100644
--- a/system.h
+++ b/system.h
@@ -246,7 +246,7 @@ int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg);
int system_bridge_delbr(struct device *bridge);
int system_bridge_addif(struct device *bridge, struct device *dev);
int system_bridge_delif(struct device *bridge, struct device *dev);
-int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags);
+int system_bridge_vlan(const char *iface, uint16_t vid, int16_t vid_end, bool add, unsigned int vflags);
int system_bridge_vlan_check(struct device *dev, char *ifname);
void system_bridge_set_stp_state(struct device *dev, bool val);