Commit d0d2cc53 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bridge_vlan_ranges'



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: default avatarRoopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: default avatarWilson Kok <wkok@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents dd2e8bf5 36cd0ffb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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 */

+192 −57
Original line number Diff line number Diff line
@@ -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.
@@ -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);
@@ -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))
		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);
	}

@@ -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,
@@ -218,33 +321,11 @@ out:
	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_afspec(struct net_bridge *br,
		     struct net_bridge_port *p,
		     struct nlattr *af_spec,
		     int cmd)
static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
			int cmd, struct bridge_vlan_info *vinfo)
{
	struct nlattr *tb[IFLA_BRIDGE_MAX+1];
	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) {
	case RTM_SETLINK:
		if (p) {
@@ -255,9 +336,9 @@ static int br_afspec(struct net_bridge *br,
			if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
				err = br_vlan_add(p->br, vinfo->vid,
						  vinfo->flags);
			} else
		} else {
			err = br_vlan_add(br, vinfo->vid, vinfo->flags);

		}
		break;

	case RTM_DELLINK:
@@ -265,10 +346,64 @@ static int br_afspec(struct net_bridge *br,
			nbp_vlan_delete(p, vinfo->vid);
			if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
				br_vlan_delete(p->br, vinfo->vid);
			} else
		} 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 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;
			}
			vinfo_start = NULL;
		} else {
			err = br_vlan_info(br, p, cmd, vinfo);
		}
		if (err)
			break;
	}

	return err;