Skip to content

Commit

Permalink
Merge branch 'bridge_vlan_ranges'
Browse files Browse the repository at this point in the history
Roopa Prabhu says:

====================
bridge: support for vlan range in setlink/dellink

This series adds new flags in IFLA_BRIDGE_VLAN_INFO to indicate
vlan range.

Will post corresponding iproute2 patches if these get accepted.

v1-> v2
    - changed patches to use a nested list attribute
    IFLA_BRIDGE_VLAN_INFO_LIST as suggested by scott feldman
    - dropped notification changes from the series. Will post them
    separately after this range message is accepted.

v2 -> v3
    - incorporated some review feedback
    - include patches to fill vlan ranges during getlink
    - Dropped IFLA_BRIDGE_VLAN_INFO_LIST. I think it may get
    confusing to userspace if we introduce yet another way to
    send lists. With getlink already sending nested
    IFLA_BRIDGE_VLAN_INFO in IFLA_AF_SPEC, It seems better to
    use the existing format for lists and just use the flags from v2
    to mark vlan ranges
====================

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: Wilson Kok <wkok@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jan 12, 2015
2 parents dd2e8bf + 36cd0ff commit d0d2cc5
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 57 deletions.
2 changes: 2 additions & 0 deletions include/uapi/linux/if_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ enum {
#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_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 {
__u16 flags;
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/rtnetlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ struct tcamsg {
/* New extended info filters for IFLA_EXT_MASK */
#define RTEXT_FILTER_VF (1 << 0)
#define RTEXT_FILTER_BRVLAN (1 << 1)
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)

/* End of information exported to user level */

Expand Down
249 changes: 192 additions & 57 deletions net/bridge/br_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,118 @@ static int br_port_fill_attrs(struct sk_buff *skb,
return 0;
}

static int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start,
u16 vid_end, u16 flags)
{
struct bridge_vlan_info vinfo;

if ((vid_end - vid_start) > 0) {
/* add range to skb */
vinfo.vid = vid_start;
vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_BEGIN;
if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
goto nla_put_failure;

vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;

vinfo.vid = vid_end;
vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_END;
if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
goto nla_put_failure;
} else {
vinfo.vid = vid_start;
vinfo.flags = flags;
if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
goto nla_put_failure;
}

return 0;

nla_put_failure:
return -EMSGSIZE;
}

static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
const struct net_port_vlans *pv)
{
u16 vid_range_start = 0, vid_range_end = 0;
u16 vid_range_flags;
u16 pvid, vid, flags;
int err = 0;

/* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan
* and mark vlan info with begin and end flags
* if vlaninfo represents a range
*/
pvid = br_get_pvid(pv);
for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
flags = 0;
if (vid == pvid)
flags |= BRIDGE_VLAN_INFO_PVID;

if (test_bit(vid, pv->untagged_bitmap))
flags |= BRIDGE_VLAN_INFO_UNTAGGED;

if (vid_range_start == 0) {
goto initvars;
} else if ((vid - vid_range_end) == 1 &&
flags == vid_range_flags) {
vid_range_end = vid;
continue;
} else {
err = br_fill_ifvlaninfo_range(skb, vid_range_start,
vid_range_end,
vid_range_flags);
if (err)
return err;
}

initvars:
vid_range_start = vid;
vid_range_end = vid;
vid_range_flags = flags;
}

/* Call it once more to send any left over vlans */
err = br_fill_ifvlaninfo_range(skb, vid_range_start,
vid_range_end,
vid_range_flags);
if (err)
return err;

return 0;
}

static int br_fill_ifvlaninfo(struct sk_buff *skb,
const struct net_port_vlans *pv)
{
struct bridge_vlan_info vinfo;
u16 pvid, vid;

pvid = br_get_pvid(pv);
for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
vinfo.vid = vid;
vinfo.flags = 0;
if (vid == pvid)
vinfo.flags |= BRIDGE_VLAN_INFO_PVID;

if (test_bit(vid, pv->untagged_bitmap))
vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;

if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
goto nla_put_failure;
}

return 0;

nla_put_failure:
return -EMSGSIZE;
}

/*
* Create one netlink message for one interface
* Contains port and master info as well as carrier and bridge state.
Expand Down Expand Up @@ -121,12 +233,11 @@ static int br_fill_ifinfo(struct sk_buff *skb,
}

/* Check if the VID information is requested */
if (filter_mask & RTEXT_FILTER_BRVLAN) {
struct nlattr *af;
if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
const struct net_port_vlans *pv;
struct bridge_vlan_info vinfo;
u16 vid;
u16 pvid;
struct nlattr *af;
int err;

if (port)
pv = nbp_get_vlan_info(port);
Expand All @@ -140,21 +251,12 @@ static int br_fill_ifinfo(struct sk_buff *skb,
if (!af)
goto nla_put_failure;

pvid = br_get_pvid(pv);
for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
vinfo.vid = vid;
vinfo.flags = 0;
if (vid == pvid)
vinfo.flags |= BRIDGE_VLAN_INFO_PVID;

if (test_bit(vid, pv->untagged_bitmap))
vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;

if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
goto nla_put_failure;
}

if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
err = br_fill_ifvlaninfo_compressed(skb, pv);
else
err = br_fill_ifvlaninfo(skb, pv);
if (err)
goto nla_put_failure;
nla_nest_end(skb, af);
}

Expand Down Expand Up @@ -209,7 +311,8 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
int err = 0;
struct net_bridge_port *port = br_port_get_rtnl(dev);

if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN))
if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN) &&
!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED))
goto out;

err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI,
Expand All @@ -218,57 +321,89 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return err;
}

static const struct nla_policy ifla_br_policy[IFLA_MAX+1] = {
[IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 },
[IFLA_BRIDGE_MODE] = { .type = NLA_U16 },
[IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY,
.len = sizeof(struct bridge_vlan_info), },
};
static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
int cmd, struct bridge_vlan_info *vinfo)
{
int err = 0;

switch (cmd) {
case RTM_SETLINK:
if (p) {
err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
if (err)
break;

if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
err = br_vlan_add(p->br, vinfo->vid,
vinfo->flags);
} else {
err = br_vlan_add(br, vinfo->vid, vinfo->flags);
}
break;

case RTM_DELLINK:
if (p) {
nbp_vlan_delete(p, vinfo->vid);
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
br_vlan_delete(p->br, vinfo->vid);
} else {
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 nlattr *tb[IFLA_BRIDGE_MAX+1];
struct bridge_vlan_info *vinfo_start = NULL;
struct bridge_vlan_info *vinfo = NULL;
struct nlattr *attr;
int err = 0;
int rem;

err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy);
if (err)
return err;
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 (tb[IFLA_BRIDGE_VLAN_INFO]) {
struct bridge_vlan_info *vinfo;
if (vinfo_start) {
struct bridge_vlan_info tmp_vinfo;
int v;

vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
return -EINVAL;

if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
return -EINVAL;
if (vinfo->vid <= vinfo_start->vid)
return -EINVAL;

memcpy(&tmp_vinfo, vinfo_start,
sizeof(struct bridge_vlan_info));

switch (cmd) {
case RTM_SETLINK:
if (p) {
err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
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;

if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
err = br_vlan_add(p->br, vinfo->vid,
vinfo->flags);
} else
err = br_vlan_add(br, vinfo->vid, vinfo->flags);

break;

case RTM_DELLINK:
if (p) {
nbp_vlan_delete(p, vinfo->vid);
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
br_vlan_delete(p->br, vinfo->vid);
} else
br_vlan_delete(br, vinfo->vid);
break;
}
vinfo_start = NULL;
} else {
err = br_vlan_info(br, p, cmd, vinfo);
}
if (err)
break;
}

return err;
Expand Down

0 comments on commit d0d2cc5

Please sign in to comment.