Commit bdced7ef authored by Roopa Prabhu's avatar Roopa Prabhu Committed by David S. Miller

bridge: support for multiple vlans and vlan ranges in setlink and dellink requests

This patch changes bridge IFLA_AF_SPEC netlink attribute parser to
look for more than one IFLA_BRIDGE_VLAN_INFO attribute. This allows
userspace to pack more than one vlan in the setlink msg.

The dumps were already sending more than one vlan info in the getlink msg.

This patch also adds bridge_vlan_info flags BRIDGE_VLAN_INFO_RANGE_BEGIN and
BRIDGE_VLAN_INFO_RANGE_END to indicate start and end of vlan range

This patch also deletes unused ifla_br_policy.
Signed-off-by: default avatarRoopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent dd2e8bf5
...@@ -125,6 +125,8 @@ enum { ...@@ -125,6 +125,8 @@ enum {
#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ #define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */
#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ #define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */
#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ #define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */
#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */
#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */
struct bridge_vlan_info { struct bridge_vlan_info {
__u16 flags; __u16 flags;
......
...@@ -218,33 +218,11 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, ...@@ -218,33 +218,11 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return err; return err;
} }
static const struct nla_policy ifla_br_policy[IFLA_MAX+1] = { static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
[IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 }, int cmd, struct bridge_vlan_info *vinfo)
[IFLA_BRIDGE_MODE] = { .type = NLA_U16 },
[IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY,
.len = sizeof(struct bridge_vlan_info), },
};
static int br_afspec(struct net_bridge *br,
struct net_bridge_port *p,
struct nlattr *af_spec,
int cmd)
{ {
struct nlattr *tb[IFLA_BRIDGE_MAX+1];
int err = 0; int err = 0;
err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy);
if (err)
return err;
if (tb[IFLA_BRIDGE_VLAN_INFO]) {
struct bridge_vlan_info *vinfo;
vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
return -EINVAL;
switch (cmd) { switch (cmd) {
case RTM_SETLINK: case RTM_SETLINK:
if (p) { if (p) {
...@@ -255,9 +233,9 @@ static int br_afspec(struct net_bridge *br, ...@@ -255,9 +233,9 @@ static int br_afspec(struct net_bridge *br,
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
err = br_vlan_add(p->br, vinfo->vid, err = br_vlan_add(p->br, vinfo->vid,
vinfo->flags); vinfo->flags);
} else } else {
err = br_vlan_add(br, vinfo->vid, vinfo->flags); err = br_vlan_add(br, vinfo->vid, vinfo->flags);
}
break; break;
case RTM_DELLINK: case RTM_DELLINK:
...@@ -265,10 +243,64 @@ static int br_afspec(struct net_bridge *br, ...@@ -265,10 +243,64 @@ static int br_afspec(struct net_bridge *br,
nbp_vlan_delete(p, vinfo->vid); nbp_vlan_delete(p, vinfo->vid);
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
br_vlan_delete(p->br, vinfo->vid); br_vlan_delete(p->br, vinfo->vid);
} else } else {
br_vlan_delete(br, vinfo->vid); br_vlan_delete(br, vinfo->vid);
}
break;
}
return err;
}
static int br_afspec(struct net_bridge *br,
struct net_bridge_port *p,
struct nlattr *af_spec,
int cmd)
{
struct bridge_vlan_info *vinfo_start = NULL;
struct bridge_vlan_info *vinfo = NULL;
struct nlattr *attr;
int err = 0;
int rem;
nla_for_each_nested(attr, af_spec, rem) {
if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
continue;
if (nla_len(attr) != sizeof(struct bridge_vlan_info))
return -EINVAL;
vinfo = nla_data(attr);
if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
if (vinfo_start)
return -EINVAL;
vinfo_start = vinfo;
continue;
}
if (vinfo_start) {
struct bridge_vlan_info tmp_vinfo;
int v;
if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
return -EINVAL;
if (vinfo->vid <= vinfo_start->vid)
return -EINVAL;
memcpy(&tmp_vinfo, vinfo_start,
sizeof(struct bridge_vlan_info));
for (v = vinfo_start->vid; v <= vinfo->vid; v++) {
tmp_vinfo.vid = v;
err = br_vlan_info(br, p, cmd, &tmp_vinfo);
if (err)
break; break;
} }
vinfo_start = NULL;
} else {
err = br_vlan_info(br, p, cmd, vinfo);
}
if (err)
break;
} }
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment