Netdev List
 help / color / mirror / Atom feed
* [PATCH V2 07/12] bridge: Add netlink interface to configure vlans on bridge ports
From: Vlad Yasevich @ 2012-12-18 19:00 UTC (permalink / raw)
  To: netdev; +Cc: shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-1-git-send-email-vyasevic@redhat.com>

Add a netlink interface to add and remove vlan configuration on bridge port.
The interface uses the RTM_SETLINK message and encodes the vlan
configuration inside the IFLA_AF_SPEC.  It is possble to include multiple
vlans to either add or remove in a single message.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/uapi/linux/if_bridge.h |   17 ++++++
 net/bridge/br_if.c             |    1 +
 net/bridge/br_netlink.c        |  107 +++++++++++++++++++++++++++++++++------
 3 files changed, 108 insertions(+), 17 deletions(-)

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 52aa738..d0b4f5c 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -108,15 +108,32 @@ struct __fdb_entry {
  * [IFLA_AF_SPEC] = {
  *     [IFLA_BRIDGE_FLAGS]
  *     [IFLA_BRIDGE_MODE]
+ *     [IFLA_BRIDGE_VLAN_INFO]
  * }
  */
 enum {
 	IFLA_BRIDGE_FLAGS,
 	IFLA_BRIDGE_MODE,
+	IFLA_BRIDGE_VLAN_INFO,
 	__IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
 
+/* Bridge VLAN info
+ * [IFLA_BRIDGE_VLAN_INFO]
+ */
+enum {
+	BR_VLAN_ADD,
+	BR_VLAN_DEL,
+};
+
+struct bridge_vlan_info {
+	u16 op_code;
+	u16 flags;
+	u16 vid;
+	u16 unused;
+};
+
 /* Bridge multicast database attributes
  * [MDBA_MDB] = {
  *     [MDBA_MDB_ENTRY] = {
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 14c7c6a..57bbb35 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -23,6 +23,7 @@
 #include <linux/if_ether.h>
 #include <linux/slab.h>
 #include <net/sock.h>
+#include <linux/if_vlan.h>
 
 #include "br_private.h"
 
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index dead9df..9cf2879 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -16,6 +16,7 @@
 #include <net/rtnetlink.h>
 #include <net/net_namespace.h>
 #include <net/sock.h>
+#include <uapi/linux/if_bridge.h>
 
 #include "br_private.h"
 #include "br_private_stp.h"
@@ -123,6 +124,9 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
 	struct sk_buff *skb;
 	int err = -ENOBUFS;
 
+	if (!port)
+		return;
+
 	br_debug(port->br, "port %u(%s) event %d\n",
 		 (unsigned int)port->port_no, port->dev->name, event);
 
@@ -162,6 +166,60 @@ out:
 	return err;
 }
 
+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)
+{
+	struct nlattr *tb[IFLA_BRIDGE_MAX+1];
+	int err = 0;
+
+	if (nla_type(af_spec) != AF_BRIDGE)
+		return -EINVAL;
+
+	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 > VLAN_N_VID)
+			return -EINVAL;
+
+		switch (vinfo->op_code) {
+		case BR_VLAN_ADD:
+			if (p)
+				err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
+			else {
+				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
+				if (!br_vlan_add(br, vinfo->vid, flags))
+					err = -ENOMEM;
+			}
+			break;
+
+		case BR_VLAN_DEL:
+			if (p)
+				err = nbp_vlan_delete(p, vinfo->vid,
+						      vinfo->flags);
+			else {
+				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
+				err = br_vlan_delete(br, vinfo->vid, flags);
+			}
+			break;
+		}
+	}
+
+	return err;
+}
+
 static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = {
 	[IFLA_BRPORT_STATE]	= { .type = NLA_U8 },
 	[IFLA_BRPORT_COST]	= { .type = NLA_U32 },
@@ -238,6 +296,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
 {
 	struct ifinfomsg *ifm;
 	struct nlattr *protinfo;
+	struct nlattr *afspec;
 	struct net_bridge_port *p;
 	struct nlattr *tb[IFLA_BRPORT_MAX + 1];
 	int err;
@@ -245,35 +304,49 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
 	ifm = nlmsg_data(nlh);
 
 	protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
-	if (!protinfo)
+	afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
+	if (!protinfo && !afspec)
 		return 0;
 
 	p = br_port_get_rtnl(dev);
-	if (!p)
+	/* We want to accept dev as bridge itself if the AF_SPEC
+	 * is set to see if someone is setting vlan info on the brigde.
+	 */
+	if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec))
 		return -EINVAL;
 
-	if (protinfo->nla_type & NLA_F_NESTED) {
-		err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
-				       protinfo, ifla_brport_policy);
+	if (p && protinfo) {
+		if (protinfo->nla_type & NLA_F_NESTED) {
+			err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
+					       protinfo, ifla_brport_policy);
+			if (err)
+				return err;
+
+			spin_lock_bh(&p->br->lock);
+			err = br_setport(p, tb);
+			spin_unlock_bh(&p->br->lock);
+		} else {
+			/* Binary compatability with old RSTP */
+			if (nla_len(protinfo) < sizeof(u8))
+				return -EINVAL;
+
+			spin_lock_bh(&p->br->lock);
+			err = br_set_port_state(p, nla_get_u8(protinfo));
+			spin_unlock_bh(&p->br->lock);
+		}
 		if (err)
-			return err;
-
-		spin_lock_bh(&p->br->lock);
-		err = br_setport(p, tb);
-		spin_unlock_bh(&p->br->lock);
-	} else {
-		/* Binary compatability with old RSTP */
-		if (nla_len(protinfo) < sizeof(u8))
-			return -EINVAL;
+			goto out;
+	}
 
-		spin_lock_bh(&p->br->lock);
-		err = br_set_port_state(p, nla_get_u8(protinfo));
-		spin_unlock_bh(&p->br->lock);
+	if (afspec) {
+		err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
+				afspec);
 	}
 
 	if (err == 0)
 		br_ifinfo_notify(RTM_NEWLINK, p);
 
+out:
 	return err;
 }
 
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH V2 08/12] bridge: Add vlan support to static neighbors
From: Vlad Yasevich @ 2012-12-18 19:00 UTC (permalink / raw)
  To: netdev; +Cc: shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-1-git-send-email-vyasevic@redhat.com>

When a user adds bridge neighbors, allow him to specify VLAN id.
If the VLAN id is not specified, the neighbor will be added
for VLANs currently in the ports filter list.  If the list is
empty, we use vlan 0 and only add 1 entry.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c |    2 +-
 drivers/net/macvlan.c                         |    2 +-
 drivers/net/vxlan.c                           |    3 +-
 include/linux/netdevice.h                     |    1 +
 include/uapi/linux/neighbour.h                |    1 +
 net/bridge/br_fdb.c                           |  139 ++++++++++++++++++++++---
 net/bridge/br_private.h                       |    2 +-
 net/core/rtnetlink.c                          |   24 +++--
 8 files changed, 146 insertions(+), 28 deletions(-)

diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 20a5af6..ec97efe 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -6985,7 +6985,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 	return err;
 }
 
-static int ixgbe_ndo_fdb_del(struct ndmsg *ndm,
+static int ixgbe_ndo_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 			     struct net_device *dev,
 			     const unsigned char *addr)
 {
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 68a43fe..480189c 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -565,7 +565,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 	return err;
 }
 
-static int macvlan_fdb_del(struct ndmsg *ndm,
+static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 			   struct net_device *dev,
 			   const unsigned char *addr)
 {
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 3b3fdf6..09340c9 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -392,7 +392,8 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 }
 
 /* Delete entry (via netlink) */
-static int vxlan_fdb_delete(struct ndmsg *ndm, struct net_device *dev,
+static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
+			    struct net_device *dev,
 			    const unsigned char *addr)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c6a14d4..3ee7a80 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -996,6 +996,7 @@ struct net_device_ops {
 					       const unsigned char *addr,
 					       u16 flags);
 	int			(*ndo_fdb_del)(struct ndmsg *ndm,
+					       struct nlattr *tb[],
 					       struct net_device *dev,
 					       const unsigned char *addr);
 	int			(*ndo_fdb_dump)(struct sk_buff *skb,
diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h
index 275e5d6..adb068c 100644
--- a/include/uapi/linux/neighbour.h
+++ b/include/uapi/linux/neighbour.h
@@ -20,6 +20,7 @@ enum {
 	NDA_LLADDR,
 	NDA_CACHEINFO,
 	NDA_PROBES,
+	NDA_VLAN,
 	__NDA_MAX
 };
 
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index a244efc..f1199e4 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -506,6 +506,10 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
 	ci.ndm_refcnt	 = 0;
 	if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
 		goto nla_put_failure;
+
+	if (nla_put(skb, NDA_VLAN, sizeof(u16), &fdb->vlan_id))
+		goto nla_put_failure;
+
 	return nlmsg_end(skb, nlh);
 
 nla_put_failure:
@@ -517,6 +521,7 @@ static inline size_t fdb_nlmsg_size(void)
 {
 	return NLMSG_ALIGN(sizeof(struct ndmsg))
 		+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */
+		+ nla_total_size(sizeof(u16)) /* NDA_VLAN */
 		+ nla_total_size(sizeof(struct nda_cacheinfo));
 }
 
@@ -618,19 +623,55 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
 	return 0;
 }
 
+static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p,
+	       const unsigned char *addr, u16 nlh_flags, u16 vid)
+{
+	int err = 0;
+
+	if (ndm->ndm_flags & NTF_USE) {
+		rcu_read_lock();
+		br_fdb_update(p->br, p, addr, vid);
+		rcu_read_unlock();
+	} else {
+		spin_lock_bh(&p->br->hash_lock);
+		err = fdb_add_entry(p, addr, ndm->ndm_state,
+				    nlh_flags, vid);
+		spin_unlock_bh(&p->br->hash_lock);
+	}
+
+	return err;
+}
+
 /* Add new permanent fdb entry with RTM_NEWNEIGH */
 int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 	       struct net_device *dev,
 	       const unsigned char *addr, u16 nlh_flags)
 {
 	struct net_bridge_port *p;
+	struct net_port_vlan *pve;
 	int err = 0;
+	unsigned short vid = BR_INVALID_VID;
 
 	if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
 		pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
 		return -EINVAL;
 	}
 
+	if (tb[NDA_VLAN]) {
+		if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
+			pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
+			return -EINVAL;
+		}
+
+		vid = nla_get_u16(tb[NDA_VLAN]);
+
+		if (vid > VLAN_N_VID) {
+			pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
+				vid);
+			return -EINVAL;
+		}
+	}
+
 	p = br_port_get_rtnl(dev);
 	if (p == NULL) {
 		pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
@@ -638,27 +679,45 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 		return -EINVAL;
 	}
 
-	if (ndm->ndm_flags & NTF_USE) {
-		rcu_read_lock();
-		br_fdb_update(p->br, p, addr, 0);
-		rcu_read_unlock();
+	if (vid != BR_INVALID_VID) {
+		pve = nbp_vlan_find(p, vid);
+		if (!pve) {
+			pr_info("bridge: RTM_NEWNEIGH with unconfigured "
+				"vlan %d on port %s\n", vid, dev->name);
+			return -EINVAL;
+		}
+
+		/* VID was specified, so use it. */
+		err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
 	} else {
-		spin_lock_bh(&p->br->hash_lock);
-		err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags,
-				0);
-		spin_unlock_bh(&p->br->hash_lock);
+		if (list_empty(&p->vlan_list)) {
+			err = __br_fdb_add(ndm, p, addr, nlh_flags, 0);
+			goto out;
+		}
+
+		/* We have vlans configured on this port and user didn't
+		 * specify a VLAN.  To be nice, add/update entry for every
+		 * vlan on this port.
+		 */
+		list_for_each_entry_rcu(pve, &p->vlan_list, list) {
+			err = __br_fdb_add(ndm, p, addr, nlh_flags, pve->vid);
+			if (err)
+				goto out;
+		}
 	}
 
+out:
 	return err;
 }
 
-static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
+static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr,
+			      u16 vlan)
 {
 	struct net_bridge *br = p->br;
-	struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)];
+	struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
 	struct net_bridge_fdb_entry *fdb;
 
-	fdb = fdb_find(head, addr, 0);
+	fdb = fdb_find(head, addr, vlan);
 	if (!fdb)
 		return -ENOENT;
 
@@ -666,13 +725,42 @@ static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
 	return 0;
 }
 
+static int __br_fdb_delete(struct net_bridge_port *p,
+			    const unsigned char *addr, u16 vid)
+{
+	int err;
+
+	spin_lock_bh(&p->br->hash_lock);
+	err = fdb_delete_by_addr(p, addr, vid);
+	spin_unlock_bh(&p->br->hash_lock);
+
+	return err;
+}
+
 /* Remove neighbor entry with RTM_DELNEIGH */
-int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev,
+int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
+		  struct net_device *dev,
 		  const unsigned char *addr)
 {
 	struct net_bridge_port *p;
+	struct net_port_vlan *pve;
 	int err;
+	unsigned short vid = BR_INVALID_VID;
 
+	if (tb[NDA_VLAN]) {
+		if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
+			pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
+			return -EINVAL;
+		}
+
+		vid = nla_get_u16(tb[NDA_VLAN]);
+
+		if (vid > VLAN_N_VID) {
+			pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
+				vid);
+			return -EINVAL;
+		}
+	}
 	p = br_port_get_rtnl(dev);
 	if (p == NULL) {
 		pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
@@ -680,9 +768,30 @@ int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev,
 		return -EINVAL;
 	}
 
-	spin_lock_bh(&p->br->hash_lock);
-	err = fdb_delete_by_addr(p, addr);
-	spin_unlock_bh(&p->br->hash_lock);
+	if (vid != BR_INVALID_VID) {
+		pve = nbp_vlan_find(p, vid);
+		if (!pve) {
+			pr_info("bridge: RTM_DELNEIGH with unconfigured "
+				"vlan %d on port %s\n", vid, dev->name);
+			return -EINVAL;
+		}
 
+		err = __br_fdb_delete(p, addr, vid);
+	} else {
+		if (list_empty(&p->vlan_list)) {
+			err = __br_fdb_delete(p, addr, 0);
+			goto out;
+		}
+
+		/* We have vlans configured on this port and user didn't
+		 * specify a VLAN.  To be nice, add/update entry for every
+		 * vlan on this port.
+		 */
+		err = -ENOENT;
+		list_for_each_entry_rcu(pve, &p->vlan_list, list) {
+			err &= __br_fdb_delete(p, addr, pve->vid);
+		}
+	}
+out:
 	return err;
 }
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 2569afb..cc75212 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -407,7 +407,7 @@ extern void br_fdb_update(struct net_bridge *br,
 			  const unsigned char *addr,
 			  u16 vid);
 
-extern int br_fdb_delete(struct ndmsg *ndm,
+extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 			 struct net_device *dev,
 			 const unsigned char *addr);
 extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[],
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 1868625..8352302 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2125,7 +2125,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
 	struct net *net = sock_net(skb->sk);
 	struct ndmsg *ndm;
-	struct nlattr *llattr;
+	struct nlattr *tb[NDA_MAX+1];
 	struct net_device *dev;
 	int err = -EINVAL;
 	__u8 *addr;
@@ -2133,8 +2133,9 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 	if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
 
-	if (nlmsg_len(nlh) < sizeof(*ndm))
-		return -EINVAL;
+	err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
+	if (err < 0)
+		return err;
 
 	ndm = nlmsg_data(nlh);
 	if (ndm->ndm_ifindex == 0) {
@@ -2148,13 +2149,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 		return -ENODEV;
 	}
 
-	llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR);
-	if (llattr == NULL || nla_len(llattr) != ETH_ALEN) {
-		pr_info("PF_BRIGDE: RTM_DELNEIGH with invalid address\n");
+	if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
+		pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid address\n");
+		return -EINVAL;
+	}
+
+	addr = nla_data(tb[NDA_LLADDR]);
+	if (!is_valid_ether_addr(addr)) {
+		pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ether address\n");
 		return -EINVAL;
 	}
 
-	addr = nla_data(llattr);
 	err = -EOPNOTSUPP;
 
 	/* Support fdb on master device the net/bridge default case */
@@ -2163,7 +2168,8 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 		struct net_device *master = dev->master;
 
 		if (master->netdev_ops->ndo_fdb_del)
-			err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr);
+			err = master->netdev_ops->ndo_fdb_del(ndm, tb,
+							      dev, addr);
 
 		if (err)
 			goto out;
@@ -2173,7 +2179,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 
 	/* Embedded bridge, macvlan, and any other device support */
 	if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) {
-		err = dev->netdev_ops->ndo_fdb_del(ndm, dev, addr);
+		err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
 
 		if (!err) {
 			rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH V2 09/12] bridge: Add the ability to configure untagged vlans
From: Vlad Yasevich @ 2012-12-18 19:01 UTC (permalink / raw)
  To: netdev; +Cc: shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-1-git-send-email-vyasevic@redhat.com>

A user may designate a certain vlan as untagged.  This means that
any ingress frame is assigned to this vlan and any forwarding decisions
are made with this vlan in mind.  On egress, any frames tagged/labeled
with untagged vlan have the vlan tag removed and are send as regular
ethernet frames.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/uapi/linux/if_bridge.h |    3 +
 net/bridge/br_if.c             |  146 +++++++++++++++++++++++++++++++++++++---
 net/bridge/br_netlink.c        |    6 +-
 net/bridge/br_private.h        |    2 +
 4 files changed, 144 insertions(+), 13 deletions(-)

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index d0b4f5c..988d858 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -127,6 +127,9 @@ enum {
 	BR_VLAN_DEL,
 };
 
+#define BRIDGE_VLAN_INFO_MASTER		1
+#define BRIDGE_VLAN_INFO_UNTAGGED	2
+
 struct bridge_vlan_info {
 	u16 op_code;
 	u16 flags;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 57bbb35..14563fb 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -108,6 +108,34 @@ static void br_vlan_put(struct net_bridge_vlan *vlan)
 		br_vlan_destroy(vlan);
 }
 
+/* Must be protected by RTNL */
+static void br_vlan_add_untagged(struct net_bridge *br,
+				 struct net_bridge_vlan *vlan)
+{
+	ASSERT_RTNL();
+	if (br->untagged == vlan)
+		return;
+	else if (br->untagged) {
+		/* Untagged vlan is already set on the master,
+		 * so drop the ref since we'll be replacing it.
+		 */
+		br_vlan_put(br->untagged);
+	}
+	br_vlan_hold(vlan);
+	rcu_assign_pointer(br->untagged, vlan);
+}
+
+/* Must be protected by RTNL */
+static void br_vlan_del_untagged(struct net_bridge *br,
+				 struct net_bridge_vlan *vlan)
+{
+	ASSERT_RTNL();
+	if (br->untagged == vlan) {
+		br_vlan_put(vlan);
+		rcu_assign_pointer(br->untagged, NULL);
+	}
+}
+
 struct net_bridge_vlan *br_vlan_find(struct net_bridge *br, u16 vid)
 {
 	struct net_bridge_vlan *vlan;
@@ -132,7 +160,7 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
 
 	vlan = br_vlan_find(br, vid);
 	if (vlan)
-		return vlan;
+		goto untagged;
 
 	vlan = kzalloc(sizeof(struct net_bridge_vlan), GFP_KERNEL);
 	if (!vlan)
@@ -141,7 +169,7 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
 	vlan->vid = vid;
 	atomic_set(&vlan->refcnt, 1);
 
-	if (flags & BRIDGE_FLAGS_SELF) {
+	if (flags & BRIDGE_VLAN_INFO_MASTER) {
 		/* Set bit 0 that is associated with the bridge master
 		 * device.  Port numbers start with 1.
 		 */
@@ -149,15 +177,24 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
 	}
 
 	hlist_add_head_rcu(&vlan->hlist, &br->vlan_hlist[br_vlan_hash(vid)]);
+
+untagged:
+	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
+		br_vlan_add_untagged(br, vlan);
+
 	return vlan;
 }
 
 /* Must be protected by RTNL */
-static void br_vlan_del(struct net_bridge_vlan *vlan, u16 flags)
+static void br_vlan_del(struct net_bridge *br, struct net_bridge_vlan *vlan,
+			u16 flags)
 {
 	ASSERT_RTNL();
 
-	if (flags & BRIDGE_FLAGS_SELF) {
+	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
+		br_vlan_del_untagged(br, vlan);
+
+	if (flags & BRIDGE_VLAN_INFO_MASTER) {
 		/* Clear bit 0 that is associated with the bridge master
 		 * device.
 		 */
@@ -172,6 +209,14 @@ static void br_vlan_del(struct net_bridge_vlan *vlan, u16 flags)
 
 	vlan->vid = BR_INVALID_VID;
 
+	/* If, for whatever reason, bridge still has a ref on this vlan
+	 * through the @untagged pointer, drop that ref and clear untagged.
+	 */
+	if (br->untagged == vlan) {
+		br_vlan_put(vlan);
+		rcu_assign_pointer(br->untagged, NULL);
+	}
+
 	/* Drop the self-ref to trigger descrution. */
 	br_vlan_put(vlan);
 }
@@ -187,7 +232,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid, u16 flags)
 	if (!vlan)
 		return -ENOENT;
 
-	br_vlan_del(vlan, flags);
+	br_vlan_del(br, vlan, flags);
 	return 0;
 }
 
@@ -204,7 +249,9 @@ static void br_vlan_flush(struct net_bridge *br)
 	for (i = 0; i < BR_VID_HASH_SIZE; i++) {
 		hlist_for_each_entry_safe(vlan, node, tmp,
 					  &br->vlan_hlist[i], hlist) {
-			br_vlan_del(vlan, BRIDGE_FLAGS_SELF);
+			br_vlan_del(br, vlan,
+				    (BRIDGE_VLAN_INFO_MASTER |
+				     BRIDGE_VLAN_INFO_UNTAGGED));
 		}
 	}
 }
@@ -224,10 +271,70 @@ struct net_port_vlan *nbp_vlan_find(const struct net_bridge_port *p, u16 vid)
 	return NULL;
 }
 
+static int nbp_vlan_add_untagged(struct net_bridge_port *p,
+			  struct net_bridge_vlan *vlan,
+			  u16 flags)
+{
+	struct net_device *dev = p->dev;
+
+	if (p->untagged) {
+		/* Port already has untagged vlan set.  Drop the ref
+		 * to the old one since we'll be replace it.
+		 */
+		br_vlan_put(p->untagged);
+	} else {
+		int err;
+
+		/* Add vid 0 to filter if filter is available. */
+		if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
+		    dev->netdev_ops->ndo_vlan_rx_add_vid &&
+		    dev->netdev_ops->ndo_vlan_rx_kill_vid) {
+			err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, 0);
+			if (err)
+				return err;
+		}
+	}
+
+	/* This VLAN is handled as untagged/native. Save an
+	 * additional ref.
+	 */
+	br_vlan_hold(vlan);
+	rcu_assign_pointer(p->untagged, vlan);
+
+	return 0;
+}
+
+static void nbp_vlan_delete_untagged(struct net_bridge_port *p,
+				     struct net_bridge_vlan *vlan)
+{
+	if (p->untagged != vlan)
+		return;
+
+	/* Remove VLAN from the device filter if it is supported. */
+	if ((p->dev->features & NETIF_F_HW_VLAN_FILTER) &&
+	    p->dev->netdev_ops->ndo_vlan_rx_kill_vid) {
+		int err;
+
+		err = p->dev->netdev_ops->ndo_vlan_rx_kill_vid(p->dev, 0);
+		if (err) {
+			pr_warn("failed to kill vid %d for device %s\n",
+				vlan->vid, p->dev->name);
+		}
+	}
+
+	/* If this VLAN is currently functioning as untagged, clear it.
+	 * It's safe to drop the refcount, since the vlan is still held
+	 * by the port.
+	 */
+	br_vlan_put(vlan);
+	rcu_assign_pointer(p->untagged, NULL);
+
+}
+
 /* Must be protected by RTNL */
 int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
 {
-	struct net_port_vlan *pve;
+	struct net_port_vlan *pve = NULL;
 	struct net_bridge_vlan *vlan;
 	struct net_device *dev = p->dev;
 	int err;
@@ -275,11 +382,21 @@ int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
 	set_bit(p->port_no, vlan->port_bitmap);
 
 	list_add_tail_rcu(&pve->list, &p->vlan_list);
+
+	if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
+		err = nbp_vlan_add_untagged(p, vlan, flags);
+		if (err)
+			goto del_vlan;
+	}
+
 	return 0;
 
 clean_up:
 	kfree(pve);
-	br_vlan_del(vlan, flags);
+	br_vlan_del(p->br, vlan, flags);
+	return err;
+del_vlan:
+	nbp_vlan_delete(p, vid, flags);
 	return err;
 }
 
@@ -296,6 +413,9 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
 	if (!pve)
 		return -ENOENT;
 
+	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
+		nbp_vlan_delete_untagged(p, pve->vlan);
+
 	/* Remove VLAN from the device filter if it is supported. */
 	if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
 	    dev->netdev_ops->ndo_vlan_rx_kill_vid) {
@@ -306,6 +426,7 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
 			pr_warn("failed to kill vid %d for device %s\n",
 				vid, dev->name);
 	}
+
 	pve->vid = BR_INVALID_VID;
 
 	vlan = pve->vlan;
@@ -316,7 +437,7 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
 	list_del_rcu(&pve->list);
 	kfree_rcu(pve, rcu);
 
-	br_vlan_del(vlan, flags);
+	br_vlan_del(p->br, vlan, flags);
 
 	return 0;
 }
@@ -328,8 +449,11 @@ static void nbp_vlan_flush(struct net_bridge_port *p)
 
 	ASSERT_RTNL();
 
-	list_for_each_entry_safe(pve, tmp, &p->vlan_list, list)
-		nbp_vlan_delete(p, pve->vid, BRIDGE_FLAGS_SELF);
+	list_for_each_entry_safe(pve, tmp, &p->vlan_list, list)  {
+		nbp_vlan_delete(p, pve->vid,
+				(BRIDGE_VLAN_INFO_MASTER |
+				 BRIDGE_VLAN_INFO_UNTAGGED));
+	}
 }
 
 static void release_nbp(struct kobject *kobj)
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 9cf2879..1b302ce 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -199,7 +199,8 @@ static int br_afspec(struct net_bridge *br, struct net_bridge_port *p,
 			if (p)
 				err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
 			else {
-				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
+				u16 flags = vinfo->flags |
+					    BRIDGE_VLAN_INFO_MASTER;
 				if (!br_vlan_add(br, vinfo->vid, flags))
 					err = -ENOMEM;
 			}
@@ -210,7 +211,8 @@ static int br_afspec(struct net_bridge *br, struct net_bridge_port *p,
 				err = nbp_vlan_delete(p, vinfo->vid,
 						      vinfo->flags);
 			else {
-				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
+				u16 flags = vinfo->flags |
+					    BRIDGE_VLAN_INFO_MASTER;
 				err = br_vlan_delete(br, vinfo->vid, flags);
 			}
 			break;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index cc75212..9328463 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -179,6 +179,7 @@ struct net_bridge_port
 	struct netpoll			*np;
 #endif
 	struct list_head		vlan_list;
+	struct net_bridge_vlan __rcu	*untagged;
 };
 
 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
@@ -298,6 +299,7 @@ struct net_bridge
 	struct timer_list		gc_timer;
 	struct kobject			*ifobj;
 	struct hlist_head		vlan_hlist[BR_VID_HASH_SIZE];
+	struct net_bridge_vlan __rcu	*untagged;
 };
 
 struct br_input_skb_cb {
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH V2 11/12] bridge: Dump vlan information from a bridge port
From: Vlad Yasevich @ 2012-12-18 19:01 UTC (permalink / raw)
  To: netdev; +Cc: shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-1-git-send-email-vyasevic@redhat.com>

Using the RTM_GETLINK dump the vlan filter list of a given
bridge port.  The information depends on setting the filter
flag similar to how nic VF info is dumped.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c |    3 +-
 include/linux/netdevice.h                     |    3 +-
 include/uapi/linux/if_bridge.h                |    1 +
 include/uapi/linux/rtnetlink.h                |    1 +
 net/bridge/br_if.c                            |    2 +
 net/bridge/br_netlink.c                       |   69 ++++++++++++++++++++++--
 net/bridge/br_private.h                       |    3 +-
 net/core/rtnetlink.c                          |   16 ++++--
 8 files changed, 85 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index ec97efe..2f2a8e0 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7062,7 +7062,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
 }
 
 static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
-				    struct net_device *dev)
+				    struct net_device *dev,
+				    u32 filter_mask)
 {
 	struct ixgbe_adapter *adapter = netdev_priv(dev);
 	u16 mode;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 3ee7a80..d93c47c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1008,7 +1008,8 @@ struct net_device_ops {
 						      struct nlmsghdr *nlh);
 	int			(*ndo_bridge_getlink)(struct sk_buff *skb,
 						      u32 pid, u32 seq,
-						      struct net_device *dev);
+						      struct net_device *dev,
+						      u32 filter_mask);
 };
 
 /*
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 988d858..6b63e3b 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -115,6 +115,7 @@ enum {
 	IFLA_BRIDGE_FLAGS,
 	IFLA_BRIDGE_MODE,
 	IFLA_BRIDGE_VLAN_INFO,
+	IFLA_BRIDGE_VLAN,
 	__IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 354a1e7..f20654a 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -624,6 +624,7 @@ struct tcamsg {
 
 /* New extended info filters for IFLA_EXT_MASK */
 #define RTEXT_FILTER_VF		(1 << 0)
+#define RTEXT_FILTER_BRVLAN	(1 << 1)
 
 /* End of information exported to user level */
 
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 14563fb..3b67ab7 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -382,6 +382,7 @@ int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
 	set_bit(p->port_no, vlan->port_bitmap);
 
 	list_add_tail_rcu(&pve->list, &p->vlan_list);
+	p->num_vlans++;
 
 	if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
 		err = nbp_vlan_add_untagged(p, vlan, flags);
@@ -435,6 +436,7 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
 	br_vlan_put(vlan);
 
 	list_del_rcu(&pve->list);
+	p->num_vlans--;
 	kfree_rcu(pve, rcu);
 
 	br_vlan_del(p->br, vlan, flags);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 1b302ce..b1b7c70 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -65,8 +65,10 @@ static int br_port_fill_attrs(struct sk_buff *skb,
  * Create one netlink message for one interface
  * Contains port and master info as well as carrier and bridge state.
  */
-static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *port,
-			  u32 pid, u32 seq, int event, unsigned int flags)
+static int br_fill_ifinfo(struct sk_buff *skb,
+			  const struct net_bridge_port *port,
+			  u32 pid, u32 seq, int event, unsigned int flags,
+			  u32 filter_mask)
 {
 	const struct net_bridge *br = port->br;
 	const struct net_device *dev = port->dev;
@@ -108,6 +110,27 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
 		nla_nest_end(skb, nest);
 	}
 
+	/* Check if  the VID information is requested */
+	if (filter_mask & RTEXT_FILTER_BRVLAN) {
+		struct nlattr *af;
+		struct net_port_vlan *pve;
+
+		if (list_empty(&port->vlan_list))
+			goto done;
+
+		af = nla_nest_start(skb, IFLA_AF_SPEC | NLA_F_NESTED);
+		if (!af)
+			goto nla_put_failure;
+
+		list_for_each_entry_rcu(pve, &port->vlan_list, list) {
+			if (nla_put_u16(skb, IFLA_BRIDGE_VLAN, pve->vid))
+				goto nla_put_failure;
+		}
+
+		nla_nest_end(skb, af);
+	}
+
+done:
 	return nlmsg_end(skb, nlh);
 
 nla_put_failure:
@@ -134,7 +157,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
 	if (skb == NULL)
 		goto errout;
 
-	err = br_fill_ifinfo(skb, port, 0, 0, event, 0);
+	err = br_fill_ifinfo(skb, port, 0, 0, event, 0, 0);
 	if (err < 0) {
 		/* -EMSGSIZE implies BUG in br_nlmsg_size() */
 		WARN_ON(err == -EMSGSIZE);
@@ -152,7 +175,7 @@ errout:
  * Dump information about all ports, in response to GETLINK
  */
 int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
-	       struct net_device *dev)
+	       struct net_device *dev, u32 filter_mask)
 {
 	int err = 0;
 	struct net_bridge_port *port = br_port_get_rcu(dev);
@@ -161,7 +184,8 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
 	if (!port)
 		goto out;
 
-	err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI);
+	err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI,
+			     filter_mask);
 out:
 	return err;
 }
@@ -364,6 +388,23 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[])
 	return 0;
 }
 
+static size_t br_get_link_af_size(const struct net_device *dev)
+{
+	struct net_bridge_port *p;
+
+	p = br_port_get_rcu(dev);
+	if (!p)
+		return 0;
+
+	/* Each VLAN is returned as a short in IFLA_BRIDGE_VLAN attr */
+	return p->num_vlans * nla_total_size(2);
+}
+
+struct rtnl_af_ops br_af_ops = {
+	.family			= AF_BRIDGE,
+	.get_link_af_size	= br_get_link_af_size,
+};
+
 struct rtnl_link_ops br_link_ops __read_mostly = {
 	.kind		= "bridge",
 	.priv_size	= sizeof(struct net_bridge),
@@ -374,11 +415,27 @@ struct rtnl_link_ops br_link_ops __read_mostly = {
 
 int __init br_netlink_init(void)
 {
-	return rtnl_link_register(&br_link_ops);
+	int err;
+
+	err = rtnl_af_register(&br_af_ops);
+	if (err < 0)
+		goto err2;
+
+	err = rtnl_link_register(&br_link_ops);
+	if (err < 0)
+		goto err1;
+
+	return 0;
+
+err2:
+	rtnl_af_unregister(&br_af_ops);
+err1:
+	return err;
 }
 
 void __exit br_netlink_fini(void)
 {
+	rtnl_af_unregister(&br_af_ops);
 	rtnl_link_unregister(&br_link_ops);
 	rtnl_unregister_all(PF_BRIDGE);
 }
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 6f662a4..00e07c8 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -178,6 +178,7 @@ struct net_bridge_port
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	struct netpoll			*np;
 #endif
+	u16				num_vlans;
 	struct list_head		vlan_list;
 	struct net_bridge_vlan __rcu	*untagged;
 };
@@ -618,7 +619,7 @@ extern void br_netlink_fini(void);
 extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
 extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg);
 extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
-		      struct net_device *dev);
+		      struct net_device *dev, u32 filter_mask);
 
 #ifdef CONFIG_SYSFS
 /* br_sysfs_if.c */
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 8352302..208965f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2326,6 +2326,13 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
 	int idx = 0;
 	u32 portid = NETLINK_CB(cb->skb).portid;
 	u32 seq = cb->nlh->nlmsg_seq;
+	struct nlattr *extfilt;
+	u32 filter_mask = 0;
+
+	extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct rtgenmsg),
+				  IFLA_EXT_MASK);
+	if (extfilt)
+		filter_mask = nla_get_u32(extfilt);
 
 	rcu_read_lock();
 	for_each_netdev_rcu(net, dev) {
@@ -2335,14 +2342,15 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
 		if (master && master->netdev_ops->ndo_bridge_getlink) {
 			if (idx >= cb->args[0] &&
 			    master->netdev_ops->ndo_bridge_getlink(
-				    skb, portid, seq, dev) < 0)
+				    skb, portid, seq, dev, filter_mask) < 0)
 				break;
 			idx++;
 		}
 
 		if (ops->ndo_bridge_getlink) {
 			if (idx >= cb->args[0] &&
-			    ops->ndo_bridge_getlink(skb, portid, seq, dev) < 0)
+			    ops->ndo_bridge_getlink(skb, portid, seq, dev,
+						    filter_mask) < 0)
 				break;
 			idx++;
 		}
@@ -2383,14 +2391,14 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags)
 
 	if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) &&
 	    master && master->netdev_ops->ndo_bridge_getlink) {
-		err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
+		err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0);
 		if (err < 0)
 			goto errout;
 	}
 
 	if ((flags & BRIDGE_FLAGS_SELF) &&
 	    dev->netdev_ops->ndo_bridge_getlink) {
-		err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
+		err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0);
 		if (err < 0)
 			goto errout;
 	}
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH V2 10/12] bridge: Implement untagged vlan handling
From: Vlad Yasevich @ 2012-12-18 19:01 UTC (permalink / raw)
  To: netdev; +Cc: shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-1-git-send-email-vyasevic@redhat.com>

When an untagged frame arrives on a port that has untagged vlan set,
the frame is assigned to the untagged VLAN.  It will then procede
through the forwarding/egress process with the VLAN id set to the
untagged VLAN.

At egress, a frame with a VLAN id matching untagged vid will have its
vlan header stripped off.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_device.c    |   25 +++++++-
 net/bridge/br_forward.c   |  134 ++++++++++++++++++++++++++++++++++++++++++++-
 net/bridge/br_input.c     |   46 ++++++++++++---
 net/bridge/br_multicast.c |   37 +++++++-----
 net/bridge/br_private.h   |   20 ++++--
 5 files changed, 224 insertions(+), 38 deletions(-)

diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 1f9d0f9..37441b10 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -31,7 +31,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	struct net_bridge_mdb_entry *mdst;
 	struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
 	struct net_bridge_vlan *vlan;
-	u16 vid;
+	struct br_input_skb_cb *brcb;
+	u16 vid = 0;
 
 	rcu_read_lock();
 #ifdef CONFIG_BRIDGE_NETFILTER
@@ -47,15 +48,31 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	brstats->tx_bytes += skb->len;
 	u64_stats_update_end(&brstats->syncp);
 
-	BR_INPUT_SKB_CB(skb)->brdev = dev;
+	brcb = BR_INPUT_SKB_CB(skb);
+	memset(brcb, 0, sizeof(struct br_input_skb_cb));
+	brcb->brdev = dev;
+
+	if (br_get_vlan(skb, &vid)) {
+		u16 untagged_vid;
+
+		/* Untagged frame.  See if there is an untagged VLAN
+		 * configured.
+		 */
+		if ((vlan = rcu_dereference(br->untagged)) != NULL &&
+		    (untagged_vid = vlan->vid) != BR_INVALID_VID) {
+			__vlan_hwaccel_put_tag(skb, untagged_vid);
+			brcb->untagged = 1;
+			goto skip_lookup;
+		}
+	}
 
 	/* Any vlan transmitted by the bridge itself is permitted.
 	 * Try to cache the vlan in the CB to speed up forwarding.
 	 */
-	vid = br_get_vlan(skb);
 	vlan = br_vlan_find(br, vid);
+skip_lookup:
 	if (vlan)
-		BR_INPUT_SKB_CB(skb)->vlan = vlan;
+		brcb->vlan = vlan;
 
 	skb_reset_mac_header(skb);
 	skb_pull(skb, ETH_HLEN);
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 4ae5f55..bcd16e8 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -31,7 +31,7 @@ static inline bool br_allowed_egress(const struct net_bridge_port *p,
 {
 	struct net_port_vlan *pve;
 	struct net_bridge_vlan *vlan = NULL;
-	u16 vid;
+	u16 vid = 0;
 
 	if (list_empty(&p->vlan_list))
 		return true;
@@ -49,14 +49,134 @@ static inline bool br_allowed_egress(const struct net_bridge_port *p,
 	/* We don't have cached vlan information, so we need to do
 	 * it the hard way.
 	 */
-	vid = br_get_vlan(skb);
+	br_get_vlan(skb, &vid);
 	pve = nbp_vlan_find(p, vid);
-	if (pve)
+	if (pve) {
+		BR_INPUT_SKB_CB(skb)->vlan = pve->vlan;
 		return true;
+	}
 
 	return false;
 }
 
+/* Almost an exact copy of vlan_untag.  Needed here since it's not exported
+ * and not available if vlan support isn't built in.
+ */
+static struct sk_buff *br_vlan_untag(struct sk_buff *skb)
+{
+	struct vlan_hdr *vhdr;
+
+	if (skb->protocol != htons(ETH_P_8021Q)) {
+		skb->vlan_tci = 0;
+		return skb;
+	}
+
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (unlikely(!skb))
+		goto err_free;
+
+	if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
+		goto err_free;
+
+	vhdr = (struct vlan_hdr *)skb->data;
+	skb_pull_rcsum(skb, VLAN_HLEN);
+	vlan_set_encap_proto(skb, vhdr);
+
+	if (skb_cow(skb, skb_headroom(skb)) < 0)
+		goto err_free;
+
+	memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 2 * ETH_ALEN);
+	skb->mac_header += VLAN_HLEN;
+	skb->vlan_tci = 0;
+
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb_reset_mac_len(skb);
+
+	return skb;
+
+err_free:
+	kfree_skb(skb);
+	return NULL;
+}
+
+struct sk_buff *br_handle_vlan(const struct net_bridge *br,
+			      const struct net_bridge_port *p,
+			      struct sk_buff *skb)
+{
+	struct net_bridge_vlan *untag_vlan;
+	struct net_bridge_vlan *skb_vlan = BR_INPUT_SKB_CB(skb)->vlan;
+
+	if (p) {
+		/* If there are no vlans defined on this port, there
+		 * is nothing to do
+		 */
+		if (list_empty(&p->vlan_list))
+			goto out;
+
+		untag_vlan = rcu_dereference(p->untagged);
+	} else if (br)
+		untag_vlan = rcu_dereference(br->untagged);
+	else {
+		kfree_skb(skb);
+		goto out;
+	}
+
+	if (BR_INPUT_SKB_CB(skb)->untagged) {
+		/* Frame arrived on an untagged vlan. If it is leaving
+		 * on untagged interface, remove the tag we added.
+		 */
+		if (skb_vlan && untag_vlan == skb_vlan) {
+			skb->vlan_tci = 0;
+			goto out;
+		}
+
+		/* Frame leaving on a tagged vlan.  If output device is the
+		 * bridge, we need to add the VLAN header.  If we sending to
+		 * port, we let dev_hard_start_xmit() add the header.
+		 */
+		if (br && !p) {
+			/* vlan_put_tag expects skb->data to point to mac
+			 * header.
+			 */
+			skb_push(skb, ETH_HLEN);
+			skb = __vlan_put_tag(skb, skb->vlan_tci);
+			if (!skb)
+				goto out;
+			/* put skb->data back to where it was */
+			skb_pull(skb, ETH_HLEN);
+			skb->vlan_tci = 0;
+		}
+	} else {
+		/* Here we consider the frame tagged */
+		if (!skb_vlan && br) {
+			/* Bridge output device is special in that it doesn't
+			 * do any egress filtering.  This means that we need
+			 * to check against untagged vlan a little differently.
+			 */
+			u16 vid = 0;
+
+			br_get_vlan(skb, &vid);
+			if (untag_vlan && vid == untag_vlan->vid) {
+				/* VLAN is untagged on the bridge, strip */
+				skb = br_vlan_untag(skb);
+			}
+		} else {
+			/* Port egress check will find us the right vlan in
+			 * case we couldn't find one on ingress.
+			 * Now, if the egress vlan is "untagged/native",
+			 * strip the tag.  Otherwise, don't do anything.
+			 */
+			if (untag_vlan && untag_vlan->vid != BR_INVALID_VID &&
+			    skb_vlan == untag_vlan)
+				skb = br_vlan_untag(skb);
+		}
+	}
+
+out:
+	return skb;
+}
+
 /* Don't forward packets to originating port or forwarding diasabled */
 static inline int should_deliver(const struct net_bridge_port *p,
 				 const struct sk_buff *skb)
@@ -97,6 +217,10 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
 	skb->dev = to->dev;
 
+	skb = br_handle_vlan(NULL, to, skb);
+	if (!skb)
+		return;
+
 	if (unlikely(netpoll_tx_running(to->br->dev))) {
 		if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
 			kfree_skb(skb);
@@ -120,6 +244,10 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 		return;
 	}
 
+	skb = br_handle_vlan(NULL, to, skb);
+	if (!skb)
+		return;
+
 	indev = skb->dev;
 	skb->dev = to->dev;
 	skb_forward_csum(skb);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index e51eb24..1ff7f2c 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -35,6 +35,10 @@ static int br_pass_frame_up(struct sk_buff *skb)
 	brstats->rx_bytes += skb->len;
 	u64_stats_update_end(&brstats->syncp);
 
+	skb = br_handle_vlan(br, NULL, skb);
+	if (!skb)
+		return NET_RX_DROP;
+
 	indev = skb->dev;
 	skb->dev = brdev;
 
@@ -43,11 +47,11 @@ static int br_pass_frame_up(struct sk_buff *skb)
 }
 
 static bool br_allowed_ingress(struct net_bridge_port *p, struct sk_buff *skb,
-			       u16 vid)
+			       u16 *vid)
 {
 	struct net_port_vlan *pve;
-
-	BR_INPUT_SKB_CB(skb)->vlan = NULL;
+	struct net_bridge_vlan *vlan;
+	struct br_input_skb_cb *brcb = BR_INPUT_SKB_CB(skb);
 
 	/* If there are no vlan in the permitted list, all packets are
 	 * permitted.
@@ -55,7 +59,27 @@ static bool br_allowed_ingress(struct net_bridge_port *p, struct sk_buff *skb,
 	if (list_empty(&p->vlan_list))
 		return true;
 
-	pve = nbp_vlan_find(p, vid);
+	if (br_get_vlan(skb, vid)) {
+		u16 untagged_vid;
+		/* Frame did not have a tag. See if untagged vlan is set
+		 * on this port.
+		 */
+		if ((vlan = rcu_dereference(p->untagged)) == NULL ||
+		    (untagged_vid = vlan->vid) == BR_INVALID_VID)
+			return false;
+
+		/* Untagged vlan is set on this port.  Any untagged ingress
+		 * frame is considered to belong to the untagged vlan, so
+		 * mark it as such.
+		 */
+		__vlan_hwaccel_put_tag(skb, untagged_vid);
+		brcb->vlan = vlan;
+		brcb->untagged = 1;
+		return true;
+	}
+
+	/* Frame has a valid vlan tag.  Find the VLAN it belongs to. */
+	pve = nbp_vlan_find(p, *vid);
 	if (pve) {
 		BR_INPUT_SKB_CB(skb)->vlan = pve->vlan;
 		return true;
@@ -73,13 +97,15 @@ int br_handle_frame_finish(struct sk_buff *skb)
 	struct net_bridge_fdb_entry *dst;
 	struct net_bridge_mdb_entry *mdst;
 	struct sk_buff *skb2;
-	u16 vid;
+	struct br_input_skb_cb *brcb = BR_INPUT_SKB_CB(skb);
+	u16 vid = 0;
 
 	if (!p || p->state == BR_STATE_DISABLED)
 		goto drop;
 
-	vid = br_get_vlan(skb);
-	if (!br_allowed_ingress(p, skb, vid))
+	memset(brcb, 0, sizeof(struct br_input_skb_cb));
+
+	if (!br_allowed_ingress(p, skb, &vid))
 		goto drop;
 
 	/* insert into forwarding database after filtering to avoid spoofing */
@@ -93,7 +119,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
 	if (p->state == BR_STATE_LEARNING)
 		goto drop;
 
-	BR_INPUT_SKB_CB(skb)->brdev = br->dev;
+	brcb->brdev = br->dev;
 
 	/* The packet skb2 goes to the local host (NULL to skip). */
 	skb2 = NULL;
@@ -148,8 +174,10 @@ drop:
 static int br_handle_local_finish(struct sk_buff *skb)
 {
 	struct net_bridge_port *p = br_port_get_rcu(skb->dev);
+	u16 vid = 0;
 
-	br_fdb_update(p->br, p, eth_hdr(skb)->h_source, br_get_vlan(skb));
+	br_get_vlan(skb, &vid);
+	br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid);
 	return 0;	 /* process further */
 }
 
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 072aa2d..a7fc2c7 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -905,6 +905,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
 	int type;
 	int err = 0;
 	__be32 group;
+	u16 vid = 0;
 
 	if (!pskb_may_pull(skb, sizeof(*ih)))
 		return -EINVAL;
@@ -940,8 +941,8 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
 			continue;
 		}
 
-		err = br_ip4_multicast_add_group(br, port, group,
-						 br_get_vlan(skb));
+		br_get_vlan(skb, &vid);
+		err = br_ip4_multicast_add_group(br, port, group, vid);
 		if (err)
 			break;
 	}
@@ -960,6 +961,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 	int len;
 	int num;
 	int err = 0;
+	u16 vid = 0;
 
 	if (!pskb_may_pull(skb, sizeof(*icmp6h)))
 		return -EINVAL;
@@ -1001,8 +1003,9 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 			continue;
 		}
 
+		br_get_vlan(skb, &vid);
 		err = br_ip6_multicast_add_group(br, port, &grec->grec_mca,
-						 br_get_vlan(skb));
+						 vid);
 		if (!err)
 			break;
 	}
@@ -1086,6 +1089,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
 	unsigned long now = jiffies;
 	__be32 group;
 	int err = 0;
+	u16 vid = 0;
 
 	spin_lock(&br->multicast_lock);
 	if (!netif_running(br->dev) ||
@@ -1120,8 +1124,8 @@ static int br_ip4_multicast_query(struct net_bridge *br,
 	if (!group)
 		goto out;
 
-	mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group,
-			    br_get_vlan(skb));
+	br_get_vlan(skb, &vid);
+	mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
 	if (!mp)
 		goto out;
 
@@ -1162,6 +1166,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
 	unsigned long now = jiffies;
 	const struct in6_addr *group = NULL;
 	int err = 0;
+	u16 vid = 0;
 
 	spin_lock(&br->multicast_lock);
 	if (!netif_running(br->dev) ||
@@ -1193,8 +1198,8 @@ static int br_ip6_multicast_query(struct net_bridge *br,
 	if (!group)
 		goto out;
 
-	mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group,
-			    br_get_vlan(skb));
+	br_get_vlan(skb, &vid);
+	mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
 	if (!mp)
 		goto out;
 
@@ -1343,6 +1348,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
 	unsigned int len;
 	unsigned int offset;
 	int err;
+	u16 vid = 0;
 
 	/* We treat OOM as packet loss for now. */
 	if (!pskb_may_pull(skb, sizeof(*iph)))
@@ -1410,8 +1416,8 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
 	case IGMP_HOST_MEMBERSHIP_REPORT:
 	case IGMPV2_HOST_MEMBERSHIP_REPORT:
 		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
-		err = br_ip4_multicast_add_group(br, port, ih->group,
-						 br_get_vlan(skb2));
+		vid = br_get_vlan(skb2, &vid);
+		err = br_ip4_multicast_add_group(br, port, ih->group, vid);
 		break;
 	case IGMPV3_HOST_MEMBERSHIP_REPORT:
 		err = br_ip4_multicast_igmp3_report(br, port, skb2);
@@ -1420,8 +1426,8 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
 		err = br_ip4_multicast_query(br, port, skb2);
 		break;
 	case IGMP_HOST_LEAVE_MESSAGE:
-		br_ip4_multicast_leave_group(br, port, ih->group,
-					     br_get_vlan(skb2));
+		vid = br_get_vlan(skb2, &vid);
+		br_ip4_multicast_leave_group(br, port, ih->group, vid);
 		break;
 	}
 
@@ -1446,6 +1452,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
 	unsigned int len;
 	int offset;
 	int err;
+	u16 vid = 0;
 
 	if (!pskb_may_pull(skb, sizeof(*ip6h)))
 		return -EINVAL;
@@ -1541,8 +1548,8 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
 		}
 		mld = (struct mld_msg *)skb_transport_header(skb2);
 		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
-		err = br_ip6_multicast_add_group(br, port, &mld->mld_mca,
-						 br_get_vlan(skb2));
+		br_get_vlan(skb2, &vid);
+		err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid);
 		break;
 	    }
 	case ICMPV6_MLD2_REPORT:
@@ -1559,8 +1566,8 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
 			goto out;
 		}
 		mld = (struct mld_msg *)skb_transport_header(skb2);
-		br_ip6_multicast_leave_group(br, port, &mld->mld_mca,
-					     br_get_vlan(skb2));
+		br_get_vlan(skb2, &vid);
+		br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid);
 	    }
 	}
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 9328463..6f662a4 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -198,18 +198,20 @@ static inline struct net_bridge_port *br_port_get_rtnl(struct net_device *dev)
 		rtnl_dereference(dev->rx_handler_data) : NULL;
 }
 
-static inline u16 br_get_vlan(const struct sk_buff *skb)
+static inline int br_get_vlan(const struct sk_buff *skb, u16 *tag)
 {
-	u16 tag;
+	int rc = 0;
 
-	if (vlan_tx_tag_present(skb))
-		return vlan_tx_tag_get(skb) & VLAN_VID_MASK;
+	if (vlan_tx_tag_present(skb)) {
+		*tag = vlan_tx_tag_get(skb) & VLAN_VID_MASK;
+		return rc;
+	}
 
 	/* Untagged and VLAN 0 traffic is handled the same way */
-	if (vlan_get_tag(skb, &tag))
-		return 0;
+	rc = vlan_get_tag(skb, tag);
+	*tag = *tag & VLAN_VID_MASK;
 
-	return tag & VLAN_VID_MASK;
+	return rc;
 }
 
 struct br_cpu_netstats {
@@ -305,6 +307,7 @@ struct net_bridge
 struct br_input_skb_cb {
 	struct net_device *brdev;
 	struct net_bridge_vlan *vlan;
+	int untagged;
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 	int igmp;
 	int mrouters_only;
@@ -431,6 +434,9 @@ extern int br_forward_finish(struct sk_buff *skb);
 extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb);
 extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
 			     struct sk_buff *skb2);
+extern struct sk_buff *br_handle_vlan(const struct net_bridge *br,
+				      const struct net_bridge_port *p,
+				      struct sk_buff *skb);
 
 /* br_if.c */
 extern void br_port_carrier_check(struct net_bridge_port *p);
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH V2 12/12] bridge: Add vlan support for local fdb entries
From: Vlad Yasevich @ 2012-12-18 19:01 UTC (permalink / raw)
  To: netdev; +Cc: shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-1-git-send-email-vyasevic@redhat.com>

When VLAN is added to the port, a local fdb entry for that port
(the entry with the mac address of the port) is added for that
VLAN.  This way we can correctly determine if the traffic
is for the bridge itself.  If the address of the port changes,
we try to change all the local fdb entries we have for that port.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_fdb.c     |   66 ++++++++++++++++++++++++++++++++++-------------
 net/bridge/br_if.c      |   28 +++++++++++++++++++-
 net/bridge/br_private.h |    4 ++-
 3 files changed, 78 insertions(+), 20 deletions(-)

diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index f1199e4..0666295 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -28,7 +28,7 @@
 
 static struct kmem_cache *br_fdb_cache __read_mostly;
 static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
-		      const unsigned char *addr);
+		      const unsigned char *addr, u16 vid);
 static void fdb_notify(struct net_bridge *br,
 		       const struct net_bridge_fdb_entry *, int);
 
@@ -92,6 +92,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
 void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
 {
 	struct net_bridge *br = p->br;
+	int no_vlan = list_empty(&p->vlan_list);
 	int i;
 
 	spin_lock_bh(&br->hash_lock);
@@ -106,10 +107,12 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
 			if (f->dst == p && f->is_local) {
 				/* maybe another port has same hw addr? */
 				struct net_bridge_port *op;
+				u16 vid = f->vlan_id;
 				list_for_each_entry(op, &br->port_list, list) {
 					if (op != p &&
 					    ether_addr_equal(op->dev->dev_addr,
-							     f->addr.addr)) {
+							     f->addr.addr) &&
+					    nbp_vlan_find(op, vid)) {
 						f->dst = op;
 						goto insert;
 					}
@@ -117,27 +120,55 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
 
 				/* delete old one */
 				fdb_delete(br, f);
-				goto insert;
+insert:
+				/* insert new address,  may fail if invalid
+				 * address or dup.
+				 */
+				fdb_insert(br, p, newaddr, vid);
+
+				/* if this port has no vlan information
+				 * configured, we can safely be done at
+				 * this point.
+				 */
+				if (no_vlan)
+					goto done;
 			}
 		}
 	}
- insert:
-	/* insert new address,  may fail if invalid address or dup. */
-	fdb_insert(br, p, newaddr);
 
+done:
 	spin_unlock_bh(&br->hash_lock);
 }
 
 void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
 {
 	struct net_bridge_fdb_entry *f;
+	struct net_bridge_vlan *vlan;
+	struct hlist_node *node;
+	int i;
 
 	/* If old entry was unassociated with any port, then delete it. */
 	f = __br_fdb_get(br, br->dev->dev_addr, 0);
 	if (f && f->is_local && !f->dst)
 		fdb_delete(br, f);
 
-	fdb_insert(br, NULL, newaddr);
+	fdb_insert(br, NULL, newaddr, 0);
+
+	/* Now remove and add entries for every VLAN configured on the
+	 * bridge.
+	 */
+	for (i = 0; i < BR_VID_HASH_SIZE; i++) {
+		hlist_for_each_entry_rcu(vlan, node,
+					 &br->vlan_hlist[i], hlist) {
+			if (vlan->vid == BR_INVALID_VID || vlan->vid == 0)
+				continue;
+
+			f = __br_fdb_get(br, br->dev->dev_addr, vlan->vid);
+			if (f && f->is_local && !f->dst)
+				fdb_delete(br, f);
+			fdb_insert(br, NULL, newaddr, vlan->vid);
+		}
+	}
 }
 
 void br_fdb_cleanup(unsigned long _data)
@@ -380,15 +411,15 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
 }
 
 static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
-		  const unsigned char *addr)
+		  const unsigned char *addr, u16 vid)
 {
-	struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)];
+	struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
 	struct net_bridge_fdb_entry *fdb;
 
 	if (!is_valid_ether_addr(addr))
 		return -EINVAL;
 
-	fdb = fdb_find(head, addr, 0);
+	fdb = fdb_find(head, addr, vid);
 	if (fdb) {
 		/* it is okay to have multiple ports with same
 		 * address, just use the first one.
@@ -401,7 +432,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 		fdb_delete(br, fdb);
 	}
 
-	fdb = fdb_create(head, source, addr, 0);
+	fdb = fdb_create(head, source, addr, vid);
 	if (!fdb)
 		return -ENOMEM;
 
@@ -412,12 +443,12 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 
 /* Add entry for local address of interface */
 int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
-		  const unsigned char *addr)
+		  const unsigned char *addr, u16 vid)
 {
 	int ret;
 
 	spin_lock_bh(&br->hash_lock);
-	ret = fdb_insert(br, source, addr);
+	ret = fdb_insert(br, source, addr, vid);
 	spin_unlock_bh(&br->hash_lock);
 	return ret;
 }
@@ -710,10 +741,9 @@ out:
 	return err;
 }
 
-static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr,
-			      u16 vlan)
+int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr,
+			u16 vlan)
 {
-	struct net_bridge *br = p->br;
 	struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
 	struct net_bridge_fdb_entry *fdb;
 
@@ -721,7 +751,7 @@ static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr,
 	if (!fdb)
 		return -ENOENT;
 
-	fdb_delete(p->br, fdb);
+	fdb_delete(br, fdb);
 	return 0;
 }
 
@@ -731,7 +761,7 @@ static int __br_fdb_delete(struct net_bridge_port *p,
 	int err;
 
 	spin_lock_bh(&p->br->hash_lock);
-	err = fdb_delete_by_addr(p, addr, vid);
+	err = fdb_delete_by_addr(p->br, addr, vid);
 	spin_unlock_bh(&p->br->hash_lock);
 
 	return err;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 3b67ab7..041a3c8 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -178,6 +178,11 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
 
 	hlist_add_head_rcu(&vlan->hlist, &br->vlan_hlist[br_vlan_hash(vid)]);
 
+	if (br_fdb_insert(br, NULL, br->dev->dev_addr, vid)) {
+		br_err(br,
+		       "failed insert local address bridge forwarding table\n");
+	}
+
 untagged:
 	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
 		br_vlan_add_untagged(br, vlan);
@@ -207,6 +212,12 @@ static void br_vlan_del(struct net_bridge *br, struct net_bridge_vlan *vlan,
 	if (!bitmap_empty(vlan->port_bitmap, PORT_BITMAP_LEN))
 		return;
 
+	if (vlan->vid) {
+		spin_lock_bh(&br->hash_lock);
+		fdb_delete_by_addr(br, br->dev->dev_addr, vlan->vid);
+		spin_unlock_bh(&br->hash_lock);
+	}
+
 	vlan->vid = BR_INVALID_VID;
 
 	/* If, for whatever reason, bridge still has a ref on this vlan
@@ -390,6 +401,12 @@ int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
 			goto del_vlan;
 	}
 
+	if (br_fdb_insert(p->br, p, dev->dev_addr, vid)) {
+		br_err(p->br,
+		       "failed insert local address bridge forwarding table\n");
+		goto del_vlan;
+	}
+
 	return 0;
 
 clean_up:
@@ -428,6 +445,15 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
 				vid, dev->name);
 	}
 
+	if (vid) {
+		/* If the VID !=0 remove fdb for this vid. VID 0 is special
+		 * in that it's the default and is always there in the fdb.
+		 */
+		spin_lock_bh(&p->br->hash_lock);
+		fdb_delete_by_addr(p->br, p->dev->dev_addr, vid);
+		spin_unlock_bh(&p->br->hash_lock);
+	}
+
 	pve->vid = BR_INVALID_VID;
 
 	vlan = pve->vlan;
@@ -773,7 +799,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 
 	dev_set_mtu(br->dev, br_min_mtu(br));
 
-	if (br_fdb_insert(br, p, dev->dev_addr))
+	if (br_fdb_insert(br, p, dev->dev_addr, 0))
 		netdev_err(dev, "failed insert local address bridge forwarding table\n");
 
 	kobject_uevent(&p->kobj, KOBJ_ADD);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 00e07c8..495ec5a 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -407,11 +407,13 @@ extern int br_fdb_fillbuf(struct net_bridge *br, void *buf,
 			  unsigned long count, unsigned long off);
 extern int br_fdb_insert(struct net_bridge *br,
 			 struct net_bridge_port *source,
-			 const unsigned char *addr);
+			 const unsigned char *addr,
+			 u16 vid);
 extern void br_fdb_update(struct net_bridge *br,
 			  struct net_bridge_port *source,
 			  const unsigned char *addr,
 			  u16 vid);
+extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid);
 
 extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 			 struct net_device *dev,
-- 
1.7.7.6

^ permalink raw reply related

* Re: [PATCH V2 01/12] bridge: Add vlan filtering infrastructure
From: Eric Dumazet @ 2012-12-18 21:13 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-2-git-send-email-vyasevic@redhat.com>

On Tue, 2012-12-18 at 14:00 -0500, Vlad Yasevich wrote:

> +static void br_vlan_destroy(struct net_bridge_vlan *vlan)
> +{
> +	if (!bitmap_empty(vlan->port_bitmap, PORT_BITMAP_LEN)) {
> +		pr_err("Attempt to delete a VLAN %d from the bridge with "
> +		       "non-empty port bitmap (%p)\n", vlan->vid, vlan);
> +		BUG();
> +	}
> +
> +	hlist_del_rcu(&vlan->hlist);
> +	synchronize_net();
> +	kfree_rcu(vlan, rcu);
> +}

Not clear why you both use synchronize_net() and kfree_rcu()

^ permalink raw reply

* Re: [PATCH V2 01/12] bridge: Add vlan filtering infrastructure
From: Vlad Yasevich @ 2012-12-18 21:26 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355865231.9380.119.camel@edumazet-glaptop>

On 12/18/2012 04:13 PM, Eric Dumazet wrote:
> On Tue, 2012-12-18 at 14:00 -0500, Vlad Yasevich wrote:
>
>> +static void br_vlan_destroy(struct net_bridge_vlan *vlan)
>> +{
>> +	if (!bitmap_empty(vlan->port_bitmap, PORT_BITMAP_LEN)) {
>> +		pr_err("Attempt to delete a VLAN %d from the bridge with "
>> +		       "non-empty port bitmap (%p)\n", vlan->vid, vlan);
>> +		BUG();
>> +	}
>> +
>> +	hlist_del_rcu(&vlan->hlist);
>> +	synchronize_net();
>> +	kfree_rcu(vlan, rcu);
>> +}
>
> Not clear why you both use synchronize_net() and kfree_rcu()
>


I think this was a left-over from old code.  I think this was
originally here to mimic del_nbp behavior, but I don't think
it's really needed as kfree_rcu should wait until all current
rcu section have completed.

-vlad

^ permalink raw reply

* [PATCH] bridge:  Do not unregister PF_BRIDGE rtnl operations
From: Vlad Yasevich @ 2012-12-18 21:43 UTC (permalink / raw)
  To: netdev; +Cc: davem, shemminger

Now that bridge rtnl ops are in core, do not undergister
all PF_BRIDGE ops when bridge module is unloaded.  It makes
it so that after reload, none of the messages work.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_netlink.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index dead9df..97ba018 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -305,5 +305,4 @@ int __init br_netlink_init(void)
 void __exit br_netlink_fini(void)
 {
 	rtnl_link_unregister(&br_link_ops);
-	rtnl_unregister_all(PF_BRIDGE);
 }
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH] bridge: Correctly encode addresses when dumping mdb entries
From: Vlad Yasevich @ 2012-12-18 21:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, shemminger

When dumping mdb table, set the addresses the kernel returns
based on the address protocol type.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_mdb.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index 9cf5d2b..ac22bb2 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -84,9 +84,11 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
 					struct br_mdb_entry e;
 					e.ifindex = port->dev->ifindex;
 					e.state = p->state;
-					e.addr.u.ip4 = p->addr.u.ip4;
+					if (p->addr.proto == htons(ETH_P_IP))
+						e.addr.u.ip4 = p->addr.u.ip4;
 #if IS_ENABLED(CONFIG_IPV6)
-					e.addr.u.ip6 = p->addr.u.ip6;
+					if (p->addr.proto == htons(ETH_P_IPV6))
+						e.addr.u.ip6 = p->addr.u.ip6;
 #endif
 					e.addr.proto = p->addr.proto;
 					if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(e), &e)) {
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH] net: mvneta: remove unneeded version.h include
From: Jesper Juhl @ 2012-12-18 21:54 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: etdev, netdev, linux-kernel, Rami Rosen, David S. Miller, trivial

The file uses nothing from the version.h header, so there is no reason
to include it.

Signed-off-by: Jesper Juhl <jj@chaosbits.net>
---
 drivers/net/ethernet/marvell/mvneta.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 3f8086b..a1e0e9f 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -12,7 +12,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/version.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/platform_device.h>
-- 
1.7.1


-- 
Jesper Juhl <jj@chaosbits.net>       http://www.chaosbits.net/
Don't top-post http://www.catb.org/jargon/html/T/top-post.html
Plain text mails only, please.

^ permalink raw reply related

* Re: [PATCH v2] netlink: align attributes on 64-bits
From: Nicolas Dichtel @ 2012-12-18 22:07 UTC (permalink / raw)
  To: Thomas Graf; +Cc: bhutchings, netdev, davem, David.Laight
In-Reply-To: <20121218170853.GH27746@casper.infradead.org>

Le 18/12/2012 18:08, Thomas Graf a écrit :
> On 12/18/12 at 05:23pm, Nicolas Dichtel wrote:
>> Le 18/12/2012 13:57, Thomas Graf a écrit :
>>> -static inline int nla_padlen(int payload)
>>> -{
>>> -	return nla_total_size(payload) - nla_attr_size(payload);
>>> +	if (!IS_ALIGNED(len, NLA_ATTR_ALIGN))
>>> +		len = ALIGN(len + NLA_HDRLEN, NLA_ATTR_ALIGN);
>> Two comments:
>> 1/ should it be ALIGN(len, NLA_ATTR_ALIGN)? If we want to add a __u64:
>>     => nla_attr_size(sizeof(__u64)) = 12
>>     => NLA_ALIGN(nla_attr_size(sizeof(__u64))) => 12 (= len)
>>     => ALIGN(len + NLA_HDRLEN, NLA_ATTR_ALIGN) = 0 but it should be 4
>
> We can't add 1-3 bytes of padding, therefore we need to add
> NLA_HDRLEN to len before aligning it to enforce a minimal
> padding. We can't hit it right now because 4 byte alignment
> of the previous attribute is a given but if we ever change
> the alignment it could become an issue and the above should
> be bullet proof.
>
> Your example would come out like this:
>    nla_attr_size(8) = 12
>    ALIGN(12 + 4, 8) = 16
Got it, right.

>
>> 2/ Suppose that the attribute is:
>>
>>    struct foo {
>>    	__u64 bar1;
>>    	__u32 bar2;
>>    }
>>    => sizeof(struct foo) = 12 (= payload)
>>    => nla_attr_size(payload) = 16
>>    => NLA_ALIGN(nla_attr_size(payload)) = 16 (= len)
>>    => IS_ALIGNED(len, NLA_ATTR_ALIGN) = true
>>    => extra room is not reserved
>>    But it's not guaranteed that bar1 is aligned on 8 bytes, only on 4 bytes.
>
> That's correct, that's why I have added the additional
> NLA_ATTR_ALIGN of room in nlmsg_new(). It will account
> for the one time padding that is needed before we add
> the very first attribute.
>
> If all attributes after that have a size aligned to 8
> bytes no padding is needed. Padding will only be needed
> again if a struct is missized in which case we reserve
> room with the above. Correct?
Seems good ;-)

>
>>> +	offset = (size_t) skb_tail_pointer(skb);
>>> +	if (!IS_ALIGNED(offset + NLA_HDRLEN, NLA_ATTR_ALIGN)) {
>> With the previous struct foo, this test may be true even if we don't
>> have reserved extra room. This test depends on previous attribute.
>> I think the exact size of the netlink message depends on the order
>> of attributes, not only on the attribute itself.
>> What about taking the assumption that the start will never be
>> aligned and always allocating extra room: ALIGN(NLA_ALIGNTO,
>> NLA_ATTR_ALIGN) (= 4)?
>
> See my explanation above. I think this works. The order does not
> matter, the sum of all padding required will always be the same.
I will do more test.

>
>>> +static bool nla_insufficient_space(struct sk_buff *skb, int attrlen)
>>> +{
>>> +	size_t needed = nla_pre_padlen(skb) + nla_total_size(attrlen);
>> If nla_total_size() was right, nla_pre_padlen(skb) should already be
>> included. Am I wrong?
>
> No, nla_pre_padlen() contains the number of bytes needed to align
> skb_tail_pointer() to an alignment of 8. If that is > 0 but the
> attribute to follow is already aligned.
>
> The tricky part here is that accounting for padding in
> nla_total_size() only works for the sum of all attributes.
> It does not account for the specific padding required for the
> previous attribute.
>
> Therefore the above check. The above could be changed to
> nla_attr_size() theoretically as we don't need space for the
> final padding eventually but we checked for space before so I
> kept it that way.
>
> I realize it's slightly confusign and needs better documentation
> and please double check my thinking :-)
Ok, you convince me ;-)

^ permalink raw reply

* [PATCH] ipv6: addrconf.c: remove unnecessary "if"
From: Cong Ding @ 2012-12-18 22:08 UTC (permalink / raw)
  To: David S. Miller, Alexey Kuznetsov, James Morris,
	Hideaki YOSHIFUJI, Patrick McHardy, netdev, linux-kernel
  Cc: Cong Ding

the value of err is always negative if it goes to errout, so we don't need to
check the value of err.

Signed-off-by: Cong Ding <dinggnu@gmail.com>
---
 net/ipv6/addrconf.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 6fca01f..408cac4a 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -534,8 +534,7 @@ void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex,
 	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_ATOMIC);
 	return;
 errout:
-	if (err < 0)
-		rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err);
+	rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err);
 }
 
 static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
-- 
1.7.10.4

^ permalink raw reply related

* [patch net-next V2 0/4] net: allow to change carrier from userspace
From: Jiri Pirko @ 2012-12-18 22:14 UTC (permalink / raw)
  To: netdev
  Cc: davem, edumazet, bhutchings, mirqus, shemminger, greearb, fbl,
	john.r.fastabend

This is basically a V2 of a repost of my previous patchset:
"[patch net-next-2.6 0/2] net: allow to change carrier via sysfs" from Aug 30

The way net-sysfs stores values changed and this patchset reflects it.
Also, I exposed carrier via rtnetlink iface.

So far, only dummy driver uses carrier change ndo. In very near future
team driver will use that as well.

V1->v2:
 - added bigger comment to ndo and also note to operstate.txt documentation
   stating the clear purpose of this iface

Jiri Pirko (4):
  net: add change_carrier netdev op
  net: allow to change carrier via sysfs
  rtnl: expose carrier value with possibility to set it
  dummy: implement carrier change

 Documentation/networking/operstates.txt |  4 ++++
 drivers/net/dummy.c                     | 10 ++++++++++
 include/linux/netdevice.h               |  9 +++++++++
 include/uapi/linux/if_link.h            |  1 +
 net/core/dev.c                          | 19 +++++++++++++++++++
 net/core/net-sysfs.c                    | 15 ++++++++++++++-
 net/core/rtnetlink.c                    | 10 ++++++++++
 7 files changed, 67 insertions(+), 1 deletion(-)

-- 
1.8.0

^ permalink raw reply

* [patch net-next 1/4] net: add change_carrier netdev op
From: Jiri Pirko @ 2012-12-18 22:14 UTC (permalink / raw)
  To: netdev
  Cc: davem, edumazet, bhutchings, mirqus, shemminger, greearb, fbl,
	john.r.fastabend
In-Reply-To: <1355868858-1713-1-git-send-email-jiri@resnulli.us>

This allows a driver to register change_carrier callback which will be
called whenever user will like to change carrier state. This is useful
for devices like dummy, gre, team and so on.

Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
 include/linux/netdevice.h |  9 +++++++++
 net/core/dev.c            | 19 +++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 02e0f6b..8047330 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -891,6 +891,11 @@ struct netdev_fcoe_hbainfo {
  * int (*ndo_bridge_setlink)(struct net_device *dev, struct nlmsghdr *nlh)
  * int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq,
  *			     struct net_device *dev)
+ *
+ * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
+ *	Called to update device carrier. Soft-devices which do not manage
+ *	real hardware like dummy, team, etc. can define this to provide
+ *	possibility to set their carrier state.
  */
 struct net_device_ops {
 	int			(*ndo_init)(struct net_device *dev);
@@ -1008,6 +1013,8 @@ struct net_device_ops {
 	int			(*ndo_bridge_getlink)(struct sk_buff *skb,
 						      u32 pid, u32 seq,
 						      struct net_device *dev);
+	int			(*ndo_change_carrier)(struct net_device *dev,
+						      bool new_carrier);
 };
 
 /*
@@ -2194,6 +2201,8 @@ extern int		dev_set_mtu(struct net_device *, int);
 extern void		dev_set_group(struct net_device *, int);
 extern int		dev_set_mac_address(struct net_device *,
 					    struct sockaddr *);
+extern int		dev_change_carrier(struct net_device *,
+					   bool new_carrier);
 extern int		dev_hard_start_xmit(struct sk_buff *skb,
 					    struct net_device *dev,
 					    struct netdev_queue *txq);
diff --git a/net/core/dev.c b/net/core/dev.c
index d0cbc93..268a714 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5027,6 +5027,25 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
 }
 EXPORT_SYMBOL(dev_set_mac_address);
 
+/**
+ *	dev_change_carrier - Change device carrier
+ *	@dev: device
+ *	@new_carries: new value
+ *
+ *	Change device carrier
+ */
+int dev_change_carrier(struct net_device *dev, bool new_carrier)
+{
+	const struct net_device_ops *ops = dev->netdev_ops;
+
+	if (!ops->ndo_change_carrier)
+		return -EOPNOTSUPP;
+	if (!netif_device_present(dev))
+		return -ENODEV;
+	return ops->ndo_change_carrier(dev, new_carrier);
+}
+EXPORT_SYMBOL(dev_change_carrier);
+
 /*
  *	Perform the SIOCxIFxxx calls, inside rcu_read_lock()
  */
-- 
1.8.0

^ permalink raw reply related

* [patch net-next 2/4] net: allow to change carrier via sysfs
From: Jiri Pirko @ 2012-12-18 22:14 UTC (permalink / raw)
  To: netdev
  Cc: davem, edumazet, bhutchings, mirqus, shemminger, greearb, fbl,
	john.r.fastabend
In-Reply-To: <1355868858-1713-1-git-send-email-jiri@resnulli.us>

Make carrier writable

Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
 net/core/net-sysfs.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 334efd5..7eda40a 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -126,6 +126,19 @@ static ssize_t show_broadcast(struct device *dev,
 	return -EINVAL;
 }
 
+static int change_carrier(struct net_device *net, unsigned long new_carrier)
+{
+	if (!netif_running(net))
+		return -EINVAL;
+	return dev_change_carrier(net, (bool) new_carrier);
+}
+
+static ssize_t store_carrier(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t len)
+{
+	return netdev_store(dev, attr, buf, len, change_carrier);
+}
+
 static ssize_t show_carrier(struct device *dev,
 			    struct device_attribute *attr, char *buf)
 {
@@ -331,7 +344,7 @@ static struct device_attribute net_class_attributes[] = {
 	__ATTR(link_mode, S_IRUGO, show_link_mode, NULL),
 	__ATTR(address, S_IRUGO, show_address, NULL),
 	__ATTR(broadcast, S_IRUGO, show_broadcast, NULL),
-	__ATTR(carrier, S_IRUGO, show_carrier, NULL),
+	__ATTR(carrier, S_IRUGO | S_IWUSR, show_carrier, store_carrier),
 	__ATTR(speed, S_IRUGO, show_speed, NULL),
 	__ATTR(duplex, S_IRUGO, show_duplex, NULL),
 	__ATTR(dormant, S_IRUGO, show_dormant, NULL),
-- 
1.8.0

^ permalink raw reply related

* [patch net-next 3/4] rtnl: expose carrier value with possibility to set it
From: Jiri Pirko @ 2012-12-18 22:14 UTC (permalink / raw)
  To: netdev
  Cc: davem, edumazet, bhutchings, mirqus, shemminger, greearb, fbl,
	john.r.fastabend
In-Reply-To: <1355868858-1713-1-git-send-email-jiri@resnulli.us>

Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
 Documentation/networking/operstates.txt |  4 ++++
 include/uapi/linux/if_link.h            |  1 +
 net/core/rtnetlink.c                    | 10 ++++++++++
 3 files changed, 15 insertions(+)

diff --git a/Documentation/networking/operstates.txt b/Documentation/networking/operstates.txt
index 1a77a3c..9769457 100644
--- a/Documentation/networking/operstates.txt
+++ b/Documentation/networking/operstates.txt
@@ -88,6 +88,10 @@ set this flag. On netif_carrier_off(), the scheduler stops sending
 packets. The name 'carrier' and the inversion are historical, think of
 it as lower layer.
 
+Note that for certain kind of soft-devices, which are not managing any
+real hardware, there is possible to set this bit from userpsace.
+One should use TVL IFLA_CARRIER to do so.
+
 netif_carrier_ok() can be used to query that bit.
 
 __LINK_STATE_DORMANT, maps to IFF_DORMANT:
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 60f3b6b..c4edfe1 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -142,6 +142,7 @@ enum {
 #define IFLA_PROMISCUITY IFLA_PROMISCUITY
 	IFLA_NUM_TX_QUEUES,
 	IFLA_NUM_RX_QUEUES,
+	IFLA_CARRIER,
 	__IFLA_MAX
 };
 
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 1868625..2ef7a56 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -780,6 +780,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
 	       + nla_total_size(4) /* IFLA_MTU */
 	       + nla_total_size(4) /* IFLA_LINK */
 	       + nla_total_size(4) /* IFLA_MASTER */
+	       + nla_total_size(1) /* IFLA_CARRIER */
 	       + nla_total_size(4) /* IFLA_PROMISCUITY */
 	       + nla_total_size(4) /* IFLA_NUM_TX_QUEUES */
 	       + nla_total_size(4) /* IFLA_NUM_RX_QUEUES */
@@ -909,6 +910,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 	     nla_put_u32(skb, IFLA_LINK, dev->iflink)) ||
 	    (dev->master &&
 	     nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) ||
+	    nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
 	    (dev->qdisc &&
 	     nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
 	    (dev->ifalias &&
@@ -1108,6 +1110,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
 	[IFLA_MTU]		= { .type = NLA_U32 },
 	[IFLA_LINK]		= { .type = NLA_U32 },
 	[IFLA_MASTER]		= { .type = NLA_U32 },
+	[IFLA_CARRIER]		= { .type = NLA_U8 },
 	[IFLA_TXQLEN]		= { .type = NLA_U32 },
 	[IFLA_WEIGHT]		= { .type = NLA_U32 },
 	[IFLA_OPERSTATE]	= { .type = NLA_U8 },
@@ -1438,6 +1441,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
 		modified = 1;
 	}
 
+	if (tb[IFLA_CARRIER]) {
+		err = dev_change_carrier(dev, nla_get_u8(tb[IFLA_CARRIER]));
+		if (err)
+			goto errout;
+		modified = 1;
+	}
+
 	if (tb[IFLA_TXQLEN])
 		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
 
-- 
1.8.0

^ permalink raw reply related

* [patch net-next 4/4] dummy: implement carrier change
From: Jiri Pirko @ 2012-12-18 22:14 UTC (permalink / raw)
  To: netdev
  Cc: davem, edumazet, bhutchings, mirqus, shemminger, greearb, fbl,
	john.r.fastabend
In-Reply-To: <1355868858-1713-1-git-send-email-jiri@resnulli.us>

Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
 drivers/net/dummy.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index c260af5..42aa54a 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -100,6 +100,15 @@ static void dummy_dev_uninit(struct net_device *dev)
 	free_percpu(dev->dstats);
 }
 
+static int dummy_change_carrier(struct net_device *dev, bool new_carrier)
+{
+	if (new_carrier)
+		netif_carrier_on(dev);
+	else
+		netif_carrier_off(dev);
+	return 0;
+}
+
 static const struct net_device_ops dummy_netdev_ops = {
 	.ndo_init		= dummy_dev_init,
 	.ndo_uninit		= dummy_dev_uninit,
@@ -108,6 +117,7 @@ static const struct net_device_ops dummy_netdev_ops = {
 	.ndo_set_rx_mode	= set_multicast_list,
 	.ndo_set_mac_address	= eth_mac_addr,
 	.ndo_get_stats64	= dummy_get_stats64,
+	.ndo_change_carrier	= dummy_change_carrier,
 };
 
 static void dummy_setup(struct net_device *dev)
-- 
1.8.0

^ permalink raw reply related

* Re: [PATCH V2 00/12] Add basic VLAN support to bridges
From: Jiri Pirko @ 2012-12-18 22:32 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <1355857263-31197-1-git-send-email-vyasevic@redhat.com>



I see that this patchset replicates a lot of code which is already
present in net/8021q/ or include/linux/if_vlan.h. I think it would
be nice to move this code into some "common" place, wouldn't it?

Jiri

Tue, Dec 18, 2012 at 08:00:51PM CET, vyasevic@redhat.com wrote:
>This series of patches provides an ability to add VLANs to the bridge
>ports.  This is similar to what can be found in most switches.  The bridge
>port may have any number of VLANs added to it including vlan 0 priority tagged
>traffic.  When vlans are added to the port, only traffic tagged with particular
>vlan will forwarded over this port.  Additionally, vlan ids are added to FDB
>entries and become part of the lookup.  This way we correctly identify the FDB
>entry.
>
>A single vlan may also be designated as untagged.  Any untagged traffic
>recieved by the port will be assigned to this vlan.  Any traffic exiting
>the port with a VID matching the untagged vlan will exit untagged (the
>bridge will strip the vlan header).  This is similar to "Native Vlan" support
>available in most switches.
>
>The default behavior ofthe bridge is unchanged if no vlans have been
>configured.
>
>Changes since v1:
> - Fixed some forwarding bugs.
> - Add vlan to local fdb entries.  New local entries are created per vlan
>   to facilite correct forwarding to bridge interface.
> - Allow configuration of vlans directly on the bridge master device
>   in addition to ports.
>
>Changes since rfc v2:
> - Per-port vlan bitmap is gone and is replaced with a vlan list.
> - Added bridge vlan list, which is referenced by each port.  Entries in
>   the birdge vlan list have port bitmap that shows which port are parts
>   of which vlan.
> - Netlink API changes.
> - Dropped sysfs support for now.  If people think this is really usefull,
>   can add it back.
> - Support for native/untagged vlans.
>
>Changes since rfc v1:
> - Comments addressed regarding formatting and RCU usage
> - iocts have been removed and changed over the netlink interface.
> - Added support of user added ndb entries.
> - changed sysfs interface to export a bitmap.  Also added a write interface.
>   I am not sure how much I like it, but it made my testing easier/faster.  I
>   might change the write interface to take text instead of binary.
>
>
>Vlad Yasevich (12):
>  bridge: Add vlan filtering infrastructure
>  bridge: Validate that vlan is permitted on ingress
>  bridge: Verify that a vlan is allowed to egress on give port
>  bridge: Cache vlan in the cb for faster egress lookup.
>  bridge: Add vlan to unicast fdb entries
>  bridge: Add vlan id to multicast groups
>  bridge: Add netlink interface to configure vlans on bridge ports
>  bridge: Add vlan support to static neighbors
>  bridge: Add the ability to configure untagged vlans
>  bridge: Implement untagged vlan handling
>  bridge: Dump vlan information from a bridge port
>  bridge: Add vlan support for local fdb entries
>
> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c |    5 +-
> drivers/net/macvlan.c                         |    2 +-
> drivers/net/vxlan.c                           |    3 +-
> include/linux/netdevice.h                     |    4 +-
> include/uapi/linux/if_bridge.h                |   23 ++-
> include/uapi/linux/neighbour.h                |    1 +
> include/uapi/linux/rtnetlink.h                |    1 +
> net/bridge/br_device.c                        |   34 ++-
> net/bridge/br_fdb.c                           |  253 ++++++++++++---
> net/bridge/br_forward.c                       |  160 ++++++++++
> net/bridge/br_if.c                            |  404 ++++++++++++++++++++++++-
> net/bridge/br_input.c                         |   65 ++++-
> net/bridge/br_multicast.c                     |   71 +++--
> net/bridge/br_netlink.c                       |  178 ++++++++++--
> net/bridge/br_private.h                       |   71 ++++-
> net/core/rtnetlink.c                          |   40 ++-
> 16 files changed, 1190 insertions(+), 125 deletions(-)
>
>-- 
>1.7.7.6
>
>--
>To unsubscribe from this list: send the line "unsubscribe netdev" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH V2 00/12] Add basic VLAN support to bridges
From: Vlad Yasevich @ 2012-12-18 22:46 UTC (permalink / raw)
  To: Jiri Pirko; +Cc: netdev, shemminger, davem, or.gerlitz, jhs, mst
In-Reply-To: <20121218223244.GC1690@minipsycho.orion>

On 12/18/2012 05:32 PM, Jiri Pirko wrote:
>
>
> I see that this patchset replicates a lot of code which is already
> present in net/8021q/ or include/linux/if_vlan.h. I think it would
> be nice to move this code into some "common" place, wouldn't it?
>

The only replication that I am aware of is in br_vlan_untag().  I 
thought about pulling that piece out, but I think there is a reason
why it's not available when 801q support isn't turned on.  I noted that
openvswitch implemented its own vlan header manipulation functions as well.

What else are you seeing that's duplicate?

-vlad

> Jiri
>
> Tue, Dec 18, 2012 at 08:00:51PM CET, vyasevic@redhat.com wrote:
>> This series of patches provides an ability to add VLANs to the bridge
>> ports.  This is similar to what can be found in most switches.  The bridge
>> port may have any number of VLANs added to it including vlan 0 priority tagged
>> traffic.  When vlans are added to the port, only traffic tagged with particular
>> vlan will forwarded over this port.  Additionally, vlan ids are added to FDB
>> entries and become part of the lookup.  This way we correctly identify the FDB
>> entry.
>>
>> A single vlan may also be designated as untagged.  Any untagged traffic
>> recieved by the port will be assigned to this vlan.  Any traffic exiting
>> the port with a VID matching the untagged vlan will exit untagged (the
>> bridge will strip the vlan header).  This is similar to "Native Vlan" support
>> available in most switches.
>>
>> The default behavior ofthe bridge is unchanged if no vlans have been
>> configured.
>>
>> Changes since v1:
>> - Fixed some forwarding bugs.
>> - Add vlan to local fdb entries.  New local entries are created per vlan
>>    to facilite correct forwarding to bridge interface.
>> - Allow configuration of vlans directly on the bridge master device
>>    in addition to ports.
>>
>> Changes since rfc v2:
>> - Per-port vlan bitmap is gone and is replaced with a vlan list.
>> - Added bridge vlan list, which is referenced by each port.  Entries in
>>    the birdge vlan list have port bitmap that shows which port are parts
>>    of which vlan.
>> - Netlink API changes.
>> - Dropped sysfs support for now.  If people think this is really usefull,
>>    can add it back.
>> - Support for native/untagged vlans.
>>
>> Changes since rfc v1:
>> - Comments addressed regarding formatting and RCU usage
>> - iocts have been removed and changed over the netlink interface.
>> - Added support of user added ndb entries.
>> - changed sysfs interface to export a bitmap.  Also added a write interface.
>>    I am not sure how much I like it, but it made my testing easier/faster.  I
>>    might change the write interface to take text instead of binary.
>>
>>
>> Vlad Yasevich (12):
>>   bridge: Add vlan filtering infrastructure
>>   bridge: Validate that vlan is permitted on ingress
>>   bridge: Verify that a vlan is allowed to egress on give port
>>   bridge: Cache vlan in the cb for faster egress lookup.
>>   bridge: Add vlan to unicast fdb entries
>>   bridge: Add vlan id to multicast groups
>>   bridge: Add netlink interface to configure vlans on bridge ports
>>   bridge: Add vlan support to static neighbors
>>   bridge: Add the ability to configure untagged vlans
>>   bridge: Implement untagged vlan handling
>>   bridge: Dump vlan information from a bridge port
>>   bridge: Add vlan support for local fdb entries
>>
>> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c |    5 +-
>> drivers/net/macvlan.c                         |    2 +-
>> drivers/net/vxlan.c                           |    3 +-
>> include/linux/netdevice.h                     |    4 +-
>> include/uapi/linux/if_bridge.h                |   23 ++-
>> include/uapi/linux/neighbour.h                |    1 +
>> include/uapi/linux/rtnetlink.h                |    1 +
>> net/bridge/br_device.c                        |   34 ++-
>> net/bridge/br_fdb.c                           |  253 ++++++++++++---
>> net/bridge/br_forward.c                       |  160 ++++++++++
>> net/bridge/br_if.c                            |  404 ++++++++++++++++++++++++-
>> net/bridge/br_input.c                         |   65 ++++-
>> net/bridge/br_multicast.c                     |   71 +++--
>> net/bridge/br_netlink.c                       |  178 ++++++++++--
>> net/bridge/br_private.h                       |   71 ++++-
>> net/core/rtnetlink.c                          |   40 ++-
>> 16 files changed, 1190 insertions(+), 125 deletions(-)
>>
>> --
>> 1.7.7.6
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe netdev" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [RFC PATCH v3 0/2] Fix some multiqueue TUN problems
From: Paul Moore @ 2012-12-18 22:53 UTC (permalink / raw)
  To: netdev, linux-security-module, selinux; +Cc: jasowang, mst

A refresh/respin of the LSM/SELinux fixes to work on top of Jason's
latest API tweak (now living in DaveM's net tree).  In general, I
believe the hooks and thinking behind the v2 patchset still make sense
so no changes there, although I did change the SELinux permission from
"create_queue" to "attach_queue" to match the API changes.

Comments are welcome and encouraged; we need to get this fixed before
3.8 is released.

---

Paul Moore (2):
      selinux: add the "attach_queue" permission to the "tun_socket" class
      tun: fix LSM/SELinux labeling of tun/tap devices


 drivers/net/tun.c                   |   27 ++++++++++++----
 include/linux/security.h            |   59 +++++++++++++++++++++++++++--------
 security/capability.c               |   24 ++++++++++++--
 security/security.c                 |   28 ++++++++++++++---
 security/selinux/hooks.c            |   50 +++++++++++++++++++++++-------
 security/selinux/include/classmap.h |    2 +
 security/selinux/include/objsec.h   |    4 ++
 7 files changed, 155 insertions(+), 39 deletions(-)

^ permalink raw reply

* [RFC PATCH v3 1/2] selinux: add the "attach_queue" permission to the "tun_socket" class
From: Paul Moore @ 2012-12-18 22:53 UTC (permalink / raw)
  To: netdev, linux-security-module, selinux; +Cc: jasowang, mst
In-Reply-To: <20121218225001.16104.34454.stgit@localhost>

Add a new permission to align with the new TUN multiqueue support,
"tun_socket:attach_queue".

The corresponding SELinux reference policy patch is show below:

 diff --git a/policy/flask/access_vectors b/policy/flask/access_vectors
 index 28802c5..a0664a1 100644
 --- a/policy/flask/access_vectors
 +++ b/policy/flask/access_vectors
 @@ -827,6 +827,9 @@ class kernel_service

  class tun_socket
  inherits socket
 +{
 +       attach_queue
 +}

  class x_pointer
  inherits x_device

Signed-off-by: Paul Moore <pmoore@redhat.com>
---
 security/selinux/include/classmap.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index df2de54..14d04e6 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -150,6 +150,6 @@ struct security_class_mapping secclass_map[] = {
 	    NULL } },
 	{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
 	{ "tun_socket",
-	  { COMMON_SOCK_PERMS, NULL } },
+	  { COMMON_SOCK_PERMS, "attach_queue", NULL } },
 	{ NULL }
   };

^ permalink raw reply related

* [RFC PATCH v3 2/2] tun: fix LSM/SELinux labeling of tun/tap devices
From: Paul Moore @ 2012-12-18 22:53 UTC (permalink / raw)
  To: netdev, linux-security-module, selinux; +Cc: jasowang, mst
In-Reply-To: <20121218225001.16104.34454.stgit@localhost>

This patch corrects some problems with LSM/SELinux that were introduced
with the multiqueue patchset.  The problem stems from the fact that the
multiqueue work changed the relationship between the tun device and its
associated socket; before the socket persisted for the life of the
device, however after the multiqueue changes the socket only persisted
for the life of the userspace connection (fd open).  For non-persistent
devices this is not an issue, but for persistent devices this can cause
the tun device to lose its SELinux label.

We correct this problem by adding an opaque LSM security blob to the
tun device struct which allows us to have the LSM security state, e.g.
SELinux labeling information, persist for the lifetime of the tun
device.  In the process we tweak the LSM hooks to work with this new
approach to TUN device/socket labeling and introduce a new LSM hook,
security_tun_dev_attach_queue(), to approve requests to attach to a
TUN queue via TUNSETQUEUE.

The SELinux code has been adjusted to match the new LSM hooks, the
other LSMs do not make use of the LSM TUN controls.  This patch makes
use of the recently added "tun_socket:attach_queue" permission to
restrict access to the TUNSETQUEUE operation.  On older SELinux
policies which do not define the "tun_socket:attach_queue" permission
the access control decision for TUNSETQUEUE will be handled according
to the SELinux policy's unknown permission setting.

Signed-off-by: Paul Moore <pmoore@redhat.com>
---
 drivers/net/tun.c                 |   27 +++++++++++++----
 include/linux/security.h          |   59 +++++++++++++++++++++++++++++--------
 security/capability.c             |   24 +++++++++++++--
 security/security.c               |   28 ++++++++++++++----
 security/selinux/hooks.c          |   50 ++++++++++++++++++++++++-------
 security/selinux/include/objsec.h |    4 +++
 6 files changed, 154 insertions(+), 38 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 504f7f1..4b7754c 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -186,6 +186,7 @@ struct tun_struct {
 	unsigned long ageing_time;
 	unsigned int numdisabled;
 	struct list_head disabled;
+	void *security;
 };
 
 static inline u32 tun_hashfn(u32 rxhash)
@@ -496,6 +497,10 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
 	struct tun_file *tfile = file->private_data;
 	int err;
 
+	err = security_tun_dev_attach(tfile->socket.sk, tun->security);
+	if (err < 0)
+		goto out;
+
 	err = -EINVAL;
 	if (rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held()))
 		goto out;
@@ -1389,6 +1394,7 @@ static void tun_free_netdev(struct net_device *dev)
 
 	BUG_ON(!(list_empty(&tun->disabled)));
 	tun_flow_uninit(tun);
+	security_tun_dev_free_security(tun->security);
 	free_netdev(dev);
 }
 
@@ -1575,7 +1581,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 
 		if (tun_not_capable(tun))
 			return -EPERM;
-		err = security_tun_dev_attach(tfile->socket.sk);
+		err = security_tun_dev_open(tun->security);
 		if (err < 0)
 			return err;
 
@@ -1632,7 +1638,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 
 		spin_lock_init(&tun->lock);
 
-		security_tun_dev_post_create(&tfile->sk);
+		err = security_tun_dev_alloc_security(&tun->security);
+		if (err < 0)
+			goto err_free_dev;
 
 		tun_net_init(dev);
 
@@ -1805,12 +1813,18 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)
 
 	if (ifr->ifr_flags & IFF_ATTACH_QUEUE) {
 		tun = tfile->detached;
-		if (!tun)
+		if (!tun) {
 			ret = -EINVAL;
-		else if (tun_not_capable(tun))
+			goto unlock;
+		}
+		if (tun_not_capable(tun)) {
 			ret = -EPERM;
-		else
-			ret = tun_attach(tun, file);
+			goto unlock;
+		}
+		ret = security_tun_dev_attach_queue(tun->security);
+		if (ret < 0)
+			goto unlock;
+		ret = tun_attach(tun, file);
 	} else if (ifr->ifr_flags & IFF_DETACH_QUEUE) {
 		tun = rcu_dereference_protected(tfile->tun,
 						lockdep_rtnl_is_held());
@@ -1821,6 +1835,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)
 	} else
 		ret = -EINVAL;
 
+unlock:
 	rtnl_unlock();
 	return ret;
 }
diff --git a/include/linux/security.h b/include/linux/security.h
index 05e88bd..e09a87b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -983,17 +983,29 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	tells the LSM to decrement the number of secmark labeling rules loaded
  * @req_classify_flow:
  *	Sets the flow's sid to the openreq sid.
+ * @tun_dev_alloc_security:
+ *	This hook allows a module to allocate a security structure for a TUN
+ *	device.
+ *	@security pointer to a security structure pointer.
+ *	Returns a zero on success, negative values on failure.
+ * @tun_dev_free_security:
+ *	This hook allows a module to free the security structure for a TUN
+ *	device.
+ *	@security pointer to the TUN device's security structure
  * @tun_dev_create:
  *	Check permissions prior to creating a new TUN device.
- * @tun_dev_post_create:
- *	This hook allows a module to update or allocate a per-socket security
- *	structure.
- *	@sk contains the newly created sock structure.
+ * @tun_dev_attach_queue:
+ *	Check permissions prior to attaching to a TUN device queue.
+ *	@security pointer to the TUN device's security structure.
  * @tun_dev_attach:
- *	Check permissions prior to attaching to a persistent TUN device.  This
- *	hook can also be used by the module to update any security state
+ *	This hook can be used by the module to update any security state
  *	associated with the TUN device's sock structure.
  *	@sk contains the existing sock structure.
+ *	@security pointer to the TUN device's security structure.
+ * @tun_dev_open:
+ *	This hook can be used by the module to update any security state
+ *	associated with the TUN device's security structure.
+ *	@security pointer to the TUN devices's security structure.
  *
  * Security hooks for XFRM operations.
  *
@@ -1613,9 +1625,12 @@ struct security_operations {
 	void (*secmark_refcount_inc) (void);
 	void (*secmark_refcount_dec) (void);
 	void (*req_classify_flow) (const struct request_sock *req, struct flowi *fl);
-	int (*tun_dev_create)(void);
-	void (*tun_dev_post_create)(struct sock *sk);
-	int (*tun_dev_attach)(struct sock *sk);
+	int (*tun_dev_alloc_security) (void **security);
+	void (*tun_dev_free_security) (void *security);
+	int (*tun_dev_create) (void);
+	int (*tun_dev_attach_queue) (void *security);
+	int (*tun_dev_attach) (struct sock *sk, void *security);
+	int (*tun_dev_open) (void *security);
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -2553,9 +2568,12 @@ void security_inet_conn_established(struct sock *sk,
 int security_secmark_relabel_packet(u32 secid);
 void security_secmark_refcount_inc(void);
 void security_secmark_refcount_dec(void);
+int security_tun_dev_alloc_security(void **security);
+void security_tun_dev_free_security(void *security);
 int security_tun_dev_create(void);
-void security_tun_dev_post_create(struct sock *sk);
-int security_tun_dev_attach(struct sock *sk);
+int security_tun_dev_attach_queue(void *security);
+int security_tun_dev_attach(struct sock *sk, void *security);
+int security_tun_dev_open(void *security);
 
 #else	/* CONFIG_SECURITY_NETWORK */
 static inline int security_unix_stream_connect(struct sock *sock,
@@ -2720,16 +2738,31 @@ static inline void security_secmark_refcount_dec(void)
 {
 }
 
+static inline int security_tun_dev_alloc_security(void **security)
+{
+	return 0;
+}
+
+static inline void security_tun_dev_free_security(void *security)
+{
+}
+
 static inline int security_tun_dev_create(void)
 {
 	return 0;
 }
 
-static inline void security_tun_dev_post_create(struct sock *sk)
+static inline int security_tun_dev_attach_queue(void *security)
+{
+	return 0;
+}
+
+static inline int security_tun_dev_attach(struct sock *sk, void *security)
 {
+	return 0;
 }
 
-static inline int security_tun_dev_attach(struct sock *sk)
+static inline int security_tun_dev_open(void *security)
 {
 	return 0;
 }
diff --git a/security/capability.c b/security/capability.c
index b14a30c..76c1dc9 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -704,16 +704,31 @@ static void cap_req_classify_flow(const struct request_sock *req,
 {
 }
 
+static int cap_tun_dev_alloc_security(void **security)
+{
+	return 0;
+}
+
+static void cap_tun_dev_free_security(void *security)
+{
+}
+
 static int cap_tun_dev_create(void)
 {
 	return 0;
 }
 
-static void cap_tun_dev_post_create(struct sock *sk)
+static int cap_tun_dev_attach_queue(void *security)
+{
+	return 0;
+}
+
+static int cap_tun_dev_attach(struct sock *sk, void *security)
 {
+	return 0;
 }
 
-static int cap_tun_dev_attach(struct sock *sk)
+static int cap_tun_dev_open(void *security)
 {
 	return 0;
 }
@@ -1044,8 +1059,11 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, secmark_refcount_inc);
 	set_to_cap_if_null(ops, secmark_refcount_dec);
 	set_to_cap_if_null(ops, req_classify_flow);
+	set_to_cap_if_null(ops, tun_dev_alloc_security);
+	set_to_cap_if_null(ops, tun_dev_free_security);
 	set_to_cap_if_null(ops, tun_dev_create);
-	set_to_cap_if_null(ops, tun_dev_post_create);
+	set_to_cap_if_null(ops, tun_dev_open);
+	set_to_cap_if_null(ops, tun_dev_attach_queue);
 	set_to_cap_if_null(ops, tun_dev_attach);
 #endif	/* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
diff --git a/security/security.c b/security/security.c
index 8dcd4ae..a271ed4 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1244,24 +1244,42 @@ void security_secmark_refcount_dec(void)
 }
 EXPORT_SYMBOL(security_secmark_refcount_dec);
 
+int security_tun_dev_alloc_security(void **security)
+{
+	return security_ops->tun_dev_alloc_security(security);
+}
+EXPORT_SYMBOL(security_tun_dev_alloc_security);
+
+void security_tun_dev_free_security(void *security)
+{
+	security_ops->tun_dev_free_security(security);
+}
+EXPORT_SYMBOL(security_tun_dev_free_security);
+
 int security_tun_dev_create(void)
 {
 	return security_ops->tun_dev_create();
 }
 EXPORT_SYMBOL(security_tun_dev_create);
 
-void security_tun_dev_post_create(struct sock *sk)
+int security_tun_dev_attach_queue(void *security)
 {
-	return security_ops->tun_dev_post_create(sk);
+	return security_ops->tun_dev_attach_queue(security);
 }
-EXPORT_SYMBOL(security_tun_dev_post_create);
+EXPORT_SYMBOL(security_tun_dev_attach_queue);
 
-int security_tun_dev_attach(struct sock *sk)
+int security_tun_dev_attach(struct sock *sk, void *security)
 {
-	return security_ops->tun_dev_attach(sk);
+	return security_ops->tun_dev_attach(sk, security);
 }
 EXPORT_SYMBOL(security_tun_dev_attach);
 
+int security_tun_dev_open(void *security)
+{
+	return security_ops->tun_dev_open(security);
+}
+EXPORT_SYMBOL(security_tun_dev_open);
+
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 61a5336..ef26e96 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4399,6 +4399,24 @@ static void selinux_req_classify_flow(const struct request_sock *req,
 	fl->flowi_secid = req->secid;
 }
 
+static int selinux_tun_dev_alloc_security(void **security)
+{
+	struct tun_security_struct *tunsec;
+
+	tunsec = kzalloc(sizeof(*tunsec), GFP_KERNEL);
+	if (!tunsec)
+		return -ENOMEM;
+	tunsec->sid = current_sid();
+
+	*security = tunsec;
+	return 0;
+}
+
+static void selinux_tun_dev_free_security(void *security)
+{
+	kfree(security);
+}
+
 static int selinux_tun_dev_create(void)
 {
 	u32 sid = current_sid();
@@ -4414,8 +4432,17 @@ static int selinux_tun_dev_create(void)
 			    NULL);
 }
 
-static void selinux_tun_dev_post_create(struct sock *sk)
+static int selinux_tun_dev_attach_queue(void *security)
 {
+	struct tun_security_struct *tunsec = security;
+
+	return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET,
+			    TUN_SOCKET__ATTACH_QUEUE, NULL);
+}
+
+static int selinux_tun_dev_attach(struct sock *sk, void *security)
+{
+	struct tun_security_struct *tunsec = security;
 	struct sk_security_struct *sksec = sk->sk_security;
 
 	/* we don't currently perform any NetLabel based labeling here and it
@@ -4425,20 +4452,19 @@ static void selinux_tun_dev_post_create(struct sock *sk)
 	 * cause confusion to the TUN user that had no idea network labeling
 	 * protocols were being used */
 
-	/* see the comments in selinux_tun_dev_create() about why we don't use
-	 * the sockcreate SID here */
-
-	sksec->sid = current_sid();
+	sksec->sid = tunsec->sid;
 	sksec->sclass = SECCLASS_TUN_SOCKET;
+
+	return 0;
 }
 
-static int selinux_tun_dev_attach(struct sock *sk)
+static int selinux_tun_dev_open(void *security)
 {
-	struct sk_security_struct *sksec = sk->sk_security;
+	struct tun_security_struct *tunsec = security;
 	u32 sid = current_sid();
 	int err;
 
-	err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
+	err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET,
 			   TUN_SOCKET__RELABELFROM, NULL);
 	if (err)
 		return err;
@@ -4446,8 +4472,7 @@ static int selinux_tun_dev_attach(struct sock *sk)
 			   TUN_SOCKET__RELABELTO, NULL);
 	if (err)
 		return err;
-
-	sksec->sid = sid;
+	tunsec->sid = sid;
 
 	return 0;
 }
@@ -5642,9 +5667,12 @@ static struct security_operations selinux_ops = {
 	.secmark_refcount_inc =		selinux_secmark_refcount_inc,
 	.secmark_refcount_dec =		selinux_secmark_refcount_dec,
 	.req_classify_flow =		selinux_req_classify_flow,
+	.tun_dev_alloc_security =	selinux_tun_dev_alloc_security,
+	.tun_dev_free_security =	selinux_tun_dev_free_security,
 	.tun_dev_create =		selinux_tun_dev_create,
-	.tun_dev_post_create = 		selinux_tun_dev_post_create,
+	.tun_dev_attach_queue =		selinux_tun_dev_attach_queue,
 	.tun_dev_attach =		selinux_tun_dev_attach,
+	.tun_dev_open =			selinux_tun_dev_open,
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 	.xfrm_policy_alloc_security =	selinux_xfrm_policy_alloc,
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 26c7eee..aa47bca 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -110,6 +110,10 @@ struct sk_security_struct {
 	u16 sclass;			/* sock security class */
 };
 
+struct tun_security_struct {
+	u32 sid;			/* SID for the tun device sockets */
+};
+
 struct key_security_struct {
 	u32 sid;	/* SID of key */
 };

^ permalink raw reply related

* Re: [PATCH V2 09/12] bridge: Add the ability to configure untagged vlans
From: Michael S. Tsirkin @ 2012-12-18 23:01 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, shemminger, davem, or.gerlitz, jhs
In-Reply-To: <1355857263-31197-10-git-send-email-vyasevic@redhat.com>

On Tue, Dec 18, 2012 at 02:01:00PM -0500, Vlad Yasevich wrote:
> A user may designate a certain vlan as untagged.  This means that
> any ingress frame is assigned to this vlan and any forwarding decisions
> are made with this vlan in mind.  On egress, any frames tagged/labeled
> with untagged vlan have the vlan tag removed and are send as regular
> ethernet frames.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  include/uapi/linux/if_bridge.h |    3 +
>  net/bridge/br_if.c             |  146 +++++++++++++++++++++++++++++++++++++---
>  net/bridge/br_netlink.c        |    6 +-
>  net/bridge/br_private.h        |    2 +
>  4 files changed, 144 insertions(+), 13 deletions(-)
> 
> diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
> index d0b4f5c..988d858 100644
> --- a/include/uapi/linux/if_bridge.h
> +++ b/include/uapi/linux/if_bridge.h
> @@ -127,6 +127,9 @@ enum {
>  	BR_VLAN_DEL,
>  };
>  
> +#define BRIDGE_VLAN_INFO_MASTER		1
> +#define BRIDGE_VLAN_INFO_UNTAGGED	2
> +
>  struct bridge_vlan_info {
>  	u16 op_code;
>  	u16 flags;
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 57bbb35..14563fb 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -108,6 +108,34 @@ static void br_vlan_put(struct net_bridge_vlan *vlan)
>  		br_vlan_destroy(vlan);
>  }
>  
> +/* Must be protected by RTNL */
> +static void br_vlan_add_untagged(struct net_bridge *br,
> +				 struct net_bridge_vlan *vlan)
> +{
> +	ASSERT_RTNL();
> +	if (br->untagged == vlan)
> +		return;
> +	else if (br->untagged) {
> +		/* Untagged vlan is already set on the master,
> +		 * so drop the ref since we'll be replacing it.
> +		 */
> +		br_vlan_put(br->untagged);
> +	}
> +	br_vlan_hold(vlan);
> +	rcu_assign_pointer(br->untagged, vlan);

Is there a reason for rcu here but not else where? If all users are under
rtnl you can just assign in a simple way.
If not then rcu_dereference_protected would be more appropriate.

> +}
> +
> +/* Must be protected by RTNL */
> +static void br_vlan_del_untagged(struct net_bridge *br,
> +				 struct net_bridge_vlan *vlan)
> +{
> +	ASSERT_RTNL();
> +	if (br->untagged == vlan) {
> +		br_vlan_put(vlan);
> +		rcu_assign_pointer(br->untagged, NULL);
> +	}
> +}
> +
>  struct net_bridge_vlan *br_vlan_find(struct net_bridge *br, u16 vid)
>  {
>  	struct net_bridge_vlan *vlan;
> @@ -132,7 +160,7 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
>  
>  	vlan = br_vlan_find(br, vid);
>  	if (vlan)
> -		return vlan;
> +		goto untagged;
>  
>  	vlan = kzalloc(sizeof(struct net_bridge_vlan), GFP_KERNEL);
>  	if (!vlan)
> @@ -141,7 +169,7 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
>  	vlan->vid = vid;
>  	atomic_set(&vlan->refcnt, 1);
>  
> -	if (flags & BRIDGE_FLAGS_SELF) {
> +	if (flags & BRIDGE_VLAN_INFO_MASTER) {
>  		/* Set bit 0 that is associated with the bridge master
>  		 * device.  Port numbers start with 1.
>  		 */
> @@ -149,15 +177,24 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
>  	}
>  
>  	hlist_add_head_rcu(&vlan->hlist, &br->vlan_hlist[br_vlan_hash(vid)]);
> +
> +untagged:
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
> +		br_vlan_add_untagged(br, vlan);
> +
>  	return vlan;
>  }
>  
>  /* Must be protected by RTNL */
> -static void br_vlan_del(struct net_bridge_vlan *vlan, u16 flags)
> +static void br_vlan_del(struct net_bridge *br, struct net_bridge_vlan *vlan,
> +			u16 flags)
>  {
>  	ASSERT_RTNL();
>  
> -	if (flags & BRIDGE_FLAGS_SELF) {
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
> +		br_vlan_del_untagged(br, vlan);
> +
> +	if (flags & BRIDGE_VLAN_INFO_MASTER) {
>  		/* Clear bit 0 that is associated with the bridge master
>  		 * device.
>  		 */
> @@ -172,6 +209,14 @@ static void br_vlan_del(struct net_bridge_vlan *vlan, u16 flags)
>  
>  	vlan->vid = BR_INVALID_VID;
>  
> +	/* If, for whatever reason, bridge still has a ref on this vlan
> +	 * through the @untagged pointer, drop that ref and clear untagged.
> +	 */
> +	if (br->untagged == vlan) {
> +		br_vlan_put(vlan);
> +		rcu_assign_pointer(br->untagged, NULL);
> +	}
> +
>  	/* Drop the self-ref to trigger descrution. */
>  	br_vlan_put(vlan);
>  }
> @@ -187,7 +232,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid, u16 flags)
>  	if (!vlan)
>  		return -ENOENT;
>  
> -	br_vlan_del(vlan, flags);
> +	br_vlan_del(br, vlan, flags);
>  	return 0;
>  }
>  
> @@ -204,7 +249,9 @@ static void br_vlan_flush(struct net_bridge *br)
>  	for (i = 0; i < BR_VID_HASH_SIZE; i++) {
>  		hlist_for_each_entry_safe(vlan, node, tmp,
>  					  &br->vlan_hlist[i], hlist) {
> -			br_vlan_del(vlan, BRIDGE_FLAGS_SELF);
> +			br_vlan_del(br, vlan,
> +				    (BRIDGE_VLAN_INFO_MASTER |
> +				     BRIDGE_VLAN_INFO_UNTAGGED));
>  		}
>  	}
>  }
> @@ -224,10 +271,70 @@ struct net_port_vlan *nbp_vlan_find(const struct net_bridge_port *p, u16 vid)
>  	return NULL;
>  }
>  
> +static int nbp_vlan_add_untagged(struct net_bridge_port *p,
> +			  struct net_bridge_vlan *vlan,
> +			  u16 flags)
> +{
> +	struct net_device *dev = p->dev;
> +
> +	if (p->untagged) {
> +		/* Port already has untagged vlan set.  Drop the ref
> +		 * to the old one since we'll be replace it.
> +		 */
> +		br_vlan_put(p->untagged);
> +	} else {
> +		int err;
> +
> +		/* Add vid 0 to filter if filter is available. */
> +		if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
> +		    dev->netdev_ops->ndo_vlan_rx_add_vid &&
> +		    dev->netdev_ops->ndo_vlan_rx_kill_vid) {
> +			err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, 0);
> +			if (err)
> +				return err;
> +		}
> +	}
> +
> +	/* This VLAN is handled as untagged/native. Save an
> +	 * additional ref.
> +	 */
> +	br_vlan_hold(vlan);
> +	rcu_assign_pointer(p->untagged, vlan);
> +
> +	return 0;
> +}
> +
> +static void nbp_vlan_delete_untagged(struct net_bridge_port *p,
> +				     struct net_bridge_vlan *vlan)
> +{
> +	if (p->untagged != vlan)
> +		return;
> +
> +	/* Remove VLAN from the device filter if it is supported. */
> +	if ((p->dev->features & NETIF_F_HW_VLAN_FILTER) &&
> +	    p->dev->netdev_ops->ndo_vlan_rx_kill_vid) {
> +		int err;
> +
> +		err = p->dev->netdev_ops->ndo_vlan_rx_kill_vid(p->dev, 0);
> +		if (err) {
> +			pr_warn("failed to kill vid %d for device %s\n",
> +				vlan->vid, p->dev->name);
> +		}
> +	}
> +
> +	/* If this VLAN is currently functioning as untagged, clear it.
> +	 * It's safe to drop the refcount, since the vlan is still held
> +	 * by the port.
> +	 */
> +	br_vlan_put(vlan);
> +	rcu_assign_pointer(p->untagged, NULL);
> +
> +}
> +
>  /* Must be protected by RTNL */
>  int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
>  {
> -	struct net_port_vlan *pve;
> +	struct net_port_vlan *pve = NULL;
>  	struct net_bridge_vlan *vlan;
>  	struct net_device *dev = p->dev;
>  	int err;
> @@ -275,11 +382,21 @@ int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
>  	set_bit(p->port_no, vlan->port_bitmap);
>  
>  	list_add_tail_rcu(&pve->list, &p->vlan_list);
> +
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
> +		err = nbp_vlan_add_untagged(p, vlan, flags);
> +		if (err)
> +			goto del_vlan;
> +	}
> +
>  	return 0;
>  
>  clean_up:
>  	kfree(pve);
> -	br_vlan_del(vlan, flags);
> +	br_vlan_del(p->br, vlan, flags);
> +	return err;
> +del_vlan:
> +	nbp_vlan_delete(p, vid, flags);
>  	return err;
>  }
>  
> @@ -296,6 +413,9 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
>  	if (!pve)
>  		return -ENOENT;
>  
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
> +		nbp_vlan_delete_untagged(p, pve->vlan);
> +
>  	/* Remove VLAN from the device filter if it is supported. */
>  	if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
>  	    dev->netdev_ops->ndo_vlan_rx_kill_vid) {
> @@ -306,6 +426,7 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
>  			pr_warn("failed to kill vid %d for device %s\n",
>  				vid, dev->name);
>  	}
> +
>  	pve->vid = BR_INVALID_VID;
>  
>  	vlan = pve->vlan;
> @@ -316,7 +437,7 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
>  	list_del_rcu(&pve->list);
>  	kfree_rcu(pve, rcu);
>  
> -	br_vlan_del(vlan, flags);
> +	br_vlan_del(p->br, vlan, flags);
>  
>  	return 0;
>  }
> @@ -328,8 +449,11 @@ static void nbp_vlan_flush(struct net_bridge_port *p)
>  
>  	ASSERT_RTNL();
>  
> -	list_for_each_entry_safe(pve, tmp, &p->vlan_list, list)
> -		nbp_vlan_delete(p, pve->vid, BRIDGE_FLAGS_SELF);
> +	list_for_each_entry_safe(pve, tmp, &p->vlan_list, list)  {
> +		nbp_vlan_delete(p, pve->vid,
> +				(BRIDGE_VLAN_INFO_MASTER |
> +				 BRIDGE_VLAN_INFO_UNTAGGED));
> +	}
>  }
>  
>  static void release_nbp(struct kobject *kobj)
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 9cf2879..1b302ce 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -199,7 +199,8 @@ static int br_afspec(struct net_bridge *br, struct net_bridge_port *p,
>  			if (p)
>  				err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
>  			else {
> -				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
> +				u16 flags = vinfo->flags |
> +					    BRIDGE_VLAN_INFO_MASTER;
>  				if (!br_vlan_add(br, vinfo->vid, flags))
>  					err = -ENOMEM;
>  			}
> @@ -210,7 +211,8 @@ static int br_afspec(struct net_bridge *br, struct net_bridge_port *p,
>  				err = nbp_vlan_delete(p, vinfo->vid,
>  						      vinfo->flags);
>  			else {
> -				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
> +				u16 flags = vinfo->flags |
> +					    BRIDGE_VLAN_INFO_MASTER;
>  				err = br_vlan_delete(br, vinfo->vid, flags);
>  			}
>  			break;
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index cc75212..9328463 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -179,6 +179,7 @@ struct net_bridge_port
>  	struct netpoll			*np;
>  #endif
>  	struct list_head		vlan_list;
> +	struct net_bridge_vlan __rcu	*untagged;
>  };
>  
>  #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
> @@ -298,6 +299,7 @@ struct net_bridge
>  	struct timer_list		gc_timer;
>  	struct kobject			*ifobj;
>  	struct hlist_head		vlan_hlist[BR_VID_HASH_SIZE];
> +	struct net_bridge_vlan __rcu	*untagged;
>  };
>  
>  struct br_input_skb_cb {
> -- 
> 1.7.7.6

^ permalink raw reply

* Re: [PATCH V2 09/12] bridge: Add the ability to configure untagged vlans
From: Michael S. Tsirkin @ 2012-12-18 23:04 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, shemminger, davem, or.gerlitz, jhs
In-Reply-To: <1355857263-31197-10-git-send-email-vyasevic@redhat.com>

On Tue, Dec 18, 2012 at 02:01:00PM -0500, Vlad Yasevich wrote:
> A user may designate a certain vlan as untagged.  This means that
> any ingress frame is assigned to this vlan and any forwarding decisions
> are made with this vlan in mind.  On egress, any frames tagged/labeled
> with untagged vlan have the vlan tag removed and are send as regular
> ethernet frames.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  include/uapi/linux/if_bridge.h |    3 +
>  net/bridge/br_if.c             |  146 +++++++++++++++++++++++++++++++++++++---
>  net/bridge/br_netlink.c        |    6 +-
>  net/bridge/br_private.h        |    2 +
>  4 files changed, 144 insertions(+), 13 deletions(-)
> 
> diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
> index d0b4f5c..988d858 100644
> --- a/include/uapi/linux/if_bridge.h
> +++ b/include/uapi/linux/if_bridge.h
> @@ -127,6 +127,9 @@ enum {
>  	BR_VLAN_DEL,
>  };
>  
> +#define BRIDGE_VLAN_INFO_MASTER		1
> +#define BRIDGE_VLAN_INFO_UNTAGGED	2
> +
>  struct bridge_vlan_info {
>  	u16 op_code;
>  	u16 flags;
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 57bbb35..14563fb 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -108,6 +108,34 @@ static void br_vlan_put(struct net_bridge_vlan *vlan)
>  		br_vlan_destroy(vlan);
>  }
>  
> +/* Must be protected by RTNL */
> +static void br_vlan_add_untagged(struct net_bridge *br,
> +				 struct net_bridge_vlan *vlan)
> +{
> +	ASSERT_RTNL();
> +	if (br->untagged == vlan)
> +		return;
> +	else if (br->untagged) {
> +		/* Untagged vlan is already set on the master,
> +		 * so drop the ref since we'll be replacing it.
> +		 */
> +		br_vlan_put(br->untagged);
> +	}
> +	br_vlan_hold(vlan);
> +	rcu_assign_pointer(br->untagged, vlan);
> +}
> +
> +/* Must be protected by RTNL */
> +static void br_vlan_del_untagged(struct net_bridge *br,
> +				 struct net_bridge_vlan *vlan)
> +{
> +	ASSERT_RTNL();
> +	if (br->untagged == vlan) {
> +		br_vlan_put(vlan);
> +		rcu_assign_pointer(br->untagged, NULL);
> +	}
> +}
> +
>  struct net_bridge_vlan *br_vlan_find(struct net_bridge *br, u16 vid)
>  {
>  	struct net_bridge_vlan *vlan;
> @@ -132,7 +160,7 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
>  
>  	vlan = br_vlan_find(br, vid);
>  	if (vlan)
> -		return vlan;
> +		goto untagged;
>  
>  	vlan = kzalloc(sizeof(struct net_bridge_vlan), GFP_KERNEL);
>  	if (!vlan)
> @@ -141,7 +169,7 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
>  	vlan->vid = vid;
>  	atomic_set(&vlan->refcnt, 1);
>  
> -	if (flags & BRIDGE_FLAGS_SELF) {
> +	if (flags & BRIDGE_VLAN_INFO_MASTER) {
>  		/* Set bit 0 that is associated with the bridge master
>  		 * device.  Port numbers start with 1.
>  		 */
> @@ -149,15 +177,24 @@ struct net_bridge_vlan *br_vlan_add(struct net_bridge *br, u16 vid,
>  	}
>  
>  	hlist_add_head_rcu(&vlan->hlist, &br->vlan_hlist[br_vlan_hash(vid)]);
> +
> +untagged:
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
> +		br_vlan_add_untagged(br, vlan);
> +
>  	return vlan;
>  }
>  
>  /* Must be protected by RTNL */
> -static void br_vlan_del(struct net_bridge_vlan *vlan, u16 flags)
> +static void br_vlan_del(struct net_bridge *br, struct net_bridge_vlan *vlan,
> +			u16 flags)
>  {
>  	ASSERT_RTNL();
>  
> -	if (flags & BRIDGE_FLAGS_SELF) {
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
> +		br_vlan_del_untagged(br, vlan);
> +
> +	if (flags & BRIDGE_VLAN_INFO_MASTER) {
>  		/* Clear bit 0 that is associated with the bridge master
>  		 * device.
>  		 */
> @@ -172,6 +209,14 @@ static void br_vlan_del(struct net_bridge_vlan *vlan, u16 flags)
>  
>  	vlan->vid = BR_INVALID_VID;
>  
> +	/* If, for whatever reason, bridge still has a ref on this vlan
> +	 * through the @untagged pointer, drop that ref and clear untagged.
> +	 */
> +	if (br->untagged == vlan) {
> +		br_vlan_put(vlan);
> +		rcu_assign_pointer(br->untagged, NULL);

Is something doing an rcu sync after this point?
If yes maybe add a comment saying where it is, if not - some rcu read
side could still be using it through this pointer.

> +	}
> +
>  	/* Drop the self-ref to trigger descrution. */
>  	br_vlan_put(vlan);
>  }
> @@ -187,7 +232,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid, u16 flags)
>  	if (!vlan)
>  		return -ENOENT;
>  
> -	br_vlan_del(vlan, flags);
> +	br_vlan_del(br, vlan, flags);
>  	return 0;
>  }
>  
> @@ -204,7 +249,9 @@ static void br_vlan_flush(struct net_bridge *br)
>  	for (i = 0; i < BR_VID_HASH_SIZE; i++) {
>  		hlist_for_each_entry_safe(vlan, node, tmp,
>  					  &br->vlan_hlist[i], hlist) {
> -			br_vlan_del(vlan, BRIDGE_FLAGS_SELF);
> +			br_vlan_del(br, vlan,
> +				    (BRIDGE_VLAN_INFO_MASTER |
> +				     BRIDGE_VLAN_INFO_UNTAGGED));
>  		}
>  	}
>  }
> @@ -224,10 +271,70 @@ struct net_port_vlan *nbp_vlan_find(const struct net_bridge_port *p, u16 vid)
>  	return NULL;
>  }
>  
> +static int nbp_vlan_add_untagged(struct net_bridge_port *p,
> +			  struct net_bridge_vlan *vlan,
> +			  u16 flags)
> +{
> +	struct net_device *dev = p->dev;
> +
> +	if (p->untagged) {
> +		/* Port already has untagged vlan set.  Drop the ref
> +		 * to the old one since we'll be replace it.
> +		 */
> +		br_vlan_put(p->untagged);
> +	} else {
> +		int err;
> +
> +		/* Add vid 0 to filter if filter is available. */
> +		if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
> +		    dev->netdev_ops->ndo_vlan_rx_add_vid &&
> +		    dev->netdev_ops->ndo_vlan_rx_kill_vid) {
> +			err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, 0);
> +			if (err)
> +				return err;
> +		}
> +	}
> +
> +	/* This VLAN is handled as untagged/native. Save an
> +	 * additional ref.
> +	 */
> +	br_vlan_hold(vlan);
> +	rcu_assign_pointer(p->untagged, vlan);
> +
> +	return 0;
> +}
> +
> +static void nbp_vlan_delete_untagged(struct net_bridge_port *p,
> +				     struct net_bridge_vlan *vlan)
> +{
> +	if (p->untagged != vlan)
> +		return;
> +
> +	/* Remove VLAN from the device filter if it is supported. */
> +	if ((p->dev->features & NETIF_F_HW_VLAN_FILTER) &&
> +	    p->dev->netdev_ops->ndo_vlan_rx_kill_vid) {
> +		int err;
> +
> +		err = p->dev->netdev_ops->ndo_vlan_rx_kill_vid(p->dev, 0);
> +		if (err) {
> +			pr_warn("failed to kill vid %d for device %s\n",
> +				vlan->vid, p->dev->name);
> +		}
> +	}
> +
> +	/* If this VLAN is currently functioning as untagged, clear it.
> +	 * It's safe to drop the refcount, since the vlan is still held
> +	 * by the port.
> +	 */
> +	br_vlan_put(vlan);
> +	rcu_assign_pointer(p->untagged, NULL);
> +
> +}
> +
>  /* Must be protected by RTNL */
>  int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
>  {
> -	struct net_port_vlan *pve;
> +	struct net_port_vlan *pve = NULL;
>  	struct net_bridge_vlan *vlan;
>  	struct net_device *dev = p->dev;
>  	int err;
> @@ -275,11 +382,21 @@ int nbp_vlan_add(struct net_bridge_port *p, u16 vid, u16 flags)
>  	set_bit(p->port_no, vlan->port_bitmap);
>  
>  	list_add_tail_rcu(&pve->list, &p->vlan_list);
> +
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
> +		err = nbp_vlan_add_untagged(p, vlan, flags);
> +		if (err)
> +			goto del_vlan;
> +	}
> +
>  	return 0;
>  
>  clean_up:
>  	kfree(pve);
> -	br_vlan_del(vlan, flags);
> +	br_vlan_del(p->br, vlan, flags);
> +	return err;
> +del_vlan:
> +	nbp_vlan_delete(p, vid, flags);
>  	return err;
>  }
>  
> @@ -296,6 +413,9 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
>  	if (!pve)
>  		return -ENOENT;
>  
> +	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
> +		nbp_vlan_delete_untagged(p, pve->vlan);
> +
>  	/* Remove VLAN from the device filter if it is supported. */
>  	if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
>  	    dev->netdev_ops->ndo_vlan_rx_kill_vid) {
> @@ -306,6 +426,7 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
>  			pr_warn("failed to kill vid %d for device %s\n",
>  				vid, dev->name);
>  	}
> +
>  	pve->vid = BR_INVALID_VID;
>  
>  	vlan = pve->vlan;
> @@ -316,7 +437,7 @@ int nbp_vlan_delete(struct net_bridge_port *p, u16 vid, u16 flags)
>  	list_del_rcu(&pve->list);
>  	kfree_rcu(pve, rcu);
>  
> -	br_vlan_del(vlan, flags);
> +	br_vlan_del(p->br, vlan, flags);
>  
>  	return 0;
>  }
> @@ -328,8 +449,11 @@ static void nbp_vlan_flush(struct net_bridge_port *p)
>  
>  	ASSERT_RTNL();
>  
> -	list_for_each_entry_safe(pve, tmp, &p->vlan_list, list)
> -		nbp_vlan_delete(p, pve->vid, BRIDGE_FLAGS_SELF);
> +	list_for_each_entry_safe(pve, tmp, &p->vlan_list, list)  {
> +		nbp_vlan_delete(p, pve->vid,
> +				(BRIDGE_VLAN_INFO_MASTER |
> +				 BRIDGE_VLAN_INFO_UNTAGGED));
> +	}
>  }
>  
>  static void release_nbp(struct kobject *kobj)
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 9cf2879..1b302ce 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -199,7 +199,8 @@ static int br_afspec(struct net_bridge *br, struct net_bridge_port *p,
>  			if (p)
>  				err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
>  			else {
> -				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
> +				u16 flags = vinfo->flags |
> +					    BRIDGE_VLAN_INFO_MASTER;
>  				if (!br_vlan_add(br, vinfo->vid, flags))
>  					err = -ENOMEM;
>  			}
> @@ -210,7 +211,8 @@ static int br_afspec(struct net_bridge *br, struct net_bridge_port *p,
>  				err = nbp_vlan_delete(p, vinfo->vid,
>  						      vinfo->flags);
>  			else {
> -				u16 flags = vinfo->flags | BRIDGE_FLAGS_SELF;
> +				u16 flags = vinfo->flags |
> +					    BRIDGE_VLAN_INFO_MASTER;
>  				err = br_vlan_delete(br, vinfo->vid, flags);
>  			}
>  			break;
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index cc75212..9328463 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -179,6 +179,7 @@ struct net_bridge_port
>  	struct netpoll			*np;
>  #endif
>  	struct list_head		vlan_list;
> +	struct net_bridge_vlan __rcu	*untagged;
>  };
>  
>  #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
> @@ -298,6 +299,7 @@ struct net_bridge
>  	struct timer_list		gc_timer;
>  	struct kobject			*ifobj;
>  	struct hlist_head		vlan_hlist[BR_VID_HASH_SIZE];
> +	struct net_bridge_vlan __rcu	*untagged;
>  };
>  
>  struct br_input_skb_cb {
> -- 
> 1.7.7.6

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox