All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC 0/7] Non-promisc bidge ports support
@ 2014-02-26 15:18 ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

This patch series is a complete re-design and re-implementation of
prior attempts to support non-promiscuous bridge ports.

The basic design is as follows.  The bridge keeps track of
all the ports that flood packets to unknown destinations.  If
the flooding is disabled on the port, to get traffic to flow
through, user/management would need to add an fdb describing
such traffic.  When such fdb is added, we save the address
to bridge private hardware address list. 
Since we now have static configuration for all non-flooding
ports and only 1 flooding port, we can make this single port
non-promiscuous and program the receive filter with our list
of addresses.  On HW that doesn't support unicast filtering or
if the list too bit, the device will be placed in promiscuous mode
by the application of the filter.

There are multiple reasons I chose to do private hw address
list in the bridge in patch 3:
  1)  I tried using the fdb table itself as main repository, but
      this caused difficulties in synchronizing this table with
      the interface filters later on.
  2)  I tried using the bridge device 'uc' list to store these
      addresses, but that caused issues with devices on top of
      a bridge (vlans, bonds) that changed their mac addresses
      and propagated this down to bridge.   I recently figured
      out a way that might allow us to do this which involves
      learning to be added br_dev_xmi().  We can discuss this,
      if there serious objections to current proposal.

There are some other cases when promiscuous mode has to be turned
back on.  One is when the bridge itself if placed in promiscuous
mode (use sets promisc flag).  The other is when vlans devices are
configured on top of the bridge and vlan filtering is disabled (default).
This allows the bridge to receive all tagged frames and doesn't create
a dependency between this code and vlan filtering.

The last patch in the series is a special case where all ports
are non-flooding.  This could be useful in a routed configurations.
In this case, since all ports will be configured manually, we can
sync the our address list across all port of the bridge and make all
ports non-promiscuous.

Thanks
-vlad

Vlad Yasevich (7):
  bridge: Turn flag change macro into a function.
  bridge: Keep track of ports capable of flooding.
  bridge: Add addresses from static fdbs to bridge address list
  bridge: Automatically manage port promiscuous mode.
  bridge: Correctly manage promiscuity when user requested it.
  bridge: Manage promisc mode when vlans are configured on top of a
    bridge
  bridge: Support promisc management when all ports are non-flooding

 include/linux/netdevice.h |   9 +++
 net/bridge/br_device.c    |  23 +++++++
 net/bridge/br_fdb.c       | 122 +++++++++++++++++++++++++++++++++--
 net/bridge/br_if.c        | 159 ++++++++++++++++++++++++++++++++++++++++++++--
 net/bridge/br_netlink.c   |   3 +
 net/bridge/br_private.h   |  18 ++++++
 net/bridge/br_sysfs_if.c  |  33 +++++++---
 net/bridge/br_vlan.c      |   1 +
 net/core/dev.c            |   1 +
 net/core/dev_addr_lists.c |  21 +++---
 10 files changed, 361 insertions(+), 29 deletions(-)

-- 
1.8.5.3

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

* [Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
@ 2014-02-26 15:18 ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

This patch series is a complete re-design and re-implementation of
prior attempts to support non-promiscuous bridge ports.

The basic design is as follows.  The bridge keeps track of
all the ports that flood packets to unknown destinations.  If
the flooding is disabled on the port, to get traffic to flow
through, user/management would need to add an fdb describing
such traffic.  When such fdb is added, we save the address
to bridge private hardware address list. 
Since we now have static configuration for all non-flooding
ports and only 1 flooding port, we can make this single port
non-promiscuous and program the receive filter with our list
of addresses.  On HW that doesn't support unicast filtering or
if the list too bit, the device will be placed in promiscuous mode
by the application of the filter.

There are multiple reasons I chose to do private hw address
list in the bridge in patch 3:
  1)  I tried using the fdb table itself as main repository, but
      this caused difficulties in synchronizing this table with
      the interface filters later on.
  2)  I tried using the bridge device 'uc' list to store these
      addresses, but that caused issues with devices on top of
      a bridge (vlans, bonds) that changed their mac addresses
      and propagated this down to bridge.   I recently figured
      out a way that might allow us to do this which involves
      learning to be added br_dev_xmi().  We can discuss this,
      if there serious objections to current proposal.

There are some other cases when promiscuous mode has to be turned
back on.  One is when the bridge itself if placed in promiscuous
mode (use sets promisc flag).  The other is when vlans devices are
configured on top of the bridge and vlan filtering is disabled (default).
This allows the bridge to receive all tagged frames and doesn't create
a dependency between this code and vlan filtering.

The last patch in the series is a special case where all ports
are non-flooding.  This could be useful in a routed configurations.
In this case, since all ports will be configured manually, we can
sync the our address list across all port of the bridge and make all
ports non-promiscuous.

Thanks
-vlad

Vlad Yasevich (7):
  bridge: Turn flag change macro into a function.
  bridge: Keep track of ports capable of flooding.
  bridge: Add addresses from static fdbs to bridge address list
  bridge: Automatically manage port promiscuous mode.
  bridge: Correctly manage promiscuity when user requested it.
  bridge: Manage promisc mode when vlans are configured on top of a
    bridge
  bridge: Support promisc management when all ports are non-flooding

 include/linux/netdevice.h |   9 +++
 net/bridge/br_device.c    |  23 +++++++
 net/bridge/br_fdb.c       | 122 +++++++++++++++++++++++++++++++++--
 net/bridge/br_if.c        | 159 ++++++++++++++++++++++++++++++++++++++++++++--
 net/bridge/br_netlink.c   |   3 +
 net/bridge/br_private.h   |  18 ++++++
 net/bridge/br_sysfs_if.c  |  33 +++++++---
 net/bridge/br_vlan.c      |   1 +
 net/core/dev.c            |   1 +
 net/core/dev_addr_lists.c |  21 +++---
 10 files changed, 361 insertions(+), 29 deletions(-)

-- 
1.8.5.3


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

* [PATCH 1/7] bridge: Turn flag change macro into a function.
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:18   ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

Turn the flag change macro into a function to allow
easier updates and to reduce space.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index dd595bd..7f66aa4 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -25,6 +25,8 @@ struct brport_attribute {
 	ssize_t (*show)(struct net_bridge_port *, char *);
 	int (*store)(struct net_bridge_port *, unsigned long);
 };
+static int store_flag(struct net_bridge_port *p, unsigned long v,
+		     unsigned long mask);
 
 #define BRPORT_ATTR(_name, _mode, _show, _store)		\
 const struct brport_attribute brport_attr_##_name = { 	        \
@@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
 }								\
 static int store_##_name(struct net_bridge_port *p, unsigned long v) \
 {								\
-	unsigned long flags = p->flags;				\
-	if (v)							\
-		flags |= _mask;					\
-	else							\
-		flags &= ~_mask;				\
-	if (flags != p->flags) {				\
-		p->flags = flags;				\
-		br_ifinfo_notify(RTM_NEWLINK, p);		\
-	}							\
-	return 0;						\
+	return store_flag(p, v, _mask);				\
 }								\
 static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
 		   show_##_name, store_##_name)
 
+static int store_flag(struct net_bridge_port *p, unsigned long v,
+		     unsigned long mask)
+{
+	unsigned long flags = p->flags;
+
+	if (v)
+		flags |= mask;
+	else
+		flags &= ~mask;
+
+	if (flags != p->flags) {
+		p->flags = flags;
+		br_ifinfo_notify(RTM_NEWLINK, p);
+	}
+	return 0;
+}
 
 static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
 {
-- 
1.8.5.3

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

* [Bridge] [PATCH 1/7] bridge: Turn flag change macro into a function.
@ 2014-02-26 15:18   ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

Turn the flag change macro into a function to allow
easier updates and to reduce space.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index dd595bd..7f66aa4 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -25,6 +25,8 @@ struct brport_attribute {
 	ssize_t (*show)(struct net_bridge_port *, char *);
 	int (*store)(struct net_bridge_port *, unsigned long);
 };
+static int store_flag(struct net_bridge_port *p, unsigned long v,
+		     unsigned long mask);
 
 #define BRPORT_ATTR(_name, _mode, _show, _store)		\
 const struct brport_attribute brport_attr_##_name = { 	        \
@@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
 }								\
 static int store_##_name(struct net_bridge_port *p, unsigned long v) \
 {								\
-	unsigned long flags = p->flags;				\
-	if (v)							\
-		flags |= _mask;					\
-	else							\
-		flags &= ~_mask;				\
-	if (flags != p->flags) {				\
-		p->flags = flags;				\
-		br_ifinfo_notify(RTM_NEWLINK, p);		\
-	}							\
-	return 0;						\
+	return store_flag(p, v, _mask);				\
 }								\
 static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
 		   show_##_name, store_##_name)
 
+static int store_flag(struct net_bridge_port *p, unsigned long v,
+		     unsigned long mask)
+{
+	unsigned long flags = p->flags;
+
+	if (v)
+		flags |= mask;
+	else
+		flags &= ~mask;
+
+	if (flags != p->flags) {
+		p->flags = flags;
+		br_ifinfo_notify(RTM_NEWLINK, p);
+	}
+	return 0;
+}
 
 static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
 {
-- 
1.8.5.3


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

* [PATCH 2/7] bridge: Keep track of ports capable of flooding.
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:18   ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

Keep track of bridge ports that have unicast flooding turned on.
This will later be used by the algorithm to automatically manage
address programming and promisc mode.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
 net/bridge/br_netlink.c  |  3 +++
 net/bridge/br_private.h  |  4 ++++
 net/bridge/br_sysfs_if.c |  6 ++++-
 4 files changed, 70 insertions(+), 3 deletions(-)

diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 54d207d..f072b34 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -27,6 +27,9 @@
 
 #include "br_private.h"
 
+static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
+static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
+
 /*
  * Determine initial path cost based on speed.
  * using recommendations from 802.1d standard
@@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
 
 	br_ifinfo_notify(RTM_DELLINK, p);
 
+	list_del_rcu(&p->list);
+
+	if (p->flags & BR_FLOOD)
+		br_del_flood_port(p, br);
+
 	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
 
-	list_del_rcu(&p->list);
-
 	dev->priv_flags &= ~IFF_BRIDGE_PORT;
 
 	netdev_rx_handler_unregister(dev);
@@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 	dev_disable_lro(dev);
 
 	list_add_rcu(&p->list, &br->port_list);
+
+	if (p->flags & BR_FLOOD)
+		br_add_flood_port(p, br);
 
 	netdev_update_features(br->dev);
 
@@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
 
 	return 0;
 }
+
+static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
+{
+	/* Increment the number of  flooding ports, and if we
+	 * only have 1 flooding port cache if for future use.
+	 */
+	br->n_flood_ports++;
+	if (br->n_flood_ports == 1)
+		br->c_flood_port = p;
+}
+
+static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
+{
+	struct net_bridge_port *port;
+
+	/* Decrement the  number of flood port.
+	 * If we are deleting the current flood port, clear
+	 * the cached port.  If we are down to 1 flood port,
+	 * set it if it is not set.
+	 */
+	br->n_flood_ports--;
+	if (p == br->c_flood_port)
+		br->c_flood_port = NULL;
+
+	if (br->n_flood_ports == 1) {
+		list_for_each_entry(port, &p->br->port_list, list) {
+			if (port->flags & BR_FLOOD) {
+				br->c_flood_port = port;
+				break;
+			}
+		}
+	}
+}
+
+void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
+{
+	struct net_bridge *br = p->br;
+
+	/* We are only interested FLOOD flag */
+	if (!(mask & BR_FLOOD))
+		return;
+
+	if (p->flags & BR_FLOOD)
+		br_add_flood_port(p, br);
+	else
+		br_del_flood_port(p, br);
+}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index e74b6d53..01382b9 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
 static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 {
 	int err;
+	unsigned long old_flags = p->flags;
 
 	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
 	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
@@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 		if (err)
 			return err;
 	}
+
+	br_port_flags_change(p, old_flags ^ p->flags);
 	return 0;
 }
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3ba11bc..26a3987 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -290,6 +290,8 @@ struct net_bridge
 	struct timer_list		topology_change_timer;
 	struct timer_list		gc_timer;
 	struct kobject			*ifobj;
+	struct net_bridge_port		*c_flood_port;
+	u32				n_flood_ports;
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
 	u8				vlan_enabled;
 	struct net_port_vlans __rcu	*vlan_info;
@@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
 int br_min_mtu(const struct net_bridge *br);
 netdev_features_t br_features_recompute(struct net_bridge *br,
 					netdev_features_t features);
+void br_port_flags_change(struct net_bridge_port *port,
+			  unsigned long mask);
 
 /* br_input.c */
 int br_handle_frame_finish(struct sk_buff *skb);
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 7f66aa4..9ff6691 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
 static int store_flag(struct net_bridge_port *p, unsigned long v,
 		     unsigned long mask)
 {
-	unsigned long flags = p->flags;
+	unsigned long flags;
+	unsigned long old_flags;
+
+	old_flags = flags = p->flags;
 
 	if (v)
 		flags |= mask;
@@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
 
 	if (flags != p->flags) {
 		p->flags = flags;
+		br_port_flags_change(p, old_flags ^ flags);
 		br_ifinfo_notify(RTM_NEWLINK, p);
 	}
 	return 0;
-- 
1.8.5.3

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

* [Bridge] [PATCH 2/7] bridge: Keep track of ports capable of flooding.
@ 2014-02-26 15:18   ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

Keep track of bridge ports that have unicast flooding turned on.
This will later be used by the algorithm to automatically manage
address programming and promisc mode.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
 net/bridge/br_netlink.c  |  3 +++
 net/bridge/br_private.h  |  4 ++++
 net/bridge/br_sysfs_if.c |  6 ++++-
 4 files changed, 70 insertions(+), 3 deletions(-)

diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 54d207d..f072b34 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -27,6 +27,9 @@
 
 #include "br_private.h"
 
+static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
+static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
+
 /*
  * Determine initial path cost based on speed.
  * using recommendations from 802.1d standard
@@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
 
 	br_ifinfo_notify(RTM_DELLINK, p);
 
+	list_del_rcu(&p->list);
+
+	if (p->flags & BR_FLOOD)
+		br_del_flood_port(p, br);
+
 	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
 
-	list_del_rcu(&p->list);
-
 	dev->priv_flags &= ~IFF_BRIDGE_PORT;
 
 	netdev_rx_handler_unregister(dev);
@@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 	dev_disable_lro(dev);
 
 	list_add_rcu(&p->list, &br->port_list);
+
+	if (p->flags & BR_FLOOD)
+		br_add_flood_port(p, br);
 
 	netdev_update_features(br->dev);
 
@@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
 
 	return 0;
 }
+
+static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
+{
+	/* Increment the number of  flooding ports, and if we
+	 * only have 1 flooding port cache if for future use.
+	 */
+	br->n_flood_ports++;
+	if (br->n_flood_ports == 1)
+		br->c_flood_port = p;
+}
+
+static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
+{
+	struct net_bridge_port *port;
+
+	/* Decrement the  number of flood port.
+	 * If we are deleting the current flood port, clear
+	 * the cached port.  If we are down to 1 flood port,
+	 * set it if it is not set.
+	 */
+	br->n_flood_ports--;
+	if (p == br->c_flood_port)
+		br->c_flood_port = NULL;
+
+	if (br->n_flood_ports == 1) {
+		list_for_each_entry(port, &p->br->port_list, list) {
+			if (port->flags & BR_FLOOD) {
+				br->c_flood_port = port;
+				break;
+			}
+		}
+	}
+}
+
+void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
+{
+	struct net_bridge *br = p->br;
+
+	/* We are only interested FLOOD flag */
+	if (!(mask & BR_FLOOD))
+		return;
+
+	if (p->flags & BR_FLOOD)
+		br_add_flood_port(p, br);
+	else
+		br_del_flood_port(p, br);
+}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index e74b6d53..01382b9 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
 static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 {
 	int err;
+	unsigned long old_flags = p->flags;
 
 	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
 	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
@@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 		if (err)
 			return err;
 	}
+
+	br_port_flags_change(p, old_flags ^ p->flags);
 	return 0;
 }
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3ba11bc..26a3987 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -290,6 +290,8 @@ struct net_bridge
 	struct timer_list		topology_change_timer;
 	struct timer_list		gc_timer;
 	struct kobject			*ifobj;
+	struct net_bridge_port		*c_flood_port;
+	u32				n_flood_ports;
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
 	u8				vlan_enabled;
 	struct net_port_vlans __rcu	*vlan_info;
@@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
 int br_min_mtu(const struct net_bridge *br);
 netdev_features_t br_features_recompute(struct net_bridge *br,
 					netdev_features_t features);
+void br_port_flags_change(struct net_bridge_port *port,
+			  unsigned long mask);
 
 /* br_input.c */
 int br_handle_frame_finish(struct sk_buff *skb);
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 7f66aa4..9ff6691 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
 static int store_flag(struct net_bridge_port *p, unsigned long v,
 		     unsigned long mask)
 {
-	unsigned long flags = p->flags;
+	unsigned long flags;
+	unsigned long old_flags;
+
+	old_flags = flags = p->flags;
 
 	if (v)
 		flags |= mask;
@@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
 
 	if (flags != p->flags) {
 		p->flags = flags;
+		br_port_flags_change(p, old_flags ^ flags);
 		br_ifinfo_notify(RTM_NEWLINK, p);
 	}
 	return 0;
-- 
1.8.5.3


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

* [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:18   ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

When a static fdb entry is created, add the mac address to the bridge
address list.  This list is used to program the proper port's
address list.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/linux/netdevice.h |   6 +++
 net/bridge/br_device.c    |   2 +
 net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
 net/bridge/br_if.c        |  14 ++++--
 net/bridge/br_private.h   |   3 ++
 net/core/dev.c            |   1 +
 net/core/dev_addr_lists.c |  14 +++---
 7 files changed, 134 insertions(+), 16 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 440a02e..e29cce1 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
 void unregister_netdev(struct net_device *dev);
 
 /* General hardware address lists handling functions */
+int __hw_addr_add(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type);
+int __hw_addr_del(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type);
 int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
 		   struct netdev_hw_addr_list *from_list, int addr_len);
 void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 63f0455..1521db6 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
 		u64_stats_init(&br_dev_stats->syncp);
 	}
 
+	__hw_addr_init(&br->conf_addrs);
+
 	return 0;
 }
 
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 9203d5a..ef95e81 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 static void fdb_notify(struct net_bridge *br,
 		       const struct net_bridge_fdb_entry *, int);
 
+static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
+static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
+
 static u32 fdb_salt __read_mostly;
 
 int __init br_fdb_init(void)
@@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
 
 static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
 {
+	if (f->is_static)
+		br_addr_del(br, f->addr.addr);
+
 	hlist_del_rcu(&f->hlist);
 	fdb_notify(br, f, RTM_DELNEIGH);
 	call_rcu(&f->rcu, fdb_rcu_free);
@@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 		return -ENOMEM;
 
 	fdb->is_local = fdb->is_static = 1;
+	br_addr_add(br, addr);
 	fdb_notify(br, fdb, RTM_NEWNEIGH);
 	return 0;
 }
@@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
 	}
 
 	if (fdb_to_nud(fdb) != state) {
-		if (state & NUD_PERMANENT)
-			fdb->is_local = fdb->is_static = 1;
-		else if (state & NUD_NOARP) {
+		if (state & NUD_PERMANENT) {
+			fdb->is_local = 1;
+			if (!fdb->is_static) {
+				fdb->is_static = 1;
+				br_addr_add(br, addr);
+			}
+		} else if (state & NUD_NOARP) {
+			fdb->is_local = 0;
+			if (!fdb->is_static) {
+				fdb->is_static = 1;
+				br_addr_add(br, addr);
+			}
+		} else {
 			fdb->is_local = 0;
-			fdb->is_static = 1;
-		} else
-			fdb->is_local = fdb->is_static = 0;
+			if (fdb->is_static) {
+				fdb->is_static = 0;
+				br_addr_del(br, addr);
+			}
+		}
 
 		modified = true;
 	}
+
 	fdb->added_by_user = 1;
 
 	fdb->used = jiffies;
@@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 out:
 	return err;
 }
+
+
+/* Sync the current list to the correct flood port.  */
+void br_fdb_addrs_sync(struct net_bridge *br)
+{
+	struct net_bridge_port *p;
+	int err;
+
+	/* This function has to run under RTNL.
+	 * Program the user added addresses into the proper port.  This
+	 * fuction follows the following algorithm:
+	 *   a)  If only 1 port is flooding:
+	 *       - write all the addresses to this one port.
+	 */
+	if (br->n_flood_ports == 1) {
+		p = br->c_flood_port;
+		netif_addr_lock(p->dev);
+		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
+				     p->dev->addr_len);
+		if (!err)
+			__dev_set_rx_mode(p->dev);
+		netif_addr_unlock(p->dev);
+
+	}
+}
+
+void br_fdb_addrs_unsync(struct net_bridge *br)
+{
+	struct net_bridge_port *p = br->c_flood_port;
+
+	if (!p)
+		return;
+
+	netif_addr_lock(p->dev);
+	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
+	__dev_set_rx_mode(p->dev);
+	netif_addr_unlock(p->dev);
+}
+
+/* When a static FDB entry is added, the mac address from the entry is
+ * added to the bridge private HW address list and all required ports
+ * are then updated with the new information.
+ * Called under RTNL.
+ */
+static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
+{
+	int err;
+
+	ASSERT_RTNL();
+
+	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
+			    NETDEV_HW_ADDR_T_UNICAST);
+
+	if (!err)
+		br_fdb_addrs_sync(br);
+
+	return err;
+}
+
+/* When a static FDB entry is deleted, the HW address from that entry is
+ * also removed from the bridge private HW address list and updates all
+ * the ports with needed information.
+ * Called under RTNL.
+ */
+static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
+{
+	int err;
+
+	ASSERT_RTNL();
+
+	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
+			    NETDEV_HW_ADDR_T_UNICAST);
+	if (!err)
+		br_fdb_addrs_sync(br);
+
+	return err;
+}
+
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f072b34..e782c2e 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
 
 	br_ifinfo_notify(RTM_DELLINK, p);
 
-	list_del_rcu(&p->list);
+	dev->priv_flags &= ~IFF_BRIDGE_PORT;
 
 	if (p->flags & BR_FLOOD)
 		br_del_flood_port(p, br);
@@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
 	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
 
-	dev->priv_flags &= ~IFF_BRIDGE_PORT;
+	list_del_rcu(&p->list);
 
 	netdev_rx_handler_unregister(dev);
 
@@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	br->n_flood_ports++;
 	if (br->n_flood_ports == 1)
 		br->c_flood_port = p;
+
+	br_fdb_addrs_sync(br);
 }
 
 static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
@@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	 * set it if it is not set.
 	 */
 	br->n_flood_ports--;
-	if (p == br->c_flood_port)
+	if (p == br->c_flood_port) {
+		br_fdb_addrs_unsync(br);
 		br->c_flood_port = NULL;
+	}
 
 	if (br->n_flood_ports == 1) {
 		list_for_each_entry(port, &p->br->port_list, list) {
-			if (port->flags & BR_FLOOD) {
+			if (br_port_exists(port->dev) &&
+			    (port->flags & BR_FLOOD)) {
 				br->c_flood_port = port;
+				br_fdb_addrs_sync(br);
 				break;
 			}
 		}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 26a3987..40a6927 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -296,6 +296,7 @@ struct net_bridge
 	u8				vlan_enabled;
 	struct net_port_vlans __rcu	*vlan_info;
 #endif
+	struct netdev_hw_addr_list	conf_addrs;
 };
 
 struct br_input_skb_cb {
@@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
 	       const unsigned char *addr, u16 nlh_flags);
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 		struct net_device *dev, int idx);
+void br_fdb_addrs_sync(struct net_bridge *br);
+void br_fdb_addrs_unsync(struct net_bridge *br);
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
diff --git a/net/core/dev.c b/net/core/dev.c
index 4ad1b78..eca4d476 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
 	if (ops->ndo_set_rx_mode)
 		ops->ndo_set_rx_mode(dev);
 }
+EXPORT_SYMBOL(__dev_set_rx_mode);
 
 void dev_set_rx_mode(struct net_device *dev)
 {
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 329d579..3de44a3 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
 				   sync);
 }
 
-static int __hw_addr_add(struct netdev_hw_addr_list *list,
-			 const unsigned char *addr, int addr_len,
-			 unsigned char addr_type)
+int __hw_addr_add(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type)
 {
 	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
 				0);
 }
+EXPORT_SYMBOL(__hw_addr_add);
 
 static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
 			       struct netdev_hw_addr *ha, bool global,
@@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
 	return -ENOENT;
 }
 
-static int __hw_addr_del(struct netdev_hw_addr_list *list,
-			 const unsigned char *addr, int addr_len,
-			 unsigned char addr_type)
+int __hw_addr_del(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type)
 {
 	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
 }
+EXPORT_SYMBOL(__hw_addr_del);
 
 static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
 			       struct netdev_hw_addr *ha,
-- 
1.8.5.3

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

* [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 15:18   ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

When a static fdb entry is created, add the mac address to the bridge
address list.  This list is used to program the proper port's
address list.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/linux/netdevice.h |   6 +++
 net/bridge/br_device.c    |   2 +
 net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
 net/bridge/br_if.c        |  14 ++++--
 net/bridge/br_private.h   |   3 ++
 net/core/dev.c            |   1 +
 net/core/dev_addr_lists.c |  14 +++---
 7 files changed, 134 insertions(+), 16 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 440a02e..e29cce1 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
 void unregister_netdev(struct net_device *dev);
 
 /* General hardware address lists handling functions */
+int __hw_addr_add(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type);
+int __hw_addr_del(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type);
 int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
 		   struct netdev_hw_addr_list *from_list, int addr_len);
 void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 63f0455..1521db6 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
 		u64_stats_init(&br_dev_stats->syncp);
 	}
 
+	__hw_addr_init(&br->conf_addrs);
+
 	return 0;
 }
 
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 9203d5a..ef95e81 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 static void fdb_notify(struct net_bridge *br,
 		       const struct net_bridge_fdb_entry *, int);
 
+static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
+static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
+
 static u32 fdb_salt __read_mostly;
 
 int __init br_fdb_init(void)
@@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
 
 static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
 {
+	if (f->is_static)
+		br_addr_del(br, f->addr.addr);
+
 	hlist_del_rcu(&f->hlist);
 	fdb_notify(br, f, RTM_DELNEIGH);
 	call_rcu(&f->rcu, fdb_rcu_free);
@@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 		return -ENOMEM;
 
 	fdb->is_local = fdb->is_static = 1;
+	br_addr_add(br, addr);
 	fdb_notify(br, fdb, RTM_NEWNEIGH);
 	return 0;
 }
@@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
 	}
 
 	if (fdb_to_nud(fdb) != state) {
-		if (state & NUD_PERMANENT)
-			fdb->is_local = fdb->is_static = 1;
-		else if (state & NUD_NOARP) {
+		if (state & NUD_PERMANENT) {
+			fdb->is_local = 1;
+			if (!fdb->is_static) {
+				fdb->is_static = 1;
+				br_addr_add(br, addr);
+			}
+		} else if (state & NUD_NOARP) {
+			fdb->is_local = 0;
+			if (!fdb->is_static) {
+				fdb->is_static = 1;
+				br_addr_add(br, addr);
+			}
+		} else {
 			fdb->is_local = 0;
-			fdb->is_static = 1;
-		} else
-			fdb->is_local = fdb->is_static = 0;
+			if (fdb->is_static) {
+				fdb->is_static = 0;
+				br_addr_del(br, addr);
+			}
+		}
 
 		modified = true;
 	}
+
 	fdb->added_by_user = 1;
 
 	fdb->used = jiffies;
@@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 out:
 	return err;
 }
+
+
+/* Sync the current list to the correct flood port.  */
+void br_fdb_addrs_sync(struct net_bridge *br)
+{
+	struct net_bridge_port *p;
+	int err;
+
+	/* This function has to run under RTNL.
+	 * Program the user added addresses into the proper port.  This
+	 * fuction follows the following algorithm:
+	 *   a)  If only 1 port is flooding:
+	 *       - write all the addresses to this one port.
+	 */
+	if (br->n_flood_ports == 1) {
+		p = br->c_flood_port;
+		netif_addr_lock(p->dev);
+		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
+				     p->dev->addr_len);
+		if (!err)
+			__dev_set_rx_mode(p->dev);
+		netif_addr_unlock(p->dev);
+
+	}
+}
+
+void br_fdb_addrs_unsync(struct net_bridge *br)
+{
+	struct net_bridge_port *p = br->c_flood_port;
+
+	if (!p)
+		return;
+
+	netif_addr_lock(p->dev);
+	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
+	__dev_set_rx_mode(p->dev);
+	netif_addr_unlock(p->dev);
+}
+
+/* When a static FDB entry is added, the mac address from the entry is
+ * added to the bridge private HW address list and all required ports
+ * are then updated with the new information.
+ * Called under RTNL.
+ */
+static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
+{
+	int err;
+
+	ASSERT_RTNL();
+
+	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
+			    NETDEV_HW_ADDR_T_UNICAST);
+
+	if (!err)
+		br_fdb_addrs_sync(br);
+
+	return err;
+}
+
+/* When a static FDB entry is deleted, the HW address from that entry is
+ * also removed from the bridge private HW address list and updates all
+ * the ports with needed information.
+ * Called under RTNL.
+ */
+static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
+{
+	int err;
+
+	ASSERT_RTNL();
+
+	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
+			    NETDEV_HW_ADDR_T_UNICAST);
+	if (!err)
+		br_fdb_addrs_sync(br);
+
+	return err;
+}
+
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f072b34..e782c2e 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
 
 	br_ifinfo_notify(RTM_DELLINK, p);
 
-	list_del_rcu(&p->list);
+	dev->priv_flags &= ~IFF_BRIDGE_PORT;
 
 	if (p->flags & BR_FLOOD)
 		br_del_flood_port(p, br);
@@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
 	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
 
-	dev->priv_flags &= ~IFF_BRIDGE_PORT;
+	list_del_rcu(&p->list);
 
 	netdev_rx_handler_unregister(dev);
 
@@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	br->n_flood_ports++;
 	if (br->n_flood_ports == 1)
 		br->c_flood_port = p;
+
+	br_fdb_addrs_sync(br);
 }
 
 static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
@@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	 * set it if it is not set.
 	 */
 	br->n_flood_ports--;
-	if (p == br->c_flood_port)
+	if (p == br->c_flood_port) {
+		br_fdb_addrs_unsync(br);
 		br->c_flood_port = NULL;
+	}
 
 	if (br->n_flood_ports == 1) {
 		list_for_each_entry(port, &p->br->port_list, list) {
-			if (port->flags & BR_FLOOD) {
+			if (br_port_exists(port->dev) &&
+			    (port->flags & BR_FLOOD)) {
 				br->c_flood_port = port;
+				br_fdb_addrs_sync(br);
 				break;
 			}
 		}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 26a3987..40a6927 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -296,6 +296,7 @@ struct net_bridge
 	u8				vlan_enabled;
 	struct net_port_vlans __rcu	*vlan_info;
 #endif
+	struct netdev_hw_addr_list	conf_addrs;
 };
 
 struct br_input_skb_cb {
@@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
 	       const unsigned char *addr, u16 nlh_flags);
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 		struct net_device *dev, int idx);
+void br_fdb_addrs_sync(struct net_bridge *br);
+void br_fdb_addrs_unsync(struct net_bridge *br);
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
diff --git a/net/core/dev.c b/net/core/dev.c
index 4ad1b78..eca4d476 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
 	if (ops->ndo_set_rx_mode)
 		ops->ndo_set_rx_mode(dev);
 }
+EXPORT_SYMBOL(__dev_set_rx_mode);
 
 void dev_set_rx_mode(struct net_device *dev)
 {
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 329d579..3de44a3 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
 				   sync);
 }
 
-static int __hw_addr_add(struct netdev_hw_addr_list *list,
-			 const unsigned char *addr, int addr_len,
-			 unsigned char addr_type)
+int __hw_addr_add(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type)
 {
 	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
 				0);
 }
+EXPORT_SYMBOL(__hw_addr_add);
 
 static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
 			       struct netdev_hw_addr *ha, bool global,
@@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
 	return -ENOENT;
 }
 
-static int __hw_addr_del(struct netdev_hw_addr_list *list,
-			 const unsigned char *addr, int addr_len,
-			 unsigned char addr_type)
+int __hw_addr_del(struct netdev_hw_addr_list *list,
+		  const unsigned char *addr, int addr_len,
+		  unsigned char addr_type)
 {
 	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
 }
+EXPORT_SYMBOL(__hw_addr_del);
 
 static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
 			       struct netdev_hw_addr *ha,
-- 
1.8.5.3


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

* [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:18   ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

When there is only 1 flooding port, this port is programmed
with all the address the bridge accumulated.  This allows
us to place this port into non-promiscuous mode.
At other times, all ports are set as promiscuous.  To help
track whether the bridge set the mode or not, a new
flag is introduced.

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

diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index e782c2e..51df642 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p)
 
 	sysfs_remove_link(br->ifobj, p->dev->name);
 
-	dev_set_promiscuity(dev, -1);
+	dev_set_allmulti(dev, -1);
 
 	spin_lock_bh(&br->lock);
 	br_stp_disable_port(p);
@@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 
 	call_netdevice_notifiers(NETDEV_JOIN, dev);
 
-	err = dev_set_promiscuity(dev, 1);
+	err = dev_set_allmulti(dev, 1);
 	if (err)
 		goto put_back;
 
@@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
 	return 0;
 }
 
+static int br_port_set_promisc(struct net_bridge_port *p)
+{
+	int err = 0;
+
+	if (p->flags & BR_PROMISC)
+		return err;
+
+	err = dev_set_promiscuity(p->dev, 1);
+	if (err)
+		return err;
+
+	p->flags |= BR_PROMISC;
+	return err;
+}
+
+static void br_port_clear_promisc(struct net_bridge_port *p)
+{
+	if (!(p->flags & BR_PROMISC))
+		return;
+
+	dev_set_promiscuity(p->dev, -1);
+	p->flags &= ~BR_PROMISC;
+}
+
+/* When a port is added or removed or when the flooding status of
+ * the port changes, this function is called to automatically mange
+ * promiscuity setting of all the bridge ports.  We are always called
+ * under RTNL so can skip using rcu primitives.
+ */
+static void br_manage_promisc(struct net_bridge *br)
+{
+	struct net_bridge_port *p;
+
+	list_for_each_entry(p, &br->port_list, list) {
+		if (!br_port_exists(p->dev) ||
+		    (br->n_flood_ports == 1 && br->c_flood_port == p))
+			br_port_clear_promisc(p);
+		else
+			br_port_set_promisc(p);
+	}
+}
+
 static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 {
 	/* Increment the number of  flooding ports, and if we
@@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 		br->c_flood_port = p;
 
 	br_fdb_addrs_sync(br);
+	br_manage_promisc(br);
 }
 
 static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
@@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 			}
 		}
 	}
+	br_manage_promisc(br);
 }
 
 void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 40a6927..6670cb3 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -174,6 +174,7 @@ struct net_bridge_port
 #define BR_ADMIN_COST		0x00000010
 #define BR_LEARNING		0x00000020
 #define BR_FLOOD		0x00000040
+#define BR_PROMISC		0x00000080
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 	struct bridge_mcast_query	ip4_query;
-- 
1.8.5.3

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

* [Bridge] [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
@ 2014-02-26 15:18   ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

When there is only 1 flooding port, this port is programmed
with all the address the bridge accumulated.  This allows
us to place this port into non-promiscuous mode.
At other times, all ports are set as promiscuous.  To help
track whether the bridge set the mode or not, a new
flag is introduced.

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

diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index e782c2e..51df642 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p)
 
 	sysfs_remove_link(br->ifobj, p->dev->name);
 
-	dev_set_promiscuity(dev, -1);
+	dev_set_allmulti(dev, -1);
 
 	spin_lock_bh(&br->lock);
 	br_stp_disable_port(p);
@@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 
 	call_netdevice_notifiers(NETDEV_JOIN, dev);
 
-	err = dev_set_promiscuity(dev, 1);
+	err = dev_set_allmulti(dev, 1);
 	if (err)
 		goto put_back;
 
@@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
 	return 0;
 }
 
+static int br_port_set_promisc(struct net_bridge_port *p)
+{
+	int err = 0;
+
+	if (p->flags & BR_PROMISC)
+		return err;
+
+	err = dev_set_promiscuity(p->dev, 1);
+	if (err)
+		return err;
+
+	p->flags |= BR_PROMISC;
+	return err;
+}
+
+static void br_port_clear_promisc(struct net_bridge_port *p)
+{
+	if (!(p->flags & BR_PROMISC))
+		return;
+
+	dev_set_promiscuity(p->dev, -1);
+	p->flags &= ~BR_PROMISC;
+}
+
+/* When a port is added or removed or when the flooding status of
+ * the port changes, this function is called to automatically mange
+ * promiscuity setting of all the bridge ports.  We are always called
+ * under RTNL so can skip using rcu primitives.
+ */
+static void br_manage_promisc(struct net_bridge *br)
+{
+	struct net_bridge_port *p;
+
+	list_for_each_entry(p, &br->port_list, list) {
+		if (!br_port_exists(p->dev) ||
+		    (br->n_flood_ports == 1 && br->c_flood_port == p))
+			br_port_clear_promisc(p);
+		else
+			br_port_set_promisc(p);
+	}
+}
+
 static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 {
 	/* Increment the number of  flooding ports, and if we
@@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 		br->c_flood_port = p;
 
 	br_fdb_addrs_sync(br);
+	br_manage_promisc(br);
 }
 
 static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
@@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 			}
 		}
 	}
+	br_manage_promisc(br);
 }
 
 void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 40a6927..6670cb3 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -174,6 +174,7 @@ struct net_bridge_port
 #define BR_ADMIN_COST		0x00000010
 #define BR_LEARNING		0x00000020
 #define BR_FLOOD		0x00000040
+#define BR_PROMISC		0x00000080
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 	struct bridge_mcast_query	ip4_query;
-- 
1.8.5.3


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

* [PATCH 5/7] bridge: Correctly manage promiscuity when user requested it.
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:18   ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

When the user places the bridge device in promiscuous mode,
all ports are placed in promisc mode regardless of the number
of flooding ports configured.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_device.c  |  7 +++++++
 net/bridge/br_if.c      | 18 +++++++++++++-----
 net/bridge/br_private.h |  1 +
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 1521db6..0af9d6c 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -121,6 +121,12 @@ static void br_dev_set_multicast_list(struct net_device *dev)
 {
 }
 
+static void br_dev_change_rx_flags(struct net_device *dev, int change)
+{
+	if (change & IFF_PROMISC)
+		br_manage_promisc(netdev_priv(dev));
+}
+
 static int br_dev_stop(struct net_device *dev)
 {
 	struct net_bridge *br = netdev_priv(dev);
@@ -319,6 +325,7 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_get_stats64	 = br_get_stats64,
 	.ndo_set_mac_address	 = br_set_mac_address,
 	.ndo_set_rx_mode	 = br_dev_set_multicast_list,
+	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
 	.ndo_change_mtu		 = br_change_mtu,
 	.ndo_do_ioctl		 = br_dev_ioctl,
 #ifdef CONFIG_NET_POLL_CONTROLLER
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 51df642..7e92bd0 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -494,16 +494,24 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
  * promiscuity setting of all the bridge ports.  We are always called
  * under RTNL so can skip using rcu primitives.
  */
-static void br_manage_promisc(struct net_bridge *br)
+void br_manage_promisc(struct net_bridge *br)
 {
 	struct net_bridge_port *p;
 
 	list_for_each_entry(p, &br->port_list, list) {
-		if (!br_port_exists(p->dev) ||
-		    (br->n_flood_ports == 1 && br->c_flood_port == p))
-			br_port_clear_promisc(p);
-		else
+		if (br->dev->flags & IFF_PROMISC) {
+			/* PROMISC flag has been turned on for the bridge
+			 * itself.  Turn on promisc on all ports.
+			 */
 			br_port_set_promisc(p);
+
+		} else {
+			if (!br_port_exists(p->dev) ||
+			    (br->n_flood_ports == 1 && br->c_flood_port == p))
+				br_port_clear_promisc(p);
+			else if (br_port_exists(p->dev))
+				br_port_set_promisc(p);
+		}
 	}
 }
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 6670cb3..4042f86 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -423,6 +423,7 @@ netdev_features_t br_features_recompute(struct net_bridge *br,
 					netdev_features_t features);
 void br_port_flags_change(struct net_bridge_port *port,
 			  unsigned long mask);
+void br_manage_promisc(struct net_bridge *br);
 
 /* br_input.c */
 int br_handle_frame_finish(struct sk_buff *skb);
-- 
1.8.5.3

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

* [Bridge] [PATCH 5/7] bridge: Correctly manage promiscuity when user requested it.
@ 2014-02-26 15:18   ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

When the user places the bridge device in promiscuous mode,
all ports are placed in promisc mode regardless of the number
of flooding ports configured.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_device.c  |  7 +++++++
 net/bridge/br_if.c      | 18 +++++++++++++-----
 net/bridge/br_private.h |  1 +
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 1521db6..0af9d6c 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -121,6 +121,12 @@ static void br_dev_set_multicast_list(struct net_device *dev)
 {
 }
 
+static void br_dev_change_rx_flags(struct net_device *dev, int change)
+{
+	if (change & IFF_PROMISC)
+		br_manage_promisc(netdev_priv(dev));
+}
+
 static int br_dev_stop(struct net_device *dev)
 {
 	struct net_bridge *br = netdev_priv(dev);
@@ -319,6 +325,7 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_get_stats64	 = br_get_stats64,
 	.ndo_set_mac_address	 = br_set_mac_address,
 	.ndo_set_rx_mode	 = br_dev_set_multicast_list,
+	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
 	.ndo_change_mtu		 = br_change_mtu,
 	.ndo_do_ioctl		 = br_dev_ioctl,
 #ifdef CONFIG_NET_POLL_CONTROLLER
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 51df642..7e92bd0 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -494,16 +494,24 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
  * promiscuity setting of all the bridge ports.  We are always called
  * under RTNL so can skip using rcu primitives.
  */
-static void br_manage_promisc(struct net_bridge *br)
+void br_manage_promisc(struct net_bridge *br)
 {
 	struct net_bridge_port *p;
 
 	list_for_each_entry(p, &br->port_list, list) {
-		if (!br_port_exists(p->dev) ||
-		    (br->n_flood_ports == 1 && br->c_flood_port == p))
-			br_port_clear_promisc(p);
-		else
+		if (br->dev->flags & IFF_PROMISC) {
+			/* PROMISC flag has been turned on for the bridge
+			 * itself.  Turn on promisc on all ports.
+			 */
 			br_port_set_promisc(p);
+
+		} else {
+			if (!br_port_exists(p->dev) ||
+			    (br->n_flood_ports == 1 && br->c_flood_port == p))
+				br_port_clear_promisc(p);
+			else if (br_port_exists(p->dev))
+				br_port_set_promisc(p);
+		}
 	}
 }
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 6670cb3..4042f86 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -423,6 +423,7 @@ netdev_features_t br_features_recompute(struct net_bridge *br,
 					netdev_features_t features);
 void br_port_flags_change(struct net_bridge_port *port,
 			  unsigned long mask);
+void br_manage_promisc(struct net_bridge *br);
 
 /* br_input.c */
 int br_handle_frame_finish(struct sk_buff *skb);
-- 
1.8.5.3


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

* [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:18   ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

If the user configures vlan interfaces on top of the bridge and the bridge
doesn't have vlan filtering enabled, we have to place all the ports in
promsic mode so that we can correctly receive tagged frames.
When vlan filtering is enabled, the vlan configuration will be provided
via filtering interface.
When the vlan filtering is toggled, we also have mange promiscuity.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_device.c  | 14 ++++++++++++++
 net/bridge/br_if.c      | 17 +++++++++++++----
 net/bridge/br_private.h |  9 +++++++++
 net/bridge/br_vlan.c    |  1 +
 4 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 0af9d6c..967abb3 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
 
 #endif
 
+static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
+{
+	br_manage_promisc(netdev_priv(br_dev));
+	return 0;
+}
+
+static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
+{
+	br_manage_promisc(netdev_priv(br_dev));
+	return 0;
+}
+
 static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
 
 {
@@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
 	.ndo_change_mtu		 = br_change_mtu,
 	.ndo_do_ioctl		 = br_dev_ioctl,
+	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	.ndo_netpoll_setup	 = br_netpoll_setup,
 	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 7e92bd0..55e4e28 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
 void br_manage_promisc(struct net_bridge *br)
 {
 	struct net_bridge_port *p;
+	int set_all = false;
+
+	if (br->dev->flags & IFF_PROMISC)
+		set_all = true;
+
+	/* If vlan filtering is disabled and there are any VLANs
+	 * configured on top of the bridge, set promisc on all
+	 * ports.
+	 */
+	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
+		set_all = true;
 
 	list_for_each_entry(p, &br->port_list, list) {
-		if (br->dev->flags & IFF_PROMISC) {
-			/* PROMISC flag has been turned on for the bridge
-			 * itself.  Turn on promisc on all ports.
-			 */
+		if (set_all) {
+			/* Set all the ports to promisc mode.  */
 			br_port_set_promisc(p);
 
 		} else {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4042f86..87dcc09 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
 	return v->pvid ?: VLAN_N_VID;
 }
 
+static inline int br_vlan_enabled(struct net_bridge *br)
+{
+	return br->vlan_enabled;
+}
 #else
 static inline bool br_allowed_ingress(struct net_bridge *br,
 				      struct net_port_vlans *v,
@@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
 {
 	return VLAN_N_VID;	/* Returns invalid vid */
 }
+
+static inline int br_vlan_enabled(struct net_bridge *br);
+{
+	return 0;
+}
 #endif
 
 /* br_netfilter.c */
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 8249ca7..eddc2f6 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
 		goto unlock;
 
 	br->vlan_enabled = val;
+	br_manage_promisc(br);
 
 unlock:
 	rtnl_unlock();
-- 
1.8.5.3

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

* [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-02-26 15:18   ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

If the user configures vlan interfaces on top of the bridge and the bridge
doesn't have vlan filtering enabled, we have to place all the ports in
promsic mode so that we can correctly receive tagged frames.
When vlan filtering is enabled, the vlan configuration will be provided
via filtering interface.
When the vlan filtering is toggled, we also have mange promiscuity.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/br_device.c  | 14 ++++++++++++++
 net/bridge/br_if.c      | 17 +++++++++++++----
 net/bridge/br_private.h |  9 +++++++++
 net/bridge/br_vlan.c    |  1 +
 4 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 0af9d6c..967abb3 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
 
 #endif
 
+static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
+{
+	br_manage_promisc(netdev_priv(br_dev));
+	return 0;
+}
+
+static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
+{
+	br_manage_promisc(netdev_priv(br_dev));
+	return 0;
+}
+
 static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
 
 {
@@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
 	.ndo_change_mtu		 = br_change_mtu,
 	.ndo_do_ioctl		 = br_dev_ioctl,
+	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	.ndo_netpoll_setup	 = br_netpoll_setup,
 	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 7e92bd0..55e4e28 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
 void br_manage_promisc(struct net_bridge *br)
 {
 	struct net_bridge_port *p;
+	int set_all = false;
+
+	if (br->dev->flags & IFF_PROMISC)
+		set_all = true;
+
+	/* If vlan filtering is disabled and there are any VLANs
+	 * configured on top of the bridge, set promisc on all
+	 * ports.
+	 */
+	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
+		set_all = true;
 
 	list_for_each_entry(p, &br->port_list, list) {
-		if (br->dev->flags & IFF_PROMISC) {
-			/* PROMISC flag has been turned on for the bridge
-			 * itself.  Turn on promisc on all ports.
-			 */
+		if (set_all) {
+			/* Set all the ports to promisc mode.  */
 			br_port_set_promisc(p);
 
 		} else {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4042f86..87dcc09 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
 	return v->pvid ?: VLAN_N_VID;
 }
 
+static inline int br_vlan_enabled(struct net_bridge *br)
+{
+	return br->vlan_enabled;
+}
 #else
 static inline bool br_allowed_ingress(struct net_bridge *br,
 				      struct net_port_vlans *v,
@@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
 {
 	return VLAN_N_VID;	/* Returns invalid vid */
 }
+
+static inline int br_vlan_enabled(struct net_bridge *br);
+{
+	return 0;
+}
 #endif
 
 /* br_netfilter.c */
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 8249ca7..eddc2f6 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
 		goto unlock;
 
 	br->vlan_enabled = val;
+	br_manage_promisc(br);
 
 unlock:
 	rtnl_unlock();
-- 
1.8.5.3


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

* [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:18   ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend, Vlad Yasevich

Configuration where all ports are set to non-flooding is a slight
special case.  In this config, the user would have to manage all fdbs
for all ports.  In this special case, since we'll know all addresses,
we can turn off promisc on all ports and program all ports with the
same set of addresses.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/linux/netdevice.h |  3 +++
 net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
 net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
 net/bridge/br_private.h   |  2 +-
 net/core/dev_addr_lists.c |  7 ++++---
 5 files changed, 59 insertions(+), 15 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index e29cce1..79e97ee 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
 		  unsigned char addr_type);
 int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
 		   struct netdev_hw_addr_list *from_list, int addr_len);
+int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
+			    struct netdev_hw_addr_list *from_list,
+			    int addr_len);
 void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
 		      struct netdev_hw_addr_list *from_list, int addr_len);
 void __hw_addr_init(struct netdev_hw_addr_list *list);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index ef95e81..26ea4fe 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
 	if (br->n_flood_ports == 1) {
 		p = br->c_flood_port;
 		netif_addr_lock(p->dev);
-		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
-				     p->dev->addr_len);
+		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
+					      p->dev->addr_len);
 		if (!err)
 			__dev_set_rx_mode(p->dev);
 		netif_addr_unlock(p->dev);
+	} else if (br->n_flood_ports == 0) {
+		list_for_each_entry(p, &br->port_list, list) {
+			/* skip over ports being deleted. */
+			if (!br_port_exists(p->dev))
+				continue;
 
+			netif_addr_lock_nested(p->dev);
+			err = __hw_addr_sync_multiple(&p->dev->uc,
+						      &br->conf_addrs,
+						      p->dev->addr_len);
+			if (!err)
+				__dev_set_rx_mode(p->dev);
+			netif_addr_unlock(p->dev);
+		}
 	}
+
 }
 
-void br_fdb_addrs_unsync(struct net_bridge *br)
+void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
 {
-	struct net_bridge_port *p = br->c_flood_port;
-
 	if (!p)
 		return;
 
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 55e4e28..4ba62ef 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
 
 	if (p->flags & BR_FLOOD)
 		br_del_flood_port(p, br);
+	else
+		br_fdb_addrs_unsync(br, p);
 
 	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
@@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	 * only have 1 flooding port cache if for future use.
 	 */
 	br->n_flood_ports++;
-	if (br->n_flood_ports == 1)
+	if (br->n_flood_ports == 1) {
+		struct net_bridge_port *port;
+
+		/* We are transitioning from 0 flood ports to 1.  Remove
+		 * the addresses from all the non-flood ports and turn on
+		 * promisc on those ports.
+		 */
+		list_for_each_entry(port, &br->port_list, list) {
+			/* skip the current port we are changing */
+			if (port == p)
+				continue;
+
+			if (!(port->flags & BR_FLOOD)) {
+				br_port_set_promisc(port);
+				br_fdb_addrs_unsync(br, port);
+			}
+		}
+
 		br->c_flood_port = p;
+	}
 
 	br_fdb_addrs_sync(br);
 	br_manage_promisc(br);
@@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	 * set it if it is not set.
 	 */
 	br->n_flood_ports--;
-	if (p == br->c_flood_port) {
-		br_fdb_addrs_unsync(br);
-		br->c_flood_port = NULL;
-	}
+	if (br->n_flood_ports == 0) {
+		/* We just dropped to 0 flood ports.  If we
+		 * are deleting this port, unsync addresses
+		 * from it.
+		 */
+		if (!br_port_exists(p->dev))
+			br_fdb_addrs_unsync(br, p);
 
-	if (br->n_flood_ports == 1) {
+		br->c_flood_port = NULL;
+		br_fdb_addrs_sync(br);
+	} else if (br->n_flood_ports == 1) {
+		/* We just dropped to 1 flood port. Find this one flood
+		 * port and sync to it.
+		 */
 		list_for_each_entry(port, &p->br->port_list, list) {
 			if (br_port_exists(port->dev) &&
 			    (port->flags & BR_FLOOD)) {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 87dcc09..13840de 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 		struct net_device *dev, int idx);
 void br_fdb_addrs_sync(struct net_bridge *br);
-void br_fdb_addrs_unsync(struct net_bridge *br);
+void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 3de44a3..24da78f 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
 	__hw_addr_del_entry(from_list, ha, false, false);
 }
 
-static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
-				   struct netdev_hw_addr_list *from_list,
-				   int addr_len)
+int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
+			    struct netdev_hw_addr_list *from_list,
+			    int addr_len)
 {
 	int err = 0;
 	struct netdev_hw_addr *ha, *tmp;
@@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
 	}
 	return err;
 }
+EXPORT_SYMBOL(__hw_addr_sync_multiple);
 
 /* This function only works where there is a strict 1-1 relationship
  * between source and destionation of they synch. If you ever need to
-- 
1.8.5.3

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

* [Bridge] [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
@ 2014-02-26 15:18   ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:18 UTC (permalink / raw)
  To: netdev; +Cc: Vlad Yasevich, mst, bridge, jhs, john.r.fastabend, shemminger

Configuration where all ports are set to non-flooding is a slight
special case.  In this config, the user would have to manage all fdbs
for all ports.  In this special case, since we'll know all addresses,
we can turn off promisc on all ports and program all ports with the
same set of addresses.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/linux/netdevice.h |  3 +++
 net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
 net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
 net/bridge/br_private.h   |  2 +-
 net/core/dev_addr_lists.c |  7 ++++---
 5 files changed, 59 insertions(+), 15 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index e29cce1..79e97ee 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
 		  unsigned char addr_type);
 int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
 		   struct netdev_hw_addr_list *from_list, int addr_len);
+int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
+			    struct netdev_hw_addr_list *from_list,
+			    int addr_len);
 void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
 		      struct netdev_hw_addr_list *from_list, int addr_len);
 void __hw_addr_init(struct netdev_hw_addr_list *list);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index ef95e81..26ea4fe 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
 	if (br->n_flood_ports == 1) {
 		p = br->c_flood_port;
 		netif_addr_lock(p->dev);
-		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
-				     p->dev->addr_len);
+		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
+					      p->dev->addr_len);
 		if (!err)
 			__dev_set_rx_mode(p->dev);
 		netif_addr_unlock(p->dev);
+	} else if (br->n_flood_ports == 0) {
+		list_for_each_entry(p, &br->port_list, list) {
+			/* skip over ports being deleted. */
+			if (!br_port_exists(p->dev))
+				continue;
 
+			netif_addr_lock_nested(p->dev);
+			err = __hw_addr_sync_multiple(&p->dev->uc,
+						      &br->conf_addrs,
+						      p->dev->addr_len);
+			if (!err)
+				__dev_set_rx_mode(p->dev);
+			netif_addr_unlock(p->dev);
+		}
 	}
+
 }
 
-void br_fdb_addrs_unsync(struct net_bridge *br)
+void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
 {
-	struct net_bridge_port *p = br->c_flood_port;
-
 	if (!p)
 		return;
 
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 55e4e28..4ba62ef 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
 
 	if (p->flags & BR_FLOOD)
 		br_del_flood_port(p, br);
+	else
+		br_fdb_addrs_unsync(br, p);
 
 	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
@@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	 * only have 1 flooding port cache if for future use.
 	 */
 	br->n_flood_ports++;
-	if (br->n_flood_ports == 1)
+	if (br->n_flood_ports == 1) {
+		struct net_bridge_port *port;
+
+		/* We are transitioning from 0 flood ports to 1.  Remove
+		 * the addresses from all the non-flood ports and turn on
+		 * promisc on those ports.
+		 */
+		list_for_each_entry(port, &br->port_list, list) {
+			/* skip the current port we are changing */
+			if (port == p)
+				continue;
+
+			if (!(port->flags & BR_FLOOD)) {
+				br_port_set_promisc(port);
+				br_fdb_addrs_unsync(br, port);
+			}
+		}
+
 		br->c_flood_port = p;
+	}
 
 	br_fdb_addrs_sync(br);
 	br_manage_promisc(br);
@@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
 	 * set it if it is not set.
 	 */
 	br->n_flood_ports--;
-	if (p == br->c_flood_port) {
-		br_fdb_addrs_unsync(br);
-		br->c_flood_port = NULL;
-	}
+	if (br->n_flood_ports == 0) {
+		/* We just dropped to 0 flood ports.  If we
+		 * are deleting this port, unsync addresses
+		 * from it.
+		 */
+		if (!br_port_exists(p->dev))
+			br_fdb_addrs_unsync(br, p);
 
-	if (br->n_flood_ports == 1) {
+		br->c_flood_port = NULL;
+		br_fdb_addrs_sync(br);
+	} else if (br->n_flood_ports == 1) {
+		/* We just dropped to 1 flood port. Find this one flood
+		 * port and sync to it.
+		 */
 		list_for_each_entry(port, &p->br->port_list, list) {
 			if (br_port_exists(port->dev) &&
 			    (port->flags & BR_FLOOD)) {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 87dcc09..13840de 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 		struct net_device *dev, int idx);
 void br_fdb_addrs_sync(struct net_bridge *br);
-void br_fdb_addrs_unsync(struct net_bridge *br);
+void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 3de44a3..24da78f 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
 	__hw_addr_del_entry(from_list, ha, false, false);
 }
 
-static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
-				   struct netdev_hw_addr_list *from_list,
-				   int addr_len)
+int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
+			    struct netdev_hw_addr_list *from_list,
+			    int addr_len)
 {
 	int err = 0;
 	struct netdev_hw_addr *ha, *tmp;
@@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
 	}
 	return err;
 }
+EXPORT_SYMBOL(__hw_addr_sync_multiple);
 
 /* This function only works where there is a strict 1-1 relationship
  * between source and destionation of they synch. If you ever need to
-- 
1.8.5.3


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

* Re: [PATCH 1/7] bridge: Turn flag change macro into a function.
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:29     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:29 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:19AM -0500, Vlad Yasevich wrote:
> Turn the flag change macro into a function to allow
> easier updates and to reduce space.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++----------
>  1 file changed, 19 insertions(+), 10 deletions(-)
> 
> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> index dd595bd..7f66aa4 100644
> --- a/net/bridge/br_sysfs_if.c
> +++ b/net/bridge/br_sysfs_if.c
> @@ -25,6 +25,8 @@ struct brport_attribute {
>  	ssize_t (*show)(struct net_bridge_port *, char *);
>  	int (*store)(struct net_bridge_port *, unsigned long);
>  };
> +static int store_flag(struct net_bridge_port *p, unsigned long v,
> +		     unsigned long mask);
>  
>  #define BRPORT_ATTR(_name, _mode, _show, _store)		\
>  const struct brport_attribute brport_attr_##_name = { 	        \

nitpicking:
Do we have to have forward declarations like this?
They make it harder to find where the code is.

Also, pls add an empty line between struct and function.


> @@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
>  }								\
>  static int store_##_name(struct net_bridge_port *p, unsigned long v) \
>  {								\
> -	unsigned long flags = p->flags;				\
> -	if (v)							\
> -		flags |= _mask;					\
> -	else							\
> -		flags &= ~_mask;				\
> -	if (flags != p->flags) {				\
> -		p->flags = flags;				\
> -		br_ifinfo_notify(RTM_NEWLINK, p);		\
> -	}							\
> -	return 0;						\
> +	return store_flag(p, v, _mask);				\
>  }								\
>  static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>  		   show_##_name, store_##_name)
>  
> +static int store_flag(struct net_bridge_port *p, unsigned long v,
> +		     unsigned long mask)
> +{
> +	unsigned long flags = p->flags;
> +
> +	if (v)
> +		flags |= mask;
> +	else
> +		flags &= ~mask;
> +
> +	if (flags != p->flags) {
> +		p->flags = flags;
> +		br_ifinfo_notify(RTM_NEWLINK, p);
> +	}
> +	return 0;
> +}
>  
>  static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
>  {
> -- 
> 1.8.5.3

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

* Re: [Bridge] [PATCH 1/7] bridge: Turn flag change macro into a function.
@ 2014-02-26 15:29     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:29 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:19AM -0500, Vlad Yasevich wrote:
> Turn the flag change macro into a function to allow
> easier updates and to reduce space.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++----------
>  1 file changed, 19 insertions(+), 10 deletions(-)
> 
> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> index dd595bd..7f66aa4 100644
> --- a/net/bridge/br_sysfs_if.c
> +++ b/net/bridge/br_sysfs_if.c
> @@ -25,6 +25,8 @@ struct brport_attribute {
>  	ssize_t (*show)(struct net_bridge_port *, char *);
>  	int (*store)(struct net_bridge_port *, unsigned long);
>  };
> +static int store_flag(struct net_bridge_port *p, unsigned long v,
> +		     unsigned long mask);
>  
>  #define BRPORT_ATTR(_name, _mode, _show, _store)		\
>  const struct brport_attribute brport_attr_##_name = { 	        \

nitpicking:
Do we have to have forward declarations like this?
They make it harder to find where the code is.

Also, pls add an empty line between struct and function.


> @@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
>  }								\
>  static int store_##_name(struct net_bridge_port *p, unsigned long v) \
>  {								\
> -	unsigned long flags = p->flags;				\
> -	if (v)							\
> -		flags |= _mask;					\
> -	else							\
> -		flags &= ~_mask;				\
> -	if (flags != p->flags) {				\
> -		p->flags = flags;				\
> -		br_ifinfo_notify(RTM_NEWLINK, p);		\
> -	}							\
> -	return 0;						\
> +	return store_flag(p, v, _mask);				\
>  }								\
>  static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>  		   show_##_name, store_##_name)
>  
> +static int store_flag(struct net_bridge_port *p, unsigned long v,
> +		     unsigned long mask)
> +{
> +	unsigned long flags = p->flags;
> +
> +	if (v)
> +		flags |= mask;
> +	else
> +		flags &= ~mask;
> +
> +	if (flags != p->flags) {
> +		p->flags = flags;
> +		br_ifinfo_notify(RTM_NEWLINK, p);
> +	}
> +	return 0;
> +}
>  
>  static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
>  {
> -- 
> 1.8.5.3

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

* Re: [PATCH 1/7] bridge: Turn flag change macro into a function.
  2014-02-26 15:29     ` [Bridge] " Michael S. Tsirkin
@ 2014-02-26 15:36       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:36 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:29 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:19AM -0500, Vlad Yasevich wrote:
>> Turn the flag change macro into a function to allow
>> easier updates and to reduce space.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++----------
>>  1 file changed, 19 insertions(+), 10 deletions(-)
>>
>> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
>> index dd595bd..7f66aa4 100644
>> --- a/net/bridge/br_sysfs_if.c
>> +++ b/net/bridge/br_sysfs_if.c
>> @@ -25,6 +25,8 @@ struct brport_attribute {
>>  	ssize_t (*show)(struct net_bridge_port *, char *);
>>  	int (*store)(struct net_bridge_port *, unsigned long);
>>  };
>> +static int store_flag(struct net_bridge_port *p, unsigned long v,
>> +		     unsigned long mask);
>>  
>>  #define BRPORT_ATTR(_name, _mode, _show, _store)		\
>>  const struct brport_attribute brport_attr_##_name = { 	        \
> 
> nitpicking:
> Do we have to have forward declarations like this?
> They make it harder to find where the code is.
> 
> Also, pls add an empty line between struct and function.

Forward declaration not needed.  Will remove.

Thanks
-vlad

> 
> 
>> @@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
>>  }								\
>>  static int store_##_name(struct net_bridge_port *p, unsigned long v) \
>>  {								\
>> -	unsigned long flags = p->flags;				\
>> -	if (v)							\
>> -		flags |= _mask;					\
>> -	else							\
>> -		flags &= ~_mask;				\
>> -	if (flags != p->flags) {				\
>> -		p->flags = flags;				\
>> -		br_ifinfo_notify(RTM_NEWLINK, p);		\
>> -	}							\
>> -	return 0;						\
>> +	return store_flag(p, v, _mask);				\
>>  }								\
>>  static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>>  		   show_##_name, store_##_name)
>>  
>> +static int store_flag(struct net_bridge_port *p, unsigned long v,
>> +		     unsigned long mask)
>> +{
>> +	unsigned long flags = p->flags;
>> +
>> +	if (v)
>> +		flags |= mask;
>> +	else
>> +		flags &= ~mask;
>> +
>> +	if (flags != p->flags) {
>> +		p->flags = flags;
>> +		br_ifinfo_notify(RTM_NEWLINK, p);
>> +	}
>> +	return 0;
>> +}
>>  
>>  static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
>>  {
>> -- 
>> 1.8.5.3

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

* Re: [Bridge] [PATCH 1/7] bridge: Turn flag change macro into a function.
@ 2014-02-26 15:36       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:36 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:29 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:19AM -0500, Vlad Yasevich wrote:
>> Turn the flag change macro into a function to allow
>> easier updates and to reduce space.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++----------
>>  1 file changed, 19 insertions(+), 10 deletions(-)
>>
>> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
>> index dd595bd..7f66aa4 100644
>> --- a/net/bridge/br_sysfs_if.c
>> +++ b/net/bridge/br_sysfs_if.c
>> @@ -25,6 +25,8 @@ struct brport_attribute {
>>  	ssize_t (*show)(struct net_bridge_port *, char *);
>>  	int (*store)(struct net_bridge_port *, unsigned long);
>>  };
>> +static int store_flag(struct net_bridge_port *p, unsigned long v,
>> +		     unsigned long mask);
>>  
>>  #define BRPORT_ATTR(_name, _mode, _show, _store)		\
>>  const struct brport_attribute brport_attr_##_name = { 	        \
> 
> nitpicking:
> Do we have to have forward declarations like this?
> They make it harder to find where the code is.
> 
> Also, pls add an empty line between struct and function.

Forward declaration not needed.  Will remove.

Thanks
-vlad

> 
> 
>> @@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
>>  }								\
>>  static int store_##_name(struct net_bridge_port *p, unsigned long v) \
>>  {								\
>> -	unsigned long flags = p->flags;				\
>> -	if (v)							\
>> -		flags |= _mask;					\
>> -	else							\
>> -		flags &= ~_mask;				\
>> -	if (flags != p->flags) {				\
>> -		p->flags = flags;				\
>> -		br_ifinfo_notify(RTM_NEWLINK, p);		\
>> -	}							\
>> -	return 0;						\
>> +	return store_flag(p, v, _mask);				\
>>  }								\
>>  static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>>  		   show_##_name, store_##_name)
>>  
>> +static int store_flag(struct net_bridge_port *p, unsigned long v,
>> +		     unsigned long mask)
>> +{
>> +	unsigned long flags = p->flags;
>> +
>> +	if (v)
>> +		flags |= mask;
>> +	else
>> +		flags &= ~mask;
>> +
>> +	if (flags != p->flags) {
>> +		p->flags = flags;
>> +		br_ifinfo_notify(RTM_NEWLINK, p);
>> +	}
>> +	return 0;
>> +}
>>  
>>  static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
>>  {
>> -- 
>> 1.8.5.3


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

* Re: [PATCH 2/7] bridge: Keep track of ports capable of flooding.
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:41     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:41 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:20AM -0500, Vlad Yasevich wrote:
> Keep track of bridge ports that have unicast flooding turned on.
> This will later be used by the algorithm to automatically manage
> address programming and promisc mode.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
>  net/bridge/br_netlink.c  |  3 +++
>  net/bridge/br_private.h  |  4 ++++
>  net/bridge/br_sysfs_if.c |  6 ++++-
>  4 files changed, 70 insertions(+), 3 deletions(-)
> 
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 54d207d..f072b34 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -27,6 +27,9 @@
>  
>  #include "br_private.h"
>  
> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> +

same nitpick about forward declarations of static
functions.
maybe they are ok for bridge, but that's my $.02


>  /*
>   * Determine initial path cost based on speed.
>   * using recommendations from 802.1d standard
> @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	br_ifinfo_notify(RTM_DELLINK, p);
>  
> +	list_del_rcu(&p->list);
> +
> +	if (p->flags & BR_FLOOD)
> +		br_del_flood_port(p, br);
> +
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
>  
> -	list_del_rcu(&p->list);
> -
>  	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>  
>  	netdev_rx_handler_unregister(dev);

ok so it doesn't matter in which order we do
list_del_rcu and nbp_vlan_flush then?

> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>  	dev_disable_lro(dev);
>  
>  	list_add_rcu(&p->list, &br->port_list);
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);
>  
>  	netdev_update_features(br->dev);
>  
> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>  
>  	return 0;
>  }
> +
> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	/* Increment the number of  flooding ports, and if we
> +	 * only have 1 flooding port cache if for future use.
> +	 */
> +	br->n_flood_ports++;
> +	if (br->n_flood_ports == 1)
> +		br->c_flood_port = p;
> +}
> +
> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	struct net_bridge_port *port;
> +
> +	/* Decrement the  number of flood port.
> +	 * If we are deleting the current flood port, clear
> +	 * the cached port.  If we are down to 1 flood port,
> +	 * set it if it is not set.
> +	 */
> +	br->n_flood_ports--;
> +	if (p == br->c_flood_port)
> +		br->c_flood_port = NULL;
> +
> +	if (br->n_flood_ports == 1) {
> +		list_for_each_entry(port, &p->br->port_list, list) {
> +			if (port->flags & BR_FLOOD) {
> +				br->c_flood_port = port;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> +{
> +	struct net_bridge *br = p->br;
> +
> +	/* We are only interested FLOOD flag */
> +	if (!(mask & BR_FLOOD))
> +		return;
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);
> +	else
> +		br_del_flood_port(p, br);
> +}
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index e74b6d53..01382b9 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  {
>  	int err;
> +	unsigned long old_flags = p->flags;
>  
>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  		if (err)
>  			return err;
>  	}
> +
> +	br_port_flags_change(p, old_flags ^ p->flags);
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 3ba11bc..26a3987 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -290,6 +290,8 @@ struct net_bridge
>  	struct timer_list		topology_change_timer;
>  	struct timer_list		gc_timer;
>  	struct kobject			*ifobj;
> +	struct net_bridge_port		*c_flood_port;
> +	u32				n_flood_ports;

any hints on locking for these fields?
are these all under rtnl?

>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>  int br_min_mtu(const struct net_bridge *br);
>  netdev_features_t br_features_recompute(struct net_bridge *br,
>  					netdev_features_t features);
> +void br_port_flags_change(struct net_bridge_port *port,
> +			  unsigned long mask);
>  
>  /* br_input.c */
>  int br_handle_frame_finish(struct sk_buff *skb);
> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> index 7f66aa4..9ff6691 100644
> --- a/net/bridge/br_sysfs_if.c
> +++ b/net/bridge/br_sysfs_if.c
> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>  		     unsigned long mask)
>  {
> -	unsigned long flags = p->flags;
> +	unsigned long flags;
> +	unsigned long old_flags;
> +
> +	old_flags = flags = p->flags;
>  
>  	if (v)
>  		flags |= mask;
> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>  
>  	if (flags != p->flags) {
>  		p->flags = flags;
> +		br_port_flags_change(p, old_flags ^ flags);
>  		br_ifinfo_notify(RTM_NEWLINK, p);
>  	}
>  	return 0;
> -- 
> 1.8.5.3

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

* Re: [Bridge] [PATCH 2/7] bridge: Keep track of ports capable of flooding.
@ 2014-02-26 15:41     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:41 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:20AM -0500, Vlad Yasevich wrote:
> Keep track of bridge ports that have unicast flooding turned on.
> This will later be used by the algorithm to automatically manage
> address programming and promisc mode.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
>  net/bridge/br_netlink.c  |  3 +++
>  net/bridge/br_private.h  |  4 ++++
>  net/bridge/br_sysfs_if.c |  6 ++++-
>  4 files changed, 70 insertions(+), 3 deletions(-)
> 
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 54d207d..f072b34 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -27,6 +27,9 @@
>  
>  #include "br_private.h"
>  
> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> +

same nitpick about forward declarations of static
functions.
maybe they are ok for bridge, but that's my $.02


>  /*
>   * Determine initial path cost based on speed.
>   * using recommendations from 802.1d standard
> @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	br_ifinfo_notify(RTM_DELLINK, p);
>  
> +	list_del_rcu(&p->list);
> +
> +	if (p->flags & BR_FLOOD)
> +		br_del_flood_port(p, br);
> +
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
>  
> -	list_del_rcu(&p->list);
> -
>  	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>  
>  	netdev_rx_handler_unregister(dev);

ok so it doesn't matter in which order we do
list_del_rcu and nbp_vlan_flush then?

> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>  	dev_disable_lro(dev);
>  
>  	list_add_rcu(&p->list, &br->port_list);
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);
>  
>  	netdev_update_features(br->dev);
>  
> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>  
>  	return 0;
>  }
> +
> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	/* Increment the number of  flooding ports, and if we
> +	 * only have 1 flooding port cache if for future use.
> +	 */
> +	br->n_flood_ports++;
> +	if (br->n_flood_ports == 1)
> +		br->c_flood_port = p;
> +}
> +
> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	struct net_bridge_port *port;
> +
> +	/* Decrement the  number of flood port.
> +	 * If we are deleting the current flood port, clear
> +	 * the cached port.  If we are down to 1 flood port,
> +	 * set it if it is not set.
> +	 */
> +	br->n_flood_ports--;
> +	if (p == br->c_flood_port)
> +		br->c_flood_port = NULL;
> +
> +	if (br->n_flood_ports == 1) {
> +		list_for_each_entry(port, &p->br->port_list, list) {
> +			if (port->flags & BR_FLOOD) {
> +				br->c_flood_port = port;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> +{
> +	struct net_bridge *br = p->br;
> +
> +	/* We are only interested FLOOD flag */
> +	if (!(mask & BR_FLOOD))
> +		return;
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);
> +	else
> +		br_del_flood_port(p, br);
> +}
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index e74b6d53..01382b9 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  {
>  	int err;
> +	unsigned long old_flags = p->flags;
>  
>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  		if (err)
>  			return err;
>  	}
> +
> +	br_port_flags_change(p, old_flags ^ p->flags);
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 3ba11bc..26a3987 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -290,6 +290,8 @@ struct net_bridge
>  	struct timer_list		topology_change_timer;
>  	struct timer_list		gc_timer;
>  	struct kobject			*ifobj;
> +	struct net_bridge_port		*c_flood_port;
> +	u32				n_flood_ports;

any hints on locking for these fields?
are these all under rtnl?

>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>  int br_min_mtu(const struct net_bridge *br);
>  netdev_features_t br_features_recompute(struct net_bridge *br,
>  					netdev_features_t features);
> +void br_port_flags_change(struct net_bridge_port *port,
> +			  unsigned long mask);
>  
>  /* br_input.c */
>  int br_handle_frame_finish(struct sk_buff *skb);
> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> index 7f66aa4..9ff6691 100644
> --- a/net/bridge/br_sysfs_if.c
> +++ b/net/bridge/br_sysfs_if.c
> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>  		     unsigned long mask)
>  {
> -	unsigned long flags = p->flags;
> +	unsigned long flags;
> +	unsigned long old_flags;
> +
> +	old_flags = flags = p->flags;
>  
>  	if (v)
>  		flags |= mask;
> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>  
>  	if (flags != p->flags) {
>  		p->flags = flags;
> +		br_port_flags_change(p, old_flags ^ flags);
>  		br_ifinfo_notify(RTM_NEWLINK, p);
>  	}
>  	return 0;
> -- 
> 1.8.5.3

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

* Re: [PATCH 2/7] bridge: Keep track of ports capable of flooding.
  2014-02-26 15:41     ` [Bridge] " Michael S. Tsirkin
@ 2014-02-26 15:41       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:41 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: netdev, bridge, shemminger, jhs, john.r.fastabend

On 02/26/2014 10:41 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:20AM -0500, Vlad Yasevich wrote:
>> Keep track of bridge ports that have unicast flooding turned on.
>> This will later be used by the algorithm to automatically manage
>> address programming and promisc mode.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
>>  net/bridge/br_netlink.c  |  3 +++
>>  net/bridge/br_private.h  |  4 ++++
>>  net/bridge/br_sysfs_if.c |  6 ++++-
>>  4 files changed, 70 insertions(+), 3 deletions(-)
>>
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 54d207d..f072b34 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -27,6 +27,9 @@
>>  
>>  #include "br_private.h"
>>  
>> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
>> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
>> +
> 
> same nitpick about forward declarations of static
> functions.
> maybe they are ok for bridge, but that's my $.02
> 

Ok.  Will take a look. Just means that I might need to re-order the
code which may scatter it a bit through the file.

> 
>>  /*
>>   * Determine initial path cost based on speed.
>>   * using recommendations from 802.1d standard
>> @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	br_ifinfo_notify(RTM_DELLINK, p);
>>  
>> +	list_del_rcu(&p->list);
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_del_flood_port(p, br);
>> +
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>>  
>> -	list_del_rcu(&p->list);
>> -
>>  	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>>  
>>  	netdev_rx_handler_unregister(dev);
> 
> ok so it doesn't matter in which order we do
> list_del_rcu and nbp_vlan_flush then?

Nope.  vlan_flush knows which port its working and doesn't touch
the port list.

What does touch the port list if br_fdb_delete_by_port, but that only
touches it in fdb_delete_local(), and it's ok there since we are trying
to point an existing fdb out a different port.

Thanks
-vlad

> 
>> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>>  	dev_disable_lro(dev);
>>  
>>  	list_add_rcu(&p->list, &br->port_list);
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
>>  
>>  	netdev_update_features(br->dev);
>>  
>> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>>  
>>  	return 0;
>>  }
>> +
>> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	/* Increment the number of  flooding ports, and if we
>> +	 * only have 1 flooding port cache if for future use.
>> +	 */
>> +	br->n_flood_ports++;
>> +	if (br->n_flood_ports == 1)
>> +		br->c_flood_port = p;
>> +}
>> +
>> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *port;
>> +
>> +	/* Decrement the  number of flood port.
>> +	 * If we are deleting the current flood port, clear
>> +	 * the cached port.  If we are down to 1 flood port,
>> +	 * set it if it is not set.
>> +	 */
>> +	br->n_flood_ports--;
>> +	if (p == br->c_flood_port)
>> +		br->c_flood_port = NULL;
>> +
>> +	if (br->n_flood_ports == 1) {
>> +		list_for_each_entry(port, &p->br->port_list, list) {
>> +			if (port->flags & BR_FLOOD) {
>> +				br->c_flood_port = port;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
>> +{
>> +	struct net_bridge *br = p->br;
>> +
>> +	/* We are only interested FLOOD flag */
>> +	if (!(mask & BR_FLOOD))
>> +		return;
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
>> +	else
>> +		br_del_flood_port(p, br);
>> +}
>> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
>> index e74b6d53..01382b9 100644
>> --- a/net/bridge/br_netlink.c
>> +++ b/net/bridge/br_netlink.c
>> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  {
>>  	int err;
>> +	unsigned long old_flags = p->flags;
>>  
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
>> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  		if (err)
>>  			return err;
>>  	}
>> +
>> +	br_port_flags_change(p, old_flags ^ p->flags);
>>  	return 0;
>>  }
>>  
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 3ba11bc..26a3987 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -290,6 +290,8 @@ struct net_bridge
>>  	struct timer_list		topology_change_timer;
>>  	struct timer_list		gc_timer;
>>  	struct kobject			*ifobj;
>> +	struct net_bridge_port		*c_flood_port;
>> +	u32				n_flood_ports;
> 
> any hints on locking for these fields?
> are these all under rtnl?
> 
>>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>>  int br_min_mtu(const struct net_bridge *br);
>>  netdev_features_t br_features_recompute(struct net_bridge *br,
>>  					netdev_features_t features);
>> +void br_port_flags_change(struct net_bridge_port *port,
>> +			  unsigned long mask);
>>  
>>  /* br_input.c */
>>  int br_handle_frame_finish(struct sk_buff *skb);
>> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
>> index 7f66aa4..9ff6691 100644
>> --- a/net/bridge/br_sysfs_if.c
>> +++ b/net/bridge/br_sysfs_if.c
>> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  		     unsigned long mask)
>>  {
>> -	unsigned long flags = p->flags;
>> +	unsigned long flags;
>> +	unsigned long old_flags;
>> +
>> +	old_flags = flags = p->flags;
>>  
>>  	if (v)
>>  		flags |= mask;
>> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  
>>  	if (flags != p->flags) {
>>  		p->flags = flags;
>> +		br_port_flags_change(p, old_flags ^ flags);
>>  		br_ifinfo_notify(RTM_NEWLINK, p);
>>  	}
>>  	return 0;
>> -- 
>> 1.8.5.3

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

* Re: [Bridge] [PATCH 2/7] bridge: Keep track of ports capable of flooding.
@ 2014-02-26 15:41       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:41 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:41 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:20AM -0500, Vlad Yasevich wrote:
>> Keep track of bridge ports that have unicast flooding turned on.
>> This will later be used by the algorithm to automatically manage
>> address programming and promisc mode.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
>>  net/bridge/br_netlink.c  |  3 +++
>>  net/bridge/br_private.h  |  4 ++++
>>  net/bridge/br_sysfs_if.c |  6 ++++-
>>  4 files changed, 70 insertions(+), 3 deletions(-)
>>
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 54d207d..f072b34 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -27,6 +27,9 @@
>>  
>>  #include "br_private.h"
>>  
>> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
>> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
>> +
> 
> same nitpick about forward declarations of static
> functions.
> maybe they are ok for bridge, but that's my $.02
> 

Ok.  Will take a look. Just means that I might need to re-order the
code which may scatter it a bit through the file.

> 
>>  /*
>>   * Determine initial path cost based on speed.
>>   * using recommendations from 802.1d standard
>> @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	br_ifinfo_notify(RTM_DELLINK, p);
>>  
>> +	list_del_rcu(&p->list);
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_del_flood_port(p, br);
>> +
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>>  
>> -	list_del_rcu(&p->list);
>> -
>>  	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>>  
>>  	netdev_rx_handler_unregister(dev);
> 
> ok so it doesn't matter in which order we do
> list_del_rcu and nbp_vlan_flush then?

Nope.  vlan_flush knows which port its working and doesn't touch
the port list.

What does touch the port list if br_fdb_delete_by_port, but that only
touches it in fdb_delete_local(), and it's ok there since we are trying
to point an existing fdb out a different port.

Thanks
-vlad

> 
>> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>>  	dev_disable_lro(dev);
>>  
>>  	list_add_rcu(&p->list, &br->port_list);
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
>>  
>>  	netdev_update_features(br->dev);
>>  
>> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>>  
>>  	return 0;
>>  }
>> +
>> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	/* Increment the number of  flooding ports, and if we
>> +	 * only have 1 flooding port cache if for future use.
>> +	 */
>> +	br->n_flood_ports++;
>> +	if (br->n_flood_ports == 1)
>> +		br->c_flood_port = p;
>> +}
>> +
>> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *port;
>> +
>> +	/* Decrement the  number of flood port.
>> +	 * If we are deleting the current flood port, clear
>> +	 * the cached port.  If we are down to 1 flood port,
>> +	 * set it if it is not set.
>> +	 */
>> +	br->n_flood_ports--;
>> +	if (p == br->c_flood_port)
>> +		br->c_flood_port = NULL;
>> +
>> +	if (br->n_flood_ports == 1) {
>> +		list_for_each_entry(port, &p->br->port_list, list) {
>> +			if (port->flags & BR_FLOOD) {
>> +				br->c_flood_port = port;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
>> +{
>> +	struct net_bridge *br = p->br;
>> +
>> +	/* We are only interested FLOOD flag */
>> +	if (!(mask & BR_FLOOD))
>> +		return;
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
>> +	else
>> +		br_del_flood_port(p, br);
>> +}
>> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
>> index e74b6d53..01382b9 100644
>> --- a/net/bridge/br_netlink.c
>> +++ b/net/bridge/br_netlink.c
>> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  {
>>  	int err;
>> +	unsigned long old_flags = p->flags;
>>  
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
>> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  		if (err)
>>  			return err;
>>  	}
>> +
>> +	br_port_flags_change(p, old_flags ^ p->flags);
>>  	return 0;
>>  }
>>  
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 3ba11bc..26a3987 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -290,6 +290,8 @@ struct net_bridge
>>  	struct timer_list		topology_change_timer;
>>  	struct timer_list		gc_timer;
>>  	struct kobject			*ifobj;
>> +	struct net_bridge_port		*c_flood_port;
>> +	u32				n_flood_ports;
> 
> any hints on locking for these fields?
> are these all under rtnl?
> 
>>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>>  int br_min_mtu(const struct net_bridge *br);
>>  netdev_features_t br_features_recompute(struct net_bridge *br,
>>  					netdev_features_t features);
>> +void br_port_flags_change(struct net_bridge_port *port,
>> +			  unsigned long mask);
>>  
>>  /* br_input.c */
>>  int br_handle_frame_finish(struct sk_buff *skb);
>> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
>> index 7f66aa4..9ff6691 100644
>> --- a/net/bridge/br_sysfs_if.c
>> +++ b/net/bridge/br_sysfs_if.c
>> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  		     unsigned long mask)
>>  {
>> -	unsigned long flags = p->flags;
>> +	unsigned long flags;
>> +	unsigned long old_flags;
>> +
>> +	old_flags = flags = p->flags;
>>  
>>  	if (v)
>>  		flags |= mask;
>> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  
>>  	if (flags != p->flags) {
>>  		p->flags = flags;
>> +		br_port_flags_change(p, old_flags ^ flags);
>>  		br_ifinfo_notify(RTM_NEWLINK, p);
>>  	}
>>  	return 0;
>> -- 
>> 1.8.5.3


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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 15:46     ` [Bridge] " Michael S. Tsirkin
@ 2014-02-26 15:43       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:43 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:46 AM, Michael S. Tsirkin wrote:
>> +
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index f072b34..e782c2e 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	br_ifinfo_notify(RTM_DELLINK, p);
>>  
>> -	list_del_rcu(&p->list);
>> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>>  
>>  	if (p->flags & BR_FLOOD)
>>  		br_del_flood_port(p, br);
>> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>>  
>> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>> +	list_del_rcu(&p->list);
>>  
>>  	netdev_rx_handler_unregister(dev);
>>
> 
> Hmm here we are moving list_del_rcu
> back to after nbp_vlan_flush.
> Was the reordering in the previous patch necessary?

Oops.  Will fix it up.

-vlad

>   
>> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	br->n_flood_ports++;
>>  	if (br->n_flood_ports == 1)
>>  		br->c_flood_port = p;
>> +
>> +	br_fdb_addrs_sync(br);
>>  }
>>  
>>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * set it if it is not set.
>>  	 */
>>  	br->n_flood_ports--;
>> -	if (p == br->c_flood_port)
>> +	if (p == br->c_flood_port) {
>> +		br_fdb_addrs_unsync(br);
>>  		br->c_flood_port = NULL;
>> +	}
>>  
>>  	if (br->n_flood_ports == 1) {
>>  		list_for_each_entry(port, &p->br->port_list, list) {
>> -			if (port->flags & BR_FLOOD) {
>> +			if (br_port_exists(port->dev) &&
>> +			    (port->flags & BR_FLOOD)) {
>>  				br->c_flood_port = port;
>> +				br_fdb_addrs_sync(br);
>>  				break;
>>  			}
>>  		}
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 26a3987..40a6927 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -296,6 +296,7 @@ struct net_bridge
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>>  #endif
>> +	struct netdev_hw_addr_list	conf_addrs;
>>  };
>>  
>>  struct br_input_skb_cb {
>> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>>  	       const unsigned char *addr, u16 nlh_flags);
>>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>>  		struct net_device *dev, int idx);
>> +void br_fdb_addrs_sync(struct net_bridge *br);
>> +void br_fdb_addrs_unsync(struct net_bridge *br);
>>  
>>  /* br_forward.c */
>>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
>> diff --git a/net/core/dev.c b/net/core/dev.c
>> index 4ad1b78..eca4d476 100644
>> --- a/net/core/dev.c
>> +++ b/net/core/dev.c
>> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>>  	if (ops->ndo_set_rx_mode)
>>  		ops->ndo_set_rx_mode(dev);
>>  }
>> +EXPORT_SYMBOL(__dev_set_rx_mode);
>>  
>>  void dev_set_rx_mode(struct net_device *dev)
>>  {
>> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
>> index 329d579..3de44a3 100644
>> --- a/net/core/dev_addr_lists.c
>> +++ b/net/core/dev_addr_lists.c
>> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>>  				   sync);
>>  }
>>  
>> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_add(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>>  				0);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_add);
>>  
>>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>>  			       struct netdev_hw_addr *ha, bool global,
>> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>>  	return -ENOENT;
>>  }
>>  
>> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_del(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_del);
>>  
>>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>>  			       struct netdev_hw_addr *ha,
>> -- 
>> 1.8.5.3
> 
> 
> I would split net/core/ changes out, and  Cc more people
> on it.
> 

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 15:43       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 15:43 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:46 AM, Michael S. Tsirkin wrote:
>> +
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index f072b34..e782c2e 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	br_ifinfo_notify(RTM_DELLINK, p);
>>  
>> -	list_del_rcu(&p->list);
>> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>>  
>>  	if (p->flags & BR_FLOOD)
>>  		br_del_flood_port(p, br);
>> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>>  
>> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>> +	list_del_rcu(&p->list);
>>  
>>  	netdev_rx_handler_unregister(dev);
>>
> 
> Hmm here we are moving list_del_rcu
> back to after nbp_vlan_flush.
> Was the reordering in the previous patch necessary?

Oops.  Will fix it up.

-vlad

>   
>> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	br->n_flood_ports++;
>>  	if (br->n_flood_ports == 1)
>>  		br->c_flood_port = p;
>> +
>> +	br_fdb_addrs_sync(br);
>>  }
>>  
>>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * set it if it is not set.
>>  	 */
>>  	br->n_flood_ports--;
>> -	if (p == br->c_flood_port)
>> +	if (p == br->c_flood_port) {
>> +		br_fdb_addrs_unsync(br);
>>  		br->c_flood_port = NULL;
>> +	}
>>  
>>  	if (br->n_flood_ports == 1) {
>>  		list_for_each_entry(port, &p->br->port_list, list) {
>> -			if (port->flags & BR_FLOOD) {
>> +			if (br_port_exists(port->dev) &&
>> +			    (port->flags & BR_FLOOD)) {
>>  				br->c_flood_port = port;
>> +				br_fdb_addrs_sync(br);
>>  				break;
>>  			}
>>  		}
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 26a3987..40a6927 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -296,6 +296,7 @@ struct net_bridge
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>>  #endif
>> +	struct netdev_hw_addr_list	conf_addrs;
>>  };
>>  
>>  struct br_input_skb_cb {
>> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>>  	       const unsigned char *addr, u16 nlh_flags);
>>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>>  		struct net_device *dev, int idx);
>> +void br_fdb_addrs_sync(struct net_bridge *br);
>> +void br_fdb_addrs_unsync(struct net_bridge *br);
>>  
>>  /* br_forward.c */
>>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
>> diff --git a/net/core/dev.c b/net/core/dev.c
>> index 4ad1b78..eca4d476 100644
>> --- a/net/core/dev.c
>> +++ b/net/core/dev.c
>> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>>  	if (ops->ndo_set_rx_mode)
>>  		ops->ndo_set_rx_mode(dev);
>>  }
>> +EXPORT_SYMBOL(__dev_set_rx_mode);
>>  
>>  void dev_set_rx_mode(struct net_device *dev)
>>  {
>> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
>> index 329d579..3de44a3 100644
>> --- a/net/core/dev_addr_lists.c
>> +++ b/net/core/dev_addr_lists.c
>> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>>  				   sync);
>>  }
>>  
>> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_add(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>>  				0);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_add);
>>  
>>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>>  			       struct netdev_hw_addr *ha, bool global,
>> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>>  	return -ENOENT;
>>  }
>>  
>> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_del(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_del);
>>  
>>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>>  			       struct netdev_hw_addr *ha,
>> -- 
>> 1.8.5.3
> 
> 
> I would split net/core/ changes out, and  Cc more people
> on it.
> 


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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:46     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:46 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
> When a static fdb entry is created, add the mac address to the bridge
> address list.  This list is used to program the proper port's
> address list.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  include/linux/netdevice.h |   6 +++
>  net/bridge/br_device.c    |   2 +
>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
>  net/bridge/br_if.c        |  14 ++++--
>  net/bridge/br_private.h   |   3 ++
>  net/core/dev.c            |   1 +
>  net/core/dev_addr_lists.c |  14 +++---
>  7 files changed, 134 insertions(+), 16 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 440a02e..e29cce1 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
>  void unregister_netdev(struct net_device *dev);
>  
>  /* General hardware address lists handling functions */
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 63f0455..1521db6 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
>  		u64_stats_init(&br_dev_stats->syncp);
>  	}
>  
> +	__hw_addr_init(&br->conf_addrs);
> +
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index 9203d5a..ef95e81 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  static void fdb_notify(struct net_bridge *br,
>  		       const struct net_bridge_fdb_entry *, int);
>  
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
> +
>  static u32 fdb_salt __read_mostly;
>  
>  int __init br_fdb_init(void)
> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
>  
>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
>  {
> +	if (f->is_static)
> +		br_addr_del(br, f->addr.addr);
> +
>  	hlist_del_rcu(&f->hlist);
>  	fdb_notify(br, f, RTM_DELNEIGH);
>  	call_rcu(&f->rcu, fdb_rcu_free);
> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  		return -ENOMEM;
>  
>  	fdb->is_local = fdb->is_static = 1;
> +	br_addr_add(br, addr);
>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
>  	return 0;
>  }
> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
>  	}
>  
>  	if (fdb_to_nud(fdb) != state) {
> -		if (state & NUD_PERMANENT)
> -			fdb->is_local = fdb->is_static = 1;
> -		else if (state & NUD_NOARP) {
> +		if (state & NUD_PERMANENT) {
> +			fdb->is_local = 1;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else if (state & NUD_NOARP) {
> +			fdb->is_local = 0;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else {
>  			fdb->is_local = 0;
> -			fdb->is_static = 1;
> -		} else
> -			fdb->is_local = fdb->is_static = 0;
> +			if (fdb->is_static) {
> +				fdb->is_static = 0;
> +				br_addr_del(br, addr);
> +			}
> +		}
>  
>  		modified = true;
>  	}
> +
>  	fdb->added_by_user = 1;
>  
>  	fdb->used = jiffies;
> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
>  out:
>  	return err;
>  }
> +
> +

most places have a single line between functions,
maybe it's best to be consistent.

> +/* Sync the current list to the correct flood port.  */
> +void br_fdb_addrs_sync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p;
> +	int err;
> +
> +	/* This function has to run under RTNL.
> +	 * Program the user added addresses into the proper port.  This
> +	 * fuction follows the following algorithm:
> +	 *   a)  If only 1 port is flooding:
> +	 *       - write all the addresses to this one port.
> +	 */
> +	if (br->n_flood_ports == 1) {
> +		p = br->c_flood_port;
> +		netif_addr_lock(p->dev);
> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> +				     p->dev->addr_len);
> +		if (!err)
> +			__dev_set_rx_mode(p->dev);
> +		netif_addr_unlock(p->dev);
> +
> +	}
> +}
> +
> +void br_fdb_addrs_unsync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p = br->c_flood_port;
> +
> +	if (!p)
> +		return;
> +
> +	netif_addr_lock(p->dev);
> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
> +	__dev_set_rx_mode(p->dev);
> +	netif_addr_unlock(p->dev);
> +}
> +
> +/* When a static FDB entry is added, the mac address from the entry is
> + * added to the bridge private HW address list and all required ports
> + * are then updated with the new information.
> + * Called under RTNL.
> + */
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> +/* When a static FDB entry is deleted, the HW address from that entry is
> + * also removed from the bridge private HW address list and updates all
> + * the ports with needed information.
> + * Called under RTNL.
> + */
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index f072b34..e782c2e 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	br_ifinfo_notify(RTM_DELLINK, p);
>  
> -	list_del_rcu(&p->list);
> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
>  
> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> +	list_del_rcu(&p->list);
>  
>  	netdev_rx_handler_unregister(dev);
>

Hmm here we are moving list_del_rcu
back to after nbp_vlan_flush.
Was the reordering in the previous patch necessary?
  
> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	br->n_flood_ports++;
>  	if (br->n_flood_ports == 1)
>  		br->c_flood_port = p;
> +
> +	br_fdb_addrs_sync(br);
>  }
>  
>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port)
> +	if (p == br->c_flood_port) {
> +		br_fdb_addrs_unsync(br);
>  		br->c_flood_port = NULL;
> +	}
>  
>  	if (br->n_flood_ports == 1) {
>  		list_for_each_entry(port, &p->br->port_list, list) {
> -			if (port->flags & BR_FLOOD) {
> +			if (br_port_exists(port->dev) &&
> +			    (port->flags & BR_FLOOD)) {
>  				br->c_flood_port = port;
> +				br_fdb_addrs_sync(br);
>  				break;
>  			}
>  		}
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 26a3987..40a6927 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -296,6 +296,7 @@ struct net_bridge
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
>  #endif
> +	struct netdev_hw_addr_list	conf_addrs;
>  };
>  
>  struct br_input_skb_cb {
> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  	       const unsigned char *addr, u16 nlh_flags);
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
> +void br_fdb_addrs_sync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 4ad1b78..eca4d476 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>  	if (ops->ndo_set_rx_mode)
>  		ops->ndo_set_rx_mode(dev);
>  }
> +EXPORT_SYMBOL(__dev_set_rx_mode);
>  
>  void dev_set_rx_mode(struct net_device *dev)
>  {
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 329d579..3de44a3 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>  				   sync);
>  }
>  
> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>  				0);
>  }
> +EXPORT_SYMBOL(__hw_addr_add);
>  
>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>  			       struct netdev_hw_addr *ha, bool global,
> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>  	return -ENOENT;
>  }
>  
> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>  }
> +EXPORT_SYMBOL(__hw_addr_del);
>  
>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>  			       struct netdev_hw_addr *ha,
> -- 
> 1.8.5.3


I would split net/core/ changes out, and  Cc more people
on it.

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 15:46     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:46 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
> When a static fdb entry is created, add the mac address to the bridge
> address list.  This list is used to program the proper port's
> address list.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  include/linux/netdevice.h |   6 +++
>  net/bridge/br_device.c    |   2 +
>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
>  net/bridge/br_if.c        |  14 ++++--
>  net/bridge/br_private.h   |   3 ++
>  net/core/dev.c            |   1 +
>  net/core/dev_addr_lists.c |  14 +++---
>  7 files changed, 134 insertions(+), 16 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 440a02e..e29cce1 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
>  void unregister_netdev(struct net_device *dev);
>  
>  /* General hardware address lists handling functions */
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 63f0455..1521db6 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
>  		u64_stats_init(&br_dev_stats->syncp);
>  	}
>  
> +	__hw_addr_init(&br->conf_addrs);
> +
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index 9203d5a..ef95e81 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  static void fdb_notify(struct net_bridge *br,
>  		       const struct net_bridge_fdb_entry *, int);
>  
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
> +
>  static u32 fdb_salt __read_mostly;
>  
>  int __init br_fdb_init(void)
> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
>  
>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
>  {
> +	if (f->is_static)
> +		br_addr_del(br, f->addr.addr);
> +
>  	hlist_del_rcu(&f->hlist);
>  	fdb_notify(br, f, RTM_DELNEIGH);
>  	call_rcu(&f->rcu, fdb_rcu_free);
> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  		return -ENOMEM;
>  
>  	fdb->is_local = fdb->is_static = 1;
> +	br_addr_add(br, addr);
>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
>  	return 0;
>  }
> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
>  	}
>  
>  	if (fdb_to_nud(fdb) != state) {
> -		if (state & NUD_PERMANENT)
> -			fdb->is_local = fdb->is_static = 1;
> -		else if (state & NUD_NOARP) {
> +		if (state & NUD_PERMANENT) {
> +			fdb->is_local = 1;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else if (state & NUD_NOARP) {
> +			fdb->is_local = 0;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else {
>  			fdb->is_local = 0;
> -			fdb->is_static = 1;
> -		} else
> -			fdb->is_local = fdb->is_static = 0;
> +			if (fdb->is_static) {
> +				fdb->is_static = 0;
> +				br_addr_del(br, addr);
> +			}
> +		}
>  
>  		modified = true;
>  	}
> +
>  	fdb->added_by_user = 1;
>  
>  	fdb->used = jiffies;
> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
>  out:
>  	return err;
>  }
> +
> +

most places have a single line between functions,
maybe it's best to be consistent.

> +/* Sync the current list to the correct flood port.  */
> +void br_fdb_addrs_sync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p;
> +	int err;
> +
> +	/* This function has to run under RTNL.
> +	 * Program the user added addresses into the proper port.  This
> +	 * fuction follows the following algorithm:
> +	 *   a)  If only 1 port is flooding:
> +	 *       - write all the addresses to this one port.
> +	 */
> +	if (br->n_flood_ports == 1) {
> +		p = br->c_flood_port;
> +		netif_addr_lock(p->dev);
> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> +				     p->dev->addr_len);
> +		if (!err)
> +			__dev_set_rx_mode(p->dev);
> +		netif_addr_unlock(p->dev);
> +
> +	}
> +}
> +
> +void br_fdb_addrs_unsync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p = br->c_flood_port;
> +
> +	if (!p)
> +		return;
> +
> +	netif_addr_lock(p->dev);
> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
> +	__dev_set_rx_mode(p->dev);
> +	netif_addr_unlock(p->dev);
> +}
> +
> +/* When a static FDB entry is added, the mac address from the entry is
> + * added to the bridge private HW address list and all required ports
> + * are then updated with the new information.
> + * Called under RTNL.
> + */
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> +/* When a static FDB entry is deleted, the HW address from that entry is
> + * also removed from the bridge private HW address list and updates all
> + * the ports with needed information.
> + * Called under RTNL.
> + */
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index f072b34..e782c2e 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	br_ifinfo_notify(RTM_DELLINK, p);
>  
> -	list_del_rcu(&p->list);
> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
>  
> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> +	list_del_rcu(&p->list);
>  
>  	netdev_rx_handler_unregister(dev);
>

Hmm here we are moving list_del_rcu
back to after nbp_vlan_flush.
Was the reordering in the previous patch necessary?
  
> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	br->n_flood_ports++;
>  	if (br->n_flood_ports == 1)
>  		br->c_flood_port = p;
> +
> +	br_fdb_addrs_sync(br);
>  }
>  
>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port)
> +	if (p == br->c_flood_port) {
> +		br_fdb_addrs_unsync(br);
>  		br->c_flood_port = NULL;
> +	}
>  
>  	if (br->n_flood_ports == 1) {
>  		list_for_each_entry(port, &p->br->port_list, list) {
> -			if (port->flags & BR_FLOOD) {
> +			if (br_port_exists(port->dev) &&
> +			    (port->flags & BR_FLOOD)) {
>  				br->c_flood_port = port;
> +				br_fdb_addrs_sync(br);
>  				break;
>  			}
>  		}
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 26a3987..40a6927 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -296,6 +296,7 @@ struct net_bridge
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
>  #endif
> +	struct netdev_hw_addr_list	conf_addrs;
>  };
>  
>  struct br_input_skb_cb {
> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  	       const unsigned char *addr, u16 nlh_flags);
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
> +void br_fdb_addrs_sync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 4ad1b78..eca4d476 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>  	if (ops->ndo_set_rx_mode)
>  		ops->ndo_set_rx_mode(dev);
>  }
> +EXPORT_SYMBOL(__dev_set_rx_mode);
>  
>  void dev_set_rx_mode(struct net_device *dev)
>  {
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 329d579..3de44a3 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>  				   sync);
>  }
>  
> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>  				0);
>  }
> +EXPORT_SYMBOL(__hw_addr_add);
>  
>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>  			       struct netdev_hw_addr *ha, bool global,
> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>  	return -ENOENT;
>  }
>  
> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>  }
> +EXPORT_SYMBOL(__hw_addr_del);
>  
>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>  			       struct netdev_hw_addr *ha,
> -- 
> 1.8.5.3


I would split net/core/ changes out, and  Cc more people
on it.


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

* Re: [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:51     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:51 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:22AM -0500, Vlad Yasevich wrote:
> When there is only 1 flooding port, this port is programmed
> with all the address

all the addresses?

> the bridge accumulated.  This allows
> us to place this port into non-promiscuous mode.
> At other times, all ports are set as promiscuous.  To help
> track whether the bridge set the mode or not, a new
> flag is introduced.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_if.c      | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
>  net/bridge/br_private.h |  1 +
>  2 files changed, 47 insertions(+), 2 deletions(-)
> 
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index e782c2e..51df642 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	sysfs_remove_link(br->ifobj, p->dev->name);
>  
> -	dev_set_promiscuity(dev, -1);
> +	dev_set_allmulti(dev, -1);
>  
>  	spin_lock_bh(&br->lock);
>  	br_stp_disable_port(p);
> @@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>  
>  	call_netdevice_notifiers(NETDEV_JOIN, dev);
>  
> -	err = dev_set_promiscuity(dev, 1);
> +	err = dev_set_allmulti(dev, 1);
>  	if (err)
>  		goto put_back;
>  
> @@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>  	return 0;
>  }
>  
> +static int br_port_set_promisc(struct net_bridge_port *p)
> +{
> +	int err = 0;
> +
> +	if (p->flags & BR_PROMISC)
> +		return err;
> +
> +	err = dev_set_promiscuity(p->dev, 1);
> +	if (err)
> +		return err;
> +
> +	p->flags |= BR_PROMISC;
> +	return err;
> +}
> +

you take pains to return an error code here,
only to ignore it in the caller?

> +static void br_port_clear_promisc(struct net_bridge_port *p)
> +{
> +	if (!(p->flags & BR_PROMISC))
> +		return;
> +
> +	dev_set_promiscuity(p->dev, -1);
> +	p->flags &= ~BR_PROMISC;
> +}
> +
> +/* When a port is added or removed or when the flooding status of
> + * the port changes, this function is called to automatically mange
> + * promiscuity setting of all the bridge ports.  We are always called
> + * under RTNL so can skip using rcu primitives.
> + */
> +static void br_manage_promisc(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p;
> +
> +	list_for_each_entry(p, &br->port_list, list) {
> +		if (!br_port_exists(p->dev) ||
> +		    (br->n_flood_ports == 1 && br->c_flood_port == p))
> +			br_port_clear_promisc(p);
> +		else
> +			br_port_set_promisc(p);
> +	}
> +}
> +
>  static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  {
>  	/* Increment the number of  flooding ports, and if we
> @@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  		br->c_flood_port = p;
>  
>  	br_fdb_addrs_sync(br);
> +	br_manage_promisc(br);
>  }
>  
>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> @@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  			}
>  		}
>  	}
> +	br_manage_promisc(br);
>  }
>  
>  void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 40a6927..6670cb3 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -174,6 +174,7 @@ struct net_bridge_port
>  #define BR_ADMIN_COST		0x00000010
>  #define BR_LEARNING		0x00000020
>  #define BR_FLOOD		0x00000040
> +#define BR_PROMISC		0x00000080
>

Considering you then have a separate logic in 7/7
will this be cleaner as a per-port flag?
  
>  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
>  	struct bridge_mcast_query	ip4_query;
> -- 
> 1.8.5.3

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

* Re: [Bridge] [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
@ 2014-02-26 15:51     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:51 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:22AM -0500, Vlad Yasevich wrote:
> When there is only 1 flooding port, this port is programmed
> with all the address

all the addresses?

> the bridge accumulated.  This allows
> us to place this port into non-promiscuous mode.
> At other times, all ports are set as promiscuous.  To help
> track whether the bridge set the mode or not, a new
> flag is introduced.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_if.c      | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
>  net/bridge/br_private.h |  1 +
>  2 files changed, 47 insertions(+), 2 deletions(-)
> 
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index e782c2e..51df642 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	sysfs_remove_link(br->ifobj, p->dev->name);
>  
> -	dev_set_promiscuity(dev, -1);
> +	dev_set_allmulti(dev, -1);
>  
>  	spin_lock_bh(&br->lock);
>  	br_stp_disable_port(p);
> @@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>  
>  	call_netdevice_notifiers(NETDEV_JOIN, dev);
>  
> -	err = dev_set_promiscuity(dev, 1);
> +	err = dev_set_allmulti(dev, 1);
>  	if (err)
>  		goto put_back;
>  
> @@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>  	return 0;
>  }
>  
> +static int br_port_set_promisc(struct net_bridge_port *p)
> +{
> +	int err = 0;
> +
> +	if (p->flags & BR_PROMISC)
> +		return err;
> +
> +	err = dev_set_promiscuity(p->dev, 1);
> +	if (err)
> +		return err;
> +
> +	p->flags |= BR_PROMISC;
> +	return err;
> +}
> +

you take pains to return an error code here,
only to ignore it in the caller?

> +static void br_port_clear_promisc(struct net_bridge_port *p)
> +{
> +	if (!(p->flags & BR_PROMISC))
> +		return;
> +
> +	dev_set_promiscuity(p->dev, -1);
> +	p->flags &= ~BR_PROMISC;
> +}
> +
> +/* When a port is added or removed or when the flooding status of
> + * the port changes, this function is called to automatically mange
> + * promiscuity setting of all the bridge ports.  We are always called
> + * under RTNL so can skip using rcu primitives.
> + */
> +static void br_manage_promisc(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p;
> +
> +	list_for_each_entry(p, &br->port_list, list) {
> +		if (!br_port_exists(p->dev) ||
> +		    (br->n_flood_ports == 1 && br->c_flood_port == p))
> +			br_port_clear_promisc(p);
> +		else
> +			br_port_set_promisc(p);
> +	}
> +}
> +
>  static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  {
>  	/* Increment the number of  flooding ports, and if we
> @@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  		br->c_flood_port = p;
>  
>  	br_fdb_addrs_sync(br);
> +	br_manage_promisc(br);
>  }
>  
>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> @@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  			}
>  		}
>  	}
> +	br_manage_promisc(br);
>  }
>  
>  void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 40a6927..6670cb3 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -174,6 +174,7 @@ struct net_bridge_port
>  #define BR_ADMIN_COST		0x00000010
>  #define BR_LEARNING		0x00000020
>  #define BR_FLOOD		0x00000040
> +#define BR_PROMISC		0x00000080
>

Considering you then have a separate logic in 7/7
will this be cleaner as a per-port flag?
  
>  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
>  	struct bridge_mcast_query	ip4_query;
> -- 
> 1.8.5.3

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

* Re: [PATCH 2/7] bridge: Keep track of ports capable of flooding.
  2014-02-26 15:41       ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:53         ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:53 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:41:57AM -0500, Vlad Yasevich wrote:
> On 02/26/2014 10:41 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:20AM -0500, Vlad Yasevich wrote:
> >> Keep track of bridge ports that have unicast flooding turned on.
> >> This will later be used by the algorithm to automatically manage
> >> address programming and promisc mode.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> >> ---
> >>  net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
> >>  net/bridge/br_netlink.c  |  3 +++
> >>  net/bridge/br_private.h  |  4 ++++
> >>  net/bridge/br_sysfs_if.c |  6 ++++-
> >>  4 files changed, 70 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index 54d207d..f072b34 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -27,6 +27,9 @@
> >>  
> >>  #include "br_private.h"
> >>  
> >> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> >> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> >> +
> > 
> > same nitpick about forward declarations of static
> > functions.
> > maybe they are ok for bridge, but that's my $.02
> > 
> 
> Ok.  Will take a look. Just means that I might need to re-order the
> code which may scatter it a bit through the file.

If you would like to make the diff smaller,
this can be a separate patch.

Same applies to all files ...

> > 
> >>  /*
> >>   * Determine initial path cost based on speed.
> >>   * using recommendations from 802.1d standard
> >> @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
> >>  
> >>  	br_ifinfo_notify(RTM_DELLINK, p);
> >>  
> >> +	list_del_rcu(&p->list);
> >> +
> >> +	if (p->flags & BR_FLOOD)
> >> +		br_del_flood_port(p, br);
> >> +
> >>  	nbp_vlan_flush(p);
> >>  	br_fdb_delete_by_port(br, p, 1);
> >>  
> >> -	list_del_rcu(&p->list);
> >> -
> >>  	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> >>  
> >>  	netdev_rx_handler_unregister(dev);
> > 
> > ok so it doesn't matter in which order we do
> > list_del_rcu and nbp_vlan_flush then?
> 
> Nope.  vlan_flush knows which port its working and doesn't touch
> the port list.
> 
> What does touch the port list if br_fdb_delete_by_port, but that only
> touches it in fdb_delete_local(), and it's ok there since we are trying
> to point an existing fdb out a different port.
> 
> Thanks
> -vlad
> 
> > 
> >> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
> >>  	dev_disable_lro(dev);
> >>  
> >>  	list_add_rcu(&p->list, &br->port_list);
> >> +
> >> +	if (p->flags & BR_FLOOD)
> >> +		br_add_flood_port(p, br);
> >>  
> >>  	netdev_update_features(br->dev);
> >>  
> >> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
> >>  
> >>  	return 0;
> >>  }
> >> +
> >> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >> +{
> >> +	/* Increment the number of  flooding ports, and if we
> >> +	 * only have 1 flooding port cache if for future use.
> >> +	 */
> >> +	br->n_flood_ports++;
> >> +	if (br->n_flood_ports == 1)
> >> +		br->c_flood_port = p;
> >> +}
> >> +
> >> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >> +{
> >> +	struct net_bridge_port *port;
> >> +
> >> +	/* Decrement the  number of flood port.
> >> +	 * If we are deleting the current flood port, clear
> >> +	 * the cached port.  If we are down to 1 flood port,
> >> +	 * set it if it is not set.
> >> +	 */
> >> +	br->n_flood_ports--;
> >> +	if (p == br->c_flood_port)
> >> +		br->c_flood_port = NULL;
> >> +
> >> +	if (br->n_flood_ports == 1) {
> >> +		list_for_each_entry(port, &p->br->port_list, list) {
> >> +			if (port->flags & BR_FLOOD) {
> >> +				br->c_flood_port = port;
> >> +				break;
> >> +			}
> >> +		}
> >> +	}
> >> +}
> >> +
> >> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> >> +{
> >> +	struct net_bridge *br = p->br;
> >> +
> >> +	/* We are only interested FLOOD flag */
> >> +	if (!(mask & BR_FLOOD))
> >> +		return;
> >> +
> >> +	if (p->flags & BR_FLOOD)
> >> +		br_add_flood_port(p, br);
> >> +	else
> >> +		br_del_flood_port(p, br);
> >> +}
> >> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> >> index e74b6d53..01382b9 100644
> >> --- a/net/bridge/br_netlink.c
> >> +++ b/net/bridge/br_netlink.c
> >> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
> >>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
> >>  {
> >>  	int err;
> >> +	unsigned long old_flags = p->flags;
> >>  
> >>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
> >>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
> >> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
> >>  		if (err)
> >>  			return err;
> >>  	}
> >> +
> >> +	br_port_flags_change(p, old_flags ^ p->flags);
> >>  	return 0;
> >>  }
> >>  
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 3ba11bc..26a3987 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -290,6 +290,8 @@ struct net_bridge
> >>  	struct timer_list		topology_change_timer;
> >>  	struct timer_list		gc_timer;
> >>  	struct kobject			*ifobj;
> >> +	struct net_bridge_port		*c_flood_port;
> >> +	u32				n_flood_ports;
> > 
> > any hints on locking for these fields?
> > are these all under rtnl?
> > 
> >>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
> >>  	u8				vlan_enabled;
> >>  	struct net_port_vlans __rcu	*vlan_info;
> >> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
> >>  int br_min_mtu(const struct net_bridge *br);
> >>  netdev_features_t br_features_recompute(struct net_bridge *br,
> >>  					netdev_features_t features);
> >> +void br_port_flags_change(struct net_bridge_port *port,
> >> +			  unsigned long mask);
> >>  
> >>  /* br_input.c */
> >>  int br_handle_frame_finish(struct sk_buff *skb);
> >> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> >> index 7f66aa4..9ff6691 100644
> >> --- a/net/bridge/br_sysfs_if.c
> >> +++ b/net/bridge/br_sysfs_if.c
> >> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
> >>  static int store_flag(struct net_bridge_port *p, unsigned long v,
> >>  		     unsigned long mask)
> >>  {
> >> -	unsigned long flags = p->flags;
> >> +	unsigned long flags;
> >> +	unsigned long old_flags;
> >> +
> >> +	old_flags = flags = p->flags;
> >>  
> >>  	if (v)
> >>  		flags |= mask;
> >> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
> >>  
> >>  	if (flags != p->flags) {
> >>  		p->flags = flags;
> >> +		br_port_flags_change(p, old_flags ^ flags);
> >>  		br_ifinfo_notify(RTM_NEWLINK, p);
> >>  	}
> >>  	return 0;
> >> -- 
> >> 1.8.5.3

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

* Re: [Bridge] [PATCH 2/7] bridge: Keep track of ports capable of flooding.
@ 2014-02-26 15:53         ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:53 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:41:57AM -0500, Vlad Yasevich wrote:
> On 02/26/2014 10:41 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:20AM -0500, Vlad Yasevich wrote:
> >> Keep track of bridge ports that have unicast flooding turned on.
> >> This will later be used by the algorithm to automatically manage
> >> address programming and promisc mode.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> >> ---
> >>  net/bridge/br_if.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
> >>  net/bridge/br_netlink.c  |  3 +++
> >>  net/bridge/br_private.h  |  4 ++++
> >>  net/bridge/br_sysfs_if.c |  6 ++++-
> >>  4 files changed, 70 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index 54d207d..f072b34 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -27,6 +27,9 @@
> >>  
> >>  #include "br_private.h"
> >>  
> >> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> >> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br);
> >> +
> > 
> > same nitpick about forward declarations of static
> > functions.
> > maybe they are ok for bridge, but that's my $.02
> > 
> 
> Ok.  Will take a look. Just means that I might need to re-order the
> code which may scatter it a bit through the file.

If you would like to make the diff smaller,
this can be a separate patch.

Same applies to all files ...

> > 
> >>  /*
> >>   * Determine initial path cost based on speed.
> >>   * using recommendations from 802.1d standard
> >> @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p)
> >>  
> >>  	br_ifinfo_notify(RTM_DELLINK, p);
> >>  
> >> +	list_del_rcu(&p->list);
> >> +
> >> +	if (p->flags & BR_FLOOD)
> >> +		br_del_flood_port(p, br);
> >> +
> >>  	nbp_vlan_flush(p);
> >>  	br_fdb_delete_by_port(br, p, 1);
> >>  
> >> -	list_del_rcu(&p->list);
> >> -
> >>  	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> >>  
> >>  	netdev_rx_handler_unregister(dev);
> > 
> > ok so it doesn't matter in which order we do
> > list_del_rcu and nbp_vlan_flush then?
> 
> Nope.  vlan_flush knows which port its working and doesn't touch
> the port list.
> 
> What does touch the port list if br_fdb_delete_by_port, but that only
> touches it in fdb_delete_local(), and it's ok there since we are trying
> to point an existing fdb out a different port.
> 
> Thanks
> -vlad
> 
> > 
> >> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
> >>  	dev_disable_lro(dev);
> >>  
> >>  	list_add_rcu(&p->list, &br->port_list);
> >> +
> >> +	if (p->flags & BR_FLOOD)
> >> +		br_add_flood_port(p, br);
> >>  
> >>  	netdev_update_features(br->dev);
> >>  
> >> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
> >>  
> >>  	return 0;
> >>  }
> >> +
> >> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >> +{
> >> +	/* Increment the number of  flooding ports, and if we
> >> +	 * only have 1 flooding port cache if for future use.
> >> +	 */
> >> +	br->n_flood_ports++;
> >> +	if (br->n_flood_ports == 1)
> >> +		br->c_flood_port = p;
> >> +}
> >> +
> >> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >> +{
> >> +	struct net_bridge_port *port;
> >> +
> >> +	/* Decrement the  number of flood port.
> >> +	 * If we are deleting the current flood port, clear
> >> +	 * the cached port.  If we are down to 1 flood port,
> >> +	 * set it if it is not set.
> >> +	 */
> >> +	br->n_flood_ports--;
> >> +	if (p == br->c_flood_port)
> >> +		br->c_flood_port = NULL;
> >> +
> >> +	if (br->n_flood_ports == 1) {
> >> +		list_for_each_entry(port, &p->br->port_list, list) {
> >> +			if (port->flags & BR_FLOOD) {
> >> +				br->c_flood_port = port;
> >> +				break;
> >> +			}
> >> +		}
> >> +	}
> >> +}
> >> +
> >> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> >> +{
> >> +	struct net_bridge *br = p->br;
> >> +
> >> +	/* We are only interested FLOOD flag */
> >> +	if (!(mask & BR_FLOOD))
> >> +		return;
> >> +
> >> +	if (p->flags & BR_FLOOD)
> >> +		br_add_flood_port(p, br);
> >> +	else
> >> +		br_del_flood_port(p, br);
> >> +}
> >> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> >> index e74b6d53..01382b9 100644
> >> --- a/net/bridge/br_netlink.c
> >> +++ b/net/bridge/br_netlink.c
> >> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
> >>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
> >>  {
> >>  	int err;
> >> +	unsigned long old_flags = p->flags;
> >>  
> >>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
> >>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
> >> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
> >>  		if (err)
> >>  			return err;
> >>  	}
> >> +
> >> +	br_port_flags_change(p, old_flags ^ p->flags);
> >>  	return 0;
> >>  }
> >>  
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 3ba11bc..26a3987 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -290,6 +290,8 @@ struct net_bridge
> >>  	struct timer_list		topology_change_timer;
> >>  	struct timer_list		gc_timer;
> >>  	struct kobject			*ifobj;
> >> +	struct net_bridge_port		*c_flood_port;
> >> +	u32				n_flood_ports;
> > 
> > any hints on locking for these fields?
> > are these all under rtnl?
> > 
> >>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
> >>  	u8				vlan_enabled;
> >>  	struct net_port_vlans __rcu	*vlan_info;
> >> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
> >>  int br_min_mtu(const struct net_bridge *br);
> >>  netdev_features_t br_features_recompute(struct net_bridge *br,
> >>  					netdev_features_t features);
> >> +void br_port_flags_change(struct net_bridge_port *port,
> >> +			  unsigned long mask);
> >>  
> >>  /* br_input.c */
> >>  int br_handle_frame_finish(struct sk_buff *skb);
> >> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> >> index 7f66aa4..9ff6691 100644
> >> --- a/net/bridge/br_sysfs_if.c
> >> +++ b/net/bridge/br_sysfs_if.c
> >> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
> >>  static int store_flag(struct net_bridge_port *p, unsigned long v,
> >>  		     unsigned long mask)
> >>  {
> >> -	unsigned long flags = p->flags;
> >> +	unsigned long flags;
> >> +	unsigned long old_flags;
> >> +
> >> +	old_flags = flags = p->flags;
> >>  
> >>  	if (v)
> >>  		flags |= mask;
> >> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
> >>  
> >>  	if (flags != p->flags) {
> >>  		p->flags = flags;
> >> +		br_port_flags_change(p, old_flags ^ flags);
> >>  		br_ifinfo_notify(RTM_NEWLINK, p);
> >>  	}
> >>  	return 0;
> >> -- 
> >> 1.8.5.3

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

* Re: [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 15:57     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:57 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
> Configuration where all ports are set to non-flooding is a slight
> special case.  In this config, the user would have to manage all fdbs
> for all ports.  In this special case, since we'll know all addresses,
> we can turn off promisc on all ports and program all ports with the
> same set of addresses.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

Here the logic is different be we could sync them,
just make this a per-port flag and
scan all ports instead of relying on flood_port.
This will be slower in the single flooding port
case but worst case remains the same since
this patch scans all ports.

What do you think?

> ---
>  include/linux/netdevice.h |  3 +++
>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
>  net/bridge/br_private.h   |  2 +-
>  net/core/dev_addr_lists.c |  7 ++++---
>  5 files changed, 59 insertions(+), 15 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index e29cce1..79e97ee 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
>  		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>  		      struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_init(struct netdev_hw_addr_list *list);
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index ef95e81..26ea4fe 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
>  	if (br->n_flood_ports == 1) {
>  		p = br->c_flood_port;
>  		netif_addr_lock(p->dev);
> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> -				     p->dev->addr_len);
> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
> +					      p->dev->addr_len);
>  		if (!err)
>  			__dev_set_rx_mode(p->dev);
>  		netif_addr_unlock(p->dev);
> +	} else if (br->n_flood_ports == 0) {
> +		list_for_each_entry(p, &br->port_list, list) {
> +			/* skip over ports being deleted. */
> +			if (!br_port_exists(p->dev))
> +				continue;
>  
> +			netif_addr_lock_nested(p->dev);
> +			err = __hw_addr_sync_multiple(&p->dev->uc,
> +						      &br->conf_addrs,
> +						      p->dev->addr_len);
> +			if (!err)
> +				__dev_set_rx_mode(p->dev);
> +			netif_addr_unlock(p->dev);
> +		}
>  	}
> +
>  }
>  
> -void br_fdb_addrs_unsync(struct net_bridge *br)
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
>  {
> -	struct net_bridge_port *p = br->c_flood_port;
> -
>  	if (!p)
>  		return;
>  
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 55e4e28..4ba62ef 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> +	else
> +		br_fdb_addrs_unsync(br, p);
>  
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * only have 1 flooding port cache if for future use.
>  	 */
>  	br->n_flood_ports++;
> -	if (br->n_flood_ports == 1)
> +	if (br->n_flood_ports == 1) {
> +		struct net_bridge_port *port;
> +
> +		/* We are transitioning from 0 flood ports to 1.  Remove
> +		 * the addresses from all the non-flood ports and turn on
> +		 * promisc on those ports.
> +		 */
> +		list_for_each_entry(port, &br->port_list, list) {
> +			/* skip the current port we are changing */
> +			if (port == p)
> +				continue;
> +
> +			if (!(port->flags & BR_FLOOD)) {
> +				br_port_set_promisc(port);
> +				br_fdb_addrs_unsync(br, port);
> +			}
> +		}
> +
>  		br->c_flood_port = p;
> +	}
>  
>  	br_fdb_addrs_sync(br);
>  	br_manage_promisc(br);
> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port) {
> -		br_fdb_addrs_unsync(br);
> -		br->c_flood_port = NULL;
> -	}
> +	if (br->n_flood_ports == 0) {
> +		/* We just dropped to 0 flood ports.  If we
> +		 * are deleting this port, unsync addresses
> +		 * from it.
> +		 */
> +		if (!br_port_exists(p->dev))
> +			br_fdb_addrs_unsync(br, p);
>  
> -	if (br->n_flood_ports == 1) {
> +		br->c_flood_port = NULL;
> +		br_fdb_addrs_sync(br);
> +	} else if (br->n_flood_ports == 1) {
> +		/* We just dropped to 1 flood port. Find this one flood
> +		 * port and sync to it.
> +		 */
>  		list_for_each_entry(port, &p->br->port_list, list) {
>  			if (br_port_exists(port->dev) &&
>  			    (port->flags & BR_FLOOD)) {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 87dcc09..13840de 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
>  void br_fdb_addrs_sync(struct net_bridge *br);
> -void br_fdb_addrs_unsync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 3de44a3..24da78f 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
>  	__hw_addr_del_entry(from_list, ha, false, false);
>  }
>  
> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> -				   struct netdev_hw_addr_list *from_list,
> -				   int addr_len)
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len)
>  {
>  	int err = 0;
>  	struct netdev_hw_addr *ha, *tmp;
> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>  	}
>  	return err;
>  }
> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
>  
>  /* This function only works where there is a strict 1-1 relationship
>   * between source and destionation of they synch. If you ever need to
> -- 
> 1.8.5.3

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

* Re: [Bridge] [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
@ 2014-02-26 15:57     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 15:57 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
> Configuration where all ports are set to non-flooding is a slight
> special case.  In this config, the user would have to manage all fdbs
> for all ports.  In this special case, since we'll know all addresses,
> we can turn off promisc on all ports and program all ports with the
> same set of addresses.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

Here the logic is different be we could sync them,
just make this a per-port flag and
scan all ports instead of relying on flood_port.
This will be slower in the single flooding port
case but worst case remains the same since
this patch scans all ports.

What do you think?

> ---
>  include/linux/netdevice.h |  3 +++
>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
>  net/bridge/br_private.h   |  2 +-
>  net/core/dev_addr_lists.c |  7 ++++---
>  5 files changed, 59 insertions(+), 15 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index e29cce1..79e97ee 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
>  		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>  		      struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_init(struct netdev_hw_addr_list *list);
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index ef95e81..26ea4fe 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
>  	if (br->n_flood_ports == 1) {
>  		p = br->c_flood_port;
>  		netif_addr_lock(p->dev);
> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> -				     p->dev->addr_len);
> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
> +					      p->dev->addr_len);
>  		if (!err)
>  			__dev_set_rx_mode(p->dev);
>  		netif_addr_unlock(p->dev);
> +	} else if (br->n_flood_ports == 0) {
> +		list_for_each_entry(p, &br->port_list, list) {
> +			/* skip over ports being deleted. */
> +			if (!br_port_exists(p->dev))
> +				continue;
>  
> +			netif_addr_lock_nested(p->dev);
> +			err = __hw_addr_sync_multiple(&p->dev->uc,
> +						      &br->conf_addrs,
> +						      p->dev->addr_len);
> +			if (!err)
> +				__dev_set_rx_mode(p->dev);
> +			netif_addr_unlock(p->dev);
> +		}
>  	}
> +
>  }
>  
> -void br_fdb_addrs_unsync(struct net_bridge *br)
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
>  {
> -	struct net_bridge_port *p = br->c_flood_port;
> -
>  	if (!p)
>  		return;
>  
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 55e4e28..4ba62ef 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> +	else
> +		br_fdb_addrs_unsync(br, p);
>  
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * only have 1 flooding port cache if for future use.
>  	 */
>  	br->n_flood_ports++;
> -	if (br->n_flood_ports == 1)
> +	if (br->n_flood_ports == 1) {
> +		struct net_bridge_port *port;
> +
> +		/* We are transitioning from 0 flood ports to 1.  Remove
> +		 * the addresses from all the non-flood ports and turn on
> +		 * promisc on those ports.
> +		 */
> +		list_for_each_entry(port, &br->port_list, list) {
> +			/* skip the current port we are changing */
> +			if (port == p)
> +				continue;
> +
> +			if (!(port->flags & BR_FLOOD)) {
> +				br_port_set_promisc(port);
> +				br_fdb_addrs_unsync(br, port);
> +			}
> +		}
> +
>  		br->c_flood_port = p;
> +	}
>  
>  	br_fdb_addrs_sync(br);
>  	br_manage_promisc(br);
> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port) {
> -		br_fdb_addrs_unsync(br);
> -		br->c_flood_port = NULL;
> -	}
> +	if (br->n_flood_ports == 0) {
> +		/* We just dropped to 0 flood ports.  If we
> +		 * are deleting this port, unsync addresses
> +		 * from it.
> +		 */
> +		if (!br_port_exists(p->dev))
> +			br_fdb_addrs_unsync(br, p);
>  
> -	if (br->n_flood_ports == 1) {
> +		br->c_flood_port = NULL;
> +		br_fdb_addrs_sync(br);
> +	} else if (br->n_flood_ports == 1) {
> +		/* We just dropped to 1 flood port. Find this one flood
> +		 * port and sync to it.
> +		 */
>  		list_for_each_entry(port, &p->br->port_list, list) {
>  			if (br_port_exists(port->dev) &&
>  			    (port->flags & BR_FLOOD)) {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 87dcc09..13840de 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
>  void br_fdb_addrs_sync(struct net_bridge *br);
> -void br_fdb_addrs_unsync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 3de44a3..24da78f 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
>  	__hw_addr_del_entry(from_list, ha, false, false);
>  }
>  
> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> -				   struct netdev_hw_addr_list *from_list,
> -				   int addr_len)
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len)
>  {
>  	int err = 0;
>  	struct netdev_hw_addr *ha, *tmp;
> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>  	}
>  	return err;
>  }
> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
>  
>  /* This function only works where there is a strict 1-1 relationship
>   * between source and destionation of they synch. If you ever need to
> -- 
> 1.8.5.3

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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 16:00     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:00 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:24AM -0500, Vlad Yasevich wrote:
> If the user configures vlan interfaces on top of the bridge and the bridge
> doesn't have vlan filtering enabled, we have to place all the ports in
> promsic mode so that we can correctly receive tagged frames.
> When vlan filtering is enabled, the vlan configuration will be provided
> via filtering interface.
> When the vlan filtering is toggled, we also have mange promiscuity.

have to manage?

> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>


I wonder if it matters that we scan all ports
on vlan add/del now.

If yes, we could optimize some cases by using
a counter of promisc ports.


> ---
>  net/bridge/br_device.c  | 14 ++++++++++++++
>  net/bridge/br_if.c      | 17 +++++++++++++----
>  net/bridge/br_private.h |  9 +++++++++
>  net/bridge/br_vlan.c    |  1 +
>  4 files changed, 37 insertions(+), 4 deletions(-)
> 
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 0af9d6c..967abb3 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>  
>  #endif
>  
> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>  
>  {
> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>  	.ndo_change_mtu		 = br_change_mtu,
>  	.ndo_do_ioctl		 = br_dev_ioctl,
> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>  #ifdef CONFIG_NET_POLL_CONTROLLER
>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 7e92bd0..55e4e28 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>  void br_manage_promisc(struct net_bridge *br)
>  {
>  	struct net_bridge_port *p;
> +	int set_all = false;
> +
> +	if (br->dev->flags & IFF_PROMISC)
> +		set_all = true;
> +
> +	/* If vlan filtering is disabled and there are any VLANs
> +	 * configured on top of the bridge, set promisc on all
> +	 * ports.
> +	 */
> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
> +		set_all = true;
>  
>  	list_for_each_entry(p, &br->port_list, list) {
> -		if (br->dev->flags & IFF_PROMISC) {
> -			/* PROMISC flag has been turned on for the bridge
> -			 * itself.  Turn on promisc on all ports.
> -			 */
> +		if (set_all) {
> +			/* Set all the ports to promisc mode.  */
>  			br_port_set_promisc(p);
>  
>  		} else {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 4042f86..87dcc09 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  	return v->pvid ?: VLAN_N_VID;
>  }
>  
> +static inline int br_vlan_enabled(struct net_bridge *br)
> +{
> +	return br->vlan_enabled;
> +}
>  #else
>  static inline bool br_allowed_ingress(struct net_bridge *br,
>  				      struct net_port_vlans *v,
> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  {
>  	return VLAN_N_VID;	/* Returns invalid vid */
>  }
> +
> +static inline int br_vlan_enabled(struct net_bridge *br);
> +{
> +	return 0;
> +}
>  #endif
>  
>  /* br_netfilter.c */
> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> index 8249ca7..eddc2f6 100644
> --- a/net/bridge/br_vlan.c
> +++ b/net/bridge/br_vlan.c
> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>  		goto unlock;
>  
>  	br->vlan_enabled = val;
> +	br_manage_promisc(br);
>  
>  unlock:
>  	rtnl_unlock();
> -- 
> 1.8.5.3

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-02-26 16:00     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:00 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:24AM -0500, Vlad Yasevich wrote:
> If the user configures vlan interfaces on top of the bridge and the bridge
> doesn't have vlan filtering enabled, we have to place all the ports in
> promsic mode so that we can correctly receive tagged frames.
> When vlan filtering is enabled, the vlan configuration will be provided
> via filtering interface.
> When the vlan filtering is toggled, we also have mange promiscuity.

have to manage?

> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>


I wonder if it matters that we scan all ports
on vlan add/del now.

If yes, we could optimize some cases by using
a counter of promisc ports.


> ---
>  net/bridge/br_device.c  | 14 ++++++++++++++
>  net/bridge/br_if.c      | 17 +++++++++++++----
>  net/bridge/br_private.h |  9 +++++++++
>  net/bridge/br_vlan.c    |  1 +
>  4 files changed, 37 insertions(+), 4 deletions(-)
> 
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 0af9d6c..967abb3 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>  
>  #endif
>  
> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>  
>  {
> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>  	.ndo_change_mtu		 = br_change_mtu,
>  	.ndo_do_ioctl		 = br_dev_ioctl,
> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>  #ifdef CONFIG_NET_POLL_CONTROLLER
>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 7e92bd0..55e4e28 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>  void br_manage_promisc(struct net_bridge *br)
>  {
>  	struct net_bridge_port *p;
> +	int set_all = false;
> +
> +	if (br->dev->flags & IFF_PROMISC)
> +		set_all = true;
> +
> +	/* If vlan filtering is disabled and there are any VLANs
> +	 * configured on top of the bridge, set promisc on all
> +	 * ports.
> +	 */
> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
> +		set_all = true;
>  
>  	list_for_each_entry(p, &br->port_list, list) {
> -		if (br->dev->flags & IFF_PROMISC) {
> -			/* PROMISC flag has been turned on for the bridge
> -			 * itself.  Turn on promisc on all ports.
> -			 */
> +		if (set_all) {
> +			/* Set all the ports to promisc mode.  */
>  			br_port_set_promisc(p);
>  
>  		} else {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 4042f86..87dcc09 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  	return v->pvid ?: VLAN_N_VID;
>  }
>  
> +static inline int br_vlan_enabled(struct net_bridge *br)
> +{
> +	return br->vlan_enabled;
> +}
>  #else
>  static inline bool br_allowed_ingress(struct net_bridge *br,
>  				      struct net_port_vlans *v,
> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  {
>  	return VLAN_N_VID;	/* Returns invalid vid */
>  }
> +
> +static inline int br_vlan_enabled(struct net_bridge *br);
> +{
> +	return 0;
> +}
>  #endif
>  
>  /* br_netfilter.c */
> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> index 8249ca7..eddc2f6 100644
> --- a/net/bridge/br_vlan.c
> +++ b/net/bridge/br_vlan.c
> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>  		goto unlock;
>  
>  	br->vlan_enabled = val;
> +	br_manage_promisc(br);
>  
>  unlock:
>  	rtnl_unlock();
> -- 
> 1.8.5.3

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

* Re: [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 16:01     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:01 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
> Configuration where all ports are set to non-flooding is a slight
> special case.  In this config, the user would have to manage all fdbs
> for all ports.  In this special case, since we'll know all addresses,
> we can turn off promisc on all ports and program all ports with the
> same set of addresses.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  include/linux/netdevice.h |  3 +++
>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
>  net/bridge/br_private.h   |  2 +-
>  net/core/dev_addr_lists.c |  7 ++++---
>  5 files changed, 59 insertions(+), 15 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index e29cce1..79e97ee 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
>  		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>  		      struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_init(struct netdev_hw_addr_list *list);
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index ef95e81..26ea4fe 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
>  	if (br->n_flood_ports == 1) {
>  		p = br->c_flood_port;
>  		netif_addr_lock(p->dev);
> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> -				     p->dev->addr_len);
> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
> +					      p->dev->addr_len);
>  		if (!err)
>  			__dev_set_rx_mode(p->dev);
>  		netif_addr_unlock(p->dev);
> +	} else if (br->n_flood_ports == 0) {
> +		list_for_each_entry(p, &br->port_list, list) {
> +			/* skip over ports being deleted. */
> +			if (!br_port_exists(p->dev))
> +				continue;
>  
> +			netif_addr_lock_nested(p->dev);
> +			err = __hw_addr_sync_multiple(&p->dev->uc,
> +						      &br->conf_addrs,
> +						      p->dev->addr_len);
> +			if (!err)
> +				__dev_set_rx_mode(p->dev);
> +			netif_addr_unlock(p->dev);
> +		}
>  	}
> +
>  }
>  
> -void br_fdb_addrs_unsync(struct net_bridge *br)
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
>  {
> -	struct net_bridge_port *p = br->c_flood_port;
> -
>  	if (!p)
>  		return;
>  
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 55e4e28..4ba62ef 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> +	else
> +		br_fdb_addrs_unsync(br, p);
>  
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * only have 1 flooding port cache if for future use.
>  	 */
>  	br->n_flood_ports++;
> -	if (br->n_flood_ports == 1)
> +	if (br->n_flood_ports == 1) {
> +		struct net_bridge_port *port;
> +
> +		/* We are transitioning from 0 flood ports to 1.  Remove
> +		 * the addresses from all the non-flood ports and turn on
> +		 * promisc on those ports.
> +		 */
> +		list_for_each_entry(port, &br->port_list, list) {
> +			/* skip the current port we are changing */
> +			if (port == p)
> +				continue;
> +
> +			if (!(port->flags & BR_FLOOD)) {
> +				br_port_set_promisc(port);
> +				br_fdb_addrs_unsync(br, port);
> +			}
> +		}
> +
>  		br->c_flood_port = p;
> +	}
>  
>  	br_fdb_addrs_sync(br);
>  	br_manage_promisc(br);
> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port) {
> -		br_fdb_addrs_unsync(br);
> -		br->c_flood_port = NULL;
> -	}
> +	if (br->n_flood_ports == 0) {
> +		/* We just dropped to 0 flood ports.  If we
> +		 * are deleting this port, unsync addresses
> +		 * from it.
> +		 */
> +		if (!br_port_exists(p->dev))
> +			br_fdb_addrs_unsync(br, p);
>  
> -	if (br->n_flood_ports == 1) {
> +		br->c_flood_port = NULL;
> +		br_fdb_addrs_sync(br);
> +	} else if (br->n_flood_ports == 1) {
> +		/* We just dropped to 1 flood port. Find this one flood
> +		 * port and sync to it.
> +		 */
>  		list_for_each_entry(port, &p->br->port_list, list) {
>  			if (br_port_exists(port->dev) &&
>  			    (port->flags & BR_FLOOD)) {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 87dcc09..13840de 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
>  void br_fdb_addrs_sync(struct net_bridge *br);
> -void br_fdb_addrs_unsync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 3de44a3..24da78f 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
>  	__hw_addr_del_entry(from_list, ha, false, false);
>  }
>  
> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> -				   struct netdev_hw_addr_list *from_list,
> -				   int addr_len)
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len)
>  {
>  	int err = 0;
>  	struct netdev_hw_addr *ha, *tmp;
> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>  	}
>  	return err;
>  }
> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
>  
>  /* This function only works where there is a strict 1-1 relationship
>   * between source and destionation of they synch. If you ever need to
> -- 
> 1.8.5.3


Same comment about core changes here
Pls CC more people so this gets proper review.

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

* Re: [Bridge] [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
@ 2014-02-26 16:01     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:01 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
> Configuration where all ports are set to non-flooding is a slight
> special case.  In this config, the user would have to manage all fdbs
> for all ports.  In this special case, since we'll know all addresses,
> we can turn off promisc on all ports and program all ports with the
> same set of addresses.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  include/linux/netdevice.h |  3 +++
>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
>  net/bridge/br_private.h   |  2 +-
>  net/core/dev_addr_lists.c |  7 ++++---
>  5 files changed, 59 insertions(+), 15 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index e29cce1..79e97ee 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
>  		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>  		      struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_init(struct netdev_hw_addr_list *list);
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index ef95e81..26ea4fe 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
>  	if (br->n_flood_ports == 1) {
>  		p = br->c_flood_port;
>  		netif_addr_lock(p->dev);
> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> -				     p->dev->addr_len);
> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
> +					      p->dev->addr_len);
>  		if (!err)
>  			__dev_set_rx_mode(p->dev);
>  		netif_addr_unlock(p->dev);
> +	} else if (br->n_flood_ports == 0) {
> +		list_for_each_entry(p, &br->port_list, list) {
> +			/* skip over ports being deleted. */
> +			if (!br_port_exists(p->dev))
> +				continue;
>  
> +			netif_addr_lock_nested(p->dev);
> +			err = __hw_addr_sync_multiple(&p->dev->uc,
> +						      &br->conf_addrs,
> +						      p->dev->addr_len);
> +			if (!err)
> +				__dev_set_rx_mode(p->dev);
> +			netif_addr_unlock(p->dev);
> +		}
>  	}
> +
>  }
>  
> -void br_fdb_addrs_unsync(struct net_bridge *br)
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
>  {
> -	struct net_bridge_port *p = br->c_flood_port;
> -
>  	if (!p)
>  		return;
>  
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 55e4e28..4ba62ef 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> +	else
> +		br_fdb_addrs_unsync(br, p);
>  
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * only have 1 flooding port cache if for future use.
>  	 */
>  	br->n_flood_ports++;
> -	if (br->n_flood_ports == 1)
> +	if (br->n_flood_ports == 1) {
> +		struct net_bridge_port *port;
> +
> +		/* We are transitioning from 0 flood ports to 1.  Remove
> +		 * the addresses from all the non-flood ports and turn on
> +		 * promisc on those ports.
> +		 */
> +		list_for_each_entry(port, &br->port_list, list) {
> +			/* skip the current port we are changing */
> +			if (port == p)
> +				continue;
> +
> +			if (!(port->flags & BR_FLOOD)) {
> +				br_port_set_promisc(port);
> +				br_fdb_addrs_unsync(br, port);
> +			}
> +		}
> +
>  		br->c_flood_port = p;
> +	}
>  
>  	br_fdb_addrs_sync(br);
>  	br_manage_promisc(br);
> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port) {
> -		br_fdb_addrs_unsync(br);
> -		br->c_flood_port = NULL;
> -	}
> +	if (br->n_flood_ports == 0) {
> +		/* We just dropped to 0 flood ports.  If we
> +		 * are deleting this port, unsync addresses
> +		 * from it.
> +		 */
> +		if (!br_port_exists(p->dev))
> +			br_fdb_addrs_unsync(br, p);
>  
> -	if (br->n_flood_ports == 1) {
> +		br->c_flood_port = NULL;
> +		br_fdb_addrs_sync(br);
> +	} else if (br->n_flood_ports == 1) {
> +		/* We just dropped to 1 flood port. Find this one flood
> +		 * port and sync to it.
> +		 */
>  		list_for_each_entry(port, &p->br->port_list, list) {
>  			if (br_port_exists(port->dev) &&
>  			    (port->flags & BR_FLOOD)) {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 87dcc09..13840de 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
>  void br_fdb_addrs_sync(struct net_bridge *br);
> -void br_fdb_addrs_unsync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 3de44a3..24da78f 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
>  	__hw_addr_del_entry(from_list, ha, false, false);
>  }
>  
> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> -				   struct netdev_hw_addr_list *from_list,
> -				   int addr_len)
> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> +			    struct netdev_hw_addr_list *from_list,
> +			    int addr_len)
>  {
>  	int err = 0;
>  	struct netdev_hw_addr *ha, *tmp;
> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>  	}
>  	return err;
>  }
> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
>  
>  /* This function only works where there is a strict 1-1 relationship
>   * between source and destionation of they synch. If you ever need to
> -- 
> 1.8.5.3


Same comment about core changes here
Pls CC more people so this gets proper review.


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

* Re: [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
  2014-02-26 15:51     ` [Bridge] " Michael S. Tsirkin
@ 2014-02-26 16:02       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 16:02 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:51 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:22AM -0500, Vlad Yasevich wrote:
>> When there is only 1 flooding port, this port is programmed
>> with all the address
> 
> all the addresses?


All statically configured ones plus locals.
Locals are needed preserve the bridge behavior of
being able to receive packets addresses to any of the
ports mac addresses on any interface.
This actually cuts the usability of this feature
in about half. :(

> 
>> the bridge accumulated.  This allows
>> us to place this port into non-promiscuous mode.
>> At other times, all ports are set as promiscuous.  To help
>> track whether the bridge set the mode or not, a new
>> flag is introduced.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_if.c      | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
>>  net/bridge/br_private.h |  1 +
>>  2 files changed, 47 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index e782c2e..51df642 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	sysfs_remove_link(br->ifobj, p->dev->name);
>>  
>> -	dev_set_promiscuity(dev, -1);
>> +	dev_set_allmulti(dev, -1);
>>  
>>  	spin_lock_bh(&br->lock);
>>  	br_stp_disable_port(p);
>> @@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>>  
>>  	call_netdevice_notifiers(NETDEV_JOIN, dev);
>>  
>> -	err = dev_set_promiscuity(dev, 1);
>> +	err = dev_set_allmulti(dev, 1);
>>  	if (err)
>>  		goto put_back;
>>  
>> @@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>>  	return 0;
>>  }
>>  
>> +static int br_port_set_promisc(struct net_bridge_port *p)
>> +{
>> +	int err = 0;
>> +
>> +	if (p->flags & BR_PROMISC)
>> +		return err;
>> +
>> +	err = dev_set_promiscuity(p->dev, 1);
>> +	if (err)
>> +		return err;
>> +
>> +	p->flags |= BR_PROMISC;
>> +	return err;
>> +}
>> +
> 
> you take pains to return an error code here,
> only to ignore it in the caller?

You are right.  This can be turned to void.

> 
>> +static void br_port_clear_promisc(struct net_bridge_port *p)
>> +{
>> +	if (!(p->flags & BR_PROMISC))
>> +		return;
>> +
>> +	dev_set_promiscuity(p->dev, -1);
>> +	p->flags &= ~BR_PROMISC;
>> +}
>> +
>> +/* When a port is added or removed or when the flooding status of
>> + * the port changes, this function is called to automatically mange
>> + * promiscuity setting of all the bridge ports.  We are always called
>> + * under RTNL so can skip using rcu primitives.
>> + */
>> +static void br_manage_promisc(struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *p;
>> +
>> +	list_for_each_entry(p, &br->port_list, list) {
>> +		if (!br_port_exists(p->dev) ||
>> +		    (br->n_flood_ports == 1 && br->c_flood_port == p))
>> +			br_port_clear_promisc(p);
>> +		else
>> +			br_port_set_promisc(p);
>> +	}
>> +}
>> +
>>  static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  {
>>  	/* Increment the number of  flooding ports, and if we
>> @@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  		br->c_flood_port = p;
>>  
>>  	br_fdb_addrs_sync(br);
>> +	br_manage_promisc(br);
>>  }
>>  
>>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> @@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  			}
>>  		}
>>  	}
>> +	br_manage_promisc(br);
>>  }
>>  
>>  void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 40a6927..6670cb3 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -174,6 +174,7 @@ struct net_bridge_port
>>  #define BR_ADMIN_COST		0x00000010
>>  #define BR_LEARNING		0x00000020
>>  #define BR_FLOOD		0x00000040
>> +#define BR_PROMISC		0x00000080
>>
> 
> Considering you then have a separate logic in 7/7
> will this be cleaner as a per-port flag?

It is a per-port flag here.  I can change it to
BR_PORT_PROMISC to make it clearer.

-vlad

>   
>>  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
>>  	struct bridge_mcast_query	ip4_query;
>> -- 
>> 1.8.5.3

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

* Re: [Bridge] [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
@ 2014-02-26 16:02       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 16:02 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:51 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:22AM -0500, Vlad Yasevich wrote:
>> When there is only 1 flooding port, this port is programmed
>> with all the address
> 
> all the addresses?


All statically configured ones plus locals.
Locals are needed preserve the bridge behavior of
being able to receive packets addresses to any of the
ports mac addresses on any interface.
This actually cuts the usability of this feature
in about half. :(

> 
>> the bridge accumulated.  This allows
>> us to place this port into non-promiscuous mode.
>> At other times, all ports are set as promiscuous.  To help
>> track whether the bridge set the mode or not, a new
>> flag is introduced.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_if.c      | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
>>  net/bridge/br_private.h |  1 +
>>  2 files changed, 47 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index e782c2e..51df642 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	sysfs_remove_link(br->ifobj, p->dev->name);
>>  
>> -	dev_set_promiscuity(dev, -1);
>> +	dev_set_allmulti(dev, -1);
>>  
>>  	spin_lock_bh(&br->lock);
>>  	br_stp_disable_port(p);
>> @@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>>  
>>  	call_netdevice_notifiers(NETDEV_JOIN, dev);
>>  
>> -	err = dev_set_promiscuity(dev, 1);
>> +	err = dev_set_allmulti(dev, 1);
>>  	if (err)
>>  		goto put_back;
>>  
>> @@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>>  	return 0;
>>  }
>>  
>> +static int br_port_set_promisc(struct net_bridge_port *p)
>> +{
>> +	int err = 0;
>> +
>> +	if (p->flags & BR_PROMISC)
>> +		return err;
>> +
>> +	err = dev_set_promiscuity(p->dev, 1);
>> +	if (err)
>> +		return err;
>> +
>> +	p->flags |= BR_PROMISC;
>> +	return err;
>> +}
>> +
> 
> you take pains to return an error code here,
> only to ignore it in the caller?

You are right.  This can be turned to void.

> 
>> +static void br_port_clear_promisc(struct net_bridge_port *p)
>> +{
>> +	if (!(p->flags & BR_PROMISC))
>> +		return;
>> +
>> +	dev_set_promiscuity(p->dev, -1);
>> +	p->flags &= ~BR_PROMISC;
>> +}
>> +
>> +/* When a port is added or removed or when the flooding status of
>> + * the port changes, this function is called to automatically mange
>> + * promiscuity setting of all the bridge ports.  We are always called
>> + * under RTNL so can skip using rcu primitives.
>> + */
>> +static void br_manage_promisc(struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *p;
>> +
>> +	list_for_each_entry(p, &br->port_list, list) {
>> +		if (!br_port_exists(p->dev) ||
>> +		    (br->n_flood_ports == 1 && br->c_flood_port == p))
>> +			br_port_clear_promisc(p);
>> +		else
>> +			br_port_set_promisc(p);
>> +	}
>> +}
>> +
>>  static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  {
>>  	/* Increment the number of  flooding ports, and if we
>> @@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  		br->c_flood_port = p;
>>  
>>  	br_fdb_addrs_sync(br);
>> +	br_manage_promisc(br);
>>  }
>>  
>>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> @@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  			}
>>  		}
>>  	}
>> +	br_manage_promisc(br);
>>  }
>>  
>>  void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 40a6927..6670cb3 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -174,6 +174,7 @@ struct net_bridge_port
>>  #define BR_ADMIN_COST		0x00000010
>>  #define BR_LEARNING		0x00000020
>>  #define BR_FLOOD		0x00000040
>> +#define BR_PROMISC		0x00000080
>>
> 
> Considering you then have a separate logic in 7/7
> will this be cleaner as a per-port flag?

It is a per-port flag here.  I can change it to
BR_PORT_PROMISC to make it clearer.

-vlad

>   
>>  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
>>  	struct bridge_mcast_query	ip4_query;
>> -- 
>> 1.8.5.3


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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-26 16:00     ` [Bridge] " Michael S. Tsirkin
@ 2014-02-26 16:05       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 16:05 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 11:00 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:24AM -0500, Vlad Yasevich wrote:
>> If the user configures vlan interfaces on top of the bridge and the bridge
>> doesn't have vlan filtering enabled, we have to place all the ports in
>> promsic mode so that we can correctly receive tagged frames.
>> When vlan filtering is enabled, the vlan configuration will be provided
>> via filtering interface.
>> When the vlan filtering is toggled, we also have mange promiscuity.
> 
> have to manage?
> 
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> 
> I wonder if it matters that we scan all ports
> on vlan add/del now.
> 
> If yes, we could optimize some cases by using
> a counter of promisc ports.

Or a bridge flag.  I'll see if can add that to save
repeated list walks without doing any useful work.

Thanks
-vlad

> 
> 
>> ---
>>  net/bridge/br_device.c  | 14 ++++++++++++++
>>  net/bridge/br_if.c      | 17 +++++++++++++----
>>  net/bridge/br_private.h |  9 +++++++++
>>  net/bridge/br_vlan.c    |  1 +
>>  4 files changed, 37 insertions(+), 4 deletions(-)
>>
>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>> index 0af9d6c..967abb3 100644
>> --- a/net/bridge/br_device.c
>> +++ b/net/bridge/br_device.c
>> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>>  
>>  #endif
>>  
>> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>>  
>>  {
>> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>>  	.ndo_change_mtu		 = br_change_mtu,
>>  	.ndo_do_ioctl		 = br_dev_ioctl,
>> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
>> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>>  #ifdef CONFIG_NET_POLL_CONTROLLER
>>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 7e92bd0..55e4e28 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>>  void br_manage_promisc(struct net_bridge *br)
>>  {
>>  	struct net_bridge_port *p;
>> +	int set_all = false;
>> +
>> +	if (br->dev->flags & IFF_PROMISC)
>> +		set_all = true;
>> +
>> +	/* If vlan filtering is disabled and there are any VLANs
>> +	 * configured on top of the bridge, set promisc on all
>> +	 * ports.
>> +	 */
>> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
>> +		set_all = true;
>>  
>>  	list_for_each_entry(p, &br->port_list, list) {
>> -		if (br->dev->flags & IFF_PROMISC) {
>> -			/* PROMISC flag has been turned on for the bridge
>> -			 * itself.  Turn on promisc on all ports.
>> -			 */
>> +		if (set_all) {
>> +			/* Set all the ports to promisc mode.  */
>>  			br_port_set_promisc(p);
>>  
>>  		} else {
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 4042f86..87dcc09 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  	return v->pvid ?: VLAN_N_VID;
>>  }
>>  
>> +static inline int br_vlan_enabled(struct net_bridge *br)
>> +{
>> +	return br->vlan_enabled;
>> +}
>>  #else
>>  static inline bool br_allowed_ingress(struct net_bridge *br,
>>  				      struct net_port_vlans *v,
>> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  {
>>  	return VLAN_N_VID;	/* Returns invalid vid */
>>  }
>> +
>> +static inline int br_vlan_enabled(struct net_bridge *br);
>> +{
>> +	return 0;
>> +}
>>  #endif
>>  
>>  /* br_netfilter.c */
>> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
>> index 8249ca7..eddc2f6 100644
>> --- a/net/bridge/br_vlan.c
>> +++ b/net/bridge/br_vlan.c
>> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>>  		goto unlock;
>>  
>>  	br->vlan_enabled = val;
>> +	br_manage_promisc(br);
>>  
>>  unlock:
>>  	rtnl_unlock();
>> -- 
>> 1.8.5.3

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-02-26 16:05       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 16:05 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 11:00 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:24AM -0500, Vlad Yasevich wrote:
>> If the user configures vlan interfaces on top of the bridge and the bridge
>> doesn't have vlan filtering enabled, we have to place all the ports in
>> promsic mode so that we can correctly receive tagged frames.
>> When vlan filtering is enabled, the vlan configuration will be provided
>> via filtering interface.
>> When the vlan filtering is toggled, we also have mange promiscuity.
> 
> have to manage?
> 
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> 
> I wonder if it matters that we scan all ports
> on vlan add/del now.
> 
> If yes, we could optimize some cases by using
> a counter of promisc ports.

Or a bridge flag.  I'll see if can add that to save
repeated list walks without doing any useful work.

Thanks
-vlad

> 
> 
>> ---
>>  net/bridge/br_device.c  | 14 ++++++++++++++
>>  net/bridge/br_if.c      | 17 +++++++++++++----
>>  net/bridge/br_private.h |  9 +++++++++
>>  net/bridge/br_vlan.c    |  1 +
>>  4 files changed, 37 insertions(+), 4 deletions(-)
>>
>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>> index 0af9d6c..967abb3 100644
>> --- a/net/bridge/br_device.c
>> +++ b/net/bridge/br_device.c
>> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>>  
>>  #endif
>>  
>> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>>  
>>  {
>> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>>  	.ndo_change_mtu		 = br_change_mtu,
>>  	.ndo_do_ioctl		 = br_dev_ioctl,
>> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
>> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>>  #ifdef CONFIG_NET_POLL_CONTROLLER
>>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 7e92bd0..55e4e28 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>>  void br_manage_promisc(struct net_bridge *br)
>>  {
>>  	struct net_bridge_port *p;
>> +	int set_all = false;
>> +
>> +	if (br->dev->flags & IFF_PROMISC)
>> +		set_all = true;
>> +
>> +	/* If vlan filtering is disabled and there are any VLANs
>> +	 * configured on top of the bridge, set promisc on all
>> +	 * ports.
>> +	 */
>> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
>> +		set_all = true;
>>  
>>  	list_for_each_entry(p, &br->port_list, list) {
>> -		if (br->dev->flags & IFF_PROMISC) {
>> -			/* PROMISC flag has been turned on for the bridge
>> -			 * itself.  Turn on promisc on all ports.
>> -			 */
>> +		if (set_all) {
>> +			/* Set all the ports to promisc mode.  */
>>  			br_port_set_promisc(p);
>>  
>>  		} else {
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 4042f86..87dcc09 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  	return v->pvid ?: VLAN_N_VID;
>>  }
>>  
>> +static inline int br_vlan_enabled(struct net_bridge *br)
>> +{
>> +	return br->vlan_enabled;
>> +}
>>  #else
>>  static inline bool br_allowed_ingress(struct net_bridge *br,
>>  				      struct net_port_vlans *v,
>> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  {
>>  	return VLAN_N_VID;	/* Returns invalid vid */
>>  }
>> +
>> +static inline int br_vlan_enabled(struct net_bridge *br);
>> +{
>> +	return 0;
>> +}
>>  #endif
>>  
>>  /* br_netfilter.c */
>> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
>> index 8249ca7..eddc2f6 100644
>> --- a/net/bridge/br_vlan.c
>> +++ b/net/bridge/br_vlan.c
>> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>>  		goto unlock;
>>  
>>  	br->vlan_enabled = val;
>> +	br_manage_promisc(br);
>>  
>>  unlock:
>>  	rtnl_unlock();
>> -- 
>> 1.8.5.3


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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 16:23     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:23 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
> When a static fdb entry is created, add the mac address to the bridge
> address list.  This list is used to program the proper port's
> address list.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

Hmm won't this mean learned addresses are missing there?
And if so isn't this a problem when we use this list
in the next patch?

I guess we could limit this to configurations where
learning is also disabled (not just flood) -
seems a bit arbitrary but will likely cover
most use-cases.


> ---
>  include/linux/netdevice.h |   6 +++
>  net/bridge/br_device.c    |   2 +
>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
>  net/bridge/br_if.c        |  14 ++++--
>  net/bridge/br_private.h   |   3 ++
>  net/core/dev.c            |   1 +
>  net/core/dev_addr_lists.c |  14 +++---
>  7 files changed, 134 insertions(+), 16 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 440a02e..e29cce1 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
>  void unregister_netdev(struct net_device *dev);
>  
>  /* General hardware address lists handling functions */
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 63f0455..1521db6 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
>  		u64_stats_init(&br_dev_stats->syncp);
>  	}
>  
> +	__hw_addr_init(&br->conf_addrs);
> +
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index 9203d5a..ef95e81 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  static void fdb_notify(struct net_bridge *br,
>  		       const struct net_bridge_fdb_entry *, int);
>  
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
> +
>  static u32 fdb_salt __read_mostly;
>  
>  int __init br_fdb_init(void)
> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
>  
>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
>  {
> +	if (f->is_static)
> +		br_addr_del(br, f->addr.addr);
> +
>  	hlist_del_rcu(&f->hlist);
>  	fdb_notify(br, f, RTM_DELNEIGH);
>  	call_rcu(&f->rcu, fdb_rcu_free);
> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  		return -ENOMEM;
>  
>  	fdb->is_local = fdb->is_static = 1;
> +	br_addr_add(br, addr);
>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
>  	return 0;
>  }
> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
>  	}
>  
>  	if (fdb_to_nud(fdb) != state) {
> -		if (state & NUD_PERMANENT)
> -			fdb->is_local = fdb->is_static = 1;
> -		else if (state & NUD_NOARP) {
> +		if (state & NUD_PERMANENT) {
> +			fdb->is_local = 1;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else if (state & NUD_NOARP) {
> +			fdb->is_local = 0;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else {
>  			fdb->is_local = 0;
> -			fdb->is_static = 1;
> -		} else
> -			fdb->is_local = fdb->is_static = 0;
> +			if (fdb->is_static) {
> +				fdb->is_static = 0;
> +				br_addr_del(br, addr);
> +			}
> +		}
>  
>  		modified = true;
>  	}
> +
>  	fdb->added_by_user = 1;
>  
>  	fdb->used = jiffies;
> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
>  out:
>  	return err;
>  }
> +
> +
> +/* Sync the current list to the correct flood port.  */
> +void br_fdb_addrs_sync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p;
> +	int err;
> +
> +	/* This function has to run under RTNL.
> +	 * Program the user added addresses into the proper port.  This
> +	 * fuction follows the following algorithm:
> +	 *   a)  If only 1 port is flooding:
> +	 *       - write all the addresses to this one port.
> +	 */
> +	if (br->n_flood_ports == 1) {
> +		p = br->c_flood_port;
> +		netif_addr_lock(p->dev);
> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> +				     p->dev->addr_len);
> +		if (!err)
> +			__dev_set_rx_mode(p->dev);
> +		netif_addr_unlock(p->dev);
> +
> +	}
> +}
> +
> +void br_fdb_addrs_unsync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p = br->c_flood_port;
> +
> +	if (!p)
> +		return;
> +
> +	netif_addr_lock(p->dev);
> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
> +	__dev_set_rx_mode(p->dev);
> +	netif_addr_unlock(p->dev);
> +}
> +
> +/* When a static FDB entry is added, the mac address from the entry is
> + * added to the bridge private HW address list and all required ports
> + * are then updated with the new information.
> + * Called under RTNL.
> + */
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> +/* When a static FDB entry is deleted, the HW address from that entry is
> + * also removed from the bridge private HW address list and updates all
> + * the ports with needed information.
> + * Called under RTNL.
> + */
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index f072b34..e782c2e 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	br_ifinfo_notify(RTM_DELLINK, p);
>  
> -	list_del_rcu(&p->list);
> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
>  
> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> +	list_del_rcu(&p->list);
>  
>  	netdev_rx_handler_unregister(dev);
>  
> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	br->n_flood_ports++;
>  	if (br->n_flood_ports == 1)
>  		br->c_flood_port = p;
> +
> +	br_fdb_addrs_sync(br);
>  }
>  
>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port)
> +	if (p == br->c_flood_port) {
> +		br_fdb_addrs_unsync(br);
>  		br->c_flood_port = NULL;
> +	}
>  
>  	if (br->n_flood_ports == 1) {
>  		list_for_each_entry(port, &p->br->port_list, list) {
> -			if (port->flags & BR_FLOOD) {
> +			if (br_port_exists(port->dev) &&
> +			    (port->flags & BR_FLOOD)) {
>  				br->c_flood_port = port;
> +				br_fdb_addrs_sync(br);
>  				break;
>  			}
>  		}
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 26a3987..40a6927 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -296,6 +296,7 @@ struct net_bridge
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
>  #endif
> +	struct netdev_hw_addr_list	conf_addrs;
>  };
>  
>  struct br_input_skb_cb {
> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  	       const unsigned char *addr, u16 nlh_flags);
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
> +void br_fdb_addrs_sync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 4ad1b78..eca4d476 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>  	if (ops->ndo_set_rx_mode)
>  		ops->ndo_set_rx_mode(dev);
>  }
> +EXPORT_SYMBOL(__dev_set_rx_mode);
>  
>  void dev_set_rx_mode(struct net_device *dev)
>  {
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 329d579..3de44a3 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>  				   sync);
>  }
>  
> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>  				0);
>  }
> +EXPORT_SYMBOL(__hw_addr_add);
>  
>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>  			       struct netdev_hw_addr *ha, bool global,
> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>  	return -ENOENT;
>  }
>  
> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>  }
> +EXPORT_SYMBOL(__hw_addr_del);
>  
>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>  			       struct netdev_hw_addr *ha,
> -- 
> 1.8.5.3

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 16:23     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:23 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
> When a static fdb entry is created, add the mac address to the bridge
> address list.  This list is used to program the proper port's
> address list.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

Hmm won't this mean learned addresses are missing there?
And if so isn't this a problem when we use this list
in the next patch?

I guess we could limit this to configurations where
learning is also disabled (not just flood) -
seems a bit arbitrary but will likely cover
most use-cases.


> ---
>  include/linux/netdevice.h |   6 +++
>  net/bridge/br_device.c    |   2 +
>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
>  net/bridge/br_if.c        |  14 ++++--
>  net/bridge/br_private.h   |   3 ++
>  net/core/dev.c            |   1 +
>  net/core/dev_addr_lists.c |  14 +++---
>  7 files changed, 134 insertions(+), 16 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 440a02e..e29cce1 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
>  void unregister_netdev(struct net_device *dev);
>  
>  /* General hardware address lists handling functions */
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type);
>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 63f0455..1521db6 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
>  		u64_stats_init(&br_dev_stats->syncp);
>  	}
>  
> +	__hw_addr_init(&br->conf_addrs);
> +
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index 9203d5a..ef95e81 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  static void fdb_notify(struct net_bridge *br,
>  		       const struct net_bridge_fdb_entry *, int);
>  
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
> +
>  static u32 fdb_salt __read_mostly;
>  
>  int __init br_fdb_init(void)
> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
>  
>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
>  {
> +	if (f->is_static)
> +		br_addr_del(br, f->addr.addr);
> +
>  	hlist_del_rcu(&f->hlist);
>  	fdb_notify(br, f, RTM_DELNEIGH);
>  	call_rcu(&f->rcu, fdb_rcu_free);
> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>  		return -ENOMEM;
>  
>  	fdb->is_local = fdb->is_static = 1;
> +	br_addr_add(br, addr);
>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
>  	return 0;
>  }
> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
>  	}
>  
>  	if (fdb_to_nud(fdb) != state) {
> -		if (state & NUD_PERMANENT)
> -			fdb->is_local = fdb->is_static = 1;
> -		else if (state & NUD_NOARP) {
> +		if (state & NUD_PERMANENT) {
> +			fdb->is_local = 1;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else if (state & NUD_NOARP) {
> +			fdb->is_local = 0;
> +			if (!fdb->is_static) {
> +				fdb->is_static = 1;
> +				br_addr_add(br, addr);
> +			}
> +		} else {
>  			fdb->is_local = 0;
> -			fdb->is_static = 1;
> -		} else
> -			fdb->is_local = fdb->is_static = 0;
> +			if (fdb->is_static) {
> +				fdb->is_static = 0;
> +				br_addr_del(br, addr);
> +			}
> +		}
>  
>  		modified = true;
>  	}
> +
>  	fdb->added_by_user = 1;
>  
>  	fdb->used = jiffies;
> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
>  out:
>  	return err;
>  }
> +
> +
> +/* Sync the current list to the correct flood port.  */
> +void br_fdb_addrs_sync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p;
> +	int err;
> +
> +	/* This function has to run under RTNL.
> +	 * Program the user added addresses into the proper port.  This
> +	 * fuction follows the following algorithm:
> +	 *   a)  If only 1 port is flooding:
> +	 *       - write all the addresses to this one port.
> +	 */
> +	if (br->n_flood_ports == 1) {
> +		p = br->c_flood_port;
> +		netif_addr_lock(p->dev);
> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> +				     p->dev->addr_len);
> +		if (!err)
> +			__dev_set_rx_mode(p->dev);
> +		netif_addr_unlock(p->dev);
> +
> +	}
> +}
> +
> +void br_fdb_addrs_unsync(struct net_bridge *br)
> +{
> +	struct net_bridge_port *p = br->c_flood_port;
> +
> +	if (!p)
> +		return;
> +
> +	netif_addr_lock(p->dev);
> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
> +	__dev_set_rx_mode(p->dev);
> +	netif_addr_unlock(p->dev);
> +}
> +
> +/* When a static FDB entry is added, the mac address from the entry is
> + * added to the bridge private HW address list and all required ports
> + * are then updated with the new information.
> + * Called under RTNL.
> + */
> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> +/* When a static FDB entry is deleted, the HW address from that entry is
> + * also removed from the bridge private HW address list and updates all
> + * the ports with needed information.
> + * Called under RTNL.
> + */
> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
> +{
> +	int err;
> +
> +	ASSERT_RTNL();
> +
> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
> +			    NETDEV_HW_ADDR_T_UNICAST);
> +	if (!err)
> +		br_fdb_addrs_sync(br);
> +
> +	return err;
> +}
> +
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index f072b34..e782c2e 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>  
>  	br_ifinfo_notify(RTM_DELLINK, p);
>  
> -	list_del_rcu(&p->list);
> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>  
>  	if (p->flags & BR_FLOOD)
>  		br_del_flood_port(p, br);
> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>  	nbp_vlan_flush(p);
>  	br_fdb_delete_by_port(br, p, 1);
>  
> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> +	list_del_rcu(&p->list);
>  
>  	netdev_rx_handler_unregister(dev);
>  
> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	br->n_flood_ports++;
>  	if (br->n_flood_ports == 1)
>  		br->c_flood_port = p;
> +
> +	br_fdb_addrs_sync(br);
>  }
>  
>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>  	 * set it if it is not set.
>  	 */
>  	br->n_flood_ports--;
> -	if (p == br->c_flood_port)
> +	if (p == br->c_flood_port) {
> +		br_fdb_addrs_unsync(br);
>  		br->c_flood_port = NULL;
> +	}
>  
>  	if (br->n_flood_ports == 1) {
>  		list_for_each_entry(port, &p->br->port_list, list) {
> -			if (port->flags & BR_FLOOD) {
> +			if (br_port_exists(port->dev) &&
> +			    (port->flags & BR_FLOOD)) {
>  				br->c_flood_port = port;
> +				br_fdb_addrs_sync(br);
>  				break;
>  			}
>  		}
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 26a3987..40a6927 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -296,6 +296,7 @@ struct net_bridge
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
>  #endif
> +	struct netdev_hw_addr_list	conf_addrs;
>  };
>  
>  struct br_input_skb_cb {
> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>  	       const unsigned char *addr, u16 nlh_flags);
>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>  		struct net_device *dev, int idx);
> +void br_fdb_addrs_sync(struct net_bridge *br);
> +void br_fdb_addrs_unsync(struct net_bridge *br);
>  
>  /* br_forward.c */
>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 4ad1b78..eca4d476 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>  	if (ops->ndo_set_rx_mode)
>  		ops->ndo_set_rx_mode(dev);
>  }
> +EXPORT_SYMBOL(__dev_set_rx_mode);
>  
>  void dev_set_rx_mode(struct net_device *dev)
>  {
> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> index 329d579..3de44a3 100644
> --- a/net/core/dev_addr_lists.c
> +++ b/net/core/dev_addr_lists.c
> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>  				   sync);
>  }
>  
> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>  				0);
>  }
> +EXPORT_SYMBOL(__hw_addr_add);
>  
>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>  			       struct netdev_hw_addr *ha, bool global,
> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>  	return -ENOENT;
>  }
>  
> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
> -			 const unsigned char *addr, int addr_len,
> -			 unsigned char addr_type)
> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> +		  const unsigned char *addr, int addr_len,
> +		  unsigned char addr_type)
>  {
>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>  }
> +EXPORT_SYMBOL(__hw_addr_del);
>  
>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>  			       struct netdev_hw_addr *ha,
> -- 
> 1.8.5.3

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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-26 16:05       ` [Bridge] " Vlad Yasevich
@ 2014-02-26 16:25         ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:25 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 11:05:40AM -0500, Vlad Yasevich wrote:
> On 02/26/2014 11:00 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:24AM -0500, Vlad Yasevich wrote:
> >> If the user configures vlan interfaces on top of the bridge and the bridge
> >> doesn't have vlan filtering enabled, we have to place all the ports in
> >> promsic mode so that we can correctly receive tagged frames.
> >> When vlan filtering is enabled, the vlan configuration will be provided
> >> via filtering interface.
> >> When the vlan filtering is toggled, we also have mange promiscuity.
> > 
> > have to manage?
> > 
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > 
> > I wonder if it matters that we scan all ports
> > on vlan add/del now.
> > 
> > If yes, we could optimize some cases by using
> > a counter of promisc ports.
> 
> Or a bridge flag.  I'll see if can add that to save
> repeated list walks without doing any useful work.
> 
> Thanks
> -vlad

Need to check if there are port list walks there anyway.
If yes then it doesn't matter much I think.

> > 
> > 
> >> ---
> >>  net/bridge/br_device.c  | 14 ++++++++++++++
> >>  net/bridge/br_if.c      | 17 +++++++++++++----
> >>  net/bridge/br_private.h |  9 +++++++++
> >>  net/bridge/br_vlan.c    |  1 +
> >>  4 files changed, 37 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> >> index 0af9d6c..967abb3 100644
> >> --- a/net/bridge/br_device.c
> >> +++ b/net/bridge/br_device.c
> >> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
> >>  
> >>  #endif
> >>  
> >> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> >> +{
> >> +	br_manage_promisc(netdev_priv(br_dev));
> >> +	return 0;
> >> +}
> >> +
> >> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> >> +{
> >> +	br_manage_promisc(netdev_priv(br_dev));
> >> +	return 0;
> >> +}
> >> +
> >>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
> >>  
> >>  {
> >> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
> >>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
> >>  	.ndo_change_mtu		 = br_change_mtu,
> >>  	.ndo_do_ioctl		 = br_dev_ioctl,
> >> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
> >> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
> >>  #ifdef CONFIG_NET_POLL_CONTROLLER
> >>  	.ndo_netpoll_setup	 = br_netpoll_setup,
> >>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index 7e92bd0..55e4e28 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
> >>  void br_manage_promisc(struct net_bridge *br)
> >>  {
> >>  	struct net_bridge_port *p;
> >> +	int set_all = false;
> >> +
> >> +	if (br->dev->flags & IFF_PROMISC)
> >> +		set_all = true;
> >> +
> >> +	/* If vlan filtering is disabled and there are any VLANs
> >> +	 * configured on top of the bridge, set promisc on all
> >> +	 * ports.
> >> +	 */
> >> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
> >> +		set_all = true;
> >>  
> >>  	list_for_each_entry(p, &br->port_list, list) {
> >> -		if (br->dev->flags & IFF_PROMISC) {
> >> -			/* PROMISC flag has been turned on for the bridge
> >> -			 * itself.  Turn on promisc on all ports.
> >> -			 */
> >> +		if (set_all) {
> >> +			/* Set all the ports to promisc mode.  */
> >>  			br_port_set_promisc(p);
> >>  
> >>  		} else {
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 4042f86..87dcc09 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
> >>  	return v->pvid ?: VLAN_N_VID;
> >>  }
> >>  
> >> +static inline int br_vlan_enabled(struct net_bridge *br)
> >> +{
> >> +	return br->vlan_enabled;
> >> +}
> >>  #else
> >>  static inline bool br_allowed_ingress(struct net_bridge *br,
> >>  				      struct net_port_vlans *v,
> >> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
> >>  {
> >>  	return VLAN_N_VID;	/* Returns invalid vid */
> >>  }
> >> +
> >> +static inline int br_vlan_enabled(struct net_bridge *br);
> >> +{
> >> +	return 0;
> >> +}
> >>  #endif
> >>  
> >>  /* br_netfilter.c */
> >> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> >> index 8249ca7..eddc2f6 100644
> >> --- a/net/bridge/br_vlan.c
> >> +++ b/net/bridge/br_vlan.c
> >> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
> >>  		goto unlock;
> >>  
> >>  	br->vlan_enabled = val;
> >> +	br_manage_promisc(br);
> >>  
> >>  unlock:
> >>  	rtnl_unlock();
> >> -- 
> >> 1.8.5.3

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-02-26 16:25         ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:25 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 11:05:40AM -0500, Vlad Yasevich wrote:
> On 02/26/2014 11:00 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:24AM -0500, Vlad Yasevich wrote:
> >> If the user configures vlan interfaces on top of the bridge and the bridge
> >> doesn't have vlan filtering enabled, we have to place all the ports in
> >> promsic mode so that we can correctly receive tagged frames.
> >> When vlan filtering is enabled, the vlan configuration will be provided
> >> via filtering interface.
> >> When the vlan filtering is toggled, we also have mange promiscuity.
> > 
> > have to manage?
> > 
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > 
> > I wonder if it matters that we scan all ports
> > on vlan add/del now.
> > 
> > If yes, we could optimize some cases by using
> > a counter of promisc ports.
> 
> Or a bridge flag.  I'll see if can add that to save
> repeated list walks without doing any useful work.
> 
> Thanks
> -vlad

Need to check if there are port list walks there anyway.
If yes then it doesn't matter much I think.

> > 
> > 
> >> ---
> >>  net/bridge/br_device.c  | 14 ++++++++++++++
> >>  net/bridge/br_if.c      | 17 +++++++++++++----
> >>  net/bridge/br_private.h |  9 +++++++++
> >>  net/bridge/br_vlan.c    |  1 +
> >>  4 files changed, 37 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> >> index 0af9d6c..967abb3 100644
> >> --- a/net/bridge/br_device.c
> >> +++ b/net/bridge/br_device.c
> >> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
> >>  
> >>  #endif
> >>  
> >> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> >> +{
> >> +	br_manage_promisc(netdev_priv(br_dev));
> >> +	return 0;
> >> +}
> >> +
> >> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> >> +{
> >> +	br_manage_promisc(netdev_priv(br_dev));
> >> +	return 0;
> >> +}
> >> +
> >>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
> >>  
> >>  {
> >> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
> >>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
> >>  	.ndo_change_mtu		 = br_change_mtu,
> >>  	.ndo_do_ioctl		 = br_dev_ioctl,
> >> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
> >> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
> >>  #ifdef CONFIG_NET_POLL_CONTROLLER
> >>  	.ndo_netpoll_setup	 = br_netpoll_setup,
> >>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index 7e92bd0..55e4e28 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
> >>  void br_manage_promisc(struct net_bridge *br)
> >>  {
> >>  	struct net_bridge_port *p;
> >> +	int set_all = false;
> >> +
> >> +	if (br->dev->flags & IFF_PROMISC)
> >> +		set_all = true;
> >> +
> >> +	/* If vlan filtering is disabled and there are any VLANs
> >> +	 * configured on top of the bridge, set promisc on all
> >> +	 * ports.
> >> +	 */
> >> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
> >> +		set_all = true;
> >>  
> >>  	list_for_each_entry(p, &br->port_list, list) {
> >> -		if (br->dev->flags & IFF_PROMISC) {
> >> -			/* PROMISC flag has been turned on for the bridge
> >> -			 * itself.  Turn on promisc on all ports.
> >> -			 */
> >> +		if (set_all) {
> >> +			/* Set all the ports to promisc mode.  */
> >>  			br_port_set_promisc(p);
> >>  
> >>  		} else {
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 4042f86..87dcc09 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
> >>  	return v->pvid ?: VLAN_N_VID;
> >>  }
> >>  
> >> +static inline int br_vlan_enabled(struct net_bridge *br)
> >> +{
> >> +	return br->vlan_enabled;
> >> +}
> >>  #else
> >>  static inline bool br_allowed_ingress(struct net_bridge *br,
> >>  				      struct net_port_vlans *v,
> >> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
> >>  {
> >>  	return VLAN_N_VID;	/* Returns invalid vid */
> >>  }
> >> +
> >> +static inline int br_vlan_enabled(struct net_bridge *br);
> >> +{
> >> +	return 0;
> >> +}
> >>  #endif
> >>  
> >>  /* br_netfilter.c */
> >> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> >> index 8249ca7..eddc2f6 100644
> >> --- a/net/bridge/br_vlan.c
> >> +++ b/net/bridge/br_vlan.c
> >> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
> >>  		goto unlock;
> >>  
> >>  	br->vlan_enabled = val;
> >> +	br_manage_promisc(br);
> >>  
> >>  unlock:
> >>  	rtnl_unlock();
> >> -- 
> >> 1.8.5.3

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

* Re: [PATCH RFC 0/7] Non-promisc bidge ports support
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 16:34   ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:34 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:18AM -0500, Vlad Yasevich wrote:
> This patch series is a complete re-design and re-implementation of
> prior attempts to support non-promiscuous bridge ports.

Nice.
For those that wonder: the setups that benefit from this
could look e.g. like this:

A---+
    |
    |
   BRIDGE--C
    |
    |
B---+

If A, B, C all use standard NICs without setting them
into promiscous mode, unicast packets sent to e.g. A that don't match
the address of its NIC will be dropped anyway.  Same applies to B and C.

With this patchset we'll be able to bypass the
need for promisc mode on NICs in the BRIDGE box.





> The basic design is as follows.  The bridge keeps track of
> all the ports that flood packets to unknown destinations.  If
> the flooding is disabled on the port, to get traffic to flow
> through, user/management would need to add an fdb describing
> such traffic.  When such fdb is added, we save the address
> to bridge private hardware address list. 
> Since we now have static configuration for all non-flooding
> ports and only 1 flooding port, we can make this single port
> non-promiscuous and program the receive filter with our list
> of addresses.  On HW that doesn't support unicast filtering or
> if the list too bit, the device will be placed in promiscuous mode
> by the application of the filter.
> 
> There are multiple reasons I chose to do private hw address
> list in the bridge in patch 3:
>   1)  I tried using the fdb table itself as main repository, but
>       this caused difficulties in synchronizing this table with
>       the interface filters later on.
>   2)  I tried using the bridge device 'uc' list to store these
>       addresses, but that caused issues with devices on top of
>       a bridge (vlans, bonds) that changed their mac addresses
>       and propagated this down to bridge.   I recently figured
>       out a way that might allow us to do this which involves
>       learning to be added br_dev_xmi().  We can discuss this,
>       if there serious objections to current proposal.
> 
> There are some other cases when promiscuous mode has to be turned
> back on.  One is when the bridge itself if placed in promiscuous
> mode (use sets promisc flag).  The other is when vlans devices are
> configured on top of the bridge and vlan filtering is disabled (default).
> This allows the bridge to receive all tagged frames and doesn't create
> a dependency between this code and vlan filtering.
> 
> The last patch in the series is a special case where all ports
> are non-flooding.  This could be useful in a routed configurations.
> In this case, since all ports will be configured manually, we can
> sync the our address list across all port of the bridge and make all
> ports non-promiscuous.
> 
> Thanks
> -vlad
> 
> Vlad Yasevich (7):
>   bridge: Turn flag change macro into a function.
>   bridge: Keep track of ports capable of flooding.
>   bridge: Add addresses from static fdbs to bridge address list
>   bridge: Automatically manage port promiscuous mode.
>   bridge: Correctly manage promiscuity when user requested it.
>   bridge: Manage promisc mode when vlans are configured on top of a
>     bridge
>   bridge: Support promisc management when all ports are non-flooding
> 
>  include/linux/netdevice.h |   9 +++
>  net/bridge/br_device.c    |  23 +++++++
>  net/bridge/br_fdb.c       | 122 +++++++++++++++++++++++++++++++++--
>  net/bridge/br_if.c        | 159 ++++++++++++++++++++++++++++++++++++++++++++--
>  net/bridge/br_netlink.c   |   3 +
>  net/bridge/br_private.h   |  18 ++++++
>  net/bridge/br_sysfs_if.c  |  33 +++++++---
>  net/bridge/br_vlan.c      |   1 +
>  net/core/dev.c            |   1 +
>  net/core/dev_addr_lists.c |  21 +++---
>  10 files changed, 361 insertions(+), 29 deletions(-)
> 
> -- 
> 1.8.5.3

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

* Re: [Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
@ 2014-02-26 16:34   ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 16:34 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:18:18AM -0500, Vlad Yasevich wrote:
> This patch series is a complete re-design and re-implementation of
> prior attempts to support non-promiscuous bridge ports.

Nice.
For those that wonder: the setups that benefit from this
could look e.g. like this:

A---+
    |
    |
   BRIDGE--C
    |
    |
B---+

If A, B, C all use standard NICs without setting them
into promiscous mode, unicast packets sent to e.g. A that don't match
the address of its NIC will be dropped anyway.  Same applies to B and C.

With this patchset we'll be able to bypass the
need for promisc mode on NICs in the BRIDGE box.





> The basic design is as follows.  The bridge keeps track of
> all the ports that flood packets to unknown destinations.  If
> the flooding is disabled on the port, to get traffic to flow
> through, user/management would need to add an fdb describing
> such traffic.  When such fdb is added, we save the address
> to bridge private hardware address list. 
> Since we now have static configuration for all non-flooding
> ports and only 1 flooding port, we can make this single port
> non-promiscuous and program the receive filter with our list
> of addresses.  On HW that doesn't support unicast filtering or
> if the list too bit, the device will be placed in promiscuous mode
> by the application of the filter.
> 
> There are multiple reasons I chose to do private hw address
> list in the bridge in patch 3:
>   1)  I tried using the fdb table itself as main repository, but
>       this caused difficulties in synchronizing this table with
>       the interface filters later on.
>   2)  I tried using the bridge device 'uc' list to store these
>       addresses, but that caused issues with devices on top of
>       a bridge (vlans, bonds) that changed their mac addresses
>       and propagated this down to bridge.   I recently figured
>       out a way that might allow us to do this which involves
>       learning to be added br_dev_xmi().  We can discuss this,
>       if there serious objections to current proposal.
> 
> There are some other cases when promiscuous mode has to be turned
> back on.  One is when the bridge itself if placed in promiscuous
> mode (use sets promisc flag).  The other is when vlans devices are
> configured on top of the bridge and vlan filtering is disabled (default).
> This allows the bridge to receive all tagged frames and doesn't create
> a dependency between this code and vlan filtering.
> 
> The last patch in the series is a special case where all ports
> are non-flooding.  This could be useful in a routed configurations.
> In this case, since all ports will be configured manually, we can
> sync the our address list across all port of the bridge and make all
> ports non-promiscuous.
> 
> Thanks
> -vlad
> 
> Vlad Yasevich (7):
>   bridge: Turn flag change macro into a function.
>   bridge: Keep track of ports capable of flooding.
>   bridge: Add addresses from static fdbs to bridge address list
>   bridge: Automatically manage port promiscuous mode.
>   bridge: Correctly manage promiscuity when user requested it.
>   bridge: Manage promisc mode when vlans are configured on top of a
>     bridge
>   bridge: Support promisc management when all ports are non-flooding
> 
>  include/linux/netdevice.h |   9 +++
>  net/bridge/br_device.c    |  23 +++++++
>  net/bridge/br_fdb.c       | 122 +++++++++++++++++++++++++++++++++--
>  net/bridge/br_if.c        | 159 ++++++++++++++++++++++++++++++++++++++++++++--
>  net/bridge/br_netlink.c   |   3 +
>  net/bridge/br_private.h   |  18 ++++++
>  net/bridge/br_sysfs_if.c  |  33 +++++++---
>  net/bridge/br_vlan.c      |   1 +
>  net/core/dev.c            |   1 +
>  net/core/dev_addr_lists.c |  21 +++---
>  10 files changed, 361 insertions(+), 29 deletions(-)
> 
> -- 
> 1.8.5.3

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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 16:57     ` Stephen Hemminger
  -1 siblings, 0 replies; 91+ messages in thread
From: Stephen Hemminger @ 2014-02-26 16:57 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On Wed, 26 Feb 2014 10:18:21 -0500
Vlad Yasevich <vyasevic@redhat.com> wrote:

> When a static fdb entry is created, add the mac address to the bridge
> address list.  This list is used to program the proper port's
> address list.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

I don't like this level of bookkeeping it starts to mix
layers between the bridge network interface as entity for talking to the
local host, and forwarding table entries.

Many times static entries are used as alternative to flooding in
environments which don't trust STP.

Plus, it looks like another major source of bugs.

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 16:57     ` Stephen Hemminger
  0 siblings, 0 replies; 91+ messages in thread
From: Stephen Hemminger @ 2014-02-26 16:57 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On Wed, 26 Feb 2014 10:18:21 -0500
Vlad Yasevich <vyasevic@redhat.com> wrote:

> When a static fdb entry is created, add the mac address to the bridge
> address list.  This list is used to program the proper port's
> address list.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

I don't like this level of bookkeeping it starts to mix
layers between the bridge network interface as entity for talking to the
local host, and forwarding table entries.

Many times static entries are used as alternative to flooding in
environments which don't trust STP.

Plus, it looks like another major source of bugs.

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

* Re: [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-26 16:58     ` Stephen Hemminger
  -1 siblings, 0 replies; 91+ messages in thread
From: Stephen Hemminger @ 2014-02-26 16:58 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On Wed, 26 Feb 2014 10:18:22 -0500
Vlad Yasevich <vyasevic@redhat.com> wrote:

> When there is only 1 flooding port, this port is programmed
> with all the address the bridge accumulated.  This allows
> us to place this port into non-promiscuous mode.
> At other times, all ports are set as promiscuous.  To help
> track whether the bridge set the mode or not, a new
> flag is introduced.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

This mixes the definition of outbound (flooding) and inbound (promiscuous).
Not sure if this is safe in all cases.

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

* Re: [Bridge] [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
@ 2014-02-26 16:58     ` Stephen Hemminger
  0 siblings, 0 replies; 91+ messages in thread
From: Stephen Hemminger @ 2014-02-26 16:58 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On Wed, 26 Feb 2014 10:18:22 -0500
Vlad Yasevich <vyasevic@redhat.com> wrote:

> When there is only 1 flooding port, this port is programmed
> with all the address the bridge accumulated.  This allows
> us to place this port into non-promiscuous mode.
> At other times, all ports are set as promiscuous.  To help
> track whether the bridge set the mode or not, a new
> flag is introduced.
> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>

This mixes the definition of outbound (flooding) and inbound (promiscuous).
Not sure if this is safe in all cases.


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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 16:23     ` [Bridge] " Michael S. Tsirkin
@ 2014-02-26 17:25       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 17:25 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 11:23 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
>> When a static fdb entry is created, add the mac address to the bridge
>> address list.  This list is used to program the proper port's
>> address list.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> Hmm won't this mean learned addresses are missing there?

Learning should be disabled on interfaces that disable flood.
The combination of learning without flood is problematic in
cases where fdbs timeout before arp entries do.
In such cases, arp will first try unicasts to validate the neighbor
and these would be dropped.

I've been thinking about automatically turning off learning
when flood is turned off.  What I don't like about it is
that it needs extra state to reverse the process.
We could log a warning, but that requires people to read logs...

> And if so isn't this a problem when we use this list
> in the next patch?
> 
> I guess we could limit this to configurations where
> learning is also disabled (not just flood) -
> seems a bit arbitrary but will likely cover
> most use-cases.
> 

Right.  From the start we wanted manual configuration here.
Doing this with learning will open us to remote mac filter
exhaustion attacks.

-vlad

> 
>> ---
>>  include/linux/netdevice.h |   6 +++
>>  net/bridge/br_device.c    |   2 +
>>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
>>  net/bridge/br_if.c        |  14 ++++--
>>  net/bridge/br_private.h   |   3 ++
>>  net/core/dev.c            |   1 +
>>  net/core/dev_addr_lists.c |  14 +++---
>>  7 files changed, 134 insertions(+), 16 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 440a02e..e29cce1 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
>>  void unregister_netdev(struct net_device *dev);
>>  
>>  /* General hardware address lists handling functions */
>> +int __hw_addr_add(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type);
>> +int __hw_addr_del(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type);
>>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>> index 63f0455..1521db6 100644
>> --- a/net/bridge/br_device.c
>> +++ b/net/bridge/br_device.c
>> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
>>  		u64_stats_init(&br_dev_stats->syncp);
>>  	}
>>  
>> +	__hw_addr_init(&br->conf_addrs);
>> +
>>  	return 0;
>>  }
>>  
>> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
>> index 9203d5a..ef95e81 100644
>> --- a/net/bridge/br_fdb.c
>> +++ b/net/bridge/br_fdb.c
>> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>>  static void fdb_notify(struct net_bridge *br,
>>  		       const struct net_bridge_fdb_entry *, int);
>>  
>> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
>> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
>> +
>>  static u32 fdb_salt __read_mostly;
>>  
>>  int __init br_fdb_init(void)
>> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
>>  
>>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
>>  {
>> +	if (f->is_static)
>> +		br_addr_del(br, f->addr.addr);
>> +
>>  	hlist_del_rcu(&f->hlist);
>>  	fdb_notify(br, f, RTM_DELNEIGH);
>>  	call_rcu(&f->rcu, fdb_rcu_free);
>> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>>  		return -ENOMEM;
>>  
>>  	fdb->is_local = fdb->is_static = 1;
>> +	br_addr_add(br, addr);
>>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
>>  	return 0;
>>  }
>> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
>>  	}
>>  
>>  	if (fdb_to_nud(fdb) != state) {
>> -		if (state & NUD_PERMANENT)
>> -			fdb->is_local = fdb->is_static = 1;
>> -		else if (state & NUD_NOARP) {
>> +		if (state & NUD_PERMANENT) {
>> +			fdb->is_local = 1;
>> +			if (!fdb->is_static) {
>> +				fdb->is_static = 1;
>> +				br_addr_add(br, addr);
>> +			}
>> +		} else if (state & NUD_NOARP) {
>> +			fdb->is_local = 0;
>> +			if (!fdb->is_static) {
>> +				fdb->is_static = 1;
>> +				br_addr_add(br, addr);
>> +			}
>> +		} else {
>>  			fdb->is_local = 0;
>> -			fdb->is_static = 1;
>> -		} else
>> -			fdb->is_local = fdb->is_static = 0;
>> +			if (fdb->is_static) {
>> +				fdb->is_static = 0;
>> +				br_addr_del(br, addr);
>> +			}
>> +		}
>>  
>>  		modified = true;
>>  	}
>> +
>>  	fdb->added_by_user = 1;
>>  
>>  	fdb->used = jiffies;
>> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
>>  out:
>>  	return err;
>>  }
>> +
>> +
>> +/* Sync the current list to the correct flood port.  */
>> +void br_fdb_addrs_sync(struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *p;
>> +	int err;
>> +
>> +	/* This function has to run under RTNL.
>> +	 * Program the user added addresses into the proper port.  This
>> +	 * fuction follows the following algorithm:
>> +	 *   a)  If only 1 port is flooding:
>> +	 *       - write all the addresses to this one port.
>> +	 */
>> +	if (br->n_flood_ports == 1) {
>> +		p = br->c_flood_port;
>> +		netif_addr_lock(p->dev);
>> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
>> +				     p->dev->addr_len);
>> +		if (!err)
>> +			__dev_set_rx_mode(p->dev);
>> +		netif_addr_unlock(p->dev);
>> +
>> +	}
>> +}
>> +
>> +void br_fdb_addrs_unsync(struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *p = br->c_flood_port;
>> +
>> +	if (!p)
>> +		return;
>> +
>> +	netif_addr_lock(p->dev);
>> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
>> +	__dev_set_rx_mode(p->dev);
>> +	netif_addr_unlock(p->dev);
>> +}
>> +
>> +/* When a static FDB entry is added, the mac address from the entry is
>> + * added to the bridge private HW address list and all required ports
>> + * are then updated with the new information.
>> + * Called under RTNL.
>> + */
>> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
>> +{
>> +	int err;
>> +
>> +	ASSERT_RTNL();
>> +
>> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
>> +			    NETDEV_HW_ADDR_T_UNICAST);
>> +
>> +	if (!err)
>> +		br_fdb_addrs_sync(br);
>> +
>> +	return err;
>> +}
>> +
>> +/* When a static FDB entry is deleted, the HW address from that entry is
>> + * also removed from the bridge private HW address list and updates all
>> + * the ports with needed information.
>> + * Called under RTNL.
>> + */
>> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
>> +{
>> +	int err;
>> +
>> +	ASSERT_RTNL();
>> +
>> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
>> +			    NETDEV_HW_ADDR_T_UNICAST);
>> +	if (!err)
>> +		br_fdb_addrs_sync(br);
>> +
>> +	return err;
>> +}
>> +
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index f072b34..e782c2e 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	br_ifinfo_notify(RTM_DELLINK, p);
>>  
>> -	list_del_rcu(&p->list);
>> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>>  
>>  	if (p->flags & BR_FLOOD)
>>  		br_del_flood_port(p, br);
>> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>>  
>> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>> +	list_del_rcu(&p->list);
>>  
>>  	netdev_rx_handler_unregister(dev);
>>  
>> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	br->n_flood_ports++;
>>  	if (br->n_flood_ports == 1)
>>  		br->c_flood_port = p;
>> +
>> +	br_fdb_addrs_sync(br);
>>  }
>>  
>>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * set it if it is not set.
>>  	 */
>>  	br->n_flood_ports--;
>> -	if (p == br->c_flood_port)
>> +	if (p == br->c_flood_port) {
>> +		br_fdb_addrs_unsync(br);
>>  		br->c_flood_port = NULL;
>> +	}
>>  
>>  	if (br->n_flood_ports == 1) {
>>  		list_for_each_entry(port, &p->br->port_list, list) {
>> -			if (port->flags & BR_FLOOD) {
>> +			if (br_port_exists(port->dev) &&
>> +			    (port->flags & BR_FLOOD)) {
>>  				br->c_flood_port = port;
>> +				br_fdb_addrs_sync(br);
>>  				break;
>>  			}
>>  		}
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 26a3987..40a6927 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -296,6 +296,7 @@ struct net_bridge
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>>  #endif
>> +	struct netdev_hw_addr_list	conf_addrs;
>>  };
>>  
>>  struct br_input_skb_cb {
>> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>>  	       const unsigned char *addr, u16 nlh_flags);
>>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>>  		struct net_device *dev, int idx);
>> +void br_fdb_addrs_sync(struct net_bridge *br);
>> +void br_fdb_addrs_unsync(struct net_bridge *br);
>>  
>>  /* br_forward.c */
>>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
>> diff --git a/net/core/dev.c b/net/core/dev.c
>> index 4ad1b78..eca4d476 100644
>> --- a/net/core/dev.c
>> +++ b/net/core/dev.c
>> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>>  	if (ops->ndo_set_rx_mode)
>>  		ops->ndo_set_rx_mode(dev);
>>  }
>> +EXPORT_SYMBOL(__dev_set_rx_mode);
>>  
>>  void dev_set_rx_mode(struct net_device *dev)
>>  {
>> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
>> index 329d579..3de44a3 100644
>> --- a/net/core/dev_addr_lists.c
>> +++ b/net/core/dev_addr_lists.c
>> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>>  				   sync);
>>  }
>>  
>> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_add(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>>  				0);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_add);
>>  
>>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>>  			       struct netdev_hw_addr *ha, bool global,
>> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>>  	return -ENOENT;
>>  }
>>  
>> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_del(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_del);
>>  
>>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>>  			       struct netdev_hw_addr *ha,
>> -- 
>> 1.8.5.3

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 17:25       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 17:25 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 11:23 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
>> When a static fdb entry is created, add the mac address to the bridge
>> address list.  This list is used to program the proper port's
>> address list.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> Hmm won't this mean learned addresses are missing there?

Learning should be disabled on interfaces that disable flood.
The combination of learning without flood is problematic in
cases where fdbs timeout before arp entries do.
In such cases, arp will first try unicasts to validate the neighbor
and these would be dropped.

I've been thinking about automatically turning off learning
when flood is turned off.  What I don't like about it is
that it needs extra state to reverse the process.
We could log a warning, but that requires people to read logs...

> And if so isn't this a problem when we use this list
> in the next patch?
> 
> I guess we could limit this to configurations where
> learning is also disabled (not just flood) -
> seems a bit arbitrary but will likely cover
> most use-cases.
> 

Right.  From the start we wanted manual configuration here.
Doing this with learning will open us to remote mac filter
exhaustion attacks.

-vlad

> 
>> ---
>>  include/linux/netdevice.h |   6 +++
>>  net/bridge/br_device.c    |   2 +
>>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
>>  net/bridge/br_if.c        |  14 ++++--
>>  net/bridge/br_private.h   |   3 ++
>>  net/core/dev.c            |   1 +
>>  net/core/dev_addr_lists.c |  14 +++---
>>  7 files changed, 134 insertions(+), 16 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 440a02e..e29cce1 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
>>  void unregister_netdev(struct net_device *dev);
>>  
>>  /* General hardware address lists handling functions */
>> +int __hw_addr_add(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type);
>> +int __hw_addr_del(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type);
>>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>> index 63f0455..1521db6 100644
>> --- a/net/bridge/br_device.c
>> +++ b/net/bridge/br_device.c
>> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
>>  		u64_stats_init(&br_dev_stats->syncp);
>>  	}
>>  
>> +	__hw_addr_init(&br->conf_addrs);
>> +
>>  	return 0;
>>  }
>>  
>> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
>> index 9203d5a..ef95e81 100644
>> --- a/net/bridge/br_fdb.c
>> +++ b/net/bridge/br_fdb.c
>> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>>  static void fdb_notify(struct net_bridge *br,
>>  		       const struct net_bridge_fdb_entry *, int);
>>  
>> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
>> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
>> +
>>  static u32 fdb_salt __read_mostly;
>>  
>>  int __init br_fdb_init(void)
>> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
>>  
>>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
>>  {
>> +	if (f->is_static)
>> +		br_addr_del(br, f->addr.addr);
>> +
>>  	hlist_del_rcu(&f->hlist);
>>  	fdb_notify(br, f, RTM_DELNEIGH);
>>  	call_rcu(&f->rcu, fdb_rcu_free);
>> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
>>  		return -ENOMEM;
>>  
>>  	fdb->is_local = fdb->is_static = 1;
>> +	br_addr_add(br, addr);
>>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
>>  	return 0;
>>  }
>> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
>>  	}
>>  
>>  	if (fdb_to_nud(fdb) != state) {
>> -		if (state & NUD_PERMANENT)
>> -			fdb->is_local = fdb->is_static = 1;
>> -		else if (state & NUD_NOARP) {
>> +		if (state & NUD_PERMANENT) {
>> +			fdb->is_local = 1;
>> +			if (!fdb->is_static) {
>> +				fdb->is_static = 1;
>> +				br_addr_add(br, addr);
>> +			}
>> +		} else if (state & NUD_NOARP) {
>> +			fdb->is_local = 0;
>> +			if (!fdb->is_static) {
>> +				fdb->is_static = 1;
>> +				br_addr_add(br, addr);
>> +			}
>> +		} else {
>>  			fdb->is_local = 0;
>> -			fdb->is_static = 1;
>> -		} else
>> -			fdb->is_local = fdb->is_static = 0;
>> +			if (fdb->is_static) {
>> +				fdb->is_static = 0;
>> +				br_addr_del(br, addr);
>> +			}
>> +		}
>>  
>>  		modified = true;
>>  	}
>> +
>>  	fdb->added_by_user = 1;
>>  
>>  	fdb->used = jiffies;
>> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
>>  out:
>>  	return err;
>>  }
>> +
>> +
>> +/* Sync the current list to the correct flood port.  */
>> +void br_fdb_addrs_sync(struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *p;
>> +	int err;
>> +
>> +	/* This function has to run under RTNL.
>> +	 * Program the user added addresses into the proper port.  This
>> +	 * fuction follows the following algorithm:
>> +	 *   a)  If only 1 port is flooding:
>> +	 *       - write all the addresses to this one port.
>> +	 */
>> +	if (br->n_flood_ports == 1) {
>> +		p = br->c_flood_port;
>> +		netif_addr_lock(p->dev);
>> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
>> +				     p->dev->addr_len);
>> +		if (!err)
>> +			__dev_set_rx_mode(p->dev);
>> +		netif_addr_unlock(p->dev);
>> +
>> +	}
>> +}
>> +
>> +void br_fdb_addrs_unsync(struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *p = br->c_flood_port;
>> +
>> +	if (!p)
>> +		return;
>> +
>> +	netif_addr_lock(p->dev);
>> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
>> +	__dev_set_rx_mode(p->dev);
>> +	netif_addr_unlock(p->dev);
>> +}
>> +
>> +/* When a static FDB entry is added, the mac address from the entry is
>> + * added to the bridge private HW address list and all required ports
>> + * are then updated with the new information.
>> + * Called under RTNL.
>> + */
>> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
>> +{
>> +	int err;
>> +
>> +	ASSERT_RTNL();
>> +
>> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
>> +			    NETDEV_HW_ADDR_T_UNICAST);
>> +
>> +	if (!err)
>> +		br_fdb_addrs_sync(br);
>> +
>> +	return err;
>> +}
>> +
>> +/* When a static FDB entry is deleted, the HW address from that entry is
>> + * also removed from the bridge private HW address list and updates all
>> + * the ports with needed information.
>> + * Called under RTNL.
>> + */
>> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
>> +{
>> +	int err;
>> +
>> +	ASSERT_RTNL();
>> +
>> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
>> +			    NETDEV_HW_ADDR_T_UNICAST);
>> +	if (!err)
>> +		br_fdb_addrs_sync(br);
>> +
>> +	return err;
>> +}
>> +
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index f072b34..e782c2e 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	br_ifinfo_notify(RTM_DELLINK, p);
>>  
>> -	list_del_rcu(&p->list);
>> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>>  
>>  	if (p->flags & BR_FLOOD)
>>  		br_del_flood_port(p, br);
>> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>>  
>> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
>> +	list_del_rcu(&p->list);
>>  
>>  	netdev_rx_handler_unregister(dev);
>>  
>> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	br->n_flood_ports++;
>>  	if (br->n_flood_ports == 1)
>>  		br->c_flood_port = p;
>> +
>> +	br_fdb_addrs_sync(br);
>>  }
>>  
>>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * set it if it is not set.
>>  	 */
>>  	br->n_flood_ports--;
>> -	if (p == br->c_flood_port)
>> +	if (p == br->c_flood_port) {
>> +		br_fdb_addrs_unsync(br);
>>  		br->c_flood_port = NULL;
>> +	}
>>  
>>  	if (br->n_flood_ports == 1) {
>>  		list_for_each_entry(port, &p->br->port_list, list) {
>> -			if (port->flags & BR_FLOOD) {
>> +			if (br_port_exists(port->dev) &&
>> +			    (port->flags & BR_FLOOD)) {
>>  				br->c_flood_port = port;
>> +				br_fdb_addrs_sync(br);
>>  				break;
>>  			}
>>  		}
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 26a3987..40a6927 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -296,6 +296,7 @@ struct net_bridge
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>>  #endif
>> +	struct netdev_hw_addr_list	conf_addrs;
>>  };
>>  
>>  struct br_input_skb_cb {
>> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>>  	       const unsigned char *addr, u16 nlh_flags);
>>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>>  		struct net_device *dev, int idx);
>> +void br_fdb_addrs_sync(struct net_bridge *br);
>> +void br_fdb_addrs_unsync(struct net_bridge *br);
>>  
>>  /* br_forward.c */
>>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
>> diff --git a/net/core/dev.c b/net/core/dev.c
>> index 4ad1b78..eca4d476 100644
>> --- a/net/core/dev.c
>> +++ b/net/core/dev.c
>> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
>>  	if (ops->ndo_set_rx_mode)
>>  		ops->ndo_set_rx_mode(dev);
>>  }
>> +EXPORT_SYMBOL(__dev_set_rx_mode);
>>  
>>  void dev_set_rx_mode(struct net_device *dev)
>>  {
>> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
>> index 329d579..3de44a3 100644
>> --- a/net/core/dev_addr_lists.c
>> +++ b/net/core/dev_addr_lists.c
>> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
>>  				   sync);
>>  }
>>  
>> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_add(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
>>  				0);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_add);
>>  
>>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
>>  			       struct netdev_hw_addr *ha, bool global,
>> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
>>  	return -ENOENT;
>>  }
>>  
>> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
>> -			 const unsigned char *addr, int addr_len,
>> -			 unsigned char addr_type)
>> +int __hw_addr_del(struct netdev_hw_addr_list *list,
>> +		  const unsigned char *addr, int addr_len,
>> +		  unsigned char addr_type)
>>  {
>>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
>>  }
>> +EXPORT_SYMBOL(__hw_addr_del);
>>  
>>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
>>  			       struct netdev_hw_addr *ha,
>> -- 
>> 1.8.5.3


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

* Re: [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
  2014-02-26 16:58     ` [Bridge] " Stephen Hemminger
@ 2014-02-26 17:32       ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 17:32 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Vlad Yasevich, netdev, bridge, jhs, john.r.fastabend, shemminger

On Wed, Feb 26, 2014 at 08:58:26AM -0800, Stephen Hemminger wrote:
> On Wed, 26 Feb 2014 10:18:22 -0500
> Vlad Yasevich <vyasevic@redhat.com> wrote:
> 
> > When there is only 1 flooding port, this port is programmed
> > with all the address the bridge accumulated.  This allows
> > us to place this port into non-promiscuous mode.
> > At other times, all ports are set as promiscuous.  To help
> > track whether the bridge set the mode or not, a new
> > flag is introduced.
> > 
> > Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> This mixes the definition of outbound (flooding) and inbound (promiscuous).
> Not sure if this is safe in all cases.

Logically: inbound on port A == outbound on all ports except A
So promisc on A == OR of flood on all ports except A
This rule should just be applied to all ports.
Makes sense, right?

I think this is what this tries to implement, even if the
optimization attempt masks the logic somewhat.

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

* Re: [Bridge] [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
@ 2014-02-26 17:32       ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 17:32 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Vlad Yasevich, netdev, bridge, jhs, john.r.fastabend, shemminger

On Wed, Feb 26, 2014 at 08:58:26AM -0800, Stephen Hemminger wrote:
> On Wed, 26 Feb 2014 10:18:22 -0500
> Vlad Yasevich <vyasevic@redhat.com> wrote:
> 
> > When there is only 1 flooding port, this port is programmed
> > with all the address the bridge accumulated.  This allows
> > us to place this port into non-promiscuous mode.
> > At other times, all ports are set as promiscuous.  To help
> > track whether the bridge set the mode or not, a new
> > flag is introduced.
> > 
> > Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> This mixes the definition of outbound (flooding) and inbound (promiscuous).
> Not sure if this is safe in all cases.

Logically: inbound on port A == outbound on all ports except A
So promisc on A == OR of flood on all ports except A
This rule should just be applied to all ports.
Makes sense, right?

I think this is what this tries to implement, even if the
optimization attempt masks the logic somewhat.



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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 17:25       ` [Bridge] " Vlad Yasevich
@ 2014-02-26 17:33         ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 17:33 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 12:25:53PM -0500, Vlad Yasevich wrote:
> On 02/26/2014 11:23 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
> >> When a static fdb entry is created, add the mac address to the bridge
> >> address list.  This list is used to program the proper port's
> >> address list.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > Hmm won't this mean learned addresses are missing there?
> 
> Learning should be disabled on interfaces that disable flood.
> The combination of learning without flood is problematic in
> cases where fdbs timeout before arp entries do.
> In such cases, arp will first try unicasts to validate the neighbor
> and these would be dropped.
> 
> I've been thinking about automatically turning off learning
> when flood is turned off.  What I don't like about it is
> that it needs extra state to reverse the process.
> We could log a warning, but that requires people to read logs...

For now maybe just keep promisc enabled in this
configuration.

> > And if so isn't this a problem when we use this list
> > in the next patch?
> > 
> > I guess we could limit this to configurations where
> > learning is also disabled (not just flood) -
> > seems a bit arbitrary but will likely cover
> > most use-cases.
> > 
> 
> Right.  From the start we wanted manual configuration here.
> Doing this with learning will open us to remote mac filter
> exhaustion attacks.
> 
> -vlad
> 
> > 
> >> ---
> >>  include/linux/netdevice.h |   6 +++
> >>  net/bridge/br_device.c    |   2 +
> >>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
> >>  net/bridge/br_if.c        |  14 ++++--
> >>  net/bridge/br_private.h   |   3 ++
> >>  net/core/dev.c            |   1 +
> >>  net/core/dev_addr_lists.c |  14 +++---
> >>  7 files changed, 134 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> >> index 440a02e..e29cce1 100644
> >> --- a/include/linux/netdevice.h
> >> +++ b/include/linux/netdevice.h
> >> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
> >>  void unregister_netdev(struct net_device *dev);
> >>  
> >>  /* General hardware address lists handling functions */
> >> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type);
> >> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type);
> >>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
> >>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> >>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> >> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> >> index 63f0455..1521db6 100644
> >> --- a/net/bridge/br_device.c
> >> +++ b/net/bridge/br_device.c
> >> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
> >>  		u64_stats_init(&br_dev_stats->syncp);
> >>  	}
> >>  
> >> +	__hw_addr_init(&br->conf_addrs);
> >> +
> >>  	return 0;
> >>  }
> >>  
> >> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> >> index 9203d5a..ef95e81 100644
> >> --- a/net/bridge/br_fdb.c
> >> +++ b/net/bridge/br_fdb.c
> >> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
> >>  static void fdb_notify(struct net_bridge *br,
> >>  		       const struct net_bridge_fdb_entry *, int);
> >>  
> >> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
> >> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
> >> +
> >>  static u32 fdb_salt __read_mostly;
> >>  
> >>  int __init br_fdb_init(void)
> >> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
> >>  
> >>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
> >>  {
> >> +	if (f->is_static)
> >> +		br_addr_del(br, f->addr.addr);
> >> +
> >>  	hlist_del_rcu(&f->hlist);
> >>  	fdb_notify(br, f, RTM_DELNEIGH);
> >>  	call_rcu(&f->rcu, fdb_rcu_free);
> >> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
> >>  		return -ENOMEM;
> >>  
> >>  	fdb->is_local = fdb->is_static = 1;
> >> +	br_addr_add(br, addr);
> >>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
> >>  	return 0;
> >>  }
> >> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
> >>  	}
> >>  
> >>  	if (fdb_to_nud(fdb) != state) {
> >> -		if (state & NUD_PERMANENT)
> >> -			fdb->is_local = fdb->is_static = 1;
> >> -		else if (state & NUD_NOARP) {
> >> +		if (state & NUD_PERMANENT) {
> >> +			fdb->is_local = 1;
> >> +			if (!fdb->is_static) {
> >> +				fdb->is_static = 1;
> >> +				br_addr_add(br, addr);
> >> +			}
> >> +		} else if (state & NUD_NOARP) {
> >> +			fdb->is_local = 0;
> >> +			if (!fdb->is_static) {
> >> +				fdb->is_static = 1;
> >> +				br_addr_add(br, addr);
> >> +			}
> >> +		} else {
> >>  			fdb->is_local = 0;
> >> -			fdb->is_static = 1;
> >> -		} else
> >> -			fdb->is_local = fdb->is_static = 0;
> >> +			if (fdb->is_static) {
> >> +				fdb->is_static = 0;
> >> +				br_addr_del(br, addr);
> >> +			}
> >> +		}
> >>  
> >>  		modified = true;
> >>  	}
> >> +
> >>  	fdb->added_by_user = 1;
> >>  
> >>  	fdb->used = jiffies;
> >> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
> >>  out:
> >>  	return err;
> >>  }
> >> +
> >> +
> >> +/* Sync the current list to the correct flood port.  */
> >> +void br_fdb_addrs_sync(struct net_bridge *br)
> >> +{
> >> +	struct net_bridge_port *p;
> >> +	int err;
> >> +
> >> +	/* This function has to run under RTNL.
> >> +	 * Program the user added addresses into the proper port.  This
> >> +	 * fuction follows the following algorithm:
> >> +	 *   a)  If only 1 port is flooding:
> >> +	 *       - write all the addresses to this one port.
> >> +	 */
> >> +	if (br->n_flood_ports == 1) {
> >> +		p = br->c_flood_port;
> >> +		netif_addr_lock(p->dev);
> >> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> >> +				     p->dev->addr_len);
> >> +		if (!err)
> >> +			__dev_set_rx_mode(p->dev);
> >> +		netif_addr_unlock(p->dev);
> >> +
> >> +	}
> >> +}
> >> +
> >> +void br_fdb_addrs_unsync(struct net_bridge *br)
> >> +{
> >> +	struct net_bridge_port *p = br->c_flood_port;
> >> +
> >> +	if (!p)
> >> +		return;
> >> +
> >> +	netif_addr_lock(p->dev);
> >> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
> >> +	__dev_set_rx_mode(p->dev);
> >> +	netif_addr_unlock(p->dev);
> >> +}
> >> +
> >> +/* When a static FDB entry is added, the mac address from the entry is
> >> + * added to the bridge private HW address list and all required ports
> >> + * are then updated with the new information.
> >> + * Called under RTNL.
> >> + */
> >> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
> >> +{
> >> +	int err;
> >> +
> >> +	ASSERT_RTNL();
> >> +
> >> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
> >> +			    NETDEV_HW_ADDR_T_UNICAST);
> >> +
> >> +	if (!err)
> >> +		br_fdb_addrs_sync(br);
> >> +
> >> +	return err;
> >> +}
> >> +
> >> +/* When a static FDB entry is deleted, the HW address from that entry is
> >> + * also removed from the bridge private HW address list and updates all
> >> + * the ports with needed information.
> >> + * Called under RTNL.
> >> + */
> >> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
> >> +{
> >> +	int err;
> >> +
> >> +	ASSERT_RTNL();
> >> +
> >> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
> >> +			    NETDEV_HW_ADDR_T_UNICAST);
> >> +	if (!err)
> >> +		br_fdb_addrs_sync(br);
> >> +
> >> +	return err;
> >> +}
> >> +
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index f072b34..e782c2e 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
> >>  
> >>  	br_ifinfo_notify(RTM_DELLINK, p);
> >>  
> >> -	list_del_rcu(&p->list);
> >> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> >>  
> >>  	if (p->flags & BR_FLOOD)
> >>  		br_del_flood_port(p, br);
> >> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
> >>  	nbp_vlan_flush(p);
> >>  	br_fdb_delete_by_port(br, p, 1);
> >>  
> >> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> >> +	list_del_rcu(&p->list);
> >>  
> >>  	netdev_rx_handler_unregister(dev);
> >>  
> >> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	br->n_flood_ports++;
> >>  	if (br->n_flood_ports == 1)
> >>  		br->c_flood_port = p;
> >> +
> >> +	br_fdb_addrs_sync(br);
> >>  }
> >>  
> >>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	 * set it if it is not set.
> >>  	 */
> >>  	br->n_flood_ports--;
> >> -	if (p == br->c_flood_port)
> >> +	if (p == br->c_flood_port) {
> >> +		br_fdb_addrs_unsync(br);
> >>  		br->c_flood_port = NULL;
> >> +	}
> >>  
> >>  	if (br->n_flood_ports == 1) {
> >>  		list_for_each_entry(port, &p->br->port_list, list) {
> >> -			if (port->flags & BR_FLOOD) {
> >> +			if (br_port_exists(port->dev) &&
> >> +			    (port->flags & BR_FLOOD)) {
> >>  				br->c_flood_port = port;
> >> +				br_fdb_addrs_sync(br);
> >>  				break;
> >>  			}
> >>  		}
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 26a3987..40a6927 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -296,6 +296,7 @@ struct net_bridge
> >>  	u8				vlan_enabled;
> >>  	struct net_port_vlans __rcu	*vlan_info;
> >>  #endif
> >> +	struct netdev_hw_addr_list	conf_addrs;
> >>  };
> >>  
> >>  struct br_input_skb_cb {
> >> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
> >>  	       const unsigned char *addr, u16 nlh_flags);
> >>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
> >>  		struct net_device *dev, int idx);
> >> +void br_fdb_addrs_sync(struct net_bridge *br);
> >> +void br_fdb_addrs_unsync(struct net_bridge *br);
> >>  
> >>  /* br_forward.c */
> >>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> >> diff --git a/net/core/dev.c b/net/core/dev.c
> >> index 4ad1b78..eca4d476 100644
> >> --- a/net/core/dev.c
> >> +++ b/net/core/dev.c
> >> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
> >>  	if (ops->ndo_set_rx_mode)
> >>  		ops->ndo_set_rx_mode(dev);
> >>  }
> >> +EXPORT_SYMBOL(__dev_set_rx_mode);
> >>  
> >>  void dev_set_rx_mode(struct net_device *dev)
> >>  {
> >> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> >> index 329d579..3de44a3 100644
> >> --- a/net/core/dev_addr_lists.c
> >> +++ b/net/core/dev_addr_lists.c
> >> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
> >>  				   sync);
> >>  }
> >>  
> >> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
> >> -			 const unsigned char *addr, int addr_len,
> >> -			 unsigned char addr_type)
> >> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type)
> >>  {
> >>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
> >>  				0);
> >>  }
> >> +EXPORT_SYMBOL(__hw_addr_add);
> >>  
> >>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
> >>  			       struct netdev_hw_addr *ha, bool global,
> >> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
> >>  	return -ENOENT;
> >>  }
> >>  
> >> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
> >> -			 const unsigned char *addr, int addr_len,
> >> -			 unsigned char addr_type)
> >> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type)
> >>  {
> >>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
> >>  }
> >> +EXPORT_SYMBOL(__hw_addr_del);
> >>  
> >>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
> >>  			       struct netdev_hw_addr *ha,
> >> -- 
> >> 1.8.5.3

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 17:33         ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-26 17:33 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 12:25:53PM -0500, Vlad Yasevich wrote:
> On 02/26/2014 11:23 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:21AM -0500, Vlad Yasevich wrote:
> >> When a static fdb entry is created, add the mac address to the bridge
> >> address list.  This list is used to program the proper port's
> >> address list.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > Hmm won't this mean learned addresses are missing there?
> 
> Learning should be disabled on interfaces that disable flood.
> The combination of learning without flood is problematic in
> cases where fdbs timeout before arp entries do.
> In such cases, arp will first try unicasts to validate the neighbor
> and these would be dropped.
> 
> I've been thinking about automatically turning off learning
> when flood is turned off.  What I don't like about it is
> that it needs extra state to reverse the process.
> We could log a warning, but that requires people to read logs...

For now maybe just keep promisc enabled in this
configuration.

> > And if so isn't this a problem when we use this list
> > in the next patch?
> > 
> > I guess we could limit this to configurations where
> > learning is also disabled (not just flood) -
> > seems a bit arbitrary but will likely cover
> > most use-cases.
> > 
> 
> Right.  From the start we wanted manual configuration here.
> Doing this with learning will open us to remote mac filter
> exhaustion attacks.
> 
> -vlad
> 
> > 
> >> ---
> >>  include/linux/netdevice.h |   6 +++
> >>  net/bridge/br_device.c    |   2 +
> >>  net/bridge/br_fdb.c       | 110 +++++++++++++++++++++++++++++++++++++++++++---
> >>  net/bridge/br_if.c        |  14 ++++--
> >>  net/bridge/br_private.h   |   3 ++
> >>  net/core/dev.c            |   1 +
> >>  net/core/dev_addr_lists.c |  14 +++---
> >>  7 files changed, 134 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> >> index 440a02e..e29cce1 100644
> >> --- a/include/linux/netdevice.h
> >> +++ b/include/linux/netdevice.h
> >> @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev);
> >>  void unregister_netdev(struct net_device *dev);
> >>  
> >>  /* General hardware address lists handling functions */
> >> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type);
> >> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type);
> >>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
> >>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> >>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> >> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> >> index 63f0455..1521db6 100644
> >> --- a/net/bridge/br_device.c
> >> +++ b/net/bridge/br_device.c
> >> @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev)
> >>  		u64_stats_init(&br_dev_stats->syncp);
> >>  	}
> >>  
> >> +	__hw_addr_init(&br->conf_addrs);
> >> +
> >>  	return 0;
> >>  }
> >>  
> >> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> >> index 9203d5a..ef95e81 100644
> >> --- a/net/bridge/br_fdb.c
> >> +++ b/net/bridge/br_fdb.c
> >> @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
> >>  static void fdb_notify(struct net_bridge *br,
> >>  		       const struct net_bridge_fdb_entry *, int);
> >>  
> >> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr);
> >> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr);
> >> +
> >>  static u32 fdb_salt __read_mostly;
> >>  
> >>  int __init br_fdb_init(void)
> >> @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head)
> >>  
> >>  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
> >>  {
> >> +	if (f->is_static)
> >> +		br_addr_del(br, f->addr.addr);
> >> +
> >>  	hlist_del_rcu(&f->hlist);
> >>  	fdb_notify(br, f, RTM_DELNEIGH);
> >>  	call_rcu(&f->rcu, fdb_rcu_free);
> >> @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
> >>  		return -ENOMEM;
> >>  
> >>  	fdb->is_local = fdb->is_static = 1;
> >> +	br_addr_add(br, addr);
> >>  	fdb_notify(br, fdb, RTM_NEWNEIGH);
> >>  	return 0;
> >>  }
> >> @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
> >>  	}
> >>  
> >>  	if (fdb_to_nud(fdb) != state) {
> >> -		if (state & NUD_PERMANENT)
> >> -			fdb->is_local = fdb->is_static = 1;
> >> -		else if (state & NUD_NOARP) {
> >> +		if (state & NUD_PERMANENT) {
> >> +			fdb->is_local = 1;
> >> +			if (!fdb->is_static) {
> >> +				fdb->is_static = 1;
> >> +				br_addr_add(br, addr);
> >> +			}
> >> +		} else if (state & NUD_NOARP) {
> >> +			fdb->is_local = 0;
> >> +			if (!fdb->is_static) {
> >> +				fdb->is_static = 1;
> >> +				br_addr_add(br, addr);
> >> +			}
> >> +		} else {
> >>  			fdb->is_local = 0;
> >> -			fdb->is_static = 1;
> >> -		} else
> >> -			fdb->is_local = fdb->is_static = 0;
> >> +			if (fdb->is_static) {
> >> +				fdb->is_static = 0;
> >> +				br_addr_del(br, addr);
> >> +			}
> >> +		}
> >>  
> >>  		modified = true;
> >>  	}
> >> +
> >>  	fdb->added_by_user = 1;
> >>  
> >>  	fdb->used = jiffies;
> >> @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
> >>  out:
> >>  	return err;
> >>  }
> >> +
> >> +
> >> +/* Sync the current list to the correct flood port.  */
> >> +void br_fdb_addrs_sync(struct net_bridge *br)
> >> +{
> >> +	struct net_bridge_port *p;
> >> +	int err;
> >> +
> >> +	/* This function has to run under RTNL.
> >> +	 * Program the user added addresses into the proper port.  This
> >> +	 * fuction follows the following algorithm:
> >> +	 *   a)  If only 1 port is flooding:
> >> +	 *       - write all the addresses to this one port.
> >> +	 */
> >> +	if (br->n_flood_ports == 1) {
> >> +		p = br->c_flood_port;
> >> +		netif_addr_lock(p->dev);
> >> +		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> >> +				     p->dev->addr_len);
> >> +		if (!err)
> >> +			__dev_set_rx_mode(p->dev);
> >> +		netif_addr_unlock(p->dev);
> >> +
> >> +	}
> >> +}
> >> +
> >> +void br_fdb_addrs_unsync(struct net_bridge *br)
> >> +{
> >> +	struct net_bridge_port *p = br->c_flood_port;
> >> +
> >> +	if (!p)
> >> +		return;
> >> +
> >> +	netif_addr_lock(p->dev);
> >> +	__hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len);
> >> +	__dev_set_rx_mode(p->dev);
> >> +	netif_addr_unlock(p->dev);
> >> +}
> >> +
> >> +/* When a static FDB entry is added, the mac address from the entry is
> >> + * added to the bridge private HW address list and all required ports
> >> + * are then updated with the new information.
> >> + * Called under RTNL.
> >> + */
> >> +static int br_addr_add(struct net_bridge *br, const unsigned char *addr)
> >> +{
> >> +	int err;
> >> +
> >> +	ASSERT_RTNL();
> >> +
> >> +	err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len,
> >> +			    NETDEV_HW_ADDR_T_UNICAST);
> >> +
> >> +	if (!err)
> >> +		br_fdb_addrs_sync(br);
> >> +
> >> +	return err;
> >> +}
> >> +
> >> +/* When a static FDB entry is deleted, the HW address from that entry is
> >> + * also removed from the bridge private HW address list and updates all
> >> + * the ports with needed information.
> >> + * Called under RTNL.
> >> + */
> >> +static int br_addr_del(struct net_bridge *br, const unsigned char *addr)
> >> +{
> >> +	int err;
> >> +
> >> +	ASSERT_RTNL();
> >> +
> >> +	err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len,
> >> +			    NETDEV_HW_ADDR_T_UNICAST);
> >> +	if (!err)
> >> +		br_fdb_addrs_sync(br);
> >> +
> >> +	return err;
> >> +}
> >> +
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index f072b34..e782c2e 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
> >>  
> >>  	br_ifinfo_notify(RTM_DELLINK, p);
> >>  
> >> -	list_del_rcu(&p->list);
> >> +	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> >>  
> >>  	if (p->flags & BR_FLOOD)
> >>  		br_del_flood_port(p, br);
> >> @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p)
> >>  	nbp_vlan_flush(p);
> >>  	br_fdb_delete_by_port(br, p, 1);
> >>  
> >> -	dev->priv_flags &= ~IFF_BRIDGE_PORT;
> >> +	list_del_rcu(&p->list);
> >>  
> >>  	netdev_rx_handler_unregister(dev);
> >>  
> >> @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	br->n_flood_ports++;
> >>  	if (br->n_flood_ports == 1)
> >>  		br->c_flood_port = p;
> >> +
> >> +	br_fdb_addrs_sync(br);
> >>  }
> >>  
> >>  static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >> @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	 * set it if it is not set.
> >>  	 */
> >>  	br->n_flood_ports--;
> >> -	if (p == br->c_flood_port)
> >> +	if (p == br->c_flood_port) {
> >> +		br_fdb_addrs_unsync(br);
> >>  		br->c_flood_port = NULL;
> >> +	}
> >>  
> >>  	if (br->n_flood_ports == 1) {
> >>  		list_for_each_entry(port, &p->br->port_list, list) {
> >> -			if (port->flags & BR_FLOOD) {
> >> +			if (br_port_exists(port->dev) &&
> >> +			    (port->flags & BR_FLOOD)) {
> >>  				br->c_flood_port = port;
> >> +				br_fdb_addrs_sync(br);
> >>  				break;
> >>  			}
> >>  		}
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 26a3987..40a6927 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -296,6 +296,7 @@ struct net_bridge
> >>  	u8				vlan_enabled;
> >>  	struct net_port_vlans __rcu	*vlan_info;
> >>  #endif
> >> +	struct netdev_hw_addr_list	conf_addrs;
> >>  };
> >>  
> >>  struct br_input_skb_cb {
> >> @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
> >>  	       const unsigned char *addr, u16 nlh_flags);
> >>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
> >>  		struct net_device *dev, int idx);
> >> +void br_fdb_addrs_sync(struct net_bridge *br);
> >> +void br_fdb_addrs_unsync(struct net_bridge *br);
> >>  
> >>  /* br_forward.c */
> >>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> >> diff --git a/net/core/dev.c b/net/core/dev.c
> >> index 4ad1b78..eca4d476 100644
> >> --- a/net/core/dev.c
> >> +++ b/net/core/dev.c
> >> @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev)
> >>  	if (ops->ndo_set_rx_mode)
> >>  		ops->ndo_set_rx_mode(dev);
> >>  }
> >> +EXPORT_SYMBOL(__dev_set_rx_mode);
> >>  
> >>  void dev_set_rx_mode(struct net_device *dev)
> >>  {
> >> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> >> index 329d579..3de44a3 100644
> >> --- a/net/core/dev_addr_lists.c
> >> +++ b/net/core/dev_addr_lists.c
> >> @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
> >>  				   sync);
> >>  }
> >>  
> >> -static int __hw_addr_add(struct netdev_hw_addr_list *list,
> >> -			 const unsigned char *addr, int addr_len,
> >> -			 unsigned char addr_type)
> >> +int __hw_addr_add(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type)
> >>  {
> >>  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
> >>  				0);
> >>  }
> >> +EXPORT_SYMBOL(__hw_addr_add);
> >>  
> >>  static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
> >>  			       struct netdev_hw_addr *ha, bool global,
> >> @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
> >>  	return -ENOENT;
> >>  }
> >>  
> >> -static int __hw_addr_del(struct netdev_hw_addr_list *list,
> >> -			 const unsigned char *addr, int addr_len,
> >> -			 unsigned char addr_type)
> >> +int __hw_addr_del(struct netdev_hw_addr_list *list,
> >> +		  const unsigned char *addr, int addr_len,
> >> +		  unsigned char addr_type)
> >>  {
> >>  	return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
> >>  }
> >> +EXPORT_SYMBOL(__hw_addr_del);
> >>  
> >>  static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
> >>  			       struct netdev_hw_addr *ha,
> >> -- 
> >> 1.8.5.3

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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 16:57     ` [Bridge] " Stephen Hemminger
@ 2014-02-26 17:35       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 17:35 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
> On Wed, 26 Feb 2014 10:18:21 -0500
> Vlad Yasevich <vyasevic@redhat.com> wrote:
> 
>> When a static fdb entry is created, add the mac address to the bridge
>> address list.  This list is used to program the proper port's
>> address list.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> I don't like this level of bookkeeping it starts to mix
> layers between the bridge network interface as entity for talking to the
> local host, and forwarding table entries.

Actually this is one of the reasons this isn't done through the
br->dev->uc.  Forwarding table entries are still per-port.

> 
> Many times static entries are used as alternative to flooding in
> environments which don't trust STP.

Ok, and how would this be problematic?  If one wants to turn off
promisc in this environment, then receive filters needs to be properly
programmed.

> 
> Plus, it looks like another major source of bugs.
> 

Any new code is a potential source of issues.  Are you saying
No to any new code in bridge?

-vlad

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-26 17:35       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-26 17:35 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
> On Wed, 26 Feb 2014 10:18:21 -0500
> Vlad Yasevich <vyasevic@redhat.com> wrote:
> 
>> When a static fdb entry is created, add the mac address to the bridge
>> address list.  This list is used to program the proper port's
>> address list.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> I don't like this level of bookkeeping it starts to mix
> layers between the bridge network interface as entity for talking to the
> local host, and forwarding table entries.

Actually this is one of the reasons this isn't done through the
br->dev->uc.  Forwarding table entries are still per-port.

> 
> Many times static entries are used as alternative to flooding in
> environments which don't trust STP.

Ok, and how would this be problematic?  If one wants to turn off
promisc in this environment, then receive filters needs to be properly
programmed.

> 
> Plus, it looks like another major source of bugs.
> 

Any new code is a potential source of issues.  Are you saying
No to any new code in bridge?

-vlad

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

* Re: [PATCH RFC 0/7] Non-promisc bidge ports support
  2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
@ 2014-02-26 23:59   ` Jamal Hadi Salim
  -1 siblings, 0 replies; 91+ messages in thread
From: Jamal Hadi Salim @ 2014-02-26 23:59 UTC (permalink / raw)
  To: Vlad Yasevich, netdev; +Cc: john.r.fastabend, shemminger, bridge, mst

On 02/26/14 10:18, Vlad Yasevich wrote:
> This patch series is a complete re-design and re-implementation of
> prior attempts to support non-promiscuous bridge ports.
>
> The basic design is as follows.  The bridge keeps track of
> all the ports that flood packets to unknown destinations.  If
> the flooding is disabled on the port, to get traffic to flow
> through, user/management would need to add an fdb describing
> such traffic.  When such fdb is added, we save the address
> to bridge private hardware address list.

Entering the addresses in the uc list on other bridgeports seems
reasonable for the scenario described.
But would it _also_ need to be added to the fdb of the bridge?
i.e how does the bridge (if the packet was to be handed to it)
know where to forward?
BTW: on the comment that flooding off implies learning off: I would like
to be able to turn off flooding on a specific bridge port but
still want to learn from it. I dont think those two are mutually
exclusive.

cheers,
jamal

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

* Re: [Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
@ 2014-02-26 23:59   ` Jamal Hadi Salim
  0 siblings, 0 replies; 91+ messages in thread
From: Jamal Hadi Salim @ 2014-02-26 23:59 UTC (permalink / raw)
  To: Vlad Yasevich, netdev; +Cc: john.r.fastabend, shemminger, bridge, mst

On 02/26/14 10:18, Vlad Yasevich wrote:
> This patch series is a complete re-design and re-implementation of
> prior attempts to support non-promiscuous bridge ports.
>
> The basic design is as follows.  The bridge keeps track of
> all the ports that flood packets to unknown destinations.  If
> the flooding is disabled on the port, to get traffic to flow
> through, user/management would need to add an fdb describing
> such traffic.  When such fdb is added, we save the address
> to bridge private hardware address list.

Entering the addresses in the uc list on other bridgeports seems
reasonable for the scenario described.
But would it _also_ need to be added to the fdb of the bridge?
i.e how does the bridge (if the packet was to be handed to it)
know where to forward?
BTW: on the comment that flooding off implies learning off: I would like
to be able to turn off flooding on a specific bridge port but
still want to learn from it. I dont think those two are mutually
exclusive.

cheers,
jamal

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

* Re: [PATCH RFC 0/7] Non-promisc bidge ports support
  2014-02-26 23:59   ` [Bridge] " Jamal Hadi Salim
@ 2014-02-27  3:37     ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27  3:37 UTC (permalink / raw)
  To: Jamal Hadi Salim, netdev; +Cc: john.r.fastabend, shemminger, bridge, mst

On 02/26/2014 06:59 PM, Jamal Hadi Salim wrote:
> On 02/26/14 10:18, Vlad Yasevich wrote:
>> This patch series is a complete re-design and re-implementation of
>> prior attempts to support non-promiscuous bridge ports.
>>
>> The basic design is as follows.  The bridge keeps track of
>> all the ports that flood packets to unknown destinations.  If
>> the flooding is disabled on the port, to get traffic to flow
>> through, user/management would need to add an fdb describing
>> such traffic.  When such fdb is added, we save the address
>> to bridge private hardware address list.
> 
> Entering the addresses in the uc list on other bridgeports seems
> reasonable for the scenario described.
> But would it _also_ need to be added to the fdb of the bridge?
> i.e how does the bridge (if the packet was to be handed to it)
> know where to forward?

The fdb described here is actually added to the bridge.  In the case
when we are turning promiscuous mode off on a port, we program the
address from the fdb down to the port uc list as well.  This allows
the bridge to continue receiving traffic destined for this address even
though the port is not in promiscuous mode.

> BTW: on the comment that flooding off implies learning off: I would like
> to be able to turn off flooding on a specific bridge port but
> still want to learn from it. I dont think those two are mutually
> exclusive.

No they are not, but it does lead to some very interesting traffic
hang-ups that I've experienced first hand.  Everything works great
in the beginning.  However, if you go idle for a long enough period
that the fdb times out, re-establishing the connection take a rather
long time due to unicast ARPs being dropped by the bridge.  You end
up waiting until arp fails and switches to broadcast to restore the
connection.  So, this mode isn't really recommended.  Nothing currently
forbids it however.

-vlad
> 
> cheers,
> jamal

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

* Re: [Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
@ 2014-02-27  3:37     ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27  3:37 UTC (permalink / raw)
  To: Jamal Hadi Salim, netdev; +Cc: john.r.fastabend, shemminger, bridge, mst

On 02/26/2014 06:59 PM, Jamal Hadi Salim wrote:
> On 02/26/14 10:18, Vlad Yasevich wrote:
>> This patch series is a complete re-design and re-implementation of
>> prior attempts to support non-promiscuous bridge ports.
>>
>> The basic design is as follows.  The bridge keeps track of
>> all the ports that flood packets to unknown destinations.  If
>> the flooding is disabled on the port, to get traffic to flow
>> through, user/management would need to add an fdb describing
>> such traffic.  When such fdb is added, we save the address
>> to bridge private hardware address list.
> 
> Entering the addresses in the uc list on other bridgeports seems
> reasonable for the scenario described.
> But would it _also_ need to be added to the fdb of the bridge?
> i.e how does the bridge (if the packet was to be handed to it)
> know where to forward?

The fdb described here is actually added to the bridge.  In the case
when we are turning promiscuous mode off on a port, we program the
address from the fdb down to the port uc list as well.  This allows
the bridge to continue receiving traffic destined for this address even
though the port is not in promiscuous mode.

> BTW: on the comment that flooding off implies learning off: I would like
> to be able to turn off flooding on a specific bridge port but
> still want to learn from it. I dont think those two are mutually
> exclusive.

No they are not, but it does lead to some very interesting traffic
hang-ups that I've experienced first hand.  Everything works great
in the beginning.  However, if you go idle for a long enough period
that the fdb times out, re-establishing the connection take a rather
long time due to unicast ARPs being dropped by the bridge.  You end
up waiting until arp fails and switches to broadcast to restore the
connection.  So, this mode isn't really recommended.  Nothing currently
forbids it however.

-vlad
> 
> cheers,
> jamal


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

* Re: [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
  2014-02-26 15:57     ` [Bridge] " Michael S. Tsirkin
@ 2014-02-27  3:46       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27  3:46 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:57 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
>> Configuration where all ports are set to non-flooding is a slight
>> special case.  In this config, the user would have to manage all fdbs
>> for all ports.  In this special case, since we'll know all addresses,
>> we can turn off promisc on all ports and program all ports with the
>> same set of addresses.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> Here the logic is different be we could sync them,
> just make this a per-port flag and
> scan all ports instead of relying on flood_port.
> This will be slower in the single flooding port
> case but worst case remains the same since
> this patch scans all ports.
> 
> What do you think?

I had it this way before and didn't really like it.  The transitions
between the two configs needs to be detected and appropriate
actions taken.  So it'll still be special coded.

I'll see what I can do to simplify this.

-vlad

>> ---
>>  include/linux/netdevice.h |  3 +++
>>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
>>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
>>  net/bridge/br_private.h   |  2 +-
>>  net/core/dev_addr_lists.c |  7 ++++---
>>  5 files changed, 59 insertions(+), 15 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index e29cce1..79e97ee 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
>>  		  unsigned char addr_type);
>>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>> +			    struct netdev_hw_addr_list *from_list,
>> +			    int addr_len);
>>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>>  		      struct netdev_hw_addr_list *from_list, int addr_len);
>>  void __hw_addr_init(struct netdev_hw_addr_list *list);
>> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
>> index ef95e81..26ea4fe 100644
>> --- a/net/bridge/br_fdb.c
>> +++ b/net/bridge/br_fdb.c
>> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
>>  	if (br->n_flood_ports == 1) {
>>  		p = br->c_flood_port;
>>  		netif_addr_lock(p->dev);
>> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
>> -				     p->dev->addr_len);
>> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
>> +					      p->dev->addr_len);
>>  		if (!err)
>>  			__dev_set_rx_mode(p->dev);
>>  		netif_addr_unlock(p->dev);
>> +	} else if (br->n_flood_ports == 0) {
>> +		list_for_each_entry(p, &br->port_list, list) {
>> +			/* skip over ports being deleted. */
>> +			if (!br_port_exists(p->dev))
>> +				continue;
>>  
>> +			netif_addr_lock_nested(p->dev);
>> +			err = __hw_addr_sync_multiple(&p->dev->uc,
>> +						      &br->conf_addrs,
>> +						      p->dev->addr_len);
>> +			if (!err)
>> +				__dev_set_rx_mode(p->dev);
>> +			netif_addr_unlock(p->dev);
>> +		}
>>  	}
>> +
>>  }
>>  
>> -void br_fdb_addrs_unsync(struct net_bridge *br)
>> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
>>  {
>> -	struct net_bridge_port *p = br->c_flood_port;
>> -
>>  	if (!p)
>>  		return;
>>  
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 55e4e28..4ba62ef 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	if (p->flags & BR_FLOOD)
>>  		br_del_flood_port(p, br);
>> +	else
>> +		br_fdb_addrs_unsync(br, p);
>>  
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * only have 1 flooding port cache if for future use.
>>  	 */
>>  	br->n_flood_ports++;
>> -	if (br->n_flood_ports == 1)
>> +	if (br->n_flood_ports == 1) {
>> +		struct net_bridge_port *port;
>> +
>> +		/* We are transitioning from 0 flood ports to 1.  Remove
>> +		 * the addresses from all the non-flood ports and turn on
>> +		 * promisc on those ports.
>> +		 */
>> +		list_for_each_entry(port, &br->port_list, list) {
>> +			/* skip the current port we are changing */
>> +			if (port == p)
>> +				continue;
>> +
>> +			if (!(port->flags & BR_FLOOD)) {
>> +				br_port_set_promisc(port);
>> +				br_fdb_addrs_unsync(br, port);
>> +			}
>> +		}
>> +
>>  		br->c_flood_port = p;
>> +	}
>>  
>>  	br_fdb_addrs_sync(br);
>>  	br_manage_promisc(br);
>> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * set it if it is not set.
>>  	 */
>>  	br->n_flood_ports--;
>> -	if (p == br->c_flood_port) {
>> -		br_fdb_addrs_unsync(br);
>> -		br->c_flood_port = NULL;
>> -	}
>> +	if (br->n_flood_ports == 0) {
>> +		/* We just dropped to 0 flood ports.  If we
>> +		 * are deleting this port, unsync addresses
>> +		 * from it.
>> +		 */
>> +		if (!br_port_exists(p->dev))
>> +			br_fdb_addrs_unsync(br, p);
>>  
>> -	if (br->n_flood_ports == 1) {
>> +		br->c_flood_port = NULL;
>> +		br_fdb_addrs_sync(br);
>> +	} else if (br->n_flood_ports == 1) {
>> +		/* We just dropped to 1 flood port. Find this one flood
>> +		 * port and sync to it.
>> +		 */
>>  		list_for_each_entry(port, &p->br->port_list, list) {
>>  			if (br_port_exists(port->dev) &&
>>  			    (port->flags & BR_FLOOD)) {
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 87dcc09..13840de 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>>  		struct net_device *dev, int idx);
>>  void br_fdb_addrs_sync(struct net_bridge *br);
>> -void br_fdb_addrs_unsync(struct net_bridge *br);
>> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
>>  
>>  /* br_forward.c */
>>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
>> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
>> index 3de44a3..24da78f 100644
>> --- a/net/core/dev_addr_lists.c
>> +++ b/net/core/dev_addr_lists.c
>> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
>>  	__hw_addr_del_entry(from_list, ha, false, false);
>>  }
>>  
>> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>> -				   struct netdev_hw_addr_list *from_list,
>> -				   int addr_len)
>> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>> +			    struct netdev_hw_addr_list *from_list,
>> +			    int addr_len)
>>  {
>>  	int err = 0;
>>  	struct netdev_hw_addr *ha, *tmp;
>> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>>  	}
>>  	return err;
>>  }
>> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
>>  
>>  /* This function only works where there is a strict 1-1 relationship
>>   * between source and destionation of they synch. If you ever need to
>> -- 
>> 1.8.5.3

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

* Re: [Bridge] [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
@ 2014-02-27  3:46       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27  3:46 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On 02/26/2014 10:57 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
>> Configuration where all ports are set to non-flooding is a slight
>> special case.  In this config, the user would have to manage all fdbs
>> for all ports.  In this special case, since we'll know all addresses,
>> we can turn off promisc on all ports and program all ports with the
>> same set of addresses.
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> 
> Here the logic is different be we could sync them,
> just make this a per-port flag and
> scan all ports instead of relying on flood_port.
> This will be slower in the single flooding port
> case but worst case remains the same since
> this patch scans all ports.
> 
> What do you think?

I had it this way before and didn't really like it.  The transitions
between the two configs needs to be detected and appropriate
actions taken.  So it'll still be special coded.

I'll see what I can do to simplify this.

-vlad

>> ---
>>  include/linux/netdevice.h |  3 +++
>>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
>>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
>>  net/bridge/br_private.h   |  2 +-
>>  net/core/dev_addr_lists.c |  7 ++++---
>>  5 files changed, 59 insertions(+), 15 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index e29cce1..79e97ee 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
>>  		  unsigned char addr_type);
>>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
>>  		   struct netdev_hw_addr_list *from_list, int addr_len);
>> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>> +			    struct netdev_hw_addr_list *from_list,
>> +			    int addr_len);
>>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
>>  		      struct netdev_hw_addr_list *from_list, int addr_len);
>>  void __hw_addr_init(struct netdev_hw_addr_list *list);
>> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
>> index ef95e81..26ea4fe 100644
>> --- a/net/bridge/br_fdb.c
>> +++ b/net/bridge/br_fdb.c
>> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
>>  	if (br->n_flood_ports == 1) {
>>  		p = br->c_flood_port;
>>  		netif_addr_lock(p->dev);
>> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
>> -				     p->dev->addr_len);
>> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
>> +					      p->dev->addr_len);
>>  		if (!err)
>>  			__dev_set_rx_mode(p->dev);
>>  		netif_addr_unlock(p->dev);
>> +	} else if (br->n_flood_ports == 0) {
>> +		list_for_each_entry(p, &br->port_list, list) {
>> +			/* skip over ports being deleted. */
>> +			if (!br_port_exists(p->dev))
>> +				continue;
>>  
>> +			netif_addr_lock_nested(p->dev);
>> +			err = __hw_addr_sync_multiple(&p->dev->uc,
>> +						      &br->conf_addrs,
>> +						      p->dev->addr_len);
>> +			if (!err)
>> +				__dev_set_rx_mode(p->dev);
>> +			netif_addr_unlock(p->dev);
>> +		}
>>  	}
>> +
>>  }
>>  
>> -void br_fdb_addrs_unsync(struct net_bridge *br)
>> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
>>  {
>> -	struct net_bridge_port *p = br->c_flood_port;
>> -
>>  	if (!p)
>>  		return;
>>  
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 55e4e28..4ba62ef 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
>>  
>>  	if (p->flags & BR_FLOOD)
>>  		br_del_flood_port(p, br);
>> +	else
>> +		br_fdb_addrs_unsync(br, p);
>>  
>>  	nbp_vlan_flush(p);
>>  	br_fdb_delete_by_port(br, p, 1);
>> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * only have 1 flooding port cache if for future use.
>>  	 */
>>  	br->n_flood_ports++;
>> -	if (br->n_flood_ports == 1)
>> +	if (br->n_flood_ports == 1) {
>> +		struct net_bridge_port *port;
>> +
>> +		/* We are transitioning from 0 flood ports to 1.  Remove
>> +		 * the addresses from all the non-flood ports and turn on
>> +		 * promisc on those ports.
>> +		 */
>> +		list_for_each_entry(port, &br->port_list, list) {
>> +			/* skip the current port we are changing */
>> +			if (port == p)
>> +				continue;
>> +
>> +			if (!(port->flags & BR_FLOOD)) {
>> +				br_port_set_promisc(port);
>> +				br_fdb_addrs_unsync(br, port);
>> +			}
>> +		}
>> +
>>  		br->c_flood_port = p;
>> +	}
>>  
>>  	br_fdb_addrs_sync(br);
>>  	br_manage_promisc(br);
>> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>>  	 * set it if it is not set.
>>  	 */
>>  	br->n_flood_ports--;
>> -	if (p == br->c_flood_port) {
>> -		br_fdb_addrs_unsync(br);
>> -		br->c_flood_port = NULL;
>> -	}
>> +	if (br->n_flood_ports == 0) {
>> +		/* We just dropped to 0 flood ports.  If we
>> +		 * are deleting this port, unsync addresses
>> +		 * from it.
>> +		 */
>> +		if (!br_port_exists(p->dev))
>> +			br_fdb_addrs_unsync(br, p);
>>  
>> -	if (br->n_flood_ports == 1) {
>> +		br->c_flood_port = NULL;
>> +		br_fdb_addrs_sync(br);
>> +	} else if (br->n_flood_ports == 1) {
>> +		/* We just dropped to 1 flood port. Find this one flood
>> +		 * port and sync to it.
>> +		 */
>>  		list_for_each_entry(port, &p->br->port_list, list) {
>>  			if (br_port_exists(port->dev) &&
>>  			    (port->flags & BR_FLOOD)) {
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 87dcc09..13840de 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
>>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
>>  		struct net_device *dev, int idx);
>>  void br_fdb_addrs_sync(struct net_bridge *br);
>> -void br_fdb_addrs_unsync(struct net_bridge *br);
>> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
>>  
>>  /* br_forward.c */
>>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
>> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
>> index 3de44a3..24da78f 100644
>> --- a/net/core/dev_addr_lists.c
>> +++ b/net/core/dev_addr_lists.c
>> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
>>  	__hw_addr_del_entry(from_list, ha, false, false);
>>  }
>>  
>> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>> -				   struct netdev_hw_addr_list *from_list,
>> -				   int addr_len)
>> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>> +			    struct netdev_hw_addr_list *from_list,
>> +			    int addr_len)
>>  {
>>  	int err = 0;
>>  	struct netdev_hw_addr *ha, *tmp;
>> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
>>  	}
>>  	return err;
>>  }
>> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
>>  
>>  /* This function only works where there is a strict 1-1 relationship
>>   * between source and destionation of they synch. If you ever need to
>> -- 
>> 1.8.5.3


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

* Re: [PATCH RFC 0/7] Non-promisc bidge ports support
  2014-02-26 23:59   ` [Bridge] " Jamal Hadi Salim
@ 2014-02-27  7:20     ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27  7:20 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: Vlad Yasevich, netdev, bridge, shemminger, john.r.fastabend

On Wed, Feb 26, 2014 at 06:59:29PM -0500, Jamal Hadi Salim wrote:
> On 02/26/14 10:18, Vlad Yasevich wrote:
> >This patch series is a complete re-design and re-implementation of
> >prior attempts to support non-promiscuous bridge ports.
> >
> >The basic design is as follows.  The bridge keeps track of
> >all the ports that flood packets to unknown destinations.  If
> >the flooding is disabled on the port, to get traffic to flow
> >through, user/management would need to add an fdb describing
> >such traffic.  When such fdb is added, we save the address
> >to bridge private hardware address list.
> 
> Entering the addresses in the uc list on other bridgeports seems
> reasonable for the scenario described.
> But would it _also_ need to be added to the fdb of the bridge?
> i.e how does the bridge (if the packet was to be handed to it)
> know where to forward?
> BTW: on the comment that flooding off implies learning off: I would like
> to be able to turn off flooding on a specific bridge port but
> still want to learn from it. I dont think those two are mutually
> exclusive.
> 
> cheers,
> jamal

I agree.

It seems a reasonable tradeoff to limit any specific
optimization to !flood && !learn if this simplifies the
implementation significantly and if everything works
as it did before even with learning on.


-- 
MST

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

* Re: [Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
@ 2014-02-27  7:20     ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27  7:20 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: Vlad Yasevich, netdev, shemminger, bridge, john.r.fastabend

On Wed, Feb 26, 2014 at 06:59:29PM -0500, Jamal Hadi Salim wrote:
> On 02/26/14 10:18, Vlad Yasevich wrote:
> >This patch series is a complete re-design and re-implementation of
> >prior attempts to support non-promiscuous bridge ports.
> >
> >The basic design is as follows.  The bridge keeps track of
> >all the ports that flood packets to unknown destinations.  If
> >the flooding is disabled on the port, to get traffic to flow
> >through, user/management would need to add an fdb describing
> >such traffic.  When such fdb is added, we save the address
> >to bridge private hardware address list.
> 
> Entering the addresses in the uc list on other bridgeports seems
> reasonable for the scenario described.
> But would it _also_ need to be added to the fdb of the bridge?
> i.e how does the bridge (if the packet was to be handed to it)
> know where to forward?
> BTW: on the comment that flooding off implies learning off: I would like
> to be able to turn off flooding on a specific bridge port but
> still want to learn from it. I dont think those two are mutually
> exclusive.
> 
> cheers,
> jamal

I agree.

It seems a reasonable tradeoff to limit any specific
optimization to !flood && !learn if this simplifies the
implementation significantly and if everything works
as it did before even with learning on.


-- 
MST

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

* Re: [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
  2014-02-27  3:46       ` [Bridge] " Vlad Yasevich
@ 2014-02-27  7:29         ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27  7:29 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:46:20PM -0500, Vlad Yasevich wrote:
> On 02/26/2014 10:57 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
> >> Configuration where all ports are set to non-flooding is a slight
> >> special case.  In this config, the user would have to manage all fdbs
> >> for all ports.  In this special case, since we'll know all addresses,
> >> we can turn off promisc on all ports and program all ports with the
> >> same set of addresses.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > Here the logic is different be we could sync them,
> > just make this a per-port flag and
> > scan all ports instead of relying on flood_port.
> > This will be slower in the single flooding port
> > case but worst case remains the same since
> > this patch scans all ports.
> > 
> > What do you think?
> 
> I had it this way before and didn't really like it.  The transitions
> between the two configs needs to be detected and appropriate
> actions taken.  So it'll still be special coded.
> 
> I'll see what I can do to simplify this.
> 
> -vlad

Not really.
Basically if you have per-port flags p->promisc and p->flood
then the logic becomes:

	int nr_flood = 0;
	for each port (br, p) {
		nr_flood += p->flood;
	}

	for each port (br, p) {
		int promisc = nr_flood - p->flood;
		if (p->promisc != promisc) {
			dev_set_promisc(p, promisc);
			dev_set_allmulti(p, !promisc);
		}
	}

Just re-run this whenever user tweaks any flood flags.

Replace p->flood with p->flood && p->learn if you
need to disable the optimization when learning
is on.

> >> ---
> >>  include/linux/netdevice.h |  3 +++
> >>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
> >>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
> >>  net/bridge/br_private.h   |  2 +-
> >>  net/core/dev_addr_lists.c |  7 ++++---
> >>  5 files changed, 59 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> >> index e29cce1..79e97ee 100644
> >> --- a/include/linux/netdevice.h
> >> +++ b/include/linux/netdevice.h
> >> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
> >>  		  unsigned char addr_type);
> >>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
> >>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> >> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >> +			    struct netdev_hw_addr_list *from_list,
> >> +			    int addr_len);
> >>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> >>  		      struct netdev_hw_addr_list *from_list, int addr_len);
> >>  void __hw_addr_init(struct netdev_hw_addr_list *list);
> >> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> >> index ef95e81..26ea4fe 100644
> >> --- a/net/bridge/br_fdb.c
> >> +++ b/net/bridge/br_fdb.c
> >> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
> >>  	if (br->n_flood_ports == 1) {
> >>  		p = br->c_flood_port;
> >>  		netif_addr_lock(p->dev);
> >> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> >> -				     p->dev->addr_len);
> >> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
> >> +					      p->dev->addr_len);
> >>  		if (!err)
> >>  			__dev_set_rx_mode(p->dev);
> >>  		netif_addr_unlock(p->dev);
> >> +	} else if (br->n_flood_ports == 0) {
> >> +		list_for_each_entry(p, &br->port_list, list) {
> >> +			/* skip over ports being deleted. */
> >> +			if (!br_port_exists(p->dev))
> >> +				continue;
> >>  
> >> +			netif_addr_lock_nested(p->dev);
> >> +			err = __hw_addr_sync_multiple(&p->dev->uc,
> >> +						      &br->conf_addrs,
> >> +						      p->dev->addr_len);
> >> +			if (!err)
> >> +				__dev_set_rx_mode(p->dev);
> >> +			netif_addr_unlock(p->dev);
> >> +		}
> >>  	}
> >> +
> >>  }
> >>  
> >> -void br_fdb_addrs_unsync(struct net_bridge *br)
> >> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
> >>  {
> >> -	struct net_bridge_port *p = br->c_flood_port;
> >> -
> >>  	if (!p)
> >>  		return;
> >>  
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index 55e4e28..4ba62ef 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
> >>  
> >>  	if (p->flags & BR_FLOOD)
> >>  		br_del_flood_port(p, br);
> >> +	else
> >> +		br_fdb_addrs_unsync(br, p);
> >>  
> >>  	nbp_vlan_flush(p);
> >>  	br_fdb_delete_by_port(br, p, 1);
> >> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	 * only have 1 flooding port cache if for future use.
> >>  	 */
> >>  	br->n_flood_ports++;
> >> -	if (br->n_flood_ports == 1)
> >> +	if (br->n_flood_ports == 1) {
> >> +		struct net_bridge_port *port;
> >> +
> >> +		/* We are transitioning from 0 flood ports to 1.  Remove
> >> +		 * the addresses from all the non-flood ports and turn on
> >> +		 * promisc on those ports.
> >> +		 */
> >> +		list_for_each_entry(port, &br->port_list, list) {
> >> +			/* skip the current port we are changing */
> >> +			if (port == p)
> >> +				continue;
> >> +
> >> +			if (!(port->flags & BR_FLOOD)) {
> >> +				br_port_set_promisc(port);
> >> +				br_fdb_addrs_unsync(br, port);
> >> +			}
> >> +		}
> >> +
> >>  		br->c_flood_port = p;
> >> +	}
> >>  
> >>  	br_fdb_addrs_sync(br);
> >>  	br_manage_promisc(br);
> >> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	 * set it if it is not set.
> >>  	 */
> >>  	br->n_flood_ports--;
> >> -	if (p == br->c_flood_port) {
> >> -		br_fdb_addrs_unsync(br);
> >> -		br->c_flood_port = NULL;
> >> -	}
> >> +	if (br->n_flood_ports == 0) {
> >> +		/* We just dropped to 0 flood ports.  If we
> >> +		 * are deleting this port, unsync addresses
> >> +		 * from it.
> >> +		 */
> >> +		if (!br_port_exists(p->dev))
> >> +			br_fdb_addrs_unsync(br, p);
> >>  
> >> -	if (br->n_flood_ports == 1) {
> >> +		br->c_flood_port = NULL;
> >> +		br_fdb_addrs_sync(br);
> >> +	} else if (br->n_flood_ports == 1) {
> >> +		/* We just dropped to 1 flood port. Find this one flood
> >> +		 * port and sync to it.
> >> +		 */
> >>  		list_for_each_entry(port, &p->br->port_list, list) {
> >>  			if (br_port_exists(port->dev) &&
> >>  			    (port->flags & BR_FLOOD)) {
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 87dcc09..13840de 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
> >>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
> >>  		struct net_device *dev, int idx);
> >>  void br_fdb_addrs_sync(struct net_bridge *br);
> >> -void br_fdb_addrs_unsync(struct net_bridge *br);
> >> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
> >>  
> >>  /* br_forward.c */
> >>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> >> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> >> index 3de44a3..24da78f 100644
> >> --- a/net/core/dev_addr_lists.c
> >> +++ b/net/core/dev_addr_lists.c
> >> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
> >>  	__hw_addr_del_entry(from_list, ha, false, false);
> >>  }
> >>  
> >> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >> -				   struct netdev_hw_addr_list *from_list,
> >> -				   int addr_len)
> >> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >> +			    struct netdev_hw_addr_list *from_list,
> >> +			    int addr_len)
> >>  {
> >>  	int err = 0;
> >>  	struct netdev_hw_addr *ha, *tmp;
> >> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >>  	}
> >>  	return err;
> >>  }
> >> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
> >>  
> >>  /* This function only works where there is a strict 1-1 relationship
> >>   * between source and destionation of they synch. If you ever need to
> >> -- 
> >> 1.8.5.3

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

* Re: [Bridge] [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
@ 2014-02-27  7:29         ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27  7:29 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: john.r.fastabend, netdev, shemminger, bridge, jhs

On Wed, Feb 26, 2014 at 10:46:20PM -0500, Vlad Yasevich wrote:
> On 02/26/2014 10:57 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 10:18:25AM -0500, Vlad Yasevich wrote:
> >> Configuration where all ports are set to non-flooding is a slight
> >> special case.  In this config, the user would have to manage all fdbs
> >> for all ports.  In this special case, since we'll know all addresses,
> >> we can turn off promisc on all ports and program all ports with the
> >> same set of addresses.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > Here the logic is different be we could sync them,
> > just make this a per-port flag and
> > scan all ports instead of relying on flood_port.
> > This will be slower in the single flooding port
> > case but worst case remains the same since
> > this patch scans all ports.
> > 
> > What do you think?
> 
> I had it this way before and didn't really like it.  The transitions
> between the two configs needs to be detected and appropriate
> actions taken.  So it'll still be special coded.
> 
> I'll see what I can do to simplify this.
> 
> -vlad

Not really.
Basically if you have per-port flags p->promisc and p->flood
then the logic becomes:

	int nr_flood = 0;
	for each port (br, p) {
		nr_flood += p->flood;
	}

	for each port (br, p) {
		int promisc = nr_flood - p->flood;
		if (p->promisc != promisc) {
			dev_set_promisc(p, promisc);
			dev_set_allmulti(p, !promisc);
		}
	}

Just re-run this whenever user tweaks any flood flags.

Replace p->flood with p->flood && p->learn if you
need to disable the optimization when learning
is on.

> >> ---
> >>  include/linux/netdevice.h |  3 +++
> >>  net/bridge/br_fdb.c       | 22 +++++++++++++++++-----
> >>  net/bridge/br_if.c        | 40 ++++++++++++++++++++++++++++++++++------
> >>  net/bridge/br_private.h   |  2 +-
> >>  net/core/dev_addr_lists.c |  7 ++++---
> >>  5 files changed, 59 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> >> index e29cce1..79e97ee 100644
> >> --- a/include/linux/netdevice.h
> >> +++ b/include/linux/netdevice.h
> >> @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list,
> >>  		  unsigned char addr_type);
> >>  int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
> >>  		   struct netdev_hw_addr_list *from_list, int addr_len);
> >> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >> +			    struct netdev_hw_addr_list *from_list,
> >> +			    int addr_len);
> >>  void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
> >>  		      struct netdev_hw_addr_list *from_list, int addr_len);
> >>  void __hw_addr_init(struct netdev_hw_addr_list *list);
> >> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> >> index ef95e81..26ea4fe 100644
> >> --- a/net/bridge/br_fdb.c
> >> +++ b/net/bridge/br_fdb.c
> >> @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br)
> >>  	if (br->n_flood_ports == 1) {
> >>  		p = br->c_flood_port;
> >>  		netif_addr_lock(p->dev);
> >> -		err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs,
> >> -				     p->dev->addr_len);
> >> +		err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs,
> >> +					      p->dev->addr_len);
> >>  		if (!err)
> >>  			__dev_set_rx_mode(p->dev);
> >>  		netif_addr_unlock(p->dev);
> >> +	} else if (br->n_flood_ports == 0) {
> >> +		list_for_each_entry(p, &br->port_list, list) {
> >> +			/* skip over ports being deleted. */
> >> +			if (!br_port_exists(p->dev))
> >> +				continue;
> >>  
> >> +			netif_addr_lock_nested(p->dev);
> >> +			err = __hw_addr_sync_multiple(&p->dev->uc,
> >> +						      &br->conf_addrs,
> >> +						      p->dev->addr_len);
> >> +			if (!err)
> >> +				__dev_set_rx_mode(p->dev);
> >> +			netif_addr_unlock(p->dev);
> >> +		}
> >>  	}
> >> +
> >>  }
> >>  
> >> -void br_fdb_addrs_unsync(struct net_bridge *br)
> >> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p)
> >>  {
> >> -	struct net_bridge_port *p = br->c_flood_port;
> >> -
> >>  	if (!p)
> >>  		return;
> >>  
> >> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> >> index 55e4e28..4ba62ef 100644
> >> --- a/net/bridge/br_if.c
> >> +++ b/net/bridge/br_if.c
> >> @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p)
> >>  
> >>  	if (p->flags & BR_FLOOD)
> >>  		br_del_flood_port(p, br);
> >> +	else
> >> +		br_fdb_addrs_unsync(br, p);
> >>  
> >>  	nbp_vlan_flush(p);
> >>  	br_fdb_delete_by_port(br, p, 1);
> >> @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	 * only have 1 flooding port cache if for future use.
> >>  	 */
> >>  	br->n_flood_ports++;
> >> -	if (br->n_flood_ports == 1)
> >> +	if (br->n_flood_ports == 1) {
> >> +		struct net_bridge_port *port;
> >> +
> >> +		/* We are transitioning from 0 flood ports to 1.  Remove
> >> +		 * the addresses from all the non-flood ports and turn on
> >> +		 * promisc on those ports.
> >> +		 */
> >> +		list_for_each_entry(port, &br->port_list, list) {
> >> +			/* skip the current port we are changing */
> >> +			if (port == p)
> >> +				continue;
> >> +
> >> +			if (!(port->flags & BR_FLOOD)) {
> >> +				br_port_set_promisc(port);
> >> +				br_fdb_addrs_unsync(br, port);
> >> +			}
> >> +		}
> >> +
> >>  		br->c_flood_port = p;
> >> +	}
> >>  
> >>  	br_fdb_addrs_sync(br);
> >>  	br_manage_promisc(br);
> >> @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> >>  	 * set it if it is not set.
> >>  	 */
> >>  	br->n_flood_ports--;
> >> -	if (p == br->c_flood_port) {
> >> -		br_fdb_addrs_unsync(br);
> >> -		br->c_flood_port = NULL;
> >> -	}
> >> +	if (br->n_flood_ports == 0) {
> >> +		/* We just dropped to 0 flood ports.  If we
> >> +		 * are deleting this port, unsync addresses
> >> +		 * from it.
> >> +		 */
> >> +		if (!br_port_exists(p->dev))
> >> +			br_fdb_addrs_unsync(br, p);
> >>  
> >> -	if (br->n_flood_ports == 1) {
> >> +		br->c_flood_port = NULL;
> >> +		br_fdb_addrs_sync(br);
> >> +	} else if (br->n_flood_ports == 1) {
> >> +		/* We just dropped to 1 flood port. Find this one flood
> >> +		 * port and sync to it.
> >> +		 */
> >>  		list_for_each_entry(port, &p->br->port_list, list) {
> >>  			if (br_port_exists(port->dev) &&
> >>  			    (port->flags & BR_FLOOD)) {
> >> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> >> index 87dcc09..13840de 100644
> >> --- a/net/bridge/br_private.h
> >> +++ b/net/bridge/br_private.h
> >> @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
> >>  int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
> >>  		struct net_device *dev, int idx);
> >>  void br_fdb_addrs_sync(struct net_bridge *br);
> >> -void br_fdb_addrs_unsync(struct net_bridge *br);
> >> +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p);
> >>  
> >>  /* br_forward.c */
> >>  void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
> >> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
> >> index 3de44a3..24da78f 100644
> >> --- a/net/core/dev_addr_lists.c
> >> +++ b/net/core/dev_addr_lists.c
> >> @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
> >>  	__hw_addr_del_entry(from_list, ha, false, false);
> >>  }
> >>  
> >> -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >> -				   struct netdev_hw_addr_list *from_list,
> >> -				   int addr_len)
> >> +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >> +			    struct netdev_hw_addr_list *from_list,
> >> +			    int addr_len)
> >>  {
> >>  	int err = 0;
> >>  	struct netdev_hw_addr *ha, *tmp;
> >> @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
> >>  	}
> >>  	return err;
> >>  }
> >> +EXPORT_SYMBOL(__hw_addr_sync_multiple);
> >>  
> >>  /* This function only works where there is a strict 1-1 relationship
> >>   * between source and destionation of they synch. If you ever need to
> >> -- 
> >> 1.8.5.3

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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-26 17:35       ` [Bridge] " Vlad Yasevich
@ 2014-02-27  7:53         ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27  7:53 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: john.r.fastabend, netdev, bridge, jhs, Stephen Hemminger, shemminger

On Wed, Feb 26, 2014 at 12:35:08PM -0500, Vlad Yasevich wrote:
> On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
> > On Wed, 26 Feb 2014 10:18:21 -0500
> > Vlad Yasevich <vyasevic@redhat.com> wrote:
> > 
> >> When a static fdb entry is created, add the mac address to the bridge
> >> address list.  This list is used to program the proper port's
> >> address list.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > I don't like this level of bookkeeping it starts to mix
> > layers between the bridge network interface as entity for talking to the
> > local host, and forwarding table entries.
> 
> Actually this is one of the reasons this isn't done through the
> br->dev->uc.  Forwarding table entries are still per-port.
> 
> > 
> > Many times static entries are used as alternative to flooding in
> > environments which don't trust STP.
> 
> Ok, and how would this be problematic?  If one wants to turn off
> promisc in this environment, then receive filters needs to be properly
> programmed.
> 
> > 
> > Plus, it looks like another major source of bugs.
> > 
> 
> Any new code is a potential source of issues.  Are you saying
> No to any new code in bridge?
> 
> -vlad

I'm guessing Stephen merely worries about
multiple data structures that need to stay in
sync, and asks that you revisit
using private hw address list in the bridge.

What's the issue with walking fdb exactly?
You say
 1)  I tried using the fdb table itself as main repository, but
      this caused difficulties in synchronizing this table with
      the interface filters later on.

I'm guessing you refer to writing addresses out to ports
directly when walking the hash being impossible
since this datastructure uses rcu and spinlocks?
Fair enough but the entries you care about
seem to only be modified under RTNL so just
copy them out to a temporary list.
This might be less efficient, but will be simpler I think.

-- 
MST

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-27  7:53         ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27  7:53 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: john.r.fastabend, netdev, bridge, jhs, Stephen Hemminger, shemminger

On Wed, Feb 26, 2014 at 12:35:08PM -0500, Vlad Yasevich wrote:
> On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
> > On Wed, 26 Feb 2014 10:18:21 -0500
> > Vlad Yasevich <vyasevic@redhat.com> wrote:
> > 
> >> When a static fdb entry is created, add the mac address to the bridge
> >> address list.  This list is used to program the proper port's
> >> address list.
> >>
> >> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> > 
> > I don't like this level of bookkeeping it starts to mix
> > layers between the bridge network interface as entity for talking to the
> > local host, and forwarding table entries.
> 
> Actually this is one of the reasons this isn't done through the
> br->dev->uc.  Forwarding table entries are still per-port.
> 
> > 
> > Many times static entries are used as alternative to flooding in
> > environments which don't trust STP.
> 
> Ok, and how would this be problematic?  If one wants to turn off
> promisc in this environment, then receive filters needs to be properly
> programmed.
> 
> > 
> > Plus, it looks like another major source of bugs.
> > 
> 
> Any new code is a potential source of issues.  Are you saying
> No to any new code in bridge?
> 
> -vlad

I'm guessing Stephen merely worries about
multiple data structures that need to stay in
sync, and asks that you revisit
using private hw address list in the bridge.

What's the issue with walking fdb exactly?
You say
 1)  I tried using the fdb table itself as main repository, but
      this caused difficulties in synchronizing this table with
      the interface filters later on.

I'm guessing you refer to writing addresses out to ports
directly when walking the hash being impossible
since this datastructure uses rcu and spinlocks?
Fair enough but the entries you care about
seem to only be modified under RTNL so just
copy them out to a temporary list.
This might be less efficient, but will be simpler I think.

-- 
MST

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

* Re: [Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
  2014-02-27  3:37     ` [Bridge] " Vlad Yasevich
  (?)
@ 2014-02-27  8:54     ` Amidu Sila
  -1 siblings, 0 replies; 91+ messages in thread
From: Amidu Sila @ 2014-02-27  8:54 UTC (permalink / raw)
  To: vyasevic, Jamal Hadi Salim, netdev
  Cc: john.r.fastabend, shemminger, bridge, mst

Please, unsubscribe me.
Regards
Amidu Sila

On 2/27/14, 03:37 AM, Vlad Yasevich wrote:
> On 02/26/2014 06:59 PM, Jamal Hadi Salim wrote:
>> On 02/26/14 10:18, Vlad Yasevich wrote:
>>> This patch series is a complete re-design and re-implementation of
>>> prior attempts to support non-promiscuous bridge ports.
>>>
>>> The basic design is as follows.  The bridge keeps track of
>>> all the ports that flood packets to unknown destinations.  If
>>> the flooding is disabled on the port, to get traffic to flow
>>> through, user/management would need to add an fdb describing
>>> such traffic.  When such fdb is added, we save the address
>>> to bridge private hardware address list.
>> Entering the addresses in the uc list on other bridgeports seems
>> reasonable for the scenario described.
>> But would it _also_ need to be added to the fdb of the bridge?
>> i.e how does the bridge (if the packet was to be handed to it)
>> know where to forward?
> The fdb described here is actually added to the bridge.  In the case
> when we are turning promiscuous mode off on a port, we program the
> address from the fdb down to the port uc list as well.  This allows
> the bridge to continue receiving traffic destined for this address even
> though the port is not in promiscuous mode.
>
>> BTW: on the comment that flooding off implies learning off: I would like
>> to be able to turn off flooding on a specific bridge port but
>> still want to learn from it. I dont think those two are mutually
>> exclusive.
> No they are not, but it does lead to some very interesting traffic
> hang-ups that I've experienced first hand.  Everything works great
> in the beginning.  However, if you go idle for a long enough period
> that the fdb times out, re-establishing the connection take a rather
> long time due to unicast ARPs being dropped by the bridge.  You end
> up waiting until arp fails and switches to broadcast to restore the
> connection.  So, this mode isn't really recommended.  Nothing currently
> forbids it however.
>
> -vlad
>> cheers,
>> jamal


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

* Re: [PATCH 2/7] bridge: Keep track of ports capable of flooding.
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-27 11:59     ` Toshiaki Makita
  -1 siblings, 0 replies; 91+ messages in thread
From: Toshiaki Makita @ 2014-02-27 11:59 UTC (permalink / raw)
  To: Vlad Yasevich, netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend

(2014/02/27 0:18), Vlad Yasevich wrote:
> Keep track of bridge ports that have unicast flooding turned on.
> This will later be used by the algorithm to automatically manage
> address programming and promisc mode.
> 
...
> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>  	dev_disable_lro(dev);
>  
>  	list_add_rcu(&p->list, &br->port_list);
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);

BR_FLOOD seems to be set in new_nbp().
Is this condition check needed?

>  
>  	netdev_update_features(br->dev);
>  
> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>  
>  	return 0;
>  }
> +
> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	/* Increment the number of  flooding ports, and if we
> +	 * only have 1 flooding port cache if for future use.
> +	 */
> +	br->n_flood_ports++;
> +	if (br->n_flood_ports == 1)
> +		br->c_flood_port = p;

Needn't c_flood_port be cleared if n_flood_ports == 2?

Thanks,
Toshiaki Makita

> +}
> +
> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	struct net_bridge_port *port;
> +
> +	/* Decrement the  number of flood port.
> +	 * If we are deleting the current flood port, clear
> +	 * the cached port.  If we are down to 1 flood port,
> +	 * set it if it is not set.
> +	 */
> +	br->n_flood_ports--;
> +	if (p == br->c_flood_port)
> +		br->c_flood_port = NULL;
> +
> +	if (br->n_flood_ports == 1) {
> +		list_for_each_entry(port, &p->br->port_list, list) {
> +			if (port->flags & BR_FLOOD) {
> +				br->c_flood_port = port;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> +{
> +	struct net_bridge *br = p->br;
> +
> +	/* We are only interested FLOOD flag */
> +	if (!(mask & BR_FLOOD))
> +		return;
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);
> +	else
> +		br_del_flood_port(p, br);
> +}
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index e74b6d53..01382b9 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  {
>  	int err;
> +	unsigned long old_flags = p->flags;
>  
>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  		if (err)
>  			return err;
>  	}
> +
> +	br_port_flags_change(p, old_flags ^ p->flags);
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 3ba11bc..26a3987 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -290,6 +290,8 @@ struct net_bridge
>  	struct timer_list		topology_change_timer;
>  	struct timer_list		gc_timer;
>  	struct kobject			*ifobj;
> +	struct net_bridge_port		*c_flood_port;
> +	u32				n_flood_ports;
>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>  int br_min_mtu(const struct net_bridge *br);
>  netdev_features_t br_features_recompute(struct net_bridge *br,
>  					netdev_features_t features);
> +void br_port_flags_change(struct net_bridge_port *port,
> +			  unsigned long mask);
>  
>  /* br_input.c */
>  int br_handle_frame_finish(struct sk_buff *skb);
> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> index 7f66aa4..9ff6691 100644
> --- a/net/bridge/br_sysfs_if.c
> +++ b/net/bridge/br_sysfs_if.c
> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>  		     unsigned long mask)
>  {
> -	unsigned long flags = p->flags;
> +	unsigned long flags;
> +	unsigned long old_flags;
> +
> +	old_flags = flags = p->flags;
>  
>  	if (v)
>  		flags |= mask;
> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>  
>  	if (flags != p->flags) {
>  		p->flags = flags;
> +		br_port_flags_change(p, old_flags ^ flags);
>  		br_ifinfo_notify(RTM_NEWLINK, p);
>  	}
>  	return 0;
> 

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

* Re: [Bridge] [PATCH 2/7] bridge: Keep track of ports capable of flooding.
@ 2014-02-27 11:59     ` Toshiaki Makita
  0 siblings, 0 replies; 91+ messages in thread
From: Toshiaki Makita @ 2014-02-27 11:59 UTC (permalink / raw)
  To: Vlad Yasevich, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

(2014/02/27 0:18), Vlad Yasevich wrote:
> Keep track of bridge ports that have unicast flooding turned on.
> This will later be used by the algorithm to automatically manage
> address programming and promisc mode.
> 
...
> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>  	dev_disable_lro(dev);
>  
>  	list_add_rcu(&p->list, &br->port_list);
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);

BR_FLOOD seems to be set in new_nbp().
Is this condition check needed?

>  
>  	netdev_update_features(br->dev);
>  
> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>  
>  	return 0;
>  }
> +
> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	/* Increment the number of  flooding ports, and if we
> +	 * only have 1 flooding port cache if for future use.
> +	 */
> +	br->n_flood_ports++;
> +	if (br->n_flood_ports == 1)
> +		br->c_flood_port = p;

Needn't c_flood_port be cleared if n_flood_ports == 2?

Thanks,
Toshiaki Makita

> +}
> +
> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
> +{
> +	struct net_bridge_port *port;
> +
> +	/* Decrement the  number of flood port.
> +	 * If we are deleting the current flood port, clear
> +	 * the cached port.  If we are down to 1 flood port,
> +	 * set it if it is not set.
> +	 */
> +	br->n_flood_ports--;
> +	if (p == br->c_flood_port)
> +		br->c_flood_port = NULL;
> +
> +	if (br->n_flood_ports == 1) {
> +		list_for_each_entry(port, &p->br->port_list, list) {
> +			if (port->flags & BR_FLOOD) {
> +				br->c_flood_port = port;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
> +{
> +	struct net_bridge *br = p->br;
> +
> +	/* We are only interested FLOOD flag */
> +	if (!(mask & BR_FLOOD))
> +		return;
> +
> +	if (p->flags & BR_FLOOD)
> +		br_add_flood_port(p, br);
> +	else
> +		br_del_flood_port(p, br);
> +}
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index e74b6d53..01382b9 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  {
>  	int err;
> +	unsigned long old_flags = p->flags;
>  
>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>  		if (err)
>  			return err;
>  	}
> +
> +	br_port_flags_change(p, old_flags ^ p->flags);
>  	return 0;
>  }
>  
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 3ba11bc..26a3987 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -290,6 +290,8 @@ struct net_bridge
>  	struct timer_list		topology_change_timer;
>  	struct timer_list		gc_timer;
>  	struct kobject			*ifobj;
> +	struct net_bridge_port		*c_flood_port;
> +	u32				n_flood_ports;
>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>  	u8				vlan_enabled;
>  	struct net_port_vlans __rcu	*vlan_info;
> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>  int br_min_mtu(const struct net_bridge *br);
>  netdev_features_t br_features_recompute(struct net_bridge *br,
>  					netdev_features_t features);
> +void br_port_flags_change(struct net_bridge_port *port,
> +			  unsigned long mask);
>  
>  /* br_input.c */
>  int br_handle_frame_finish(struct sk_buff *skb);
> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
> index 7f66aa4..9ff6691 100644
> --- a/net/bridge/br_sysfs_if.c
> +++ b/net/bridge/br_sysfs_if.c
> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>  		     unsigned long mask)
>  {
> -	unsigned long flags = p->flags;
> +	unsigned long flags;
> +	unsigned long old_flags;
> +
> +	old_flags = flags = p->flags;
>  
>  	if (v)
>  		flags |= mask;
> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>  
>  	if (flags != p->flags) {
>  		p->flags = flags;
> +		br_port_flags_change(p, old_flags ^ flags);
>  		br_ifinfo_notify(RTM_NEWLINK, p);
>  	}
>  	return 0;
> 

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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
@ 2014-02-27 12:06     ` Toshiaki Makita
  -1 siblings, 0 replies; 91+ messages in thread
From: Toshiaki Makita @ 2014-02-27 12:06 UTC (permalink / raw)
  To: Vlad Yasevich, netdev; +Cc: bridge, shemminger, mst, jhs, john.r.fastabend

(2014/02/27 0:18), Vlad Yasevich wrote:
> If the user configures vlan interfaces on top of the bridge and the bridge
> doesn't have vlan filtering enabled, we have to place all the ports in
> promsic mode so that we can correctly receive tagged frames.
> When vlan filtering is enabled, the vlan configuration will be provided
> via filtering interface.
> When the vlan filtering is toggled, we also have mange promiscuity.

If we disable vlan_filtering and no vlan interface is configured on the
bridge, we cannot forward any tagged traffic?
If we want to forward frames from one port to another port (not from/to
bridge device), we have to add vlan interface or set promisc mode, right?

Sorry if I misunderstood it. I'm not sure..

Thanks,
Toshiaki Makita

> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_device.c  | 14 ++++++++++++++
>  net/bridge/br_if.c      | 17 +++++++++++++----
>  net/bridge/br_private.h |  9 +++++++++
>  net/bridge/br_vlan.c    |  1 +
>  4 files changed, 37 insertions(+), 4 deletions(-)
> 
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 0af9d6c..967abb3 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>  
>  #endif
>  
> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>  
>  {
> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>  	.ndo_change_mtu		 = br_change_mtu,
>  	.ndo_do_ioctl		 = br_dev_ioctl,
> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>  #ifdef CONFIG_NET_POLL_CONTROLLER
>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 7e92bd0..55e4e28 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>  void br_manage_promisc(struct net_bridge *br)
>  {
>  	struct net_bridge_port *p;
> +	int set_all = false;
> +
> +	if (br->dev->flags & IFF_PROMISC)
> +		set_all = true;
> +
> +	/* If vlan filtering is disabled and there are any VLANs
> +	 * configured on top of the bridge, set promisc on all
> +	 * ports.
> +	 */
> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
> +		set_all = true;
>  
>  	list_for_each_entry(p, &br->port_list, list) {
> -		if (br->dev->flags & IFF_PROMISC) {
> -			/* PROMISC flag has been turned on for the bridge
> -			 * itself.  Turn on promisc on all ports.
> -			 */
> +		if (set_all) {
> +			/* Set all the ports to promisc mode.  */
>  			br_port_set_promisc(p);
>  
>  		} else {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 4042f86..87dcc09 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  	return v->pvid ?: VLAN_N_VID;
>  }
>  
> +static inline int br_vlan_enabled(struct net_bridge *br)
> +{
> +	return br->vlan_enabled;
> +}
>  #else
>  static inline bool br_allowed_ingress(struct net_bridge *br,
>  				      struct net_port_vlans *v,
> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  {
>  	return VLAN_N_VID;	/* Returns invalid vid */
>  }
> +
> +static inline int br_vlan_enabled(struct net_bridge *br);
> +{
> +	return 0;
> +}
>  #endif
>  
>  /* br_netfilter.c */
> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> index 8249ca7..eddc2f6 100644
> --- a/net/bridge/br_vlan.c
> +++ b/net/bridge/br_vlan.c
> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>  		goto unlock;
>  
>  	br->vlan_enabled = val;
> +	br_manage_promisc(br);
>  
>  unlock:
>  	rtnl_unlock();
> 

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-02-27 12:06     ` Toshiaki Makita
  0 siblings, 0 replies; 91+ messages in thread
From: Toshiaki Makita @ 2014-02-27 12:06 UTC (permalink / raw)
  To: Vlad Yasevich, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

(2014/02/27 0:18), Vlad Yasevich wrote:
> If the user configures vlan interfaces on top of the bridge and the bridge
> doesn't have vlan filtering enabled, we have to place all the ports in
> promsic mode so that we can correctly receive tagged frames.
> When vlan filtering is enabled, the vlan configuration will be provided
> via filtering interface.
> When the vlan filtering is toggled, we also have mange promiscuity.

If we disable vlan_filtering and no vlan interface is configured on the
bridge, we cannot forward any tagged traffic?
If we want to forward frames from one port to another port (not from/to
bridge device), we have to add vlan interface or set promisc mode, right?

Sorry if I misunderstood it. I'm not sure..

Thanks,
Toshiaki Makita

> 
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  net/bridge/br_device.c  | 14 ++++++++++++++
>  net/bridge/br_if.c      | 17 +++++++++++++----
>  net/bridge/br_private.h |  9 +++++++++
>  net/bridge/br_vlan.c    |  1 +
>  4 files changed, 37 insertions(+), 4 deletions(-)
> 
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 0af9d6c..967abb3 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>  
>  #endif
>  
> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
> +{
> +	br_manage_promisc(netdev_priv(br_dev));
> +	return 0;
> +}
> +
>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>  
>  {
> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>  	.ndo_change_mtu		 = br_change_mtu,
>  	.ndo_do_ioctl		 = br_dev_ioctl,
> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>  #ifdef CONFIG_NET_POLL_CONTROLLER
>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index 7e92bd0..55e4e28 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>  void br_manage_promisc(struct net_bridge *br)
>  {
>  	struct net_bridge_port *p;
> +	int set_all = false;
> +
> +	if (br->dev->flags & IFF_PROMISC)
> +		set_all = true;
> +
> +	/* If vlan filtering is disabled and there are any VLANs
> +	 * configured on top of the bridge, set promisc on all
> +	 * ports.
> +	 */
> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
> +		set_all = true;
>  
>  	list_for_each_entry(p, &br->port_list, list) {
> -		if (br->dev->flags & IFF_PROMISC) {
> -			/* PROMISC flag has been turned on for the bridge
> -			 * itself.  Turn on promisc on all ports.
> -			 */
> +		if (set_all) {
> +			/* Set all the ports to promisc mode.  */
>  			br_port_set_promisc(p);
>  
>  		} else {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 4042f86..87dcc09 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  	return v->pvid ?: VLAN_N_VID;
>  }
>  
> +static inline int br_vlan_enabled(struct net_bridge *br)
> +{
> +	return br->vlan_enabled;
> +}
>  #else
>  static inline bool br_allowed_ingress(struct net_bridge *br,
>  				      struct net_port_vlans *v,
> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>  {
>  	return VLAN_N_VID;	/* Returns invalid vid */
>  }
> +
> +static inline int br_vlan_enabled(struct net_bridge *br);
> +{
> +	return 0;
> +}
>  #endif
>  
>  /* br_netfilter.c */
> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> index 8249ca7..eddc2f6 100644
> --- a/net/bridge/br_vlan.c
> +++ b/net/bridge/br_vlan.c
> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>  		goto unlock;
>  
>  	br->vlan_enabled = val;
> +	br_manage_promisc(br);
>  
>  unlock:
>  	rtnl_unlock();
> 

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

* Re: [PATCH 2/7] bridge: Keep track of ports capable of flooding.
  2014-02-27 11:59     ` [Bridge] " Toshiaki Makita
@ 2014-02-27 12:54       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27 12:54 UTC (permalink / raw)
  To: Toshiaki Makita, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

On 02/27/2014 06:59 AM, Toshiaki Makita wrote:
> (2014/02/27 0:18), Vlad Yasevich wrote:
>> Keep track of bridge ports that have unicast flooding turned on.
>> This will later be used by the algorithm to automatically manage
>> address programming and promisc mode.
>>
> ...
>> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>>  	dev_disable_lro(dev);
>>  
>>  	list_add_rcu(&p->list, &br->port_list);
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
> 
> BR_FLOOD seems to be set in new_nbp().
> Is this condition check needed?
> 

Not currently, but I think there is an api in the works to allow
adding port with port flags set to begin with.  This just guards
for this case.

>>  
>>  	netdev_update_features(br->dev);
>>  
>> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>>  
>>  	return 0;
>>  }
>> +
>> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	/* Increment the number of  flooding ports, and if we
>> +	 * only have 1 flooding port cache if for future use.
>> +	 */
>> +	br->n_flood_ports++;
>> +	if (br->n_flood_ports == 1)
>> +		br->c_flood_port = p;
> 
> Needn't c_flood_port be cleared if n_flood_ports == 2?
> 

It can be, but there is no reason to do it.  It is not used
if the number or flood ports is > 1.
We can clean it up when we delete it, but we keep it around
on the off chance that it will be come the lone flood port again.
That's likely in some scenarios.

-vlad

> Thanks,
> Toshiaki Makita
> 
>> +}
>> +
>> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *port;
>> +
>> +	/* Decrement the  number of flood port.
>> +	 * If we are deleting the current flood port, clear
>> +	 * the cached port.  If we are down to 1 flood port,
>> +	 * set it if it is not set.
>> +	 */
>> +	br->n_flood_ports--;
>> +	if (p == br->c_flood_port)
>> +		br->c_flood_port = NULL;
>> +
>> +	if (br->n_flood_ports == 1) {
>> +		list_for_each_entry(port, &p->br->port_list, list) {
>> +			if (port->flags & BR_FLOOD) {
>> +				br->c_flood_port = port;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
>> +{
>> +	struct net_bridge *br = p->br;
>> +
>> +	/* We are only interested FLOOD flag */
>> +	if (!(mask & BR_FLOOD))
>> +		return;
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
>> +	else
>> +		br_del_flood_port(p, br);
>> +}
>> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
>> index e74b6d53..01382b9 100644
>> --- a/net/bridge/br_netlink.c
>> +++ b/net/bridge/br_netlink.c
>> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  {
>>  	int err;
>> +	unsigned long old_flags = p->flags;
>>  
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
>> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  		if (err)
>>  			return err;
>>  	}
>> +
>> +	br_port_flags_change(p, old_flags ^ p->flags);
>>  	return 0;
>>  }
>>  
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 3ba11bc..26a3987 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -290,6 +290,8 @@ struct net_bridge
>>  	struct timer_list		topology_change_timer;
>>  	struct timer_list		gc_timer;
>>  	struct kobject			*ifobj;
>> +	struct net_bridge_port		*c_flood_port;
>> +	u32				n_flood_ports;
>>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>>  int br_min_mtu(const struct net_bridge *br);
>>  netdev_features_t br_features_recompute(struct net_bridge *br,
>>  					netdev_features_t features);
>> +void br_port_flags_change(struct net_bridge_port *port,
>> +			  unsigned long mask);
>>  
>>  /* br_input.c */
>>  int br_handle_frame_finish(struct sk_buff *skb);
>> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
>> index 7f66aa4..9ff6691 100644
>> --- a/net/bridge/br_sysfs_if.c
>> +++ b/net/bridge/br_sysfs_if.c
>> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  		     unsigned long mask)
>>  {
>> -	unsigned long flags = p->flags;
>> +	unsigned long flags;
>> +	unsigned long old_flags;
>> +
>> +	old_flags = flags = p->flags;
>>  
>>  	if (v)
>>  		flags |= mask;
>> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  
>>  	if (flags != p->flags) {
>>  		p->flags = flags;
>> +		br_port_flags_change(p, old_flags ^ flags);
>>  		br_ifinfo_notify(RTM_NEWLINK, p);
>>  	}
>>  	return 0;
>>

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

* Re: [Bridge] [PATCH 2/7] bridge: Keep track of ports capable of flooding.
@ 2014-02-27 12:54       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27 12:54 UTC (permalink / raw)
  To: Toshiaki Makita, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

On 02/27/2014 06:59 AM, Toshiaki Makita wrote:
> (2014/02/27 0:18), Vlad Yasevich wrote:
>> Keep track of bridge ports that have unicast flooding turned on.
>> This will later be used by the algorithm to automatically manage
>> address programming and promisc mode.
>>
> ...
>> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
>>  	dev_disable_lro(dev);
>>  
>>  	list_add_rcu(&p->list, &br->port_list);
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
> 
> BR_FLOOD seems to be set in new_nbp().
> Is this condition check needed?
> 

Not currently, but I think there is an api in the works to allow
adding port with port flags set to begin with.  This just guards
for this case.

>>  
>>  	netdev_update_features(br->dev);
>>  
>> @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
>>  
>>  	return 0;
>>  }
>> +
>> +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	/* Increment the number of  flooding ports, and if we
>> +	 * only have 1 flooding port cache if for future use.
>> +	 */
>> +	br->n_flood_ports++;
>> +	if (br->n_flood_ports == 1)
>> +		br->c_flood_port = p;
> 
> Needn't c_flood_port be cleared if n_flood_ports == 2?
> 

It can be, but there is no reason to do it.  It is not used
if the number or flood ports is > 1.
We can clean it up when we delete it, but we keep it around
on the off chance that it will be come the lone flood port again.
That's likely in some scenarios.

-vlad

> Thanks,
> Toshiaki Makita
> 
>> +}
>> +
>> +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br)
>> +{
>> +	struct net_bridge_port *port;
>> +
>> +	/* Decrement the  number of flood port.
>> +	 * If we are deleting the current flood port, clear
>> +	 * the cached port.  If we are down to 1 flood port,
>> +	 * set it if it is not set.
>> +	 */
>> +	br->n_flood_ports--;
>> +	if (p == br->c_flood_port)
>> +		br->c_flood_port = NULL;
>> +
>> +	if (br->n_flood_ports == 1) {
>> +		list_for_each_entry(port, &p->br->port_list, list) {
>> +			if (port->flags & BR_FLOOD) {
>> +				br->c_flood_port = port;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
>> +{
>> +	struct net_bridge *br = p->br;
>> +
>> +	/* We are only interested FLOOD flag */
>> +	if (!(mask & BR_FLOOD))
>> +		return;
>> +
>> +	if (p->flags & BR_FLOOD)
>> +		br_add_flood_port(p, br);
>> +	else
>> +		br_del_flood_port(p, br);
>> +}
>> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
>> index e74b6d53..01382b9 100644
>> --- a/net/bridge/br_netlink.c
>> +++ b/net/bridge/br_netlink.c
>> @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
>>  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  {
>>  	int err;
>> +	unsigned long old_flags = p->flags;
>>  
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
>>  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
>> @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
>>  		if (err)
>>  			return err;
>>  	}
>> +
>> +	br_port_flags_change(p, old_flags ^ p->flags);
>>  	return 0;
>>  }
>>  
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 3ba11bc..26a3987 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -290,6 +290,8 @@ struct net_bridge
>>  	struct timer_list		topology_change_timer;
>>  	struct timer_list		gc_timer;
>>  	struct kobject			*ifobj;
>> +	struct net_bridge_port		*c_flood_port;
>> +	u32				n_flood_ports;
>>  #ifdef CONFIG_BRIDGE_VLAN_FILTERING
>>  	u8				vlan_enabled;
>>  	struct net_port_vlans __rcu	*vlan_info;
>> @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
>>  int br_min_mtu(const struct net_bridge *br);
>>  netdev_features_t br_features_recompute(struct net_bridge *br,
>>  					netdev_features_t features);
>> +void br_port_flags_change(struct net_bridge_port *port,
>> +			  unsigned long mask);
>>  
>>  /* br_input.c */
>>  int br_handle_frame_finish(struct sk_buff *skb);
>> diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
>> index 7f66aa4..9ff6691 100644
>> --- a/net/bridge/br_sysfs_if.c
>> +++ b/net/bridge/br_sysfs_if.c
>> @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\
>>  static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  		     unsigned long mask)
>>  {
>> -	unsigned long flags = p->flags;
>> +	unsigned long flags;
>> +	unsigned long old_flags;
>> +
>> +	old_flags = flags = p->flags;
>>  
>>  	if (v)
>>  		flags |= mask;
>> @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v,
>>  
>>  	if (flags != p->flags) {
>>  		p->flags = flags;
>> +		br_port_flags_change(p, old_flags ^ flags);
>>  		br_ifinfo_notify(RTM_NEWLINK, p);
>>  	}
>>  	return 0;
>>


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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-27  7:53         ` [Bridge] " Michael S. Tsirkin
@ 2014-02-27 13:08           ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27 13:08 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: john.r.fastabend, netdev, bridge, jhs, Stephen Hemminger, shemminger

On 02/27/2014 02:53 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 12:35:08PM -0500, Vlad Yasevich wrote:
>> On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
>>> On Wed, 26 Feb 2014 10:18:21 -0500
>>> Vlad Yasevich <vyasevic@redhat.com> wrote:
>>>
>>>> When a static fdb entry is created, add the mac address to the bridge
>>>> address list.  This list is used to program the proper port's
>>>> address list.
>>>>
>>>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>>>
>>> I don't like this level of bookkeeping it starts to mix
>>> layers between the bridge network interface as entity for talking to the
>>> local host, and forwarding table entries.
>>
>> Actually this is one of the reasons this isn't done through the
>> br->dev->uc.  Forwarding table entries are still per-port.
>>
>>>
>>> Many times static entries are used as alternative to flooding in
>>> environments which don't trust STP.
>>
>> Ok, and how would this be problematic?  If one wants to turn off
>> promisc in this environment, then receive filters needs to be properly
>> programmed.
>>
>>>
>>> Plus, it looks like another major source of bugs.
>>>
>>
>> Any new code is a potential source of issues.  Are you saying
>> No to any new code in bridge?
>>
>> -vlad
> 
> I'm guessing Stephen merely worries about
> multiple data structures that need to stay in
> sync, and asks that you revisit
> using private hw address list in the bridge.
> 
> What's the issue with walking fdb exactly?
> You say
>  1)  I tried using the fdb table itself as main repository, but
>       this caused difficulties in synchronizing this table with
>       the interface filters later on.
> 
> I'm guessing you refer to writing addresses out to ports
> directly when walking the hash being impossible
> since this datastructure uses rcu and spinlocks?
> Fair enough but the entries you care about
> seem to only be modified under RTNL so just
> copy them out to a temporary list.
> This might be less efficient, but will be simpler I think.
> 

There are 2 ways to populate the the ports uc list.
  1) We can use dev_uc_add() directly.  The issue here is
     how to know if a given entry has been written to port.
     I've played with this is and we end completely replicating
     the netdev_hw_addr functionality in fdb to support the Patch7
     (0 flooding ports).
  2) We can use dev_uc_sync() which is what this series does.
     This api needs to keep track of sync counts so that things get
     properly deleted.  For that a temporary list will not work
     since you'd be re-creating it every time.

Now, I think I've come up with a way to remove the private address list
and use bridge->dev->uc, but that requires that we implement fdb-based
filtering for local addresses.

The idea is to implement learning on the bridge device xmit path.  This
will support things like vlans on top of the bridge that change their
mac, or even stack bridge configs that exist in the wild.
My guess, however, is that Stephen would have an even bigger issue with
this. ;)

-vlad

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-27 13:08           ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27 13:08 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: john.r.fastabend, netdev, bridge, jhs, Stephen Hemminger, shemminger

On 02/27/2014 02:53 AM, Michael S. Tsirkin wrote:
> On Wed, Feb 26, 2014 at 12:35:08PM -0500, Vlad Yasevich wrote:
>> On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
>>> On Wed, 26 Feb 2014 10:18:21 -0500
>>> Vlad Yasevich <vyasevic@redhat.com> wrote:
>>>
>>>> When a static fdb entry is created, add the mac address to the bridge
>>>> address list.  This list is used to program the proper port's
>>>> address list.
>>>>
>>>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>>>
>>> I don't like this level of bookkeeping it starts to mix
>>> layers between the bridge network interface as entity for talking to the
>>> local host, and forwarding table entries.
>>
>> Actually this is one of the reasons this isn't done through the
>> br->dev->uc.  Forwarding table entries are still per-port.
>>
>>>
>>> Many times static entries are used as alternative to flooding in
>>> environments which don't trust STP.
>>
>> Ok, and how would this be problematic?  If one wants to turn off
>> promisc in this environment, then receive filters needs to be properly
>> programmed.
>>
>>>
>>> Plus, it looks like another major source of bugs.
>>>
>>
>> Any new code is a potential source of issues.  Are you saying
>> No to any new code in bridge?
>>
>> -vlad
> 
> I'm guessing Stephen merely worries about
> multiple data structures that need to stay in
> sync, and asks that you revisit
> using private hw address list in the bridge.
> 
> What's the issue with walking fdb exactly?
> You say
>  1)  I tried using the fdb table itself as main repository, but
>       this caused difficulties in synchronizing this table with
>       the interface filters later on.
> 
> I'm guessing you refer to writing addresses out to ports
> directly when walking the hash being impossible
> since this datastructure uses rcu and spinlocks?
> Fair enough but the entries you care about
> seem to only be modified under RTNL so just
> copy them out to a temporary list.
> This might be less efficient, but will be simpler I think.
> 

There are 2 ways to populate the the ports uc list.
  1) We can use dev_uc_add() directly.  The issue here is
     how to know if a given entry has been written to port.
     I've played with this is and we end completely replicating
     the netdev_hw_addr functionality in fdb to support the Patch7
     (0 flooding ports).
  2) We can use dev_uc_sync() which is what this series does.
     This api needs to keep track of sync counts so that things get
     properly deleted.  For that a temporary list will not work
     since you'd be re-creating it every time.

Now, I think I've come up with a way to remove the private address list
and use bridge->dev->uc, but that requires that we implement fdb-based
filtering for local addresses.

The idea is to implement learning on the bridge device xmit path.  This
will support things like vlans on top of the bridge that change their
mac, or even stack bridge configs that exist in the wild.
My guess, however, is that Stephen would have an even bigger issue with
this. ;)

-vlad

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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-27 12:06     ` [Bridge] " Toshiaki Makita
@ 2014-02-27 13:17       ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27 13:17 UTC (permalink / raw)
  To: Toshiaki Makita, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
> (2014/02/27 0:18), Vlad Yasevich wrote:
>> If the user configures vlan interfaces on top of the bridge and the bridge
>> doesn't have vlan filtering enabled, we have to place all the ports in
>> promsic mode so that we can correctly receive tagged frames.
>> When vlan filtering is enabled, the vlan configuration will be provided
>> via filtering interface.
>> When the vlan filtering is toggled, we also have mange promiscuity.
> 
> If we disable vlan_filtering and no vlan interface is configured on the
> bridge, we cannot forward any tagged traffic?

We can't receive tagged traffic, so we turn promisc on.

> If we want to forward frames from one port to another port (not from/to
> bridge device), we have to add vlan interface or set promisc mode, right?
> 

Hm..  Good point.  This isn't enough to address the scenario that Patch7
tries to solve.  I'll need to think about that.  This is partially why
I split functionality in Patch7 out.  It made things more difficult.

-vlad

> Sorry if I misunderstood it. I'm not sure..
> 
> Thanks,
> Toshiaki Makita
> 
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_device.c  | 14 ++++++++++++++
>>  net/bridge/br_if.c      | 17 +++++++++++++----
>>  net/bridge/br_private.h |  9 +++++++++
>>  net/bridge/br_vlan.c    |  1 +
>>  4 files changed, 37 insertions(+), 4 deletions(-)
>>
>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>> index 0af9d6c..967abb3 100644
>> --- a/net/bridge/br_device.c
>> +++ b/net/bridge/br_device.c
>> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>>  
>>  #endif
>>  
>> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>>  
>>  {
>> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>>  	.ndo_change_mtu		 = br_change_mtu,
>>  	.ndo_do_ioctl		 = br_dev_ioctl,
>> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
>> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>>  #ifdef CONFIG_NET_POLL_CONTROLLER
>>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 7e92bd0..55e4e28 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>>  void br_manage_promisc(struct net_bridge *br)
>>  {
>>  	struct net_bridge_port *p;
>> +	int set_all = false;
>> +
>> +	if (br->dev->flags & IFF_PROMISC)
>> +		set_all = true;
>> +
>> +	/* If vlan filtering is disabled and there are any VLANs
>> +	 * configured on top of the bridge, set promisc on all
>> +	 * ports.
>> +	 */
>> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
>> +		set_all = true;
>>  
>>  	list_for_each_entry(p, &br->port_list, list) {
>> -		if (br->dev->flags & IFF_PROMISC) {
>> -			/* PROMISC flag has been turned on for the bridge
>> -			 * itself.  Turn on promisc on all ports.
>> -			 */
>> +		if (set_all) {
>> +			/* Set all the ports to promisc mode.  */
>>  			br_port_set_promisc(p);
>>  
>>  		} else {
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 4042f86..87dcc09 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  	return v->pvid ?: VLAN_N_VID;
>>  }
>>  
>> +static inline int br_vlan_enabled(struct net_bridge *br)
>> +{
>> +	return br->vlan_enabled;
>> +}
>>  #else
>>  static inline bool br_allowed_ingress(struct net_bridge *br,
>>  				      struct net_port_vlans *v,
>> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  {
>>  	return VLAN_N_VID;	/* Returns invalid vid */
>>  }
>> +
>> +static inline int br_vlan_enabled(struct net_bridge *br);
>> +{
>> +	return 0;
>> +}
>>  #endif
>>  
>>  /* br_netfilter.c */
>> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
>> index 8249ca7..eddc2f6 100644
>> --- a/net/bridge/br_vlan.c
>> +++ b/net/bridge/br_vlan.c
>> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>>  		goto unlock;
>>  
>>  	br->vlan_enabled = val;
>> +	br_manage_promisc(br);
>>  
>>  unlock:
>>  	rtnl_unlock();
>>

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-02-27 13:17       ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-27 13:17 UTC (permalink / raw)
  To: Toshiaki Makita, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
> (2014/02/27 0:18), Vlad Yasevich wrote:
>> If the user configures vlan interfaces on top of the bridge and the bridge
>> doesn't have vlan filtering enabled, we have to place all the ports in
>> promsic mode so that we can correctly receive tagged frames.
>> When vlan filtering is enabled, the vlan configuration will be provided
>> via filtering interface.
>> When the vlan filtering is toggled, we also have mange promiscuity.
> 
> If we disable vlan_filtering and no vlan interface is configured on the
> bridge, we cannot forward any tagged traffic?

We can't receive tagged traffic, so we turn promisc on.

> If we want to forward frames from one port to another port (not from/to
> bridge device), we have to add vlan interface or set promisc mode, right?
> 

Hm..  Good point.  This isn't enough to address the scenario that Patch7
tries to solve.  I'll need to think about that.  This is partially why
I split functionality in Patch7 out.  It made things more difficult.

-vlad

> Sorry if I misunderstood it. I'm not sure..
> 
> Thanks,
> Toshiaki Makita
> 
>>
>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>  net/bridge/br_device.c  | 14 ++++++++++++++
>>  net/bridge/br_if.c      | 17 +++++++++++++----
>>  net/bridge/br_private.h |  9 +++++++++
>>  net/bridge/br_vlan.c    |  1 +
>>  4 files changed, 37 insertions(+), 4 deletions(-)
>>
>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>> index 0af9d6c..967abb3 100644
>> --- a/net/bridge/br_device.c
>> +++ b/net/bridge/br_device.c
>> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>>  
>>  #endif
>>  
>> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>> +{
>> +	br_manage_promisc(netdev_priv(br_dev));
>> +	return 0;
>> +}
>> +
>>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>>  
>>  {
>> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>>  	.ndo_change_mtu		 = br_change_mtu,
>>  	.ndo_do_ioctl		 = br_dev_ioctl,
>> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
>> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>>  #ifdef CONFIG_NET_POLL_CONTROLLER
>>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>> index 7e92bd0..55e4e28 100644
>> --- a/net/bridge/br_if.c
>> +++ b/net/bridge/br_if.c
>> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>>  void br_manage_promisc(struct net_bridge *br)
>>  {
>>  	struct net_bridge_port *p;
>> +	int set_all = false;
>> +
>> +	if (br->dev->flags & IFF_PROMISC)
>> +		set_all = true;
>> +
>> +	/* If vlan filtering is disabled and there are any VLANs
>> +	 * configured on top of the bridge, set promisc on all
>> +	 * ports.
>> +	 */
>> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
>> +		set_all = true;
>>  
>>  	list_for_each_entry(p, &br->port_list, list) {
>> -		if (br->dev->flags & IFF_PROMISC) {
>> -			/* PROMISC flag has been turned on for the bridge
>> -			 * itself.  Turn on promisc on all ports.
>> -			 */
>> +		if (set_all) {
>> +			/* Set all the ports to promisc mode.  */
>>  			br_port_set_promisc(p);
>>  
>>  		} else {
>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> index 4042f86..87dcc09 100644
>> --- a/net/bridge/br_private.h
>> +++ b/net/bridge/br_private.h
>> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  	return v->pvid ?: VLAN_N_VID;
>>  }
>>  
>> +static inline int br_vlan_enabled(struct net_bridge *br)
>> +{
>> +	return br->vlan_enabled;
>> +}
>>  #else
>>  static inline bool br_allowed_ingress(struct net_bridge *br,
>>  				      struct net_port_vlans *v,
>> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>  {
>>  	return VLAN_N_VID;	/* Returns invalid vid */
>>  }
>> +
>> +static inline int br_vlan_enabled(struct net_bridge *br);
>> +{
>> +	return 0;
>> +}
>>  #endif
>>  
>>  /* br_netfilter.c */
>> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
>> index 8249ca7..eddc2f6 100644
>> --- a/net/bridge/br_vlan.c
>> +++ b/net/bridge/br_vlan.c
>> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>>  		goto unlock;
>>  
>>  	br->vlan_enabled = val;
>> +	br_manage_promisc(br);
>>  
>>  unlock:
>>  	rtnl_unlock();
>>


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

* Re: [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
  2014-02-27 13:08           ` [Bridge] " Vlad Yasevich
@ 2014-02-27 13:38             ` Michael S. Tsirkin
  -1 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27 13:38 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: john.r.fastabend, netdev, bridge, jhs, Stephen Hemminger, shemminger

On Thu, Feb 27, 2014 at 08:08:05AM -0500, Vlad Yasevich wrote:
> On 02/27/2014 02:53 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 12:35:08PM -0500, Vlad Yasevich wrote:
> >> On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
> >>> On Wed, 26 Feb 2014 10:18:21 -0500
> >>> Vlad Yasevich <vyasevic@redhat.com> wrote:
> >>>
> >>>> When a static fdb entry is created, add the mac address to the bridge
> >>>> address list.  This list is used to program the proper port's
> >>>> address list.
> >>>>
> >>>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> >>>
> >>> I don't like this level of bookkeeping it starts to mix
> >>> layers between the bridge network interface as entity for talking to the
> >>> local host, and forwarding table entries.
> >>
> >> Actually this is one of the reasons this isn't done through the
> >> br->dev->uc.  Forwarding table entries are still per-port.
> >>
> >>>
> >>> Many times static entries are used as alternative to flooding in
> >>> environments which don't trust STP.
> >>
> >> Ok, and how would this be problematic?  If one wants to turn off
> >> promisc in this environment, then receive filters needs to be properly
> >> programmed.
> >>
> >>>
> >>> Plus, it looks like another major source of bugs.
> >>>
> >>
> >> Any new code is a potential source of issues.  Are you saying
> >> No to any new code in bridge?
> >>
> >> -vlad
> > 
> > I'm guessing Stephen merely worries about
> > multiple data structures that need to stay in
> > sync, and asks that you revisit
> > using private hw address list in the bridge.
> > 
> > What's the issue with walking fdb exactly?
> > You say
> >  1)  I tried using the fdb table itself as main repository, but
> >       this caused difficulties in synchronizing this table with
> >       the interface filters later on.
> > 
> > I'm guessing you refer to writing addresses out to ports
> > directly when walking the hash being impossible
> > since this datastructure uses rcu and spinlocks?
> > Fair enough but the entries you care about
> > seem to only be modified under RTNL so just
> > copy them out to a temporary list.
> > This might be less efficient, but will be simpler I think.
> > 
> 
> There are 2 ways to populate the the ports uc list.
>   1) We can use dev_uc_add() directly.  The issue here is
>      how to know if a given entry has been written to port.
>      I've played with this is and we end completely replicating
>      the netdev_hw_addr functionality in fdb to support the Patch7
>      (0 flooding ports).

I think I begin to understand.
But I still think dev_uc_add is the way to go here.

Simply build the list twice, before and after the change.

On a change, first dev_uc_add all
addresses in the new list, then
dev_uc_del all addresses in the old list.



>   2) We can use dev_uc_sync() which is what this series does.
>      This api needs to keep track of sync counts so that things get
>      properly deleted.  For that a temporary list will not work
>      since you'd be re-creating it every time.
> 
> Now, I think I've come up with a way to remove the private address list
> and use bridge->dev->uc, but that requires that we implement fdb-based
> filtering for local addresses.
> 
> The idea is to implement learning on the bridge device xmit path.  This
> will support things like vlans on top of the bridge that change their
> mac, or even stack bridge configs that exist in the wild.
> My guess, however, is that Stephen would have an even bigger issue with
> this. ;)
> 
> -vlad

It sounds like a nifty feature without respect to whether it's the
right thing to do for the non promisc feature.

-- 
MST

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

* Re: [Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
@ 2014-02-27 13:38             ` Michael S. Tsirkin
  0 siblings, 0 replies; 91+ messages in thread
From: Michael S. Tsirkin @ 2014-02-27 13:38 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: john.r.fastabend, netdev, bridge, jhs, Stephen Hemminger, shemminger

On Thu, Feb 27, 2014 at 08:08:05AM -0500, Vlad Yasevich wrote:
> On 02/27/2014 02:53 AM, Michael S. Tsirkin wrote:
> > On Wed, Feb 26, 2014 at 12:35:08PM -0500, Vlad Yasevich wrote:
> >> On 02/26/2014 11:57 AM, Stephen Hemminger wrote:
> >>> On Wed, 26 Feb 2014 10:18:21 -0500
> >>> Vlad Yasevich <vyasevic@redhat.com> wrote:
> >>>
> >>>> When a static fdb entry is created, add the mac address to the bridge
> >>>> address list.  This list is used to program the proper port's
> >>>> address list.
> >>>>
> >>>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> >>>
> >>> I don't like this level of bookkeeping it starts to mix
> >>> layers between the bridge network interface as entity for talking to the
> >>> local host, and forwarding table entries.
> >>
> >> Actually this is one of the reasons this isn't done through the
> >> br->dev->uc.  Forwarding table entries are still per-port.
> >>
> >>>
> >>> Many times static entries are used as alternative to flooding in
> >>> environments which don't trust STP.
> >>
> >> Ok, and how would this be problematic?  If one wants to turn off
> >> promisc in this environment, then receive filters needs to be properly
> >> programmed.
> >>
> >>>
> >>> Plus, it looks like another major source of bugs.
> >>>
> >>
> >> Any new code is a potential source of issues.  Are you saying
> >> No to any new code in bridge?
> >>
> >> -vlad
> > 
> > I'm guessing Stephen merely worries about
> > multiple data structures that need to stay in
> > sync, and asks that you revisit
> > using private hw address list in the bridge.
> > 
> > What's the issue with walking fdb exactly?
> > You say
> >  1)  I tried using the fdb table itself as main repository, but
> >       this caused difficulties in synchronizing this table with
> >       the interface filters later on.
> > 
> > I'm guessing you refer to writing addresses out to ports
> > directly when walking the hash being impossible
> > since this datastructure uses rcu and spinlocks?
> > Fair enough but the entries you care about
> > seem to only be modified under RTNL so just
> > copy them out to a temporary list.
> > This might be less efficient, but will be simpler I think.
> > 
> 
> There are 2 ways to populate the the ports uc list.
>   1) We can use dev_uc_add() directly.  The issue here is
>      how to know if a given entry has been written to port.
>      I've played with this is and we end completely replicating
>      the netdev_hw_addr functionality in fdb to support the Patch7
>      (0 flooding ports).

I think I begin to understand.
But I still think dev_uc_add is the way to go here.

Simply build the list twice, before and after the change.

On a change, first dev_uc_add all
addresses in the new list, then
dev_uc_del all addresses in the old list.



>   2) We can use dev_uc_sync() which is what this series does.
>      This api needs to keep track of sync counts so that things get
>      properly deleted.  For that a temporary list will not work
>      since you'd be re-creating it every time.
> 
> Now, I think I've come up with a way to remove the private address list
> and use bridge->dev->uc, but that requires that we implement fdb-based
> filtering for local addresses.
> 
> The idea is to implement learning on the bridge device xmit path.  This
> will support things like vlans on top of the bridge that change their
> mac, or even stack bridge configs that exist in the wild.
> My guess, however, is that Stephen would have an even bigger issue with
> this. ;)
> 
> -vlad

It sounds like a nifty feature without respect to whether it's the
right thing to do for the non promisc feature.

-- 
MST

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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-27 13:17       ` [Bridge] " Vlad Yasevich
@ 2014-02-28 19:34         ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-28 19:34 UTC (permalink / raw)
  To: Toshiaki Makita, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

On 02/27/2014 08:17 AM, Vlad Yasevich wrote:
> On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
>> (2014/02/27 0:18), Vlad Yasevich wrote:
>>> If the user configures vlan interfaces on top of the bridge and the bridge
>>> doesn't have vlan filtering enabled, we have to place all the ports in
>>> promsic mode so that we can correctly receive tagged frames.
>>> When vlan filtering is enabled, the vlan configuration will be provided
>>> via filtering interface.
>>> When the vlan filtering is toggled, we also have mange promiscuity.
>>
>> If we disable vlan_filtering and no vlan interface is configured on the
>> bridge, we cannot forward any tagged traffic?
> 
> We can't receive tagged traffic, so we turn promisc on.
> 
>> If we want to forward frames from one port to another port (not from/to
>> bridge device), we have to add vlan interface or set promisc mode, right?
>>
> 
> Hm..  Good point.  This isn't enough to address the scenario that Patch7
> tries to solve.  I'll need to think about that.  This is partially why
> I split functionality in Patch7 out.  It made things more difficult.
> 

I now understood what you were referring to above a bit better.
This patch solves just part of the problem.  The other part is what
happens when someone behind the bridge is using vlan tagging without
the bridge being aware of it and expects the bridge to forward such traffic.
So, if we ever want to disable promiscuous mode on the bridge ports, we
either need to depend on lan filtering being configured in the bridge
or have the ability to disable vlan filtering in the driver.

Neither is really a good thing.  I'll need to think about this.

-vlad

> -vlad
> 
>> Sorry if I misunderstood it. I'm not sure..
>>
>> Thanks,
>> Toshiaki Makita
>>
>>>
>>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>>> ---
>>>  net/bridge/br_device.c  | 14 ++++++++++++++
>>>  net/bridge/br_if.c      | 17 +++++++++++++----
>>>  net/bridge/br_private.h |  9 +++++++++
>>>  net/bridge/br_vlan.c    |  1 +
>>>  4 files changed, 37 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>>> index 0af9d6c..967abb3 100644
>>> --- a/net/bridge/br_device.c
>>> +++ b/net/bridge/br_device.c
>>> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>>>  
>>>  #endif
>>>  
>>> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>>> +{
>>> +	br_manage_promisc(netdev_priv(br_dev));
>>> +	return 0;
>>> +}
>>> +
>>> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>>> +{
>>> +	br_manage_promisc(netdev_priv(br_dev));
>>> +	return 0;
>>> +}
>>> +
>>>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>>>  
>>>  {
>>> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>>>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>>>  	.ndo_change_mtu		 = br_change_mtu,
>>>  	.ndo_do_ioctl		 = br_dev_ioctl,
>>> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
>>> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>>>  #ifdef CONFIG_NET_POLL_CONTROLLER
>>>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>>>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
>>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>>> index 7e92bd0..55e4e28 100644
>>> --- a/net/bridge/br_if.c
>>> +++ b/net/bridge/br_if.c
>>> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>>>  void br_manage_promisc(struct net_bridge *br)
>>>  {
>>>  	struct net_bridge_port *p;
>>> +	int set_all = false;
>>> +
>>> +	if (br->dev->flags & IFF_PROMISC)
>>> +		set_all = true;
>>> +
>>> +	/* If vlan filtering is disabled and there are any VLANs
>>> +	 * configured on top of the bridge, set promisc on all
>>> +	 * ports.
>>> +	 */
>>> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
>>> +		set_all = true;
>>>  
>>>  	list_for_each_entry(p, &br->port_list, list) {
>>> -		if (br->dev->flags & IFF_PROMISC) {
>>> -			/* PROMISC flag has been turned on for the bridge
>>> -			 * itself.  Turn on promisc on all ports.
>>> -			 */
>>> +		if (set_all) {
>>> +			/* Set all the ports to promisc mode.  */
>>>  			br_port_set_promisc(p);
>>>  
>>>  		} else {
>>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>>> index 4042f86..87dcc09 100644
>>> --- a/net/bridge/br_private.h
>>> +++ b/net/bridge/br_private.h
>>> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>>  	return v->pvid ?: VLAN_N_VID;
>>>  }
>>>  
>>> +static inline int br_vlan_enabled(struct net_bridge *br)
>>> +{
>>> +	return br->vlan_enabled;
>>> +}
>>>  #else
>>>  static inline bool br_allowed_ingress(struct net_bridge *br,
>>>  				      struct net_port_vlans *v,
>>> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>>  {
>>>  	return VLAN_N_VID;	/* Returns invalid vid */
>>>  }
>>> +
>>> +static inline int br_vlan_enabled(struct net_bridge *br);
>>> +{
>>> +	return 0;
>>> +}
>>>  #endif
>>>  
>>>  /* br_netfilter.c */
>>> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
>>> index 8249ca7..eddc2f6 100644
>>> --- a/net/bridge/br_vlan.c
>>> +++ b/net/bridge/br_vlan.c
>>> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>>>  		goto unlock;
>>>  
>>>  	br->vlan_enabled = val;
>>> +	br_manage_promisc(br);
>>>  
>>>  unlock:
>>>  	rtnl_unlock();
>>>
> 

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-02-28 19:34         ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-02-28 19:34 UTC (permalink / raw)
  To: Toshiaki Makita, netdev; +Cc: john.r.fastabend, shemminger, bridge, jhs, mst

On 02/27/2014 08:17 AM, Vlad Yasevich wrote:
> On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
>> (2014/02/27 0:18), Vlad Yasevich wrote:
>>> If the user configures vlan interfaces on top of the bridge and the bridge
>>> doesn't have vlan filtering enabled, we have to place all the ports in
>>> promsic mode so that we can correctly receive tagged frames.
>>> When vlan filtering is enabled, the vlan configuration will be provided
>>> via filtering interface.
>>> When the vlan filtering is toggled, we also have mange promiscuity.
>>
>> If we disable vlan_filtering and no vlan interface is configured on the
>> bridge, we cannot forward any tagged traffic?
> 
> We can't receive tagged traffic, so we turn promisc on.
> 
>> If we want to forward frames from one port to another port (not from/to
>> bridge device), we have to add vlan interface or set promisc mode, right?
>>
> 
> Hm..  Good point.  This isn't enough to address the scenario that Patch7
> tries to solve.  I'll need to think about that.  This is partially why
> I split functionality in Patch7 out.  It made things more difficult.
> 

I now understood what you were referring to above a bit better.
This patch solves just part of the problem.  The other part is what
happens when someone behind the bridge is using vlan tagging without
the bridge being aware of it and expects the bridge to forward such traffic.
So, if we ever want to disable promiscuous mode on the bridge ports, we
either need to depend on lan filtering being configured in the bridge
or have the ability to disable vlan filtering in the driver.

Neither is really a good thing.  I'll need to think about this.

-vlad

> -vlad
> 
>> Sorry if I misunderstood it. I'm not sure..
>>
>> Thanks,
>> Toshiaki Makita
>>
>>>
>>> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>>> ---
>>>  net/bridge/br_device.c  | 14 ++++++++++++++
>>>  net/bridge/br_if.c      | 17 +++++++++++++----
>>>  net/bridge/br_private.h |  9 +++++++++
>>>  net/bridge/br_vlan.c    |  1 +
>>>  4 files changed, 37 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
>>> index 0af9d6c..967abb3 100644
>>> --- a/net/bridge/br_device.c
>>> +++ b/net/bridge/br_device.c
>>> @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p)
>>>  
>>>  #endif
>>>  
>>> +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>>> +{
>>> +	br_manage_promisc(netdev_priv(br_dev));
>>> +	return 0;
>>> +}
>>> +
>>> +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid)
>>> +{
>>> +	br_manage_promisc(netdev_priv(br_dev));
>>> +	return 0;
>>> +}
>>> +
>>>  static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
>>>  
>>>  {
>>> @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = {
>>>  	.ndo_change_rx_flags	 = br_dev_change_rx_flags,
>>>  	.ndo_change_mtu		 = br_change_mtu,
>>>  	.ndo_do_ioctl		 = br_dev_ioctl,
>>> +	.ndo_vlan_rx_add_vid	 = br_dev_rx_add_vid,
>>> +	.ndo_vlan_rx_kill_vid	 = br_dev_rx_kill_vid,
>>>  #ifdef CONFIG_NET_POLL_CONTROLLER
>>>  	.ndo_netpoll_setup	 = br_netpoll_setup,
>>>  	.ndo_netpoll_cleanup	 = br_netpoll_cleanup,
>>> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
>>> index 7e92bd0..55e4e28 100644
>>> --- a/net/bridge/br_if.c
>>> +++ b/net/bridge/br_if.c
>>> @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
>>>  void br_manage_promisc(struct net_bridge *br)
>>>  {
>>>  	struct net_bridge_port *p;
>>> +	int set_all = false;
>>> +
>>> +	if (br->dev->flags & IFF_PROMISC)
>>> +		set_all = true;
>>> +
>>> +	/* If vlan filtering is disabled and there are any VLANs
>>> +	 * configured on top of the bridge, set promisc on all
>>> +	 * ports.
>>> +	 */
>>> +	if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev))
>>> +		set_all = true;
>>>  
>>>  	list_for_each_entry(p, &br->port_list, list) {
>>> -		if (br->dev->flags & IFF_PROMISC) {
>>> -			/* PROMISC flag has been turned on for the bridge
>>> -			 * itself.  Turn on promisc on all ports.
>>> -			 */
>>> +		if (set_all) {
>>> +			/* Set all the ports to promisc mode.  */
>>>  			br_port_set_promisc(p);
>>>  
>>>  		} else {
>>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>>> index 4042f86..87dcc09 100644
>>> --- a/net/bridge/br_private.h
>>> +++ b/net/bridge/br_private.h
>>> @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>>  	return v->pvid ?: VLAN_N_VID;
>>>  }
>>>  
>>> +static inline int br_vlan_enabled(struct net_bridge *br)
>>> +{
>>> +	return br->vlan_enabled;
>>> +}
>>>  #else
>>>  static inline bool br_allowed_ingress(struct net_bridge *br,
>>>  				      struct net_port_vlans *v,
>>> @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
>>>  {
>>>  	return VLAN_N_VID;	/* Returns invalid vid */
>>>  }
>>> +
>>> +static inline int br_vlan_enabled(struct net_bridge *br);
>>> +{
>>> +	return 0;
>>> +}
>>>  #endif
>>>  
>>>  /* br_netfilter.c */
>>> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
>>> index 8249ca7..eddc2f6 100644
>>> --- a/net/bridge/br_vlan.c
>>> +++ b/net/bridge/br_vlan.c
>>> @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
>>>  		goto unlock;
>>>  
>>>  	br->vlan_enabled = val;
>>> +	br_manage_promisc(br);
>>>  
>>>  unlock:
>>>  	rtnl_unlock();
>>>
> 


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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-02-28 19:34         ` [Bridge] " Vlad Yasevich
@ 2014-03-01 14:57           ` Toshiaki Makita
  -1 siblings, 0 replies; 91+ messages in thread
From: Toshiaki Makita @ 2014-03-01 14:57 UTC (permalink / raw)
  To: vyasevic
  Cc: Toshiaki Makita, netdev, bridge, shemminger, mst, jhs, john.r.fastabend

On Fri, 2014-02-28 at 14:34 -0500, Vlad Yasevich wrote:
> On 02/27/2014 08:17 AM, Vlad Yasevich wrote:
> > On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
> >> (2014/02/27 0:18), Vlad Yasevich wrote:
> >>> If the user configures vlan interfaces on top of the bridge and the bridge
> >>> doesn't have vlan filtering enabled, we have to place all the ports in
> >>> promsic mode so that we can correctly receive tagged frames.
> >>> When vlan filtering is enabled, the vlan configuration will be provided
> >>> via filtering interface.
> >>> When the vlan filtering is toggled, we also have mange promiscuity.
> >>
> >> If we disable vlan_filtering and no vlan interface is configured on the
> >> bridge, we cannot forward any tagged traffic?
> > 
> > We can't receive tagged traffic, so we turn promisc on.
> > 
> >> If we want to forward frames from one port to another port (not from/to
> >> bridge device), we have to add vlan interface or set promisc mode, right?
> >>
> > 
> > Hm..  Good point.  This isn't enough to address the scenario that Patch7
> > tries to solve.  I'll need to think about that.  This is partially why
> > I split functionality in Patch7 out.  It made things more difficult.
> > 
> 
> I now understood what you were referring to above a bit better.
> This patch solves just part of the problem.  The other part is what
> happens when someone behind the bridge is using vlan tagging without
> the bridge being aware of it and expects the bridge to forward such traffic.
> So, if we ever want to disable promiscuous mode on the bridge ports, we
> either need to depend on lan filtering being configured in the bridge
> or have the ability to disable vlan filtering in the driver.
> 
> Neither is really a good thing.  I'll need to think about this.

Yes, that is what I was worried about.
As a bridge has no way to know which vid will be used in incoming
frame's vlan tag, we maybe have to call vlan_vid_add() for all vids when
we disable promiscuous on a port?  If we had an API to simply disable
vlan filtering of a NIC, it could be better...

Thanks,
Toshiaki Makita

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-03-01 14:57           ` Toshiaki Makita
  0 siblings, 0 replies; 91+ messages in thread
From: Toshiaki Makita @ 2014-03-01 14:57 UTC (permalink / raw)
  To: vyasevic; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On Fri, 2014-02-28 at 14:34 -0500, Vlad Yasevich wrote:
> On 02/27/2014 08:17 AM, Vlad Yasevich wrote:
> > On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
> >> (2014/02/27 0:18), Vlad Yasevich wrote:
> >>> If the user configures vlan interfaces on top of the bridge and the bridge
> >>> doesn't have vlan filtering enabled, we have to place all the ports in
> >>> promsic mode so that we can correctly receive tagged frames.
> >>> When vlan filtering is enabled, the vlan configuration will be provided
> >>> via filtering interface.
> >>> When the vlan filtering is toggled, we also have mange promiscuity.
> >>
> >> If we disable vlan_filtering and no vlan interface is configured on the
> >> bridge, we cannot forward any tagged traffic?
> > 
> > We can't receive tagged traffic, so we turn promisc on.
> > 
> >> If we want to forward frames from one port to another port (not from/to
> >> bridge device), we have to add vlan interface or set promisc mode, right?
> >>
> > 
> > Hm..  Good point.  This isn't enough to address the scenario that Patch7
> > tries to solve.  I'll need to think about that.  This is partially why
> > I split functionality in Patch7 out.  It made things more difficult.
> > 
> 
> I now understood what you were referring to above a bit better.
> This patch solves just part of the problem.  The other part is what
> happens when someone behind the bridge is using vlan tagging without
> the bridge being aware of it and expects the bridge to forward such traffic.
> So, if we ever want to disable promiscuous mode on the bridge ports, we
> either need to depend on lan filtering being configured in the bridge
> or have the ability to disable vlan filtering in the driver.
> 
> Neither is really a good thing.  I'll need to think about this.

Yes, that is what I was worried about.
As a bridge has no way to know which vid will be used in incoming
frame's vlan tag, we maybe have to call vlan_vid_add() for all vids when
we disable promiscuous on a port?  If we had an API to simply disable
vlan filtering of a NIC, it could be better...

Thanks,
Toshiaki Makita


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

* Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
  2014-03-01 14:57           ` [Bridge] " Toshiaki Makita
@ 2014-03-03 12:12             ` Vlad Yasevich
  -1 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-03-03 12:12 UTC (permalink / raw)
  To: Toshiaki Makita; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On 03/01/2014 09:57 AM, Toshiaki Makita wrote:
> On Fri, 2014-02-28 at 14:34 -0500, Vlad Yasevich wrote:
>> On 02/27/2014 08:17 AM, Vlad Yasevich wrote:
>>> On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
>>>> (2014/02/27 0:18), Vlad Yasevich wrote:
>>>>> If the user configures vlan interfaces on top of the bridge and the bridge
>>>>> doesn't have vlan filtering enabled, we have to place all the ports in
>>>>> promsic mode so that we can correctly receive tagged frames.
>>>>> When vlan filtering is enabled, the vlan configuration will be provided
>>>>> via filtering interface.
>>>>> When the vlan filtering is toggled, we also have mange promiscuity.
>>>>
>>>> If we disable vlan_filtering and no vlan interface is configured on the
>>>> bridge, we cannot forward any tagged traffic?
>>>
>>> We can't receive tagged traffic, so we turn promisc on.
>>>
>>>> If we want to forward frames from one port to another port (not from/to
>>>> bridge device), we have to add vlan interface or set promisc mode, right?
>>>>
>>>
>>> Hm..  Good point.  This isn't enough to address the scenario that Patch7
>>> tries to solve.  I'll need to think about that.  This is partially why
>>> I split functionality in Patch7 out.  It made things more difficult.
>>>
>>
>> I now understood what you were referring to above a bit better.
>> This patch solves just part of the problem.  The other part is what
>> happens when someone behind the bridge is using vlan tagging without
>> the bridge being aware of it and expects the bridge to forward such traffic.
>> So, if we ever want to disable promiscuous mode on the bridge ports, we
>> either need to depend on lan filtering being configured in the bridge
>> or have the ability to disable vlan filtering in the driver.
>>
>> Neither is really a good thing.  I'll need to think about this.
> 
> Yes, that is what I was worried about.
> As a bridge has no way to know which vid will be used in incoming
> frame's vlan tag, we maybe have to call vlan_vid_add() for all vids when
> we disable promiscuous on a port?  If we had an API to simply disable
> vlan filtering of a NIC, it could be better...

That's what I am looking at now.  Some nics appear to handle this better
then others.

-vlad

> 
> Thanks,
> Toshiaki Makita
> 

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

* Re: [Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
@ 2014-03-03 12:12             ` Vlad Yasevich
  0 siblings, 0 replies; 91+ messages in thread
From: Vlad Yasevich @ 2014-03-03 12:12 UTC (permalink / raw)
  To: Toshiaki Makita; +Cc: mst, netdev, bridge, jhs, john.r.fastabend, shemminger

On 03/01/2014 09:57 AM, Toshiaki Makita wrote:
> On Fri, 2014-02-28 at 14:34 -0500, Vlad Yasevich wrote:
>> On 02/27/2014 08:17 AM, Vlad Yasevich wrote:
>>> On 02/27/2014 07:06 AM, Toshiaki Makita wrote:
>>>> (2014/02/27 0:18), Vlad Yasevich wrote:
>>>>> If the user configures vlan interfaces on top of the bridge and the bridge
>>>>> doesn't have vlan filtering enabled, we have to place all the ports in
>>>>> promsic mode so that we can correctly receive tagged frames.
>>>>> When vlan filtering is enabled, the vlan configuration will be provided
>>>>> via filtering interface.
>>>>> When the vlan filtering is toggled, we also have mange promiscuity.
>>>>
>>>> If we disable vlan_filtering and no vlan interface is configured on the
>>>> bridge, we cannot forward any tagged traffic?
>>>
>>> We can't receive tagged traffic, so we turn promisc on.
>>>
>>>> If we want to forward frames from one port to another port (not from/to
>>>> bridge device), we have to add vlan interface or set promisc mode, right?
>>>>
>>>
>>> Hm..  Good point.  This isn't enough to address the scenario that Patch7
>>> tries to solve.  I'll need to think about that.  This is partially why
>>> I split functionality in Patch7 out.  It made things more difficult.
>>>
>>
>> I now understood what you were referring to above a bit better.
>> This patch solves just part of the problem.  The other part is what
>> happens when someone behind the bridge is using vlan tagging without
>> the bridge being aware of it and expects the bridge to forward such traffic.
>> So, if we ever want to disable promiscuous mode on the bridge ports, we
>> either need to depend on lan filtering being configured in the bridge
>> or have the ability to disable vlan filtering in the driver.
>>
>> Neither is really a good thing.  I'll need to think about this.
> 
> Yes, that is what I was worried about.
> As a bridge has no way to know which vid will be used in incoming
> frame's vlan tag, we maybe have to call vlan_vid_add() for all vids when
> we disable promiscuous on a port?  If we had an API to simply disable
> vlan filtering of a NIC, it could be better...

That's what I am looking at now.  Some nics appear to handle this better
then others.

-vlad

> 
> Thanks,
> Toshiaki Makita
> 


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

end of thread, other threads:[~2014-03-03 12:12 UTC | newest]

Thread overview: 91+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-26 15:18 [PATCH RFC 0/7] Non-promisc bidge ports support Vlad Yasevich
2014-02-26 15:18 ` [Bridge] " Vlad Yasevich
2014-02-26 15:18 ` [PATCH 1/7] bridge: Turn flag change macro into a function Vlad Yasevich
2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
2014-02-26 15:29   ` Michael S. Tsirkin
2014-02-26 15:29     ` [Bridge] " Michael S. Tsirkin
2014-02-26 15:36     ` Vlad Yasevich
2014-02-26 15:36       ` [Bridge] " Vlad Yasevich
2014-02-26 15:18 ` [PATCH 2/7] bridge: Keep track of ports capable of flooding Vlad Yasevich
2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
2014-02-26 15:41   ` Michael S. Tsirkin
2014-02-26 15:41     ` [Bridge] " Michael S. Tsirkin
2014-02-26 15:41     ` Vlad Yasevich
2014-02-26 15:41       ` [Bridge] " Vlad Yasevich
2014-02-26 15:53       ` Michael S. Tsirkin
2014-02-26 15:53         ` [Bridge] " Michael S. Tsirkin
2014-02-27 11:59   ` Toshiaki Makita
2014-02-27 11:59     ` [Bridge] " Toshiaki Makita
2014-02-27 12:54     ` Vlad Yasevich
2014-02-27 12:54       ` [Bridge] " Vlad Yasevich
2014-02-26 15:18 ` [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list Vlad Yasevich
2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
2014-02-26 15:46   ` Michael S. Tsirkin
2014-02-26 15:46     ` [Bridge] " Michael S. Tsirkin
2014-02-26 15:43     ` Vlad Yasevich
2014-02-26 15:43       ` [Bridge] " Vlad Yasevich
2014-02-26 16:23   ` Michael S. Tsirkin
2014-02-26 16:23     ` [Bridge] " Michael S. Tsirkin
2014-02-26 17:25     ` Vlad Yasevich
2014-02-26 17:25       ` [Bridge] " Vlad Yasevich
2014-02-26 17:33       ` Michael S. Tsirkin
2014-02-26 17:33         ` [Bridge] " Michael S. Tsirkin
2014-02-26 16:57   ` Stephen Hemminger
2014-02-26 16:57     ` [Bridge] " Stephen Hemminger
2014-02-26 17:35     ` Vlad Yasevich
2014-02-26 17:35       ` [Bridge] " Vlad Yasevich
2014-02-27  7:53       ` Michael S. Tsirkin
2014-02-27  7:53         ` [Bridge] " Michael S. Tsirkin
2014-02-27 13:08         ` Vlad Yasevich
2014-02-27 13:08           ` [Bridge] " Vlad Yasevich
2014-02-27 13:38           ` Michael S. Tsirkin
2014-02-27 13:38             ` [Bridge] " Michael S. Tsirkin
2014-02-26 15:18 ` [PATCH 4/7] bridge: Automatically manage port promiscuous mode Vlad Yasevich
2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
2014-02-26 15:51   ` Michael S. Tsirkin
2014-02-26 15:51     ` [Bridge] " Michael S. Tsirkin
2014-02-26 16:02     ` Vlad Yasevich
2014-02-26 16:02       ` [Bridge] " Vlad Yasevich
2014-02-26 16:58   ` Stephen Hemminger
2014-02-26 16:58     ` [Bridge] " Stephen Hemminger
2014-02-26 17:32     ` Michael S. Tsirkin
2014-02-26 17:32       ` [Bridge] " Michael S. Tsirkin
2014-02-26 15:18 ` [PATCH 5/7] bridge: Correctly manage promiscuity when user requested it Vlad Yasevich
2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
2014-02-26 15:18 ` [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge Vlad Yasevich
2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
2014-02-26 16:00   ` Michael S. Tsirkin
2014-02-26 16:00     ` [Bridge] " Michael S. Tsirkin
2014-02-26 16:05     ` Vlad Yasevich
2014-02-26 16:05       ` [Bridge] " Vlad Yasevich
2014-02-26 16:25       ` Michael S. Tsirkin
2014-02-26 16:25         ` [Bridge] " Michael S. Tsirkin
2014-02-27 12:06   ` Toshiaki Makita
2014-02-27 12:06     ` [Bridge] " Toshiaki Makita
2014-02-27 13:17     ` Vlad Yasevich
2014-02-27 13:17       ` [Bridge] " Vlad Yasevich
2014-02-28 19:34       ` Vlad Yasevich
2014-02-28 19:34         ` [Bridge] " Vlad Yasevich
2014-03-01 14:57         ` Toshiaki Makita
2014-03-01 14:57           ` [Bridge] " Toshiaki Makita
2014-03-03 12:12           ` Vlad Yasevich
2014-03-03 12:12             ` [Bridge] " Vlad Yasevich
2014-02-26 15:18 ` [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding Vlad Yasevich
2014-02-26 15:18   ` [Bridge] " Vlad Yasevich
2014-02-26 15:57   ` Michael S. Tsirkin
2014-02-26 15:57     ` [Bridge] " Michael S. Tsirkin
2014-02-27  3:46     ` Vlad Yasevich
2014-02-27  3:46       ` [Bridge] " Vlad Yasevich
2014-02-27  7:29       ` Michael S. Tsirkin
2014-02-27  7:29         ` [Bridge] " Michael S. Tsirkin
2014-02-26 16:01   ` Michael S. Tsirkin
2014-02-26 16:01     ` [Bridge] " Michael S. Tsirkin
2014-02-26 16:34 ` [PATCH RFC 0/7] Non-promisc bidge ports support Michael S. Tsirkin
2014-02-26 16:34   ` [Bridge] " Michael S. Tsirkin
2014-02-26 23:59 ` Jamal Hadi Salim
2014-02-26 23:59   ` [Bridge] " Jamal Hadi Salim
2014-02-27  3:37   ` Vlad Yasevich
2014-02-27  3:37     ` [Bridge] " Vlad Yasevich
2014-02-27  8:54     ` Amidu Sila
2014-02-27  7:20   ` Michael S. Tsirkin
2014-02-27  7:20     ` [Bridge] " Michael S. Tsirkin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.