netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v9 net-next 00/12] Add basic VLAN support to bridges
@ 2013-02-01 20:01 Vlad Yasevich
  2013-02-01 20:01 ` [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure Vlad Yasevich
                   ` (12 more replies)
  0 siblings, 13 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:01 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

Changes since v8:
* Unified vlans_to_* calls into a single interface
* Fixed the rest of the issues report by Michal Miroslaw
* Fixed a bug where fdb entries were not created for all added vlans.

Changes since v7:
* Rebases on the latest net-next and removed the vlan wrapper patch from
the series.
* Fixed a crash in br_fdb_add/br_fdb_delete.

Changes since v6:
* VLANs are now stored in a VLAN bitmap per port.  This allows for O(1)
lookup at ingress and egress.  We simply check to see if the bit associated
with the vlan id is set in the map.  The drawback to this approach is that
it wastes some space when there is only a small number of VLANs.
* In addition to the build time configuration option, VLAN filtering also has
a configuration paramter in sysfs.  By default the filtering is turned off
and all traffic is permitted.  When the filtring is turned on, we do strict
matching to the filter configured.  Thus, if there is no configuration, all
packets are rejected.  This was done to make the behavior more streight
forward.  Without this (and if egress policy patch is rejected), the
decision for how to forward untagged traffic that was not filtered at ingress
is almost impossible to make.  It would not be right to deliver to every
port that has PVID set as, each port may have a different PVID.
* Separate egress policy bitmap patch has been isolated and is provided last
in the series.  This has been a more contentious piece of functionality and I
wanted to isolate it so that it could easily be dropped and not block the whole
series.

Changes since v5:
 - Pulled VLAN filtering into its own file and made it a configuration options.
 - Made new vlan filtering option dependent on VLAN_8021Q.
 - Got rid of HW filter inlines and moved then vlan_core.c.
   (All of the above suggested by Stephen Hemminger)

Changes since v4:
 - Pull per-port vlan data into its own structures and give it to the bridge
   device thus making bridge device behave like a regular port for vlan
   configuration.
 - Add a per-vlan 'untagged' bitmap that determins egress policy.  If a port
   is part of this bitmap, traffic egresses untagged.
 - PVID is now used for ingress policy only.  Incomming frames without VLAN tag
   are assigned to the PVID vlan.  Egress is determined via bitmap memberships.
 - Allow for incremental config of a vlan.  Now, PVID and untagged memberships
   may be set on existing vlans.  They however can NOT be cleared separately.
 - VLAN deletion is now done via RTM_DELLINK command for PF_BRIDGE family.
   This cleans up the netlink interface.

Changes since v3:
 - Re-integrated compiler problems that got left out last time.  Appologies.
 - checkpatches.pl errors fixed

Changes since v2:
 - Added inline functiosn to manimulate vlan hw filters and re-use in 8021q
   and bridge code.
 - Use rtnl_dereference (Michael Tsirkin)
 - Remove synchronize_net() call (Eric Dumazet)
 - Fix NULL ptr deref bug I introduced in br_ifinfo_notify.

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: Add netlink interface to configure vlans on bridge ports
  bridge: Add the ability to configure pvid
  bridge: Implement vlan ingress/egress policy
  bridge: Add vlan to unicast fdb entries
  bridge: Add vlan id to multicast groups
  bridge: Add vlan support to static neighbors
  bridge: Add vlan support for local fdb entries
  bridge: Dump vlan information from a bridge port
  bridge: Separate egress policy bitmap

 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c |    5 +-
 drivers/net/macvlan.c                         |    2 +-
 drivers/net/vxlan.c                           |    3 +-
 include/linux/netdevice.h                     |    6 +-
 include/uapi/linux/if_bridge.h                |   13 +-
 include/uapi/linux/neighbour.h                |    1 +
 include/uapi/linux/rtnetlink.h                |    1 +
 net/8021q/vlan_core.c                         |    1 +
 net/bridge/Kconfig                            |   14 +
 net/bridge/Makefile                           |    2 +
 net/bridge/br_device.c                        |    7 +-
 net/bridge/br_fdb.c                           |  259 ++++++++++++---
 net/bridge/br_forward.c                       |    9 +
 net/bridge/br_if.c                            |    4 +-
 net/bridge/br_input.c                         |   28 ++-
 net/bridge/br_multicast.c                     |   69 +++--
 net/bridge/br_netlink.c                       |  239 ++++++++++++--
 net/bridge/br_private.h                       |  152 ++++++++-
 net/bridge/br_sysfs_br.c                      |   21 ++
 net/bridge/br_vlan.c                          |  444 +++++++++++++++++++++++++
 net/core/rtnetlink.c                          |  111 ++++++-
 21 files changed, 1262 insertions(+), 129 deletions(-)
 create mode 100644 net/bridge/br_vlan.c

-- 
1.7.7.6

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
@ 2013-02-01 20:01 ` Vlad Yasevich
  2013-02-02  1:04   ` Michał Mirosław
  2013-02-01 20:02 ` [PATCH v9 net-next 02/12] bridge: Validate that vlan is permitted on ingress Vlad Yasevich
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:01 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

Adds an optional infrustructure component to bridge that would allow
native vlan filtering in the bridge.  Each bridge port (as well
as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
is associated with a vlan id.  This way if the bit corresponding to
the vid is set in the bitmap that the packet with vid is allowed to
enter and exit the port.

Write access the bitmap is protected by RTNL and read access
protected by RCU.

Vlan functionality is disabled by default.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/Kconfig       |   14 +++
 net/bridge/Makefile      |    2 +
 net/bridge/br_if.c       |    1 +
 net/bridge/br_private.h  |   55 +++++++++++
 net/bridge/br_sysfs_br.c |   21 +++++
 net/bridge/br_vlan.c     |  223 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 316 insertions(+), 0 deletions(-)
 create mode 100644 net/bridge/br_vlan.c

diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig
index 6dee7bf..aa0d3b2 100644
--- a/net/bridge/Kconfig
+++ b/net/bridge/Kconfig
@@ -46,3 +46,17 @@ config BRIDGE_IGMP_SNOOPING
 	  Say N to exclude this support and reduce the binary size.
 
 	  If unsure, say Y.
+
+config BRIDGE_VLAN_FILTERING
+	bool "VLAN filtering"
+	depends on BRIDGE
+	depends on VLAN_8021Q
+	default n
+	---help---
+	  If you say Y here, then the Ethernet bridge will be able selectively
+	  receive and forward traffic based on VLAN information in the packet
+	  any VLAN information configured on the bridge port or bridge device.
+
+	  Say N to exclude this support and reduce the binary size.
+
+	  If unsure, say Y.
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index e859098..e85498b2f 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -14,4 +14,6 @@ bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
 
 bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
 
+bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
+
 obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 2148d47..af9d65a 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -139,6 +139,7 @@ static void del_nbp(struct net_bridge_port *p)
 
 	br_ifinfo_notify(RTM_DELLINK, p);
 
+	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
 
 	list_del_rcu(&p->list);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 711094a..1c1b2f1 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -18,6 +18,7 @@
 #include <linux/netpoll.h>
 #include <linux/u64_stats_sync.h>
 #include <net/route.h>
+#include <linux/if_vlan.h>
 
 #define BR_HASH_BITS 8
 #define BR_HASH_SIZE (1 << BR_HASH_BITS)
@@ -26,6 +27,8 @@
 
 #define BR_PORT_BITS	10
 #define BR_MAX_PORTS	(1<<BR_PORT_BITS)
+#define BR_VLAN_BITMAP_LEN	BITS_TO_LONGS(VLAN_N_VID)
+#define BR_INVALID_VID	(1<<15)
 
 #define BR_VERSION	"2.3"
 
@@ -63,6 +66,13 @@ struct br_ip
 	__be16		proto;
 };
 
+struct net_port_vlans {
+	u16				port_idx;
+	void				*parent;
+	struct rcu_head			rcu;
+	unsigned long			vlan_bitmap[BR_VLAN_BITMAP_LEN];
+};
+
 struct net_bridge_fdb_entry
 {
 	struct hlist_node		hlist;
@@ -156,6 +166,7 @@ struct net_bridge_port
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	struct netpoll			*np;
 #endif
+	struct net_port_vlans __rcu	*vlan_info;
 };
 
 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
@@ -260,6 +271,10 @@ struct net_bridge
 	struct timer_list		topology_change_timer;
 	struct timer_list		gc_timer;
 	struct kobject			*ifobj;
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+	u8				vlan_enabled;
+	struct net_port_vlans __rcu	*vlan_info;
+#endif
 };
 
 struct br_input_skb_cb {
@@ -534,6 +549,46 @@ static inline void br_mdb_uninit(void)
 }
 #endif
 
+/* br_vlan.c */
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+extern int br_vlan_add(struct net_bridge *br, u16 vid);
+extern int br_vlan_delete(struct net_bridge *br, u16 vid);
+extern void br_vlan_flush(struct net_bridge *br);
+extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
+extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid);
+extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
+extern void nbp_vlan_flush(struct net_bridge_port *port);
+#else
+static inline int br_vlan_add(struct net_bridge *br, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int br_vlan_delete(struct net_bridge *br, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void br_vlan_flush(struct net_bridge *br)
+{
+}
+
+static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void nbp_vlan_flush(struct net_bridge_port *port)
+{
+}
+
+#endif
+
 /* br_netfilter.c */
 #ifdef CONFIG_BRIDGE_NETFILTER
 extern int br_netfilter_init(void);
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 5913a3a..8baa9c0 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -691,6 +691,24 @@ static ssize_t store_nf_call_arptables(
 static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR,
 		   show_nf_call_arptables, store_nf_call_arptables);
 #endif
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+static ssize_t show_vlan_filtering(struct device *d,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct net_bridge *br = to_bridge(d);
+	return sprintf(buf, "%d\n", br->vlan_enabled);
+}
+
+static ssize_t store_vlan_filtering(struct device *d,
+				    struct device_attribute *attr,
+				    const char *buf, size_t len)
+{
+	return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
+}
+static DEVICE_ATTR(vlan_filtering, S_IRUGO | S_IWUSR,
+		   show_vlan_filtering, store_vlan_filtering);
+#endif
 
 static struct attribute *bridge_attrs[] = {
 	&dev_attr_forward_delay.attr,
@@ -732,6 +750,9 @@ static struct attribute *bridge_attrs[] = {
 	&dev_attr_nf_call_ip6tables.attr,
 	&dev_attr_nf_call_arptables.attr,
 #endif
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+	&dev_attr_vlan_filtering.attr,
+#endif
 	NULL
 };
 
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
new file mode 100644
index 0000000..8d9ded5
--- /dev/null
+++ b/net/bridge/br_vlan.c
@@ -0,0 +1,223 @@
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+
+#include "br_private.h"
+
+#define vlans_to_parent(type, pv) ((type *)(pv)->parent)
+
+static int __vlan_add(struct net_port_vlans *v, u16 vid)
+{
+	int err;
+
+	if (test_bit(vid, v->vlan_bitmap))
+		return -EEXIST;
+
+	if (v->port_idx && vid) {
+		struct net_device *dev =
+			vlans_to_parent(struct net_bridge_port, v)->dev;
+
+		/* Add VLAN to the device filter if it is supported.
+		 * Stricly speaking, this is not necessary now, since devices
+		 * are made promiscuous by the bridge, but if that ever changes
+		 * this code will allow tagged traffic to enter the bridge.
+		 */
+		if (dev->features & NETIF_F_HW_VLAN_FILTER) {
+			err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid);
+			if (err)
+				return err;
+		}
+	}
+
+	set_bit(vid, v->vlan_bitmap);
+	return 0;
+}
+
+static int __vlan_del(struct net_port_vlans *v, u16 vid)
+{
+	unsigned long first_bit;
+	unsigned long last_bit;
+
+	if (!test_bit(vid, v->vlan_bitmap))
+		return -EINVAL;
+
+	/* Check to see if any other vlans are in this table.  If this
+	 * is the last vlan, delete the whole structure.  If this is not the
+	 * last vlan, just clear the bit.
+	 */
+	first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+	last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+
+	if (v->port_idx && vid) {
+		struct net_device *dev =
+			vlans_to_parent(struct net_bridge, v)->dev;
+
+		if (dev->features & NETIF_F_HW_VLAN_FILTER)
+			dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
+	}
+
+	clear_bit(vid, v->vlan_bitmap);
+	if (first_bit == last_bit) {
+		if (v->port_idx) {
+			struct net_bridge_port *p =
+				vlans_to_parent(struct net_bridge_port, v);
+			rcu_assign_pointer(p->vlan_info, NULL);
+		} else {
+			struct net_bridge *br =
+				vlans_to_parent(struct net_bridge, v);
+			rcu_assign_pointer(br->vlan_info, NULL);
+		}
+		kfree_rcu(v, rcu);
+	}
+	return 0;
+}
+
+static void __vlan_flush(struct net_port_vlans *v)
+{
+	bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+	if (v->port_idx) {
+		struct net_bridge_port *p =
+				vlans_to_parent(struct net_bridge_port, v);
+		rcu_assign_pointer(p->vlan_info, NULL);
+	} else {
+		struct net_bridge *br =
+				vlans_to_parent(struct net_bridge, v);
+		rcu_assign_pointer(br->vlan_info, NULL);
+	}
+	kfree_rcu(v, rcu);
+}
+
+/* Must be protected by RTNL */
+int br_vlan_add(struct net_bridge *br, u16 vid)
+{
+	struct net_port_vlans *pv = NULL;
+	int err;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(br->vlan_info);
+	if (pv)
+		return __vlan_add(pv, vid);
+
+	/* Create port vlan infomration
+	 */
+	pv = kzalloc(sizeof(*pv), GFP_KERNEL);
+	if (!pv)
+		return -ENOMEM;
+
+	pv->parent = br;
+	err = __vlan_add(pv, vid);
+	if (err)
+		goto out;
+
+	rcu_assign_pointer(br->vlan_info, pv);
+	return 0;
+out:
+	kfree(pv);
+	return err;
+}
+
+/* Must be protected by RTNL */
+int br_vlan_delete(struct net_bridge *br, u16 vid)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(br->vlan_info);
+	if (!pv)
+		return -EINVAL;
+
+	__vlan_del(pv, vid);
+	return 0;
+}
+
+void br_vlan_flush(struct net_bridge *br)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(br->vlan_info);
+	if (!pv)
+		return;
+
+	__vlan_flush(pv);
+}
+
+int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
+{
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	if (br->vlan_enabled == val)
+		goto unlock;
+
+	br->vlan_enabled = val;
+
+unlock:
+	rtnl_unlock();
+	return 0;
+}
+
+/* Must be protected by RTNL */
+int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
+{
+	struct net_port_vlans *pv = NULL;
+	int err;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(port->vlan_info);
+	if (pv)
+		return __vlan_add(pv, vid);
+
+	/* Create port vlan infomration
+	 */
+	pv = kzalloc(sizeof(*pv), GFP_KERNEL);
+	if (!pv) {
+		err = -ENOMEM;
+		goto clean_up;
+	}
+
+	pv->port_idx = port->port_no;
+	pv->parent = port;
+	err = __vlan_add(pv, vid);
+	if (err)
+		goto clean_up;
+
+	rcu_assign_pointer(port->vlan_info, pv);
+	return 0;
+
+clean_up:
+	kfree(pv);
+	return err;
+}
+
+/* Must be protected by RTNL */
+int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(port->vlan_info);
+	if (!pv)
+		return -EINVAL;
+
+	return __vlan_del(pv, vid);
+}
+
+void nbp_vlan_flush(struct net_bridge_port *port)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(port->vlan_info);
+	if (!pv)
+		return;
+
+	__vlan_flush(pv);
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 02/12] bridge: Validate that vlan is permitted on ingress
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
  2013-02-01 20:01 ` [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-02  1:04   ` Michał Mirosław
  2013-02-01 20:02 ` [PATCH v9 net-next 03/12] bridge: Verify that a vlan is allowed to egress on give port Vlad Yasevich
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

When a frame arrives on a port or transmitted by the bridge,
if we have VLANs configured, validate that a given VLAN is allowed
to enter the bridge.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_device.c  |    3 ++
 net/bridge/br_input.c   |    4 +++
 net/bridge/br_private.h |   48 +++++++++++++++++++++++++++++++++++++++++++++++
 net/bridge/br_vlan.c    |   25 ++++++++++++++++++++++++
 4 files changed, 80 insertions(+), 0 deletions(-)

diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index e1bc090..92b5b18 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -45,6 +45,9 @@ 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);
 
+	if (!br_allowed_ingress(br, br_get_vlan_info(br), skb))
+		goto out;
+
 	BR_INPUT_SKB_CB(skb)->brdev = dev;
 
 	skb_reset_mac_header(skb);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 4b34207..4ef3f6b 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -17,6 +17,7 @@
 #include <linux/etherdevice.h>
 #include <linux/netfilter_bridge.h>
 #include <linux/export.h>
+#include <linux/rculist.h>
 #include "br_private.h"
 
 /* Hook for brouter */
@@ -54,6 +55,9 @@ int br_handle_frame_finish(struct sk_buff *skb)
 	if (!p || p->state == BR_STATE_DISABLED)
 		goto drop;
 
+	if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb))
+		goto drop;
+
 	/* insert into forwarding database after filtering to avoid spoofing */
 	br = p->br;
 	br_fdb_update(br, p, eth_hdr(skb)->h_source);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 1c1b2f1..6db6822 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -551,6 +551,8 @@ static inline void br_mdb_uninit(void)
 
 /* br_vlan.c */
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
+extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
+			       struct sk_buff *skb);
 extern int br_vlan_add(struct net_bridge *br, u16 vid);
 extern int br_vlan_delete(struct net_bridge *br, u16 vid);
 extern void br_vlan_flush(struct net_bridge *br);
@@ -558,7 +560,38 @@ extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
 extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid);
 extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 extern void nbp_vlan_flush(struct net_bridge_port *port);
+
+static inline struct net_port_vlans *br_get_vlan_info(
+						const struct net_bridge *br)
+{
+	return rcu_dereference(br->vlan_info);
+}
+
+static inline struct net_port_vlans *nbp_get_vlan_info(
+						const struct net_bridge_port *p)
+{
+	return rcu_dereference(p->vlan_info);
+}
+
+static inline u16 br_vlan_get_tag(const struct sk_buff *skb)
+{
+	u16 tag;
+
+	if (vlan_tx_tag_present(skb))
+		tag = vlan_tx_tag_get(skb);
+	else if (vlan_get_tag(skb, &tag))
+		tag = 0;
+
+	return tag & VLAN_VID_MASK;
+}
 #else
+static inline bool br_allowed_ingress(struct net_bridge *br,
+				      struct net_port_vlans *v,
+				      struct sk_buff *skb)
+{
+	return true;
+}
+
 static inline int br_vlan_add(struct net_bridge *br, u16 vid)
 {
 	return -EOPNOTSUPP;
@@ -587,6 +620,21 @@ static inline void nbp_vlan_flush(struct net_bridge_port *port)
 {
 }
 
+static inline struct net_port_vlans *br_get_vlan_info(
+						const struct net_bridge *br)
+{
+	return NULL;
+}
+static inline struct net_port_vlans *nbp_get_vlan_info(
+						const struct net_bridge_port *p)
+{
+	return NULL;
+}
+
+static inline u16 br_vlan_get_tag(const struct sk_buff *skb)
+{
+	return 0;
+}
 #endif
 
 /* br_netfilter.c */
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 8d9ded5..c3ae3f9 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -88,6 +88,31 @@ static void __vlan_flush(struct net_port_vlans *v)
 	kfree_rcu(v, rcu);
 }
 
+/* Called under RCU */
+bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
+			struct sk_buff *skb)
+{
+	u16 vid;
+
+	/* If VLAN filtering is disabled on the bridge, all packets are
+	 * permitted.
+	 */
+	if (!br->vlan_enabled)
+		return true;
+
+	/* If there are no vlan in the permitted list, all packets are
+	 * rejected.
+	 */
+	if (!v)
+		return false;
+
+	vid = br_vlan_get_tag(skb);
+	if (test_bit(vid, v->vlan_bitmap))
+		return true;
+
+	return false;
+}
+
 /* Must be protected by RTNL */
 int br_vlan_add(struct net_bridge *br, u16 vid)
 {
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 03/12] bridge: Verify that a vlan is allowed to egress on give port
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
  2013-02-01 20:01 ` [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 02/12] bridge: Validate that vlan is permitted on ingress Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 04/12] bridge: Add netlink interface to configure vlans on bridge ports Vlad Yasevich
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

When bridge forwards a frame, make sure that a frame is allowed
to egress on that port.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_forward.c |    1 +
 net/bridge/br_input.c   |   10 ++++++++++
 net/bridge/br_private.h |   10 ++++++++++
 net/bridge/br_vlan.c    |   20 ++++++++++++++++++++
 4 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 02015a5..35b0671 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -31,6 +31,7 @@ static inline int should_deliver(const struct net_bridge_port *p,
 				 const struct sk_buff *skb)
 {
 	return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
+		br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) &&
 		p->state == BR_STATE_FORWARDING);
 }
 
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 4ef3f6b..787d7da 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -35,6 +35,16 @@ static int br_pass_frame_up(struct sk_buff *skb)
 	brstats->rx_bytes += skb->len;
 	u64_stats_update_end(&brstats->syncp);
 
+	/* Bridge is just like any other port.  Make sure the
+	 * packet is allowed except in promisc modue when someone
+	 * may be running packet capture.
+	 */
+	if (!(brdev->flags & IFF_PROMISC) &&
+	    !br_allowed_egress(br, br_get_vlan_info(br), skb)) {
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+
 	indev = skb->dev;
 	skb->dev = brdev;
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 6db6822..5e7bc90 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -553,6 +553,9 @@ static inline void br_mdb_uninit(void)
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
 extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 			       struct sk_buff *skb);
+extern bool br_allowed_egress(struct net_bridge *br,
+			      const struct net_port_vlans *v,
+			      const struct sk_buff *skb);
 extern int br_vlan_add(struct net_bridge *br, u16 vid);
 extern int br_vlan_delete(struct net_bridge *br, u16 vid);
 extern void br_vlan_flush(struct net_bridge *br);
@@ -592,6 +595,13 @@ static inline bool br_allowed_ingress(struct net_bridge *br,
 	return true;
 }
 
+static inline bool br_allowed_egress(struct net_bridge *br,
+				     const struct net_port_vlans *v,
+				     const struct sk_buff *skb)
+{
+	return true;
+}
+
 static inline int br_vlan_add(struct net_bridge *br, u16 vid)
 {
 	return -EOPNOTSUPP;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index c3ae3f9..a291bca 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -113,6 +113,26 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 	return false;
 }
 
+/* Called under RCU. */
+bool br_allowed_egress(struct net_bridge *br,
+		       const struct net_port_vlans *v,
+		       const struct sk_buff *skb)
+{
+	u16 vid;
+
+	if (!br->vlan_enabled)
+		return true;
+
+	if (!v)
+		return false;
+
+	vid = br_vlan_get_tag(skb);
+	if (test_bit(vid, v->vlan_bitmap))
+		return true;
+
+	return false;
+}
+
 /* Must be protected by RTNL */
 int br_vlan_add(struct net_bridge *br, u16 vid)
 {
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 04/12] bridge: Add netlink interface to configure vlans on bridge ports
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (2 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 03/12] bridge: Verify that a vlan is allowed to egress on give port Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid Vlad Yasevich
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

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/linux/netdevice.h      |    2 +
 include/uapi/linux/if_bridge.h |    9 +++
 net/bridge/br_device.c         |    1 +
 net/bridge/br_if.c             |    1 +
 net/bridge/br_netlink.c        |  139 ++++++++++++++++++++++++++++++++++-----
 net/bridge/br_private.h        |    1 +
 net/core/rtnetlink.c           |   72 +++++++++++++++++++++
 7 files changed, 207 insertions(+), 18 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 85b0949..51553c7 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1020,6 +1020,8 @@ struct net_device_ops {
 	int			(*ndo_bridge_getlink)(struct sk_buff *skb,
 						      u32 pid, u32 seq,
 						      struct net_device *dev);
+	int			(*ndo_bridge_dellink)(struct net_device *dev,
+						      struct nlmsghdr *nlh);
 	int			(*ndo_change_carrier)(struct net_device *dev,
 						      bool new_carrier);
 };
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 5db2975..3ca9817 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -108,15 +108,24 @@ 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)
 
+#define BRIDGE_VLAN_INFO_MASTER	(1<<0)	/* Operate on Bridge device as well */
+
+struct bridge_vlan_info {
+	u16 flags;
+	u16 vid;
+};
+
 /* Bridge multicast database attributes
  * [MDBA_MDB] = {
  *     [MDBA_MDB_ENTRY] = {
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 92b5b18..d325c2d 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -317,6 +317,7 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_fdb_dump		 = br_fdb_dump,
 	.ndo_bridge_getlink	 = br_getlink,
 	.ndo_bridge_setlink	 = br_setlink,
+	.ndo_bridge_dellink	 = br_dellink,
 };
 
 static void br_dev_free(struct net_device *dev)
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index af9d65a..335c60c 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 39ca979..534a9f4 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"
@@ -119,10 +120,14 @@ nla_put_failure:
  */
 void br_ifinfo_notify(int event, struct net_bridge_port *port)
 {
-	struct net *net = dev_net(port->dev);
+	struct net *net;
 	struct sk_buff *skb;
 	int err = -ENOBUFS;
 
+	if (!port)
+		return;
+
+	net = dev_net(port->dev);
 	br_debug(port->br, "port %u(%s) event %d\n",
 		 (unsigned int)port->port_no, port->dev->name, event);
 
@@ -144,6 +149,7 @@ errout:
 		rtnl_set_sk_err(net, RTNLGRP_LINK, err);
 }
 
+
 /*
  * Dump information about all ports, in response to GETLINK
  */
@@ -162,6 +168,64 @@ 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,
+		     int cmd)
+{
+	struct nlattr *tb[IFLA_BRIDGE_MAX+1];
+	int err = 0;
+
+	err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy);
+	if (err)
+		return err;
+
+	if (tb[IFLA_BRIDGE_VLAN_INFO]) {
+		struct bridge_vlan_info *vinfo;
+
+		vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
+
+		if (vinfo->vid >= VLAN_N_VID)
+			return -EINVAL;
+
+		switch (cmd) {
+		case RTM_SETLINK:
+			if (p) {
+				err = nbp_vlan_add(p, vinfo->vid);
+				if (err)
+					break;
+
+				if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
+					err = br_vlan_add(p->br, vinfo->vid);
+			} else
+				err = br_vlan_add(br, vinfo->vid);
+
+			if (err)
+				break;
+
+			break;
+
+		case RTM_DELLINK:
+			if (p) {
+				nbp_vlan_delete(p, vinfo->vid);
+				if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
+					br_vlan_delete(p->br, vinfo->vid);
+			} else
+				br_vlan_delete(br, vinfo->vid);
+			break;
+		}
+	}
+
+	return err;
+}
+
 static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = {
 	[IFLA_BRPORT_STATE]	= { .type = NLA_U8 },
 	[IFLA_BRPORT_COST]	= { .type = NLA_U32 },
@@ -241,6 +305,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;
@@ -248,38 +313,76 @@ 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, RTM_SETLINK);
 	}
 
 	if (err == 0)
 		br_ifinfo_notify(RTM_NEWLINK, p);
 
+out:
 	return err;
 }
 
+/* Delete port information */
+int br_dellink(struct net_device *dev, struct nlmsghdr *nlh)
+{
+	struct ifinfomsg *ifm;
+	struct nlattr *afspec;
+	struct net_bridge_port *p;
+	int err;
+
+	ifm = nlmsg_data(nlh);
+
+	afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
+	if (!afspec)
+		return 0;
+
+	p = br_port_get_rtnl(dev);
+	/* We want to accept dev as bridge itself as well */
+	if (!p && !(dev->priv_flags & IFF_EBRIDGE))
+		return -EINVAL;
+
+	err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
+			afspec, RTM_DELLINK);
+
+	return err;
+}
 static int br_validate(struct nlattr *tb[], struct nlattr *data[])
 {
 	if (tb[IFLA_ADDRESS]) {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 5e7bc90..801a707 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -707,6 +707,7 @@ extern int br_netlink_init(void);
 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_dellink(struct net_device *dev, struct nlmsghdr *nlmsg);
 extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
 		      struct net_device *dev);
 
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 9a419b0..1aba89f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2470,6 +2470,77 @@ out:
 	return err;
 }
 
+static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
+			       void *arg)
+{
+	struct net *net = sock_net(skb->sk);
+	struct ifinfomsg *ifm;
+	struct net_device *dev;
+	struct nlattr *br_spec, *attr = NULL;
+	int rem, err = -EOPNOTSUPP;
+	u16 oflags, flags = 0;
+	bool have_flags = false;
+
+	if (nlmsg_len(nlh) < sizeof(*ifm))
+		return -EINVAL;
+
+	ifm = nlmsg_data(nlh);
+	if (ifm->ifi_family != AF_BRIDGE)
+		return -EPFNOSUPPORT;
+
+	dev = __dev_get_by_index(net, ifm->ifi_index);
+	if (!dev) {
+		pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n");
+		return -ENODEV;
+	}
+
+	br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
+	if (br_spec) {
+		nla_for_each_nested(attr, br_spec, rem) {
+			if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+				have_flags = true;
+				flags = nla_get_u16(attr);
+				break;
+			}
+		}
+	}
+
+	oflags = flags;
+
+	if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
+		struct net_device *br_dev = netdev_master_upper_dev_get(dev);
+
+		if (!br_dev || !br_dev->netdev_ops->ndo_bridge_dellink) {
+			err = -EOPNOTSUPP;
+			goto out;
+		}
+
+		err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
+		if (err)
+			goto out;
+
+		flags &= ~BRIDGE_FLAGS_MASTER;
+	}
+
+	if ((flags & BRIDGE_FLAGS_SELF)) {
+		if (!dev->netdev_ops->ndo_bridge_dellink)
+			err = -EOPNOTSUPP;
+		else
+			err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
+
+		if (!err)
+			flags &= ~BRIDGE_FLAGS_SELF;
+	}
+
+	if (have_flags)
+		memcpy(nla_data(attr), &flags, sizeof(flags));
+	/* Generate event to notify upper layer of bridge change */
+	if (!err)
+		err = rtnl_bridge_notify(dev, oflags);
+out:
+	return err;
+}
+
 /* Protected by RTNL sempahore.  */
 static struct rtattr **rta_buf;
 static int rtattr_max;
@@ -2653,6 +2724,7 @@ void __init rtnetlink_init(void)
 	rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL);
 
 	rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL);
+	rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL);
 	rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
 }
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (3 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 04/12] bridge: Add netlink interface to configure vlans on bridge ports Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-02  1:04   ` Michał Mirosław
  2013-02-01 20:02 ` [PATCH v9 net-next 06/12] bridge: Implement vlan ingress/egress policy Vlad Yasevich
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

A user may designate a certain vlan as PVID.  This means that
any ingress frame that does not contain a vlan tag is assigned to
this vlan and any forwarding decisions are made with this vlan in mind.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/uapi/linux/if_bridge.h |    1 +
 net/bridge/br_netlink.c        |    7 +++--
 net/bridge/br_private.h        |    9 ++++---
 net/bridge/br_vlan.c           |   49 +++++++++++++++++++++++++++++++--------
 4 files changed, 49 insertions(+), 17 deletions(-)

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 3ca9817..c6c30e2 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -120,6 +120,7 @@ enum {
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
 
 #define BRIDGE_VLAN_INFO_MASTER	(1<<0)	/* Operate on Bridge device as well */
+#define BRIDGE_VLAN_INFO_PVID	(1<<1)	/* VLAN is PVID, ingress untagged */
 
 struct bridge_vlan_info {
 	u16 flags;
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 534a9f4..0089a3f 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -198,14 +198,15 @@ static int br_afspec(struct net_bridge *br,
 		switch (cmd) {
 		case RTM_SETLINK:
 			if (p) {
-				err = nbp_vlan_add(p, vinfo->vid);
+				err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
 				if (err)
 					break;
 
 				if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
-					err = br_vlan_add(p->br, vinfo->vid);
+					err = br_vlan_add(p->br, vinfo->vid,
+							  vinfo->flags);
 			} else
-				err = br_vlan_add(br, vinfo->vid);
+				err = br_vlan_add(br, vinfo->vid, vinfo->flags);
 
 			if (err)
 				break;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 801a707..7ad26f5 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -68,6 +68,7 @@ struct br_ip
 
 struct net_port_vlans {
 	u16				port_idx;
+	u16				pvid;
 	void				*parent;
 	struct rcu_head			rcu;
 	unsigned long			vlan_bitmap[BR_VLAN_BITMAP_LEN];
@@ -556,11 +557,11 @@ extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 extern bool br_allowed_egress(struct net_bridge *br,
 			      const struct net_port_vlans *v,
 			      const struct sk_buff *skb);
-extern int br_vlan_add(struct net_bridge *br, u16 vid);
+extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
 extern int br_vlan_delete(struct net_bridge *br, u16 vid);
 extern void br_vlan_flush(struct net_bridge *br);
 extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
-extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid);
+extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
 extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 extern void nbp_vlan_flush(struct net_bridge_port *port);
 
@@ -602,7 +603,7 @@ static inline bool br_allowed_egress(struct net_bridge *br,
 	return true;
 }
 
-static inline int br_vlan_add(struct net_bridge *br, u16 vid)
+static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
 {
 	return -EOPNOTSUPP;
 }
@@ -616,7 +617,7 @@ static inline void br_vlan_flush(struct net_bridge *br)
 {
 }
 
-static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
+static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index a291bca..b5b6265 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -7,12 +7,33 @@
 
 #define vlans_to_parent(type, pv) ((type *)(pv)->parent)
 
-static int __vlan_add(struct net_port_vlans *v, u16 vid)
+static void __vlan_add_pvid(struct net_port_vlans *v, u16 vid)
+{
+	if (v->pvid == vid)
+		return;
+
+	smp_wmb();
+	v->pvid = vid;
+}
+
+static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid)
+{
+	if (v->pvid != vid)
+		return;
+
+	smp_wmb();
+	v->pvid = BR_INVALID_VID;
+}
+
+static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 {
 	int err;
 
-	if (test_bit(vid, v->vlan_bitmap))
-		return -EEXIST;
+	if (test_bit(vid, v->vlan_bitmap)) {
+		if (flags & BRIDGE_VLAN_INFO_PVID)
+			__vlan_add_pvid(v, vid);
+		return 0;
+	}
 
 	if (v->port_idx && vid) {
 		struct net_device *dev =
@@ -31,6 +52,9 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid)
 	}
 
 	set_bit(vid, v->vlan_bitmap);
+	if (flags & BRIDGE_VLAN_INFO_PVID)
+		__vlan_add_pvid(v, vid);
+
 	return 0;
 }
 
@@ -42,6 +66,8 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
 	if (!test_bit(vid, v->vlan_bitmap))
 		return -EINVAL;
 
+	__vlan_delete_pvid(v, vid);
+
 	/* Check to see if any other vlans are in this table.  If this
 	 * is the last vlan, delete the whole structure.  If this is not the
 	 * last vlan, just clear the bit.
@@ -75,6 +101,8 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
 
 static void __vlan_flush(struct net_port_vlans *v)
 {
+	smp_wmb();
+	v->pvid = BR_INVALID_VID;
 	bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
 	if (v->port_idx) {
 		struct net_bridge_port *p =
@@ -134,7 +162,7 @@ bool br_allowed_egress(struct net_bridge *br,
 }
 
 /* Must be protected by RTNL */
-int br_vlan_add(struct net_bridge *br, u16 vid)
+int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
 {
 	struct net_port_vlans *pv = NULL;
 	int err;
@@ -143,7 +171,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid)
 
 	pv = rtnl_dereference(br->vlan_info);
 	if (pv)
-		return __vlan_add(pv, vid);
+		return __vlan_add(pv, vid, flags);
 
 	/* Create port vlan infomration
 	 */
@@ -152,7 +180,8 @@ int br_vlan_add(struct net_bridge *br, u16 vid)
 		return -ENOMEM;
 
 	pv->parent = br;
-	err = __vlan_add(pv, vid);
+	pv->pvid = BR_INVALID_VID;
+	err = __vlan_add(pv, vid, flags);
 	if (err)
 		goto out;
 
@@ -183,7 +212,6 @@ void br_vlan_flush(struct net_bridge *br)
 	struct net_port_vlans *pv;
 
 	ASSERT_RTNL();
-
 	pv = rtnl_dereference(br->vlan_info);
 	if (!pv)
 		return;
@@ -207,7 +235,7 @@ unlock:
 }
 
 /* Must be protected by RTNL */
-int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
+int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 {
 	struct net_port_vlans *pv = NULL;
 	int err;
@@ -216,7 +244,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
 
 	pv = rtnl_dereference(port->vlan_info);
 	if (pv)
-		return __vlan_add(pv, vid);
+		return __vlan_add(pv, vid, flags);
 
 	/* Create port vlan infomration
 	 */
@@ -228,7 +256,8 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
 
 	pv->port_idx = port->port_no;
 	pv->parent = port;
-	err = __vlan_add(pv, vid);
+	pv->pvid = BR_INVALID_VID;
+	err = __vlan_add(pv, vid, flags);
 	if (err)
 		goto clean_up;
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 06/12] bridge: Implement vlan ingress/egress policy
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (4 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 07/12] bridge: Add vlan to unicast fdb entries Vlad Yasevich
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

This patch implements the ingress/egress policy.  At ingress,
any untagged traffic is assigned to the PVID.  Any tagged
traffic is filtered according to membership bitmap.

At egress, if the vlan matches the pvid, the frame is sent
untagged.  Otherwise the frame is sent tagged.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/8021q/vlan_core.c   |    1 +
 net/bridge/br_device.c  |    3 +-
 net/bridge/br_forward.c |    8 ++++
 net/bridge/br_input.c   |    7 +++-
 net/bridge/br_private.h |   31 +++++++++++-----
 net/bridge/br_vlan.c    |   93 +++++++++++++++++++++++++++++++++++++++++++----
 6 files changed, 125 insertions(+), 18 deletions(-)

diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c
index 71b64fd..f3b6f51 100644
--- a/net/8021q/vlan_core.c
+++ b/net/8021q/vlan_core.c
@@ -144,6 +144,7 @@ err_free:
 	kfree_skb(skb);
 	return NULL;
 }
+EXPORT_SYMBOL(vlan_untag);
 
 
 /*
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index d325c2d..bce7a52 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -30,6 +30,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	struct net_bridge_fdb_entry *dst;
 	struct net_bridge_mdb_entry *mdst;
 	struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
+	u16 vid = 0;
 
 	rcu_read_lock();
 #ifdef CONFIG_BRIDGE_NETFILTER
@@ -45,7 +46,7 @@ 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);
 
-	if (!br_allowed_ingress(br, br_get_vlan_info(br), skb))
+	if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid))
 		goto out;
 
 	BR_INPUT_SKB_CB(skb)->brdev = dev;
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 35b0671..092b20e 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -64,6 +64,10 @@ int br_forward_finish(struct sk_buff *skb)
 
 static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
+	skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
+	if (!skb)
+		return;
+
 	skb->dev = to->dev;
 
 	if (unlikely(netpoll_tx_running(to->br->dev))) {
@@ -89,6 +93,10 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 		return;
 	}
 
+	skb = br_handle_vlan(to->br, nbp_get_vlan_info(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 787d7da..a63f227 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -45,6 +45,10 @@ static int br_pass_frame_up(struct sk_buff *skb)
 		return NET_RX_DROP;
 	}
 
+	skb = br_handle_vlan(br, br_get_vlan_info(br), skb);
+	if (!skb)
+		return NET_RX_DROP;
+
 	indev = skb->dev;
 	skb->dev = brdev;
 
@@ -61,11 +65,12 @@ 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 = 0;
 
 	if (!p || p->state == BR_STATE_DISABLED)
 		goto drop;
 
-	if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb))
+	if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid))
 		goto drop;
 
 	/* insert into forwarding database after filtering to avoid spoofing */
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 7ad26f5..907b5df 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -553,10 +553,13 @@ static inline void br_mdb_uninit(void)
 /* br_vlan.c */
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
 extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
-			       struct sk_buff *skb);
+			       struct sk_buff *skb, u16 *vid);
 extern bool br_allowed_egress(struct net_bridge *br,
 			      const struct net_port_vlans *v,
 			      const struct sk_buff *skb);
+extern struct sk_buff *br_handle_vlan(struct net_bridge *br,
+				      const struct net_port_vlans *v,
+				      struct sk_buff *skb);
 extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
 extern int br_vlan_delete(struct net_bridge *br, u16 vid);
 extern void br_vlan_flush(struct net_bridge *br);
@@ -577,21 +580,24 @@ static inline struct net_port_vlans *nbp_get_vlan_info(
 	return rcu_dereference(p->vlan_info);
 }
 
-static inline u16 br_vlan_get_tag(const struct sk_buff *skb)
+
+static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *tag)
 {
-	u16 tag;
+	int rc = 0;
 
 	if (vlan_tx_tag_present(skb))
-		tag = vlan_tx_tag_get(skb);
-	else if (vlan_get_tag(skb, &tag))
-		tag = 0;
+		*tag = vlan_tx_tag_get(skb);
+	else
+		rc = vlan_get_tag(skb, tag);
 
-	return tag & VLAN_VID_MASK;
+	*tag = *tag & VLAN_VID_MASK;
+	return rc;
 }
 #else
 static inline bool br_allowed_ingress(struct net_bridge *br,
 				      struct net_port_vlans *v,
-				      struct sk_buff *skb)
+				      struct sk_buff *skb,
+				      u16 *vid)
 {
 	return true;
 }
@@ -603,6 +609,13 @@ static inline bool br_allowed_egress(struct net_bridge *br,
 	return true;
 }
 
+static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
+					     const struct net_port_vlans *v,
+					     struct sk_buff *skb)
+{
+	return skb;
+}
+
 static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
 {
 	return -EOPNOTSUPP;
@@ -642,7 +655,7 @@ static inline struct net_port_vlans *nbp_get_vlan_info(
 	return NULL;
 }
 
-static inline u16 br_vlan_get_tag(const struct sk_buff *skb)
+static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag)
 {
 	return 0;
 }
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index b5b6265..d4c4630 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -116,12 +116,74 @@ static void __vlan_flush(struct net_port_vlans *v)
 	kfree_rcu(v, rcu);
 }
 
+static inline u16 br_get_pvid(const struct net_port_vlans *v)
+{
+	smp_rmb();
+	return v->pvid;
+}
+
+/* Strip the tag from the packet.  Will return skb with tci set 0.  */
+static struct sk_buff *br_vlan_untag(struct sk_buff *skb)
+{
+	if (skb->protocol != htons(ETH_P_8021Q)) {
+		skb->vlan_tci = 0;
+		return skb;
+	}
+
+	skb->vlan_tci = 0;
+	skb = vlan_untag(skb);
+	if (skb)
+		skb->vlan_tci = 0;
+
+	return skb;
+}
+
+struct sk_buff *br_handle_vlan(struct net_bridge *br,
+			       const struct net_port_vlans *pv,
+			       struct sk_buff *skb)
+{
+	u16 vid = 0;
+
+	if (!br->vlan_enabled)
+		goto out;
+
+	/* At this point, we know that the frame was filtered and contains
+	 * a valid vlan id.  If the vlan id matches the pvid of current port
+	 * send untagged; otherwise, send taged.
+	 */
+	br_vlan_get_tag(skb, &vid);
+	if (vid == br_get_pvid(pv))
+		skb = br_vlan_untag(skb);
+	else {
+		/* Egress policy says "send tagged".  If output device
+		 * is the  bridge, we need to add the VLAN header
+		 * ourselves since we'll be going through the RX path.
+		 * Sending to ports puts the frame on the TX path and
+		 * we let dev_hard_start_xmit() add the header.
+		 */
+		if (skb->protocol != htons(ETH_P_8021Q) &&
+		    pv->port_idx == 0) {
+			/* 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;
+		}
+	}
+
+out:
+	return skb;
+}
+
 /* Called under RCU */
 bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
-			struct sk_buff *skb)
+			struct sk_buff *skb, u16 *vid)
 {
-	u16 vid;
-
 	/* If VLAN filtering is disabled on the bridge, all packets are
 	 * permitted.
 	 */
@@ -134,8 +196,25 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 	if (!v)
 		return false;
 
-	vid = br_vlan_get_tag(skb);
-	if (test_bit(vid, v->vlan_bitmap))
+	if (br_vlan_get_tag(skb, vid)) {
+		u16 pvid = br_get_pvid(v);
+
+		/* Frame did not have a tag.  See if pvid is set
+		 * on this port.  That tells us which vlan untagged
+		 * traffic belongs to.
+		 */
+		if (pvid == BR_INVALID_VID)
+			return false;
+
+		/* PVID is set on this port.  Any untagged ingress
+		 * frame is considered to belong to this vlan.
+		 */
+		__vlan_hwaccel_put_tag(skb, pvid);
+		return true;
+	}
+
+	/* Frame had a valid vlan tag.  See if vlan is allowed */
+	if (test_bit(*vid, v->vlan_bitmap))
 		return true;
 
 	return false;
@@ -146,7 +225,7 @@ bool br_allowed_egress(struct net_bridge *br,
 		       const struct net_port_vlans *v,
 		       const struct sk_buff *skb)
 {
-	u16 vid;
+	u16 vid = 0;
 
 	if (!br->vlan_enabled)
 		return true;
@@ -154,7 +233,7 @@ bool br_allowed_egress(struct net_bridge *br,
 	if (!v)
 		return false;
 
-	vid = br_vlan_get_tag(skb);
+	br_vlan_get_tag(skb, &vid);
 	if (test_bit(vid, v->vlan_bitmap))
 		return true;
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 07/12] bridge: Add vlan to unicast fdb entries
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (5 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 06/12] bridge: Implement vlan ingress/egress policy Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 08/12] bridge: Add vlan id to multicast groups Vlad Yasevich
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

This patch adds vlan to unicast fdb entries that are created for
learned addresses (not the manually configured ones).  It adds
vlan id into the hash mix and uses vlan as an addditional parameter
for an entry match.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/uapi/linux/if_bridge.h |    2 +-
 net/bridge/br_device.c         |    2 +-
 net/bridge/br_fdb.c            |   70 +++++++++++++++++++++++----------------
 net/bridge/br_input.c          |    9 +++--
 net/bridge/br_private.h        |    7 +++-
 5 files changed, 54 insertions(+), 36 deletions(-)

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index c6c30e2..875c9e2 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -94,7 +94,7 @@ struct __fdb_entry {
 	__u32 ageing_timer_value;
 	__u8 port_hi;
 	__u8 pad0;
-	__u16 unused;
+	__u16 fdb_vid;
 };
 
 /* Bridge Flags */
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index bce7a52..d7bb57b 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -71,7 +71,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 			br_multicast_deliver(mdst, skb);
 		else
 			br_flood_deliver(br, skb);
-	} else if ((dst = __br_fdb_get(br, dest)) != NULL)
+	} else if ((dst = __br_fdb_get(br, dest, vid)) != NULL)
 		br_deliver(dst->dst, skb);
 	else
 		br_flood_deliver(br, skb);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index d9576e6..a244efc 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/atomic.h>
 #include <asm/unaligned.h>
+#include <linux/if_vlan.h>
 #include "br_private.h"
 
 static struct kmem_cache *br_fdb_cache __read_mostly;
@@ -67,11 +68,11 @@ static inline int has_expired(const struct net_bridge *br,
 		time_before_eq(fdb->updated + hold_time(br), jiffies);
 }
 
-static inline int br_mac_hash(const unsigned char *mac)
+static inline int br_mac_hash(const unsigned char *mac, __u16 vid)
 {
-	/* use 1 byte of OUI cnd 3 bytes of NIC */
+	/* use 1 byte of OUI and 3 bytes of NIC */
 	u32 key = get_unaligned((u32 *)(mac + 2));
-	return jhash_1word(key, fdb_salt) & (BR_HASH_SIZE - 1);
+	return jhash_2words(key, vid, fdb_salt) & (BR_HASH_SIZE - 1);
 }
 
 static void fdb_rcu_free(struct rcu_head *head)
@@ -132,7 +133,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
 	struct net_bridge_fdb_entry *f;
 
 	/* If old entry was unassociated with any port, then delete it. */
-	f = __br_fdb_get(br, br->dev->dev_addr);
+	f = __br_fdb_get(br, br->dev->dev_addr, 0);
 	if (f && f->is_local && !f->dst)
 		fdb_delete(br, f);
 
@@ -231,13 +232,16 @@ void br_fdb_delete_by_port(struct net_bridge *br,
 
 /* No locking or refcounting, assumes caller has rcu_read_lock */
 struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
-					  const unsigned char *addr)
+					  const unsigned char *addr,
+					  __u16 vid)
 {
 	struct hlist_node *h;
 	struct net_bridge_fdb_entry *fdb;
 
-	hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) {
-		if (ether_addr_equal(fdb->addr.addr, addr)) {
+	hlist_for_each_entry_rcu(fdb, h,
+				&br->hash[br_mac_hash(addr, vid)], hlist) {
+		if (ether_addr_equal(fdb->addr.addr, addr) &&
+		    fdb->vlan_id == vid) {
 			if (unlikely(has_expired(br, fdb)))
 				break;
 			return fdb;
@@ -261,7 +265,7 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
 	if (!port)
 		ret = 0;
 	else {
-		fdb = __br_fdb_get(port->br, addr);
+		fdb = __br_fdb_get(port->br, addr, 0);
 		ret = fdb && fdb->dst && fdb->dst->dev != dev &&
 			fdb->dst->state == BR_STATE_FORWARDING;
 	}
@@ -313,6 +317,7 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
 			fe->is_local = f->is_local;
 			if (!f->is_static)
 				fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated);
+			fe->fdb_vid = f->vlan_id;
 			++fe;
 			++num;
 		}
@@ -325,26 +330,30 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
 }
 
 static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
-					     const unsigned char *addr)
+					     const unsigned char *addr,
+					     __u16 vid)
 {
 	struct hlist_node *h;
 	struct net_bridge_fdb_entry *fdb;
 
 	hlist_for_each_entry(fdb, h, head, hlist) {
-		if (ether_addr_equal(fdb->addr.addr, addr))
+		if (ether_addr_equal(fdb->addr.addr, addr) &&
+		    fdb->vlan_id == vid)
 			return fdb;
 	}
 	return NULL;
 }
 
 static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head,
-						 const unsigned char *addr)
+						 const unsigned char *addr,
+						 __u16 vid)
 {
 	struct hlist_node *h;
 	struct net_bridge_fdb_entry *fdb;
 
 	hlist_for_each_entry_rcu(fdb, h, head, hlist) {
-		if (ether_addr_equal(fdb->addr.addr, addr))
+		if (ether_addr_equal(fdb->addr.addr, addr) &&
+		    fdb->vlan_id == vid)
 			return fdb;
 	}
 	return NULL;
@@ -352,7 +361,8 @@ static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head,
 
 static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
 					       struct net_bridge_port *source,
-					       const unsigned char *addr)
+					       const unsigned char *addr,
+					       __u16 vid)
 {
 	struct net_bridge_fdb_entry *fdb;
 
@@ -360,6 +370,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
 	if (fdb) {
 		memcpy(fdb->addr.addr, addr, ETH_ALEN);
 		fdb->dst = source;
+		fdb->vlan_id = vid;
 		fdb->is_local = 0;
 		fdb->is_static = 0;
 		fdb->updated = fdb->used = jiffies;
@@ -371,13 +382,13 @@ 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)
 {
-	struct hlist_head *head = &br->hash[br_mac_hash(addr)];
+	struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)];
 	struct net_bridge_fdb_entry *fdb;
 
 	if (!is_valid_ether_addr(addr))
 		return -EINVAL;
 
-	fdb = fdb_find(head, addr);
+	fdb = fdb_find(head, addr, 0);
 	if (fdb) {
 		/* it is okay to have multiple ports with same
 		 * address, just use the first one.
@@ -390,7 +401,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 		fdb_delete(br, fdb);
 	}
 
-	fdb = fdb_create(head, source, addr);
+	fdb = fdb_create(head, source, addr, 0);
 	if (!fdb)
 		return -ENOMEM;
 
@@ -412,9 +423,9 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 }
 
 void br_fdb_update(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)];
+	struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
 	struct net_bridge_fdb_entry *fdb;
 
 	/* some users want to always flood. */
@@ -426,7 +437,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
 	      source->state == BR_STATE_FORWARDING))
 		return;
 
-	fdb = fdb_find_rcu(head, addr);
+	fdb = fdb_find_rcu(head, addr, vid);
 	if (likely(fdb)) {
 		/* attempt to update an entry for a local interface */
 		if (unlikely(fdb->is_local)) {
@@ -441,8 +452,8 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
 		}
 	} else {
 		spin_lock(&br->hash_lock);
-		if (likely(!fdb_find(head, addr))) {
-			fdb = fdb_create(head, source, addr);
+		if (likely(!fdb_find(head, addr, vid))) {
+			fdb = fdb_create(head, source, addr, vid);
 			if (fdb)
 				fdb_notify(br, fdb, RTM_NEWNEIGH);
 		}
@@ -571,18 +582,18 @@ out:
 
 /* Update (create or replace) forwarding database entry */
 static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
-			 __u16 state, __u16 flags)
+			 __u16 state, __u16 flags, __u16 vid)
 {
 	struct net_bridge *br = source->br;
-	struct hlist_head *head = &br->hash[br_mac_hash(addr)];
+	struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
 	struct net_bridge_fdb_entry *fdb;
 
-	fdb = fdb_find(head, addr);
+	fdb = fdb_find(head, addr, vid);
 	if (fdb == NULL) {
 		if (!(flags & NLM_F_CREATE))
 			return -ENOENT;
 
-		fdb = fdb_create(head, source, addr);
+		fdb = fdb_create(head, source, addr, vid);
 		if (!fdb)
 			return -ENOMEM;
 		fdb_notify(br, fdb, RTM_NEWNEIGH);
@@ -629,11 +640,12 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 
 	if (ndm->ndm_flags & NTF_USE) {
 		rcu_read_lock();
-		br_fdb_update(p->br, p, addr);
+		br_fdb_update(p->br, p, addr, 0);
 		rcu_read_unlock();
 	} else {
 		spin_lock_bh(&p->br->hash_lock);
-		err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags);
+		err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags,
+				0);
 		spin_unlock_bh(&p->br->hash_lock);
 	}
 
@@ -643,10 +655,10 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
 {
 	struct net_bridge *br = p->br;
-	struct hlist_head *head = &br->hash[br_mac_hash(addr)];
+	struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)];
 	struct net_bridge_fdb_entry *fdb;
 
-	fdb = fdb_find(head, addr);
+	fdb = fdb_find(head, addr, 0);
 	if (!fdb)
 		return -ENOENT;
 
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index a63f227..4803301 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -75,7 +75,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
 
 	/* insert into forwarding database after filtering to avoid spoofing */
 	br = p->br;
-	br_fdb_update(br, p, eth_hdr(skb)->h_source);
+	br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
 
 	if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
 	    br_multicast_rcv(br, p, skb))
@@ -110,7 +110,8 @@ int br_handle_frame_finish(struct sk_buff *skb)
 			skb2 = skb;
 
 		br->dev->stats.multicast++;
-	} else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) {
+	} else if ((dst = __br_fdb_get(br, dest, vid)) &&
+			dst->is_local) {
 		skb2 = skb;
 		/* Do not forward the packet since it's local. */
 		skb = NULL;
@@ -138,8 +139,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_vlan_get_tag(skb, &vid);
+	br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid);
 	return 0;	 /* process further */
 }
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 907b5df..6dd94df 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -85,6 +85,7 @@ struct net_bridge_fdb_entry
 	mac_addr			addr;
 	unsigned char			is_local;
 	unsigned char			is_static;
+	__u16				vlan_id;
 };
 
 struct net_bridge_port_group {
@@ -371,7 +372,8 @@ extern void br_fdb_cleanup(unsigned long arg);
 extern void br_fdb_delete_by_port(struct net_bridge *br,
 				  const struct net_bridge_port *p, int do_all);
 extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
-						 const unsigned char *addr);
+						 const unsigned char *addr,
+						 __u16 vid);
 extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
 extern int br_fdb_fillbuf(struct net_bridge *br, void *buf,
 			  unsigned long count, unsigned long off);
@@ -380,7 +382,8 @@ extern int br_fdb_insert(struct net_bridge *br,
 			 const unsigned char *addr);
 extern void br_fdb_update(struct net_bridge *br,
 			  struct net_bridge_port *source,
-			  const unsigned char *addr);
+			  const unsigned char *addr,
+			  u16 vid);
 
 extern int br_fdb_delete(struct ndmsg *ndm,
 			 struct net_device *dev,
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 08/12] bridge: Add vlan id to multicast groups
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (6 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 07/12] bridge: Add vlan to unicast fdb entries Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 09/12] bridge: Add vlan support to static neighbors Vlad Yasevich
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

Add vlan_id to multicasts groups so that we know which vlan
each group belongs to and can correctly forward to appropriate vlan.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_multicast.c |   69 ++++++++++++++++++++++++++++++++-------------
 net/bridge/br_private.h   |    1 +
 2 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 6d6f265..7d886b0 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -39,6 +39,8 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
 {
 	if (a->proto != b->proto)
 		return 0;
+	if (a->vid != b->vid)
+		return 0;
 	switch (a->proto) {
 	case htons(ETH_P_IP):
 		return a->u.ip4 == b->u.ip4;
@@ -50,16 +52,19 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
 	return 0;
 }
 
-static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip)
+static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip,
+				__u16 vid)
 {
-	return jhash_1word(mdb->secret, (__force u32)ip) & (mdb->max - 1);
+	return jhash_2words((__force u32)ip, vid, mdb->secret) & (mdb->max - 1);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
 static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb,
-				const struct in6_addr *ip)
+				const struct in6_addr *ip,
+				__u16 vid)
 {
-	return jhash2((__force u32 *)ip->s6_addr32, 4, mdb->secret) & (mdb->max - 1);
+	return jhash_2words(ipv6_addr_hash(ip), vid,
+			    mdb->secret) & (mdb->max - 1);
 }
 #endif
 
@@ -68,10 +73,10 @@ static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb,
 {
 	switch (ip->proto) {
 	case htons(ETH_P_IP):
-		return __br_ip4_hash(mdb, ip->u.ip4);
+		return __br_ip4_hash(mdb, ip->u.ip4, ip->vid);
 #if IS_ENABLED(CONFIG_IPV6)
 	case htons(ETH_P_IPV6):
-		return __br_ip6_hash(mdb, &ip->u.ip6);
+		return __br_ip6_hash(mdb, &ip->u.ip6, ip->vid);
 #endif
 	}
 	return 0;
@@ -101,24 +106,27 @@ struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb,
 }
 
 static struct net_bridge_mdb_entry *br_mdb_ip4_get(
-	struct net_bridge_mdb_htable *mdb, __be32 dst)
+	struct net_bridge_mdb_htable *mdb, __be32 dst, __u16 vid)
 {
 	struct br_ip br_dst;
 
 	br_dst.u.ip4 = dst;
 	br_dst.proto = htons(ETH_P_IP);
+	br_dst.vid = vid;
 
 	return br_mdb_ip_get(mdb, &br_dst);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
 static struct net_bridge_mdb_entry *br_mdb_ip6_get(
-	struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst)
+	struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst,
+	__u16 vid)
 {
 	struct br_ip br_dst;
 
 	br_dst.u.ip6 = *dst;
 	br_dst.proto = htons(ETH_P_IPV6);
+	br_dst.vid = vid;
 
 	return br_mdb_ip_get(mdb, &br_dst);
 }
@@ -694,7 +702,8 @@ err:
 
 static int br_ip4_multicast_add_group(struct net_bridge *br,
 				      struct net_bridge_port *port,
-				      __be32 group)
+				      __be32 group,
+				      __u16 vid)
 {
 	struct br_ip br_group;
 
@@ -703,6 +712,7 @@ static int br_ip4_multicast_add_group(struct net_bridge *br,
 
 	br_group.u.ip4 = group;
 	br_group.proto = htons(ETH_P_IP);
+	br_group.vid = vid;
 
 	return br_multicast_add_group(br, port, &br_group);
 }
@@ -710,7 +720,8 @@ static int br_ip4_multicast_add_group(struct net_bridge *br,
 #if IS_ENABLED(CONFIG_IPV6)
 static int br_ip6_multicast_add_group(struct net_bridge *br,
 				      struct net_bridge_port *port,
-				      const struct in6_addr *group)
+				      const struct in6_addr *group,
+				      __u16 vid)
 {
 	struct br_ip br_group;
 
@@ -719,6 +730,7 @@ static int br_ip6_multicast_add_group(struct net_bridge *br,
 
 	br_group.u.ip6 = *group;
 	br_group.proto = htons(ETH_P_IPV6);
+	br_group.vid = vid;
 
 	return br_multicast_add_group(br, port, &br_group);
 }
@@ -895,10 +907,12 @@ 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;
 
+	br_vlan_get_tag(skb, &vid);
 	ih = igmpv3_report_hdr(skb);
 	num = ntohs(ih->ngrec);
 	len = sizeof(*ih);
@@ -930,7 +944,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
 			continue;
 		}
 
-		err = br_ip4_multicast_add_group(br, port, group);
+		err = br_ip4_multicast_add_group(br, port, group, vid);
 		if (err)
 			break;
 	}
@@ -949,10 +963,12 @@ 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;
 
+	br_vlan_get_tag(skb, &vid);
 	icmp6h = icmp6_hdr(skb);
 	num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);
 	len = sizeof(*icmp6h);
@@ -990,7 +1006,8 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 			continue;
 		}
 
-		err = br_ip6_multicast_add_group(br, port, &grec->grec_mca);
+		err = br_ip6_multicast_add_group(br, port, &grec->grec_mca,
+						 vid);
 		if (!err)
 			break;
 	}
@@ -1074,6 +1091,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) ||
@@ -1108,7 +1126,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_vlan_get_tag(skb, &vid);
+	mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
 	if (!mp)
 		goto out;
 
@@ -1149,6 +1168,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) ||
@@ -1180,7 +1200,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_vlan_get_tag(skb, &vid);
+	mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
 	if (!mp)
 		goto out;
 
@@ -1286,7 +1307,8 @@ out:
 
 static void br_ip4_multicast_leave_group(struct net_bridge *br,
 					 struct net_bridge_port *port,
-					 __be32 group)
+					 __be32 group,
+					 __u16 vid)
 {
 	struct br_ip br_group;
 
@@ -1295,6 +1317,7 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
 
 	br_group.u.ip4 = group;
 	br_group.proto = htons(ETH_P_IP);
+	br_group.vid = vid;
 
 	br_multicast_leave_group(br, port, &br_group);
 }
@@ -1302,7 +1325,8 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
 #if IS_ENABLED(CONFIG_IPV6)
 static void br_ip6_multicast_leave_group(struct net_bridge *br,
 					 struct net_bridge_port *port,
-					 const struct in6_addr *group)
+					 const struct in6_addr *group,
+					 __u16 vid)
 {
 	struct br_ip br_group;
 
@@ -1311,6 +1335,7 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
 
 	br_group.u.ip6 = *group;
 	br_group.proto = htons(ETH_P_IPV6);
+	br_group.vid = vid;
 
 	br_multicast_leave_group(br, port, &br_group);
 }
@@ -1326,6 +1351,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)))
@@ -1386,6 +1412,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
 
 	err = 0;
 
+	br_vlan_get_tag(skb2, &vid);
 	BR_INPUT_SKB_CB(skb)->igmp = 1;
 	ih = igmp_hdr(skb2);
 
@@ -1393,7 +1420,7 @@ 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);
+		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);
@@ -1402,7 +1429,7 @@ 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_ip4_multicast_leave_group(br, port, ih->group, vid);
 		break;
 	}
 
@@ -1427,6 +1454,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;
@@ -1510,6 +1538,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
 
 	err = 0;
 
+	br_vlan_get_tag(skb, &vid);
 	BR_INPUT_SKB_CB(skb)->igmp = 1;
 
 	switch (icmp6_type) {
@@ -1522,7 +1551,7 @@ 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);
+		err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid);
 		break;
 	    }
 	case ICMPV6_MLD2_REPORT:
@@ -1539,7 +1568,7 @@ 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_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 6dd94df..72000ef 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -64,6 +64,7 @@ struct br_ip
 #endif
 	} u;
 	__be16		proto;
+	__u16		vid;
 };
 
 struct net_port_vlans {
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 09/12] bridge: Add vlan support to static neighbors
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (7 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 08/12] bridge: Add vlan id to multicast groups Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 10/12] bridge: Add vlan support for local fdb entries Vlad Yasevich
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

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 no VLANs are
configured on the port, 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                           |  148 ++++++++++++++++++++++---
 net/bridge/br_private.h                       |    6 +-
 net/core/rtnetlink.c                          |   23 +++--
 8 files changed, 154 insertions(+), 32 deletions(-)

diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 5989b3f..23fc510 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7001,7 +7001,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 7b44ebd..afbea93 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -564,7 +564,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 656230e..6adeafe 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 51553c7..2dec09e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1008,6 +1008,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..9553cff 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,6 +623,25 @@ 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,
@@ -625,12 +649,29 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 {
 	struct net_bridge_port *p;
 	int err = 0;
+	struct net_port_vlans *pv;
+	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,41 +679,90 @@ 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();
+	pv = nbp_get_vlan_info(p);
+	if (vid != BR_INVALID_VID) {
+		if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
+			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 (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+			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.
+		 */
+		vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+		while (vid < BR_VLAN_BITMAP_LEN) {
+			err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
+			if (err)
+				goto out;
+			vid = find_next_bit(pv->vlan_bitmap,
+					    BR_VLAN_BITMAP_LEN, vid+1);
+		}
 	}
 
+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 *br, 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;
 
-	fdb_delete(p->br, fdb);
+	fdb_delete(br, fdb);
 	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->br, 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;
 	int err;
+	struct net_port_vlans *pv;
+	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 +770,33 @@ 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);
+	pv = nbp_get_vlan_info(p);
+	if (vid != BR_INVALID_VID) {
+		if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
+			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 (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+			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;
+		vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+		while (vid < BR_VLAN_BITMAP_LEN) {
+			err &= __br_fdb_delete(p, addr, vid);
+			vid = find_next_bit(pv->vlan_bitmap,
+					    BR_VLAN_BITMAP_LEN, vid+1);
+		}
+	}
+out:
 	return err;
 }
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 72000ef..0bdf138 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -386,7 +386,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[],
@@ -575,13 +575,13 @@ extern void nbp_vlan_flush(struct net_bridge_port *port);
 static inline struct net_port_vlans *br_get_vlan_info(
 						const struct net_bridge *br)
 {
-	return rcu_dereference(br->vlan_info);
+	return rcu_dereference_rtnl(br->vlan_info);
 }
 
 static inline struct net_port_vlans *nbp_get_vlan_info(
 						const struct net_bridge_port *p)
 {
-	return rcu_dereference(p->vlan_info);
+	return rcu_dereference_rtnl(p->vlan_info);
 }
 
 
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 1aba89f..87b07d8 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2122,7 +2122,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;
@@ -2130,8 +2130,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) {
@@ -2145,13 +2146,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 */
@@ -2161,7 +2166,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 		const struct net_device_ops *ops = br_dev->netdev_ops;
 
 		if (ops->ndo_fdb_del)
-			err = ops->ndo_fdb_del(ndm, dev, addr);
+			err = ops->ndo_fdb_del(ndm, tb, dev, addr);
 
 		if (err)
 			goto out;
@@ -2171,7 +2176,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	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 10/12] bridge: Add vlan support for local fdb entries
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (8 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 09/12] bridge: Add vlan support to static neighbors Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 11/12] bridge: Dump vlan information from a bridge port Vlad Yasevich
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

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     |   61 +++++++++++++++++++++++++++---------
 net/bridge/br_if.c      |    2 +-
 net/bridge/br_private.h |   10 +++++-
 net/bridge/br_vlan.c    |   78 +++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 125 insertions(+), 26 deletions(-)

diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 9553cff..574c1e0 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;
+	bool no_vlan = (nbp_get_vlan_info(p) == NULL) ? true : false;
 	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_port_vlans *pv;
+	u16 vid = 0;
 
 	/* 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.  This function runs under RTNL so the bitmap will not
+	 * change from under us.
+	 */
+	pv = br_get_vlan_info(br);
+	if (!pv)
+		return;
+
+	for (vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid);
+	     vid < BR_VLAN_BITMAP_LEN;
+	     vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid+1)) {
+		f = __br_fdb_get(br, br->dev->dev_addr, vid);
+		if (f && f->is_local && !f->dst)
+			fdb_delete(br, f);
+		fdb_insert(br, NULL, newaddr, 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;
 }
@@ -713,8 +744,8 @@ out:
 	return err;
 }
 
-static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr,
-			      u16 vlan)
+int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr,
+		       u16 vlan)
 {
 	struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
 	struct net_bridge_fdb_entry *fdb;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 335c60c..ef1b914 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -397,7 +397,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 0bdf138..f4e6cab 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -380,11 +380,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,
@@ -571,6 +573,7 @@ extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
 extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
 extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 extern void nbp_vlan_flush(struct net_bridge_port *port);
+extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid);
 
 static inline struct net_port_vlans *br_get_vlan_info(
 						const struct net_bridge *br)
@@ -659,6 +662,11 @@ static inline struct net_port_vlans *nbp_get_vlan_info(
 	return NULL;
 }
 
+static inline bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
+{
+	return false;
+}
+
 static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag)
 {
 	return 0;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index d4c4630..9c4eda6 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -27,6 +27,9 @@ static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid)
 
 static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 {
+	struct net_bridge_port *p = NULL;
+	struct net_bridge *br;
+	struct net_device *dev;
 	int err;
 
 	if (test_bit(vid, v->vlan_bitmap)) {
@@ -35,20 +38,35 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 		return 0;
 	}
 
-	if (v->port_idx && vid) {
-		struct net_device *dev =
-			vlans_to_parent(struct net_bridge_port, v)->dev;
+	if (vid) {
+		if (v->port_idx) {
+			p = vlans_to_parent(struct net_bridge_port, v);
+			br = p->br;
+			dev = p->dev;
+		} else {
+			br = vlans_to_parent(struct net_bridge, v);
+			dev = br->dev;
+		}
 
-		/* Add VLAN to the device filter if it is supported.
-		 * Stricly speaking, this is not necessary now, since devices
-		 * are made promiscuous by the bridge, but if that ever changes
-		 * this code will allow tagged traffic to enter the bridge.
-		 */
-		if (dev->features & NETIF_F_HW_VLAN_FILTER) {
+		if (p && (dev->features & NETIF_F_HW_VLAN_FILTER)) {
+			/* Add VLAN to the device filter if it is supported.
+			 * Stricly speaking, this is not necessary now, since
+			 * devices are made promiscuous by the bridge, but if
+			 * that ever changes this code will allow tagged
+			 * traffic to enter the bridge.
+			 */
 			err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid);
 			if (err)
 				return err;
 		}
+
+		err = br_fdb_insert(br, p, dev->dev_addr, vid);
+		if (err) {
+			br_err(br, "failed insert local address into bridge "
+			       "forwarding table\n");
+			goto out_filt;
+		}
+
 	}
 
 	set_bit(vid, v->vlan_bitmap);
@@ -56,6 +74,11 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 		__vlan_add_pvid(v, vid);
 
 	return 0;
+
+out_filt:
+	if (p && (dev->features & NETIF_F_HW_VLAN_FILTER))
+		dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
+	return err;
 }
 
 static int __vlan_del(struct net_port_vlans *v, u16 vid)
@@ -282,6 +305,15 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
 	if (!pv)
 		return -EINVAL;
 
+	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(&br->hash_lock);
+		fdb_delete_by_addr(br, br->dev->dev_addr, vid);
+		spin_unlock_bh(&br->hash_lock);
+	}
+
 	__vlan_del(pv, vid);
 	return 0;
 }
@@ -359,6 +391,15 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
 	if (!pv)
 		return -EINVAL;
 
+	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(&port->br->hash_lock);
+		fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
+		spin_unlock_bh(&port->br->hash_lock);
+	}
+
 	return __vlan_del(pv, vid);
 }
 
@@ -374,3 +415,22 @@ void nbp_vlan_flush(struct net_bridge_port *port)
 
 	__vlan_flush(pv);
 }
+
+bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
+{
+	struct net_port_vlans *pv;
+	bool found = false;
+
+	rcu_read_lock();
+	pv = rcu_dereference(port->vlan_info);
+
+	if (!pv)
+		goto out;
+
+	if (test_bit(vid, pv->vlan_bitmap))
+		found = true;
+
+out:
+	rcu_read_unlock();
+	return found;
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 11/12] bridge: Dump vlan information from a bridge port
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (9 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 10/12] bridge: Add vlan support for local fdb entries Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-01 20:02 ` [PATCH v9 net-next 12/12] bridge: Separate egress policy bitmap Vlad Yasevich
  2013-02-04 16:24 ` [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Stephen Hemminger
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

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/rtnetlink.h                |    1 +
 net/bridge/br_netlink.c                       |   97 ++++++++++++++++++++++---
 net/bridge/br_private.h                       |    3 +-
 net/bridge/br_vlan.c                          |    4 +-
 net/core/rtnetlink.c                          |   16 +++-
 7 files changed, 107 insertions(+), 20 deletions(-)

diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 23fc510..b5e9df5 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7078,7 +7078,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 2dec09e..6eb70eb 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1020,7 +1020,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);
 	int			(*ndo_bridge_dellink)(struct net_device *dev,
 						      struct nlmsghdr *nlh);
 	int			(*ndo_change_carrier)(struct net_device *dev,
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 7a5eb19..7a2144e 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -630,6 +630,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_netlink.c b/net/bridge/br_netlink.c
index 0089a3f..ddbb2c7 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -65,15 +65,21 @@ 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_device *dev)
 {
-	const struct net_bridge *br = port->br;
-	const struct net_device *dev = port->dev;
+	const struct net_bridge *br;
 	struct ifinfomsg *hdr;
 	struct nlmsghdr *nlh;
 	u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
 
+	if (port)
+		br = port->br;
+	else
+		br = netdev_priv(dev);
+
 	br_debug(br, "br_fill_info event %d port %s master %s\n",
 		     event, dev->name, br->dev->name);
 
@@ -99,7 +105,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
 	     nla_put_u32(skb, IFLA_LINK, dev->iflink)))
 		goto nla_put_failure;
 
-	if (event == RTM_NEWLINK) {
+	if (event == RTM_NEWLINK && port) {
 		struct nlattr *nest
 			= nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED);
 
@@ -108,6 +114,43 @@ 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;
+		const struct net_port_vlans *pv;
+		struct bridge_vlan_info vinfo;
+		u16 vid;
+
+		if (port)
+			pv = nbp_get_vlan_info(port);
+		else
+			pv = br_get_vlan_info(br);
+
+		if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN))
+			goto done;
+
+		af = nla_nest_start(skb, IFLA_AF_SPEC);
+		if (!af)
+			goto nla_put_failure;
+
+		for (vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+		     vid < BR_VLAN_BITMAP_LEN;
+		     vid = find_next_bit(pv->vlan_bitmap,
+					 BR_VLAN_BITMAP_LEN, vid+1)) {
+			vinfo.vid = vid;
+			vinfo.flags = 0;
+			if (vid == pv->pvid)
+				vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
+
+			if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
+				    sizeof(vinfo), &vinfo))
+				goto nla_put_failure;
+		}
+
+		nla_nest_end(skb, af);
+	}
+
+done:
 	return nlmsg_end(skb, nlh);
 
 nla_put_failure:
@@ -135,7 +178,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, port->dev);
 	if (err < 0) {
 		/* -EMSGSIZE implies BUG in br_nlmsg_size() */
 		WARN_ON(err == -EMSGSIZE);
@@ -154,16 +197,17 @@ 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);
 
-	/* not a bridge port */
-	if (!port)
+	/* not a bridge port and  */
+	if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN))
 		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, dev);
 out:
 	return err;
 }
@@ -396,6 +440,29 @@ 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_port_vlans *pv;
+
+	if (br_port_exists(dev))
+		pv = nbp_get_vlan_info(br_port_get_rcu(dev));
+	else if (dev->priv_flags & IFF_EBRIDGE)
+		pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
+	else
+		return 0;
+
+	if (!pv)
+		return 0;
+
+	/* Each VLAN is returned in bridge_vlan_info along with flags */
+	return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
+}
+
+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),
@@ -409,11 +476,18 @@ int __init br_netlink_init(void)
 	int err;
 
 	br_mdb_init();
-	err = rtnl_link_register(&br_link_ops);
+	err = rtnl_af_register(&br_af_ops);
 	if (err)
 		goto out;
 
+	err = rtnl_link_register(&br_link_ops);
+	if (err)
+		goto out_af;
+
 	return 0;
+
+out_af:
+	rtnl_af_unregister(&br_af_ops);
 out:
 	br_mdb_uninit();
 	return err;
@@ -422,5 +496,6 @@ out:
 void __exit br_netlink_fini(void)
 {
 	br_mdb_uninit();
+	rtnl_af_unregister(&br_af_ops);
 	rtnl_link_unregister(&br_link_ops);
 }
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index f4e6cab..1fcbc9f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -73,6 +73,7 @@ struct net_port_vlans {
 	void				*parent;
 	struct rcu_head			rcu;
 	unsigned long			vlan_bitmap[BR_VLAN_BITMAP_LEN];
+	u16				num_vlans;
 };
 
 struct net_bridge_fdb_entry
@@ -735,7 +736,7 @@ 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_dellink(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/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 9c4eda6..fec139c 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -70,6 +70,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 	}
 
 	set_bit(vid, v->vlan_bitmap);
+	v->num_vlans++;
 	if (flags & BRIDGE_VLAN_INFO_PVID)
 		__vlan_add_pvid(v, vid);
 
@@ -107,6 +108,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
 	}
 
 	clear_bit(vid, v->vlan_bitmap);
+	v->num_vlans--;
 	if (first_bit == last_bit) {
 		if (v->port_idx) {
 			struct net_bridge_port *p =
@@ -369,8 +371,6 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 	pv->parent = port;
 	pv->pvid = BR_INVALID_VID;
 	err = __vlan_add(pv, vid, flags);
-	if (err)
-		goto clean_up;
 
 	rcu_assign_pointer(port->vlan_info, pv);
 	return 0;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 87b07d8..257b73e 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 (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
 			if (idx >= cb->args[0] &&
 			    br_dev->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)) &&
 	    br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
-		err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
+		err = br_dev->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	[flat|nested] 28+ messages in thread

* [PATCH v9 net-next 12/12] bridge: Separate egress policy bitmap
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (10 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 11/12] bridge: Dump vlan information from a bridge port Vlad Yasevich
@ 2013-02-01 20:02 ` Vlad Yasevich
  2013-02-04 16:24 ` [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Stephen Hemminger
  12 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-01 20:02 UTC (permalink / raw)
  To: shemminger; +Cc: bridge, davem, netdev

Add an ability to configure a separate "untagged" egress
policy to the VLAN information of the bridge.  This superseeds PVID
policy and makes PVID ingress-only.  The policy is configured with a
new flag and is represented as a port bitmap per vlan.  Egress frames
with a VLAN id in "untagged" policy bitmap would egress
the port without VLAN header.

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

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 875c9e2..8e1fc51 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -121,6 +121,7 @@ enum {
 
 #define BRIDGE_VLAN_INFO_MASTER	(1<<0)	/* Operate on Bridge device as well */
 #define BRIDGE_VLAN_INFO_PVID	(1<<1)	/* VLAN is PVID, ingress untagged */
+#define BRIDGE_VLAN_INFO_UNTAGGED	(1<<2)	/* VLAN egresses untagged */
 
 struct bridge_vlan_info {
 	u16 flags;
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index ddbb2c7..b71d1c5 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -141,6 +141,8 @@ static int br_fill_ifinfo(struct sk_buff *skb,
 			vinfo.flags = 0;
 			if (vid == pv->pvid)
 				vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
+			if (test_bit(vid, pv->untagged_bitmap))
+				vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
 			if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
 				    sizeof(vinfo), &vinfo))
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 1fcbc9f..c0395f7 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -73,6 +73,7 @@ struct net_port_vlans {
 	void				*parent;
 	struct rcu_head			rcu;
 	unsigned long			vlan_bitmap[BR_VLAN_BITMAP_LEN];
+	unsigned long			untagged_bitmap[BR_VLAN_BITMAP_LEN];
 	u16				num_vlans;
 };
 
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index fec139c..a8e29e3 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -25,6 +25,15 @@ static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid)
 	v->pvid = BR_INVALID_VID;
 }
 
+static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)
+{
+	if (flags & BRIDGE_VLAN_INFO_PVID)
+		__vlan_add_pvid(v, vid);
+
+	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
+		set_bit(vid, v->untagged_bitmap);
+}
+
 static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 {
 	struct net_bridge_port *p = NULL;
@@ -33,8 +42,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 	int err;
 
 	if (test_bit(vid, v->vlan_bitmap)) {
-		if (flags & BRIDGE_VLAN_INFO_PVID)
-			__vlan_add_pvid(v, vid);
+		__vlan_add_flags(v, vid, flags);
 		return 0;
 	}
 
@@ -71,8 +79,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 
 	set_bit(vid, v->vlan_bitmap);
 	v->num_vlans++;
-	if (flags & BRIDGE_VLAN_INFO_PVID)
-		__vlan_add_pvid(v, vid);
+	__vlan_add_flags(v, vid, flags);
 
 	return 0;
 
@@ -91,6 +98,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
 		return -EINVAL;
 
 	__vlan_delete_pvid(v, vid);
+	clear_bit(vid, v->untagged_bitmap);
 
 	/* Check to see if any other vlans are in this table.  If this
 	 * is the last vlan, delete the whole structure.  If this is not the
@@ -173,11 +181,11 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
 		goto out;
 
 	/* At this point, we know that the frame was filtered and contains
-	 * a valid vlan id.  If the vlan id matches the pvid of current port
+	 * a valid vlan id.  If the vlan id is set in the untagged bitmap,
 	 * send untagged; otherwise, send taged.
 	 */
 	br_vlan_get_tag(skb, &vid);
-	if (vid == br_get_pvid(pv))
+	if (test_bit(vid, pv->untagged_bitmap))
 		skb = br_vlan_untag(skb);
 	else {
 		/* Egress policy says "send tagged".  If output device
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 02/12] bridge: Validate that vlan is permitted on ingress
  2013-02-01 20:02 ` [PATCH v9 net-next 02/12] bridge: Validate that vlan is permitted on ingress Vlad Yasevich
@ 2013-02-02  1:04   ` Michał Mirosław
  2013-02-02  2:13     ` Vlad Yasevich
  0 siblings, 1 reply; 28+ messages in thread
From: Michał Mirosław @ 2013-02-02  1:04 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: shemminger, bridge, davem, netdev

2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
> When a frame arrives on a port or transmitted by the bridge,
> if we have VLANs configured, validate that a given VLAN is allowed
> to enter the bridge.
[...]
> +static inline struct net_port_vlans *br_get_vlan_info(
> +                                               const struct net_bridge *br)
> +{
> +       return rcu_dereference(br->vlan_info);
> +}
> +
> +static inline struct net_port_vlans *nbp_get_vlan_info(
> +                                               const struct net_bridge_port *p)
> +{
> +       return rcu_dereference(p->vlan_info);
> +}

Those should go to patch #1.

Best Regards,
Michał Mirosław

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure
  2013-02-01 20:01 ` [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure Vlad Yasevich
@ 2013-02-02  1:04   ` Michał Mirosław
  2013-02-02  2:11     ` Vlad Yasevich
  0 siblings, 1 reply; 28+ messages in thread
From: Michał Mirosław @ 2013-02-02  1:04 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: shemminger, bridge, davem, netdev

2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
> Adds an optional infrustructure component to bridge that would allow
> native vlan filtering in the bridge.  Each bridge port (as well
> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
> is associated with a vlan id.  This way if the bit corresponding to
> the vid is set in the bitmap that the packet with vid is allowed to
> enter and exit the port.
[...]
+struct net_port_vlans {
+       u16                             port_idx;
+       void                            *parent;
+       struct rcu_head                 rcu;
+       unsigned long                   vlan_bitmap[BR_VLAN_BITMAP_LEN];
+};
[later, in br_vlan.c]
+#define vlans_to_parent(type, pv) ((type *)(pv)->parent)

A really don't like this pointer casting. It's easy to get it wrong
(and you did in __vlan_del).

1. union { struct net_bridge *br; struct net_bridge_port *port; } parent;

2. inline net_port_vlans __rcu **br_vlan_info_parent_ptr(struct
net_port_vlans *v)
{
	return v->port_idx ? &v->parent.port->vlan_info : &v->parent.br->vlan_info;
}

(I'm not insisting on #2. Just think about it. ;)

> @@ -156,6 +166,7 @@ struct net_bridge_port
>  #ifdef CONFIG_NET_POLL_CONTROLLER
>         struct netpoll                  *np;
>  #endif
> +       struct net_port_vlans __rcu     *vlan_info;
>  };

Missing #ifdef CONFIG_BRIDGE_VLAN_FILTERING?

> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
> +{
> +       unsigned long first_bit;
> +       unsigned long last_bit;
> +
> +       if (!test_bit(vid, v->vlan_bitmap))
> +               return -EINVAL;
> +
> +       /* Check to see if any other vlans are in this table.  If this
> +        * is the last vlan, delete the whole structure.  If this is not the
> +        * last vlan, just clear the bit.
> +        */
> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
> +
> +       if (v->port_idx && vid) {
> +               struct net_device *dev =
> +                       vlans_to_parent(struct net_bridge, v)->dev;

struct net_bridge_port (or union and v->parent.port)

> +
> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
> +       }
> +
> +       clear_bit(vid, v->vlan_bitmap);
> +       if (first_bit == last_bit) {

bitmap_empty() is faster than two times find_xxx_bit().

> +               if (v->port_idx) {
> +                       struct net_bridge_port *p =
> +                               vlans_to_parent(struct net_bridge_port, v);
> +                       rcu_assign_pointer(p->vlan_info, NULL);
> +               } else {
> +                       struct net_bridge *br =
> +                               vlans_to_parent(struct net_bridge, v);
> +                       rcu_assign_pointer(br->vlan_info, NULL);
> +               }

With inline function above:

rcu_assign_pointer(*br_vlan_info_parent_ptr(v), NULL);

> +               kfree_rcu(v, rcu);
> +       }
> +       return 0;
> +}
> +
> +static void __vlan_flush(struct net_port_vlans *v)
> +{
> +       bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
> +       if (v->port_idx) {
> +               struct net_bridge_port *p =
> +                               vlans_to_parent(struct net_bridge_port, v);
> +               rcu_assign_pointer(p->vlan_info, NULL);
> +       } else {
> +               struct net_bridge *br =
> +                               vlans_to_parent(struct net_bridge, v);
> +               rcu_assign_pointer(br->vlan_info, NULL);
> +       }

Same here.

> +       kfree_rcu(v, rcu);
> +}

Best Regards,
Michał Mirosław

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid
  2013-02-01 20:02 ` [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid Vlad Yasevich
@ 2013-02-02  1:04   ` Michał Mirosław
  2013-02-02  1:15     ` Michał Mirosław
  0 siblings, 1 reply; 28+ messages in thread
From: Michał Mirosław @ 2013-02-02  1:04 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: shemminger, bridge, davem, netdev

2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
> A user may designate a certain vlan as PVID.  This means that
> any ingress frame that does not contain a vlan tag is assigned to
> this vlan and any forwarding decisions are made with this vlan in mind.
[...]
>  struct net_port_vlans {
>         u16                             port_idx;
> +       u16                             pvid;

I'm confused about the implementation. I would expect pvid field in
net_bridge_port and adding a tag if it isn't there on ingress path.
The rest would be just like without PVIDs. But here you pvid field to
net_port_vlans, and don't do anything with it in receive nor transmit
path. Does it work? What am I missing?

Best Regards,
Michał Mirosław

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid
  2013-02-02  1:04   ` Michał Mirosław
@ 2013-02-02  1:15     ` Michał Mirosław
  2013-02-02  2:22       ` Vlad Yasevich
  0 siblings, 1 reply; 28+ messages in thread
From: Michał Mirosław @ 2013-02-02  1:15 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: shemminger, bridge, davem, netdev

2013/2/2 Michał Mirosław <mirqus@gmail.com>:
> 2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
>> A user may designate a certain vlan as PVID.  This means that
>> any ingress frame that does not contain a vlan tag is assigned to
>> this vlan and any forwarding decisions are made with this vlan in mind.
> [...]
>>  struct net_port_vlans {
>>         u16                             port_idx;
>> +       u16                             pvid;
>
> I'm confused about the implementation. I would expect pvid field in
> net_bridge_port and adding a tag if it isn't there on ingress path.
> The rest would be just like without PVIDs. But here you pvid field to
> net_port_vlans, and don't do anything with it in receive nor transmit
> path. Does it work? What am I missing?

Found the answer in next patch (you should merge #5 and #6). Still,
the implementation looks overly complicated. If you force the packet
to canonical form on ingress (keeping outer tag in skb->vlan_tci, and
setting skb->vlan_tci = pvid if there is no tag) the code should get
simpler.

Best Regards,
Michał Mirosław

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure
  2013-02-02  1:04   ` Michał Mirosław
@ 2013-02-02  2:11     ` Vlad Yasevich
  0 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-02  2:11 UTC (permalink / raw)
  To: Michał Mirosław; +Cc: shemminger, bridge, davem, netdev

On 02/01/2013 08:04 PM, Michał Mirosław wrote:
> 2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
>> Adds an optional infrustructure component to bridge that would allow
>> native vlan filtering in the bridge.  Each bridge port (as well
>> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
>> is associated with a vlan id.  This way if the bit corresponding to
>> the vid is set in the bitmap that the packet with vid is allowed to
>> enter and exit the port.
> [...]
> +struct net_port_vlans {
> +       u16                             port_idx;
> +       void                            *parent;
> +       struct rcu_head                 rcu;
> +       unsigned long                   vlan_bitmap[BR_VLAN_BITMAP_LEN];
> +};
> [later, in br_vlan.c]
> +#define vlans_to_parent(type, pv) ((type *)(pv)->parent)
>
> A really don't like this pointer casting. It's easy to get it wrong
> (and you did in __vlan_del).
>
> 1. union { struct net_bridge *br; struct net_bridge_port *port; } parent;
>
> 2. inline net_port_vlans __rcu **br_vlan_info_parent_ptr(struct
> net_port_vlans *v)
> {
> 	return v->port_idx ? &v->parent.port->vlan_info : &v->parent.br->vlan_info;
> }
>
> (I'm not insisting on #2. Just think about it. ;)

I need the parent, not the vlan_info.  Sometimes I need to get to the 
device and I really don't want to then use container_of().

I suppose I could do the union, but that's really not much different 
then the type casts.  Just as easy to goof.

>
>> @@ -156,6 +166,7 @@ struct net_bridge_port
>>   #ifdef CONFIG_NET_POLL_CONTROLLER
>>          struct netpoll                  *np;
>>   #endif
>> +       struct net_port_vlans __rcu     *vlan_info;
>>   };
>
> Missing #ifdef CONFIG_BRIDGE_VLAN_FILTERING?

Yep.  Its in net_bridge, but not here.  Will fix.

>
>> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
>> +{
>> +       unsigned long first_bit;
>> +       unsigned long last_bit;
>> +
>> +       if (!test_bit(vid, v->vlan_bitmap))
>> +               return -EINVAL;
>> +
>> +       /* Check to see if any other vlans are in this table.  If this
>> +        * is the last vlan, delete the whole structure.  If this is not the
>> +        * last vlan, just clear the bit.
>> +        */
>> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>> +
>> +       if (v->port_idx && vid) {
>> +               struct net_device *dev =
>> +                       vlans_to_parent(struct net_bridge, v)->dev;
>
> struct net_bridge_port (or union and v->parent.port)

Yep.  Typo when doing conversions.

>
>> +
>> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
>> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
>> +       }
>> +
>> +       clear_bit(vid, v->vlan_bitmap);
>> +       if (first_bit == last_bit) {
>
> bitmap_empty() is faster than two times find_xxx_bit().
>

WTH?  I did this change and now its gone...  Time to see what else I 
somehow lost...

>> +               if (v->port_idx) {
>> +                       struct net_bridge_port *p =
>> +                               vlans_to_parent(struct net_bridge_port, v);
>> +                       rcu_assign_pointer(p->vlan_info, NULL);
>> +               } else {
>> +                       struct net_bridge *br =
>> +                               vlans_to_parent(struct net_bridge, v);
>> +                       rcu_assign_pointer(br->vlan_info, NULL);
>> +               }
>
> With inline function above:
>
> rcu_assign_pointer(*br_vlan_info_parent_ptr(v), NULL);
>
>> +               kfree_rcu(v, rcu);
>> +       }
>> +       return 0;
>> +}
>> +
>> +static void __vlan_flush(struct net_port_vlans *v)
>> +{
>> +       bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>> +       if (v->port_idx) {
>> +               struct net_bridge_port *p =
>> +                               vlans_to_parent(struct net_bridge_port, v);
>> +               rcu_assign_pointer(p->vlan_info, NULL);
>> +       } else {
>> +               struct net_bridge *br =
>> +                               vlans_to_parent(struct net_bridge, v);
>> +               rcu_assign_pointer(br->vlan_info, NULL);
>> +       }
>
> Same here.

Thanks.  I'll probably go with the union for direct reference.

-vlad
>
>> +       kfree_rcu(v, rcu);
>> +}
>
> Best Regards,
> Michał Mirosław
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 02/12] bridge: Validate that vlan is permitted on ingress
  2013-02-02  1:04   ` Michał Mirosław
@ 2013-02-02  2:13     ` Vlad Yasevich
  0 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-02  2:13 UTC (permalink / raw)
  To: Michał Mirosław; +Cc: shemminger, bridge, davem, netdev

On 02/01/2013 08:04 PM, Michał Mirosław wrote:
> 2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
>> When a frame arrives on a port or transmitted by the bridge,
>> if we have VLANs configured, validate that a given VLAN is allowed
>> to enter the bridge.
> [...]
>> +static inline struct net_port_vlans *br_get_vlan_info(
>> +                                               const struct net_bridge *br)
>> +{
>> +       return rcu_dereference(br->vlan_info);
>> +}
>> +
>> +static inline struct net_port_vlans *nbp_get_vlan_info(
>> +                                               const struct net_bridge_port *p)
>> +{
>> +       return rcu_dereference(p->vlan_info);
>> +}
>
> Those should go to patch #1.

They are not needed till here.  I thought it would read/review better if 
I kept it together.

-vlad
>
> Best Regards,
> Michał Mirosław
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid
  2013-02-02  1:15     ` Michał Mirosław
@ 2013-02-02  2:22       ` Vlad Yasevich
  2013-02-03 14:49         ` Michał Mirosław
  0 siblings, 1 reply; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-02  2:22 UTC (permalink / raw)
  To: Michał Mirosław; +Cc: shemminger, bridge, davem, netdev

On 02/01/2013 08:15 PM, Michał Mirosław wrote:
> 2013/2/2 Michał Mirosław <mirqus@gmail.com>:
>> 2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
>>> A user may designate a certain vlan as PVID.  This means that
>>> any ingress frame that does not contain a vlan tag is assigned to
>>> this vlan and any forwarding decisions are made with this vlan in mind.
>> [...]
>>>   struct net_port_vlans {
>>>          u16                             port_idx;
>>> +       u16                             pvid;
>>
>> I'm confused about the implementation. I would expect pvid field in
>> net_bridge_port and adding a tag if it isn't there on ingress path.
>> The rest would be just like without PVIDs. But here you pvid field to
>> net_port_vlans, and don't do anything with it in receive nor transmit
>> path. Does it work? What am I missing?
>
> Found the answer in next patch (you should merge #5 and #6).

It was split for incremental testing.  #5 added the ability to set and
delete it without impacting anything.  #6 added the actual work that 
pvid does.

> Still,
> the implementation looks overly complicated. If you force the packet
> to canonical form on ingress (keeping outer tag in skb->vlan_tci, and
> setting skb->vlan_tci = pvid if there is no tag) the code should get
> simpler.

What if there is no outer tag?  That's what the ingress code is doing.
If there is no outer tag, pvid is written to vlan_tci.  If there was
outer tag in vlan_tci, it's left alone.  This way at the end of ingress
vlan_tci is always set.
At egress, we grab that tag and compare it against pvid if any.  If it
matches, it's stripped.  If it doesn't, we output with the tag thus
adding the header.

The only thing I can simplify is grab the tci directly at egress, but
that's what the code will do anyway.

-vlad

>
> Best Regards,
> Michał Mirosław
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid
  2013-02-02  2:22       ` Vlad Yasevich
@ 2013-02-03 14:49         ` Michał Mirosław
  2013-02-03 15:20           ` Michał Mirosław
  2013-02-04 16:59           ` Vlad Yasevich
  0 siblings, 2 replies; 28+ messages in thread
From: Michał Mirosław @ 2013-02-03 14:49 UTC (permalink / raw)
  To: vyasevic; +Cc: netdev, shemminger, bridge, davem

2013/2/2 Vlad Yasevich <vyasevic@redhat.com>:
> On 02/01/2013 08:15 PM, Michał Mirosław wrote:
>> 2013/2/2 Michał Mirosław <mirqus@gmail.com>:
>>> 2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
>>>> A user may designate a certain vlan as PVID.  This means that
>>>> any ingress frame that does not contain a vlan tag is assigned to
>>>> this vlan and any forwarding decisions are made with this vlan in mind.
>>>
>>> [...]
>>>>
>>>>   struct net_port_vlans {
>>>>          u16                             port_idx;
>>>> +       u16                             pvid;
>>>
>>>
>>> I'm confused about the implementation. I would expect pvid field in
>>> net_bridge_port and adding a tag if it isn't there on ingress path.
>>> The rest would be just like without PVIDs. But here you pvid field to
>>> net_port_vlans, and don't do anything with it in receive nor transmit
>>> path. Does it work? What am I missing?
>> Found the answer in next patch (you should merge #5 and #6).
> It was split for incremental testing.  #5 added the ability to set and
> delete it without impacting anything.  #6 added the actual work that pvid
> does.
>
>> Still,
>> the implementation looks overly complicated. If you force the packet
>> to canonical form on ingress (keeping outer tag in skb->vlan_tci, and
>> setting skb->vlan_tci = pvid if there is no tag) the code should get
>> simpler.
>
>
> What if there is no outer tag?  That's what the ingress code is doing.
> If there is no outer tag, pvid is written to vlan_tci.  If there was
> outer tag in vlan_tci, it's left alone.  This way at the end of ingress
> vlan_tci is always set.
> At egress, we grab that tag and compare it against pvid if any.  If it
> matches, it's stripped.  If it doesn't, we output with the tag thus
> adding the header.
>
> The only thing I can simplify is grab the tci directly at egress, but
> that's what the code will do anyway.

... br_allowed_input(..., struct sk_buff **pskb)
{
  *pskb = vlan_untag(*pskb);
  skb = *pskb;
  if (!skb)
    return 0;

  if (!skb->vlan_tci && (pvid & VLAN_TAG_PRESENT))
    skb->vlan_tci = pvid;
  return check_vlan_allowed(skb->vlan_tci);
}

struct sk_buff *br_handle_vlan(..., struct sk_buff *skb)
{
   /* we guaranteed in br_allowed_input() that all packets processed
in bridge code
    * will be like received with NETIF_F_HW_VLAN_RX feature enabled. */
   if (skb->vlan_tci & (VLAN_VID_MASK|VLAN_TAG_PRESENT) == pvid)
     skb->vlan_tci = 0;  // what about 802.1p?
   return skb;
}

BTW, you implement three features: VLAN filtering, PVID handling,
per-VLAN FDB. Yet there are 10 patches that mix and match parts of the
implementation. It's really hard to review this when you have to jump
between patches to understand whats going on. I don't know what the
best split would be, but I got the feeling that this is not the
presented one.

Best Regards,
Michał Mirosław

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid
  2013-02-03 14:49         ` Michał Mirosław
@ 2013-02-03 15:20           ` Michał Mirosław
  2013-02-04 16:59           ` Vlad Yasevich
  1 sibling, 0 replies; 28+ messages in thread
From: Michał Mirosław @ 2013-02-03 15:20 UTC (permalink / raw)
  To: vyasevic; +Cc: netdev, shemminger, bridge, davem

2013/2/3 Michał Mirosław <mirqus@gmail.com>:
> 2013/2/2 Vlad Yasevich <vyasevic@redhat.com>:
>> On 02/01/2013 08:15 PM, Michał Mirosław wrote:
>>> 2013/2/2 Michał Mirosław <mirqus@gmail.com>:
>>>> 2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
>>>>> A user may designate a certain vlan as PVID.  This means that
>>>>> any ingress frame that does not contain a vlan tag is assigned to
>>>>> this vlan and any forwarding decisions are made with this vlan in mind.
>>>>
>>>> [...]
>>>>>
>>>>>   struct net_port_vlans {
>>>>>          u16                             port_idx;
>>>>> +       u16                             pvid;
>>>>
>>>>
>>>> I'm confused about the implementation. I would expect pvid field in
>>>> net_bridge_port and adding a tag if it isn't there on ingress path.
>>>> The rest would be just like without PVIDs. But here you pvid field to
>>>> net_port_vlans, and don't do anything with it in receive nor transmit
>>>> path. Does it work? What am I missing?
>>> Found the answer in next patch (you should merge #5 and #6).
>> It was split for incremental testing.  #5 added the ability to set and
>> delete it without impacting anything.  #6 added the actual work that pvid
>> does.
>>
>>> Still,
>>> the implementation looks overly complicated. If you force the packet
>>> to canonical form on ingress (keeping outer tag in skb->vlan_tci, and
>>> setting skb->vlan_tci = pvid if there is no tag) the code should get
>>> simpler.
>>
>>
>> What if there is no outer tag?  That's what the ingress code is doing.
>> If there is no outer tag, pvid is written to vlan_tci.  If there was
>> outer tag in vlan_tci, it's left alone.  This way at the end of ingress
>> vlan_tci is always set.
>> At egress, we grab that tag and compare it against pvid if any.  If it
>> matches, it's stripped.  If it doesn't, we output with the tag thus
>> adding the header.
>>
>> The only thing I can simplify is grab the tci directly at egress, but
>> that's what the code will do anyway.
>
> ... br_allowed_input(..., struct sk_buff **pskb)
> {
>   *pskb = vlan_untag(*pskb);
>   skb = *pskb;
>   if (!skb)
>     return 0;
>
>   if (!skb->vlan_tci && (pvid & VLAN_TAG_PRESENT))
>     skb->vlan_tci = pvid;
>   return check_vlan_allowed(skb->vlan_tci);
> }

Or maybe some cheating to save unconditional vlan_untag()ging: since
vlan_tci is used only when vlan_tx_tag_present() returns true, we can
cache VID there for the bridge code when VLANs are not HW-accelerated.
I'm not sure of this one as I don't know if the netfilter part keeps
vlan_tci intact (quick grep suggests it does).

... br_allowed_input(..., struct sk_buff **pskb)
{
  skb = *pskb;
  if (!vlan_tx_tag_present(skb)) {
    if (!__vlan_get_tag(skb, &skb->vlan_tci))
      skb->vlan_tci &= VLAN_VID_MASK;
    else
      skb->vlan_tci = 0;
  }
  if (!skb->vlan_tci && pvid & VLAN_TAG_PRESENT)  // FIXME:
802.1p-tagged is put in VLAN 0
    skb->vlan_tci = pvid;
  return check_vlan_allowed(skb, skb->vlan_tci & VLAN_VID_MASK);
}

struct sk_buff *br_handle_vlan(..., struct sk_buff *skb)
{
   /* we guaranteed in br_allowed_input() that all packets processed
   /* in bridge code will be like received with NETIF_F_HW_VLAN_RX
    * feature enabled or have VID cached in vlan_tci without
    * VLAN_TAG_PRESENT bit set. */

   if ((skb->vlan_tci & VLAN_VID_MASK) | VLAN_TAG_PRESENT != pvid)
     return skb;

   if (!vlan_tx_tag_present(skb)) {
      skb = vlan_untag(skb);
      if (!skb)
        return skb;
   }
   skb->vlan_tci = 0;  // FIXME: what about 802.1p?
   return skb;
}

Best Regards,
Michał Mirosław

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 00/12] Add basic VLAN support to bridges
  2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
                   ` (11 preceding siblings ...)
  2013-02-01 20:02 ` [PATCH v9 net-next 12/12] bridge: Separate egress policy bitmap Vlad Yasevich
@ 2013-02-04 16:24 ` Stephen Hemminger
  2013-02-04 16:58   ` Vlad Yasevich
  12 siblings, 1 reply; 28+ messages in thread
From: Stephen Hemminger @ 2013-02-04 16:24 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: bridge, davem, netdev

One thing I am not clear about is whether is supposed to be just
a simple filter of VLAN traffic, or a full VLAN aware bridge.

The change to make FDB entries per-VLAN seems to be the biggest tipping
point into a full VLAN bridge. I am concerned that might break existing
API's and Spanning Tree (internal and external).

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 00/12] Add basic VLAN support to bridges
  2013-02-04 16:24 ` [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Stephen Hemminger
@ 2013-02-04 16:58   ` Vlad Yasevich
  2013-02-07 22:48     ` Vlad Yasevich
  0 siblings, 1 reply; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-04 16:58 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: bridge, davem, netdev

On 02/04/2013 11:24 AM, Stephen Hemminger wrote:
> One thing I am not clear about is whether is supposed to be just
> a simple filter of VLAN traffic, or a full VLAN aware bridge.

I started with the concept of basic VLAN filtering, but it has been 
morphing into more of a VLAN away bridge.

>
> The change to make FDB entries per-VLAN seems to be the biggest tipping
> point into a full VLAN bridge. I am concerned that might break existing
> API's and Spanning Tree (internal and external).
>

I debated for a while about whether per-VLAN FDB entries were needed. 
The typing point was that without it, you may end up with flopping FDB 
and possible packet drops or vlan leaks, if say 2 different VMs used the 
same MAC but different VLANs.  Without it, there is an exploitable gap.

I've also tried to separate FDB code changes as much as possible.  If 
you really thing this is a big risk and a barrier to entry, then we can 
drop them.  I am just concerned about the hole I described above, but I 
guess it is not much different then what's there now.

Thanks
-vlad

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid
  2013-02-03 14:49         ` Michał Mirosław
  2013-02-03 15:20           ` Michał Mirosław
@ 2013-02-04 16:59           ` Vlad Yasevich
  1 sibling, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-04 16:59 UTC (permalink / raw)
  To: Michał Mirosław; +Cc: shemminger, bridge, davem, netdev

On 02/03/2013 09:49 AM, Michał Mirosław wrote:
> 2013/2/2 Vlad Yasevich <vyasevic@redhat.com>:
>> On 02/01/2013 08:15 PM, Michał Mirosław wrote:
>>> 2013/2/2 Michał Mirosław <mirqus@gmail.com>:
>>>> 2013/2/1 Vlad Yasevich <vyasevic@redhat.com>:
>>>>> A user may designate a certain vlan as PVID.  This means that
>>>>> any ingress frame that does not contain a vlan tag is assigned to
>>>>> this vlan and any forwarding decisions are made with this vlan in mind.
>>>>
>>>> [...]
>>>>>
>>>>>    struct net_port_vlans {
>>>>>           u16                             port_idx;
>>>>> +       u16                             pvid;
>>>>
>>>>
>>>> I'm confused about the implementation. I would expect pvid field in
>>>> net_bridge_port and adding a tag if it isn't there on ingress path.
>>>> The rest would be just like without PVIDs. But here you pvid field to
>>>> net_port_vlans, and don't do anything with it in receive nor transmit
>>>> path. Does it work? What am I missing?
>>> Found the answer in next patch (you should merge #5 and #6).
>> It was split for incremental testing.  #5 added the ability to set and
>> delete it without impacting anything.  #6 added the actual work that pvid
>> does.
>>
>>> Still,
>>> the implementation looks overly complicated. If you force the packet
>>> to canonical form on ingress (keeping outer tag in skb->vlan_tci, and
>>> setting skb->vlan_tci = pvid if there is no tag) the code should get
>>> simpler.
>>
>>
>> What if there is no outer tag?  That's what the ingress code is doing.
>> If there is no outer tag, pvid is written to vlan_tci.  If there was
>> outer tag in vlan_tci, it's left alone.  This way at the end of ingress
>> vlan_tci is always set.
>> At egress, we grab that tag and compare it against pvid if any.  If it
>> matches, it's stripped.  If it doesn't, we output with the tag thus
>> adding the header.
>>
>> The only thing I can simplify is grab the tci directly at egress, but
>> that's what the code will do anyway.
>
> ... br_allowed_input(..., struct sk_buff **pskb)
> {
>    *pskb = vlan_untag(*pskb);
>    skb = *pskb;
>    if (!skb)
>      return 0;
>
>    if (!skb->vlan_tci && (pvid & VLAN_TAG_PRESENT))
>      skb->vlan_tci = pvid;
>    return check_vlan_allowed(skb->vlan_tci);
> }

vlan_untag typically already happens for non-accelerated packets, so no 
need to call it again.  We shouldn't really be touching accelerated 
packets at ingress, because we may have to undo what we've done on 
egress.  That would be very expensive in case of flooding.

>
> struct sk_buff *br_handle_vlan(..., struct sk_buff *skb)
> {
>     /* we guaranteed in br_allowed_input() that all packets processed
> in bridge code
>      * will be like received with NETIF_F_HW_VLAN_RX feature enabled. */
>     if (skb->vlan_tci & (VLAN_VID_MASK|VLAN_TAG_PRESENT) == pvid)
>       skb->vlan_tci = 0;  // what about 802.1p?
>     return skb;
> }

This doesn't work for when you should be sending a tagged frame to the 
bridge device when receive was non-accelerated.  It would mean that the
802.1q header was already stripped, and you have to put it back 
correctly so that it can go through the vlan interface (think vlan on 
top of bridge).

>
> BTW, you implement three features: VLAN filtering, PVID handling,
> per-VLAN FDB. Yet there are 10 patches that mix and match parts of the
> implementation.

I've tried to separate them as well as I can.  I guess the only ones out
of order are 11 and 12.  12 is last on purpose since its contentious and 
can be easily dropped from the end.  I can and should move 11 in with 
main vlan filtering code.  Essentially 1-5 would be VLAN filtering, 5 & 
6 would be PVID, and the rest would FDB.

If you can see a better breakdown, I would appreciate it.

Thanks
-vlad

> It's really hard to review this when you have to jump
> between patches to understand whats going on. I don't know what the
> best split would be, but I got the feeling that this is not the
> presented one.
>
> Best Regards,
> Michał Mirosław
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 00/12] Add basic VLAN support to bridges
  2013-02-04 16:58   ` Vlad Yasevich
@ 2013-02-07 22:48     ` Vlad Yasevich
  2013-02-07 22:57       ` Stephen Hemminger
  0 siblings, 1 reply; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-07 22:48 UTC (permalink / raw)
  Cc: Stephen Hemminger, bridge, davem, netdev

On 02/04/2013 11:58 AM, Vlad Yasevich wrote:
> On 02/04/2013 11:24 AM, Stephen Hemminger wrote:
>> One thing I am not clear about is whether is supposed to be just
>> a simple filter of VLAN traffic, or a full VLAN aware bridge.
>
> I started with the concept of basic VLAN filtering, but it has been
> morphing into more of a VLAN away bridge.
>
>>
>> The change to make FDB entries per-VLAN seems to be the biggest tipping
>> point into a full VLAN bridge. I am concerned that might break existing
>> API's and Spanning Tree (internal and external).
>>
>
> I debated for a while about whether per-VLAN FDB entries were needed.
> The typing point was that without it, you may end up with flopping FDB
> and possible packet drops or vlan leaks, if say 2 different VMs used the
> same MAC but different VLANs.  Without it, there is an exploitable gap.
>
> I've also tried to separate FDB code changes as much as possible.  If
> you really thing this is a big risk and a barrier to entry, then we can
> drop them.  I am just concerned about the hole I described above, but I
> guess it is not much different then what's there now.
>

So I played with STP for quite a bit and found the FDB changes have 
absolutely no effect on operation of STP.
Since all the vlan filtering code is mostly in forwarding path, STP 
works just fine.
Looking at STP code (the one in the kernel), I don't see any 
dependencies on FDB.  The only userspace code I can find is from here
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/rstp.git.  That 
only seems to ask for RTM_GETLINK, and there you will not get any vlan 
information if you don't set the filter flags.

So, I don't see any API impact as far as STP is concerned.

-vlad

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 00/12] Add basic VLAN support to bridges
  2013-02-07 22:48     ` Vlad Yasevich
@ 2013-02-07 22:57       ` Stephen Hemminger
  2013-02-07 23:00         ` Vlad Yasevich
  0 siblings, 1 reply; 28+ messages in thread
From: Stephen Hemminger @ 2013-02-07 22:57 UTC (permalink / raw)
  To: vyasevic; +Cc: bridge, davem, netdev

On Thu, 07 Feb 2013 17:48:00 -0500
Vlad Yasevich <vyasevic@redhat.com> wrote:

> On 02/04/2013 11:58 AM, Vlad Yasevich wrote:
> > On 02/04/2013 11:24 AM, Stephen Hemminger wrote:
> >> One thing I am not clear about is whether is supposed to be just
> >> a simple filter of VLAN traffic, or a full VLAN aware bridge.
> >
> > I started with the concept of basic VLAN filtering, but it has been
> > morphing into more of a VLAN away bridge.
> >
> >>
> >> The change to make FDB entries per-VLAN seems to be the biggest tipping
> >> point into a full VLAN bridge. I am concerned that might break existing
> >> API's and Spanning Tree (internal and external).
> >>
> >
> > I debated for a while about whether per-VLAN FDB entries were needed.
> > The typing point was that without it, you may end up with flopping FDB
> > and possible packet drops or vlan leaks, if say 2 different VMs used the
> > same MAC but different VLANs.  Without it, there is an exploitable gap.
> >
> > I've also tried to separate FDB code changes as much as possible.  If
> > you really thing this is a big risk and a barrier to entry, then we can
> > drop them.  I am just concerned about the hole I described above, but I
> > guess it is not much different then what's there now.
> >
> 
> So I played with STP for quite a bit and found the FDB changes have 
> absolutely no effect on operation of STP.
> Since all the vlan filtering code is mostly in forwarding path, STP 
> works just fine.
> Looking at STP code (the one in the kernel), I don't see any 
> dependencies on FDB.  The only userspace code I can find is from here
> git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/rstp.git.  That 
> only seems to ask for RTM_GETLINK, and there you will not get any vlan 
> information if you don't set the filter flags.
> 
> So, I don't see any API impact as far as STP is concerned.

Good, does bridge command (in newer iproute2) still work?

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v9 net-next 00/12] Add basic VLAN support to bridges
  2013-02-07 22:57       ` Stephen Hemminger
@ 2013-02-07 23:00         ` Vlad Yasevich
  0 siblings, 0 replies; 28+ messages in thread
From: Vlad Yasevich @ 2013-02-07 23:00 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: bridge, davem, netdev

On 02/07/2013 05:57 PM, Stephen Hemminger wrote:
> On Thu, 07 Feb 2013 17:48:00 -0500
> Vlad Yasevich <vyasevic@redhat.com> wrote:
>
>> On 02/04/2013 11:58 AM, Vlad Yasevich wrote:
>>> On 02/04/2013 11:24 AM, Stephen Hemminger wrote:
>>>> One thing I am not clear about is whether is supposed to be just
>>>> a simple filter of VLAN traffic, or a full VLAN aware bridge.
>>>
>>> I started with the concept of basic VLAN filtering, but it has been
>>> morphing into more of a VLAN away bridge.
>>>
>>>>
>>>> The change to make FDB entries per-VLAN seems to be the biggest tipping
>>>> point into a full VLAN bridge. I am concerned that might break existing
>>>> API's and Spanning Tree (internal and external).
>>>>
>>>
>>> I debated for a while about whether per-VLAN FDB entries were needed.
>>> The typing point was that without it, you may end up with flopping FDB
>>> and possible packet drops or vlan leaks, if say 2 different VMs used the
>>> same MAC but different VLANs.  Without it, there is an exploitable gap.
>>>
>>> I've also tried to separate FDB code changes as much as possible.  If
>>> you really thing this is a big risk and a barrier to entry, then we can
>>> drop them.  I am just concerned about the hole I described above, but I
>>> guess it is not much different then what's there now.
>>>
>>
>> So I played with STP for quite a bit and found the FDB changes have
>> absolutely no effect on operation of STP.
>> Since all the vlan filtering code is mostly in forwarding path, STP
>> works just fine.
>> Looking at STP code (the one in the kernel), I don't see any
>> dependencies on FDB.  The only userspace code I can find is from here
>> git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/rstp.git.  That
>> only seems to ask for RTM_GETLINK, and there you will not get any vlan
>> information if you don't set the filter flags.
>>
>> So, I don't see any API impact as far as STP is concerned.
>
> Good, does bridge command (in newer iproute2) still work?
>

Yes.  I have patches to it enable the vlan functionality, but I haven't 
posted them since I wanted the kernel pieces including the API to get 
accepted.

-vlad

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2013-02-07 23:00 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-01 20:01 [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Vlad Yasevich
2013-02-01 20:01 ` [PATCH v9 net-next 01/12] bridge: Add vlan filtering infrastructure Vlad Yasevich
2013-02-02  1:04   ` Michał Mirosław
2013-02-02  2:11     ` Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 02/12] bridge: Validate that vlan is permitted on ingress Vlad Yasevich
2013-02-02  1:04   ` Michał Mirosław
2013-02-02  2:13     ` Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 03/12] bridge: Verify that a vlan is allowed to egress on give port Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 04/12] bridge: Add netlink interface to configure vlans on bridge ports Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 05/12] bridge: Add the ability to configure pvid Vlad Yasevich
2013-02-02  1:04   ` Michał Mirosław
2013-02-02  1:15     ` Michał Mirosław
2013-02-02  2:22       ` Vlad Yasevich
2013-02-03 14:49         ` Michał Mirosław
2013-02-03 15:20           ` Michał Mirosław
2013-02-04 16:59           ` Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 06/12] bridge: Implement vlan ingress/egress policy Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 07/12] bridge: Add vlan to unicast fdb entries Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 08/12] bridge: Add vlan id to multicast groups Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 09/12] bridge: Add vlan support to static neighbors Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 10/12] bridge: Add vlan support for local fdb entries Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 11/12] bridge: Dump vlan information from a bridge port Vlad Yasevich
2013-02-01 20:02 ` [PATCH v9 net-next 12/12] bridge: Separate egress policy bitmap Vlad Yasevich
2013-02-04 16:24 ` [PATCH v9 net-next 00/12] Add basic VLAN support to bridges Stephen Hemminger
2013-02-04 16:58   ` Vlad Yasevich
2013-02-07 22:48     ` Vlad Yasevich
2013-02-07 22:57       ` Stephen Hemminger
2013-02-07 23:00         ` Vlad Yasevich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).