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
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
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);
}
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)
{
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);
}
[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 = {
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)
{
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);
}
__devlock++;
free(dev->config);
device_cleanup(dev);
+ free(dev->extra_vlan);
dev->type->free(dev);
__devlock--;
}
DEV_ATTR_AUTH,
DEV_ATTR_SPEED,
DEV_ATTR_DUPLEX,
+ DEV_ATTR_VLAN,
__DEV_ATTR_MAX,
};
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;
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;
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;
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);
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);