[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1394439067-10477-4-git-send-email-makita.toshiaki@lab.ntt.co.jp>
Date: Mon, 10 Mar 2014 17:11:06 +0900
From: Toshiaki Makita <makita.toshiaki@....ntt.co.jp>
To: netdev@...r.kernel.org, bridge@...ts.linux-foundation.org
Cc: Toshiaki Makita <makita.toshiaki@....ntt.co.jp>,
Stephen Hemminger <stephen@...workplumber.org>,
Vlad Yasevich <vyasevic@...hat.com>
Subject: [PATCH RFC 3/3] bridge: Support 802.1ad vlan filtering
This enables us to change protocols for vlan_filtering.
We come to be able to filter frames based on 802.1ad vlan tags through
a bridge.
Signed-off-by: Toshiaki Makita <makita.toshiaki@....ntt.co.jp>
---
include/uapi/linux/if_bridge.h | 2 ++
net/bridge/br_netlink.c | 67 +++++++++++++++++++++++++++++-------------
net/bridge/br_private.h | 6 ++++
net/bridge/br_vlan.c | 57 +++++++++++++++++++++++++++++++++++
4 files changed, 111 insertions(+), 21 deletions(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 39f621a..cb31364 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -110,12 +110,14 @@ struct __fdb_entry {
* [IFLA_BRIDGE_FLAGS]
* [IFLA_BRIDGE_MODE]
* [IFLA_BRIDGE_VLAN_INFO]
+ * [IFLA_BRIDGE_VLAN_PROTOCOL]
* }
*/
enum {
IFLA_BRIDGE_FLAGS,
IFLA_BRIDGE_MODE,
IFLA_BRIDGE_VLAN_INFO,
+ IFLA_BRIDGE_VLAN_PROTOCOL,
__IFLA_BRIDGE_MAX,
};
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index e74b6d53..eac82a4 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -119,7 +119,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
nla_nest_end(skb, nest);
}
- /* Check if the VID information is requested */
+ /* Check if the vlan information is requested */
if (filter_mask & RTEXT_FILTER_BRVLAN) {
struct nlattr *af;
const struct net_port_vlans *pv;
@@ -127,17 +127,27 @@ static int br_fill_ifinfo(struct sk_buff *skb,
u16 vid;
u16 pvid;
- if (port)
+ if (port) {
pv = nbp_get_vlan_info(port);
- else
- pv = br_get_vlan_info(br);
+ if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID))
+ goto done;
- if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID))
- goto done;
+ af = nla_nest_start(skb, IFLA_AF_SPEC);
+ if (!af)
+ goto nla_put_failure;
+ } else {
+ af = nla_nest_start(skb, IFLA_AF_SPEC);
+ if (!af)
+ goto nla_put_failure;
- af = nla_nest_start(skb, IFLA_AF_SPEC);
- if (!af)
- goto nla_put_failure;
+ if (nla_put_be16(skb, IFLA_BRIDGE_VLAN_PROTOCOL,
+ br_get_vlan_proto(br)))
+ goto nla_put_failure;
+
+ pv = br_get_vlan_info(br);
+ if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID))
+ goto afspec_nest_end;
+ }
pvid = br_get_pvid(pv);
for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
@@ -153,7 +163,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
sizeof(vinfo), &vinfo))
goto nla_put_failure;
}
-
+afspec_nest_end:
nla_nest_end(skb, af);
}
@@ -223,6 +233,7 @@ static const struct nla_policy ifla_br_policy[IFLA_MAX+1] = {
[IFLA_BRIDGE_MODE] = { .type = NLA_U16 },
[IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY,
.len = sizeof(struct bridge_vlan_info), },
+ [IFLA_BRIDGE_VLAN_PROTOCOL] = { .type = NLA_U16 },
};
static int br_afspec(struct net_bridge *br,
@@ -250,7 +261,7 @@ static int br_afspec(struct net_bridge *br,
if (p) {
err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
if (err)
- break;
+ return err;
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
err = br_vlan_add(p->br, vinfo->vid,
@@ -259,7 +270,7 @@ static int br_afspec(struct net_bridge *br,
err = br_vlan_add(br, vinfo->vid, vinfo->flags);
if (err)
- break;
+ return err;
break;
@@ -274,6 +285,16 @@ static int br_afspec(struct net_bridge *br,
}
}
+ if (tb[IFLA_BRIDGE_VLAN_PROTOCOL]) {
+ __be16 proto = nla_get_be16(tb[IFLA_BRIDGE_VLAN_PROTOCOL]);
+
+ if (cmd != RTM_SETLINK || p ||
+ (proto != htons(ETH_P_8021Q) && proto != htons(ETH_P_8021AD)))
+ return -EINVAL;
+
+ err = br_set_vlan_proto(br, proto);
+ }
+
return err;
}
@@ -447,20 +468,24 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[])
static size_t br_get_link_af_size(const struct net_device *dev)
{
- struct net_port_vlans *pv;
+ struct net_port_vlans *pv = NULL;
+ size_t tot = 0;
- if (br_port_exists(dev))
+ if (br_port_exists(dev)) {
pv = nbp_get_vlan_info(br_port_get_rtnl(dev));
- else if (dev->priv_flags & IFF_EBRIDGE)
+ } else if (dev->priv_flags & IFF_EBRIDGE) {
+ tot = nla_total_size(2); /* IFLA_BRIDGE_VLAN_PROTOCOL */
pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
- else
- return 0;
+ }
- if (!pv)
- return 0;
+ if (pv)
+ /* IFLA_BRIDGE_VLAN_INFO
+ * Each VLAN is returned in bridge_vlan_info along with flags.
+ */
+ tot += pv->num_vlans *
+ nla_total_size(sizeof(struct bridge_vlan_info));
- /* Each VLAN is returned in bridge_vlan_info along with flags */
- return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
+ return tot;
}
static struct rtnl_af_ops br_af_ops = {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 500cf0e..d83843a 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -591,6 +591,7 @@ void br_vlan_flush(struct net_bridge *br);
bool br_vlan_find(struct net_bridge *br, u16 vid);
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
void br_vlan_init(struct net_bridge *br);
+int br_set_vlan_proto(struct net_bridge *br, __be16 proto);
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
void nbp_vlan_flush(struct net_bridge_port *port);
@@ -730,6 +731,11 @@ static inline __be16 br_get_vlan_proto(const struct net_bridge *br)
{
return 0;
}
+
+static inline int br_set_vlan_proto(struct net_bridge *br, __be16 proto)
+{
+ return -EOPNOTSUPP;
+}
#endif
/* br_netfilter.c */
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index f05ef6a..b465542 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -328,6 +328,63 @@ void br_vlan_init(struct net_bridge *br)
br->vlan_proto = htons(ETH_P_8021Q);
}
+int br_set_vlan_proto(struct net_bridge *br, __be16 proto)
+{
+ int err = 0;
+ struct net_bridge_port *p;
+ struct net_port_vlans *pv;
+ __be16 oldproto = br_get_vlan_proto(br);
+ u16 vid, errvid;
+
+ ASSERT_RTNL();
+
+ if (oldproto == proto)
+ return 0;
+
+ /* Add VLANs for the new proto to the device filter. */
+ list_for_each_entry(p, &br->port_list, list) {
+ pv = rtnl_dereference(p->vlan_info);
+ if (!pv)
+ continue;
+
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+ err = vlan_vid_add(p->dev, proto, vid);
+ if (err)
+ goto out_filt;
+ }
+ }
+
+ br->vlan_proto = proto;
+
+ /* Delete VLANs for the old proto from the device filter. */
+ list_for_each_entry(p, &br->port_list, list) {
+ pv = rtnl_dereference(p->vlan_info);
+ if (!pv)
+ continue;
+
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
+ vlan_vid_del(p->dev, oldproto, vid);
+ }
+
+ return 0;
+
+out_filt:
+ errvid = vid;
+ for_each_set_bit(vid, pv->vlan_bitmap, errvid)
+ vlan_vid_del(p->dev, proto, vid);
+
+ list_for_each_entry_continue_reverse(p, &br->port_list, list) {
+ pv = rtnl_dereference(p->vlan_info);
+ if (!pv)
+ continue;
+
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
+ vlan_vid_del(p->dev, proto, vid);
+ }
+
+ return err;
+}
+
/* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive.
*/
--
1.8.1.2
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists