lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 22 May 2020 00:10:36 +0300
From:   Vladimir Oltean <olteanv@...il.com>
To:     andrew@...n.ch, f.fainelli@...il.com, vivien.didelot@...il.com,
        davem@...emloft.net
Cc:     jiri@...nulli.us, idosch@...sch.org, kuba@...nel.org,
        ivecera@...hat.com, netdev@...r.kernel.org,
        horatiu.vultur@...rochip.com, allan.nielsen@...rochip.com,
        nikolay@...ulusnetworks.com, roopa@...ulusnetworks.com
Subject: [PATCH RFC net-next 13/13] net: dsa: wire up multicast IGMP snooping attribute notification

From: Florian Fainelli <f.fainelli@...il.com>

The bridge can at runtime be configured with or without IGMP snooping
enabled but we were not processing the switchdev attribute that notifies
about that toggle, do this now.

Drivers that support frame parsing up to IGMP/MLD should enable trapping
of those frames towards the CPU, while pure L2 switches should trap the
entire range of 01:00:5E:00:00:01 to 01:00:5E:00:00:FF.

Signed-off-by: Florian Fainelli <f.fainelli@...il.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@....com>
---
 include/net/dsa.h  |  3 +++
 net/dsa/dsa_priv.h | 13 +++++++++++++
 net/dsa/port.c     | 47 +++++++++++++++++++++++++++++++++++++++++++++-
 net/dsa/slave.c    |  3 +++
 net/dsa/switch.c   | 36 +++++++++++++++++++++++++++++++++++
 5 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index c256467f1f4a..3f7c1f56908c 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -205,6 +205,7 @@ struct dsa_port {
 	bool			mc_flood;
 	/* Knobs from bridge */
 	unsigned long		br_flags;
+	bool			mc_disabled;
 	bool			mrouter;
 
 	struct list_head list;
@@ -564,6 +565,8 @@ struct dsa_switch_ops {
 			     const struct switchdev_obj_port_mdb *mdb);
 	int	(*port_mdb_del)(struct dsa_switch *ds, int port,
 				const struct switchdev_obj_port_mdb *mdb);
+	int	(*port_igmp_mld_snoop)(struct dsa_switch *ds, int port,
+				       bool enable);
 	/*
 	 * RXNFC
 	 */
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 91cbaefc56b3..0761f2fff994 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -24,6 +24,7 @@ enum {
 	DSA_NOTIFIER_VLAN_ADD,
 	DSA_NOTIFIER_VLAN_DEL,
 	DSA_NOTIFIER_MTU,
+	DSA_NOTIFIER_MC_DISABLED,
 };
 
 /* DSA_NOTIFIER_AGEING_TIME */
@@ -72,6 +73,14 @@ struct dsa_notifier_mtu_info {
 	int mtu;
 };
 
+/* DSA_NOTIFIER_MC_DISABLED */
+struct dsa_notifier_mc_disabled_info {
+	int tree_index;
+	int sw_index;
+	struct net_device *br;
+	bool mc_disabled;
+};
+
 struct dsa_switchdev_event_work {
 	struct dsa_switch *ds;
 	int port;
@@ -150,6 +159,10 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br);
 void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br);
 int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
 			    struct switchdev_trans *trans);
+int dsa_port_multicast_toggle(struct dsa_switch *ds, int port,
+			      bool mc_disabled);
+int dsa_port_mc_disabled(struct dsa_port *dp, bool mc_disabled,
+			 struct switchdev_trans *trans);
 bool dsa_port_skip_vlan_configuration(struct dsa_port *dp);
 int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
 			 struct switchdev_trans *trans);
diff --git a/net/dsa/port.c b/net/dsa/port.c
index b527740d03a8..962f25ee8cf2 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -144,6 +144,7 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
 	};
 	int err;
 
+	dp->cpu_dp->mc_disabled = !br_multicast_enabled(br);
 	dp->cpu_dp->mrouter = br_multicast_router(br);
 
 	/* Here the interface is already bridged. Reflect the current
@@ -175,6 +176,7 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
 	if (err)
 		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
 
+	dp->cpu_dp->mc_disabled = true;
 	dp->cpu_dp->mrouter = false;
 
 	/* Port is leaving the bridge, disable host flooding and enable
@@ -299,7 +301,17 @@ static int dsa_port_update_flooding(struct dsa_port *dp, int uc_flood_count,
 		return 0;
 
 	uc_flood = !!uc_flood_count;
-	mc_flood = dp->mrouter;
+	/* As explained in commit 8ecd4591e761 ("mlxsw: spectrum: Add an option
+	 * to flood mc by mc_router_port"), the decision whether to flood a
+	 * multicast packet to a port depends on 3 flags: mc_disabled,
+	 * mc_router_port, mc_flood.
+	 * If mc_disabled is on, the port will be flooded according to
+	 * mc_flood, otherwise, according to mc_router_port.
+	 */
+	if (dp->mc_disabled)
+		mc_flood = !!mc_flood_count;
+	else
+		mc_flood = dp->mrouter;
 
 	uc_flood_changed = dp->uc_flood ^ uc_flood;
 	mc_flood_changed = dp->mc_flood ^ mc_flood;
@@ -388,6 +400,39 @@ int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
 					dp->mc_flood_count);
 }
 
+int dsa_port_multicast_toggle(struct dsa_switch *ds, int port, bool mc_disabled)
+{
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	int err;
+
+	if (ds->ops->port_igmp_mld_snoop) {
+		err = ds->ops->port_igmp_mld_snoop(ds, port, !mc_disabled);
+		if (err)
+			return err;
+	}
+
+	dp->mc_disabled = mc_disabled;
+
+	return dsa_port_update_flooding(dp, dp->uc_flood_count,
+					dp->mc_flood_count);
+}
+
+int dsa_port_mc_disabled(struct dsa_port *dp, bool mc_disabled,
+			 struct switchdev_trans *trans)
+{
+	struct dsa_notifier_mc_disabled_info info = {
+		.tree_index = dp->ds->dst->index,
+		.sw_index = dp->ds->index,
+		.br = dp->bridge_dev,
+		.mc_disabled = mc_disabled,
+	};
+
+	if (switchdev_trans_ph_prepare(trans))
+		return 0;
+
+	return dsa_broadcast(DSA_NOTIFIER_MC_DISABLED, &info);
+}
+
 int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
 			bool propagate_upstream)
 {
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index c023f1120736..c0929613f1b4 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -475,6 +475,9 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
 		/* The local bridge is a multicast router */
 		ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans);
 		break;
+	case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
+		ret = dsa_port_mc_disabled(dp, attr->u.mc_disabled, trans);
+		break;
 	default:
 		ret = -EOPNOTSUPP;
 		break;
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 86c8dc5c32a0..9d4f8fd9cf10 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -337,6 +337,39 @@ static int dsa_switch_vlan_del(struct dsa_switch *ds,
 	return 0;
 }
 
+static bool
+dsa_switch_mc_disabled_match(struct dsa_switch *ds, int port,
+			     struct dsa_notifier_mc_disabled_info *info)
+{
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct dsa_switch_tree *dst = ds->dst;
+
+	if (dp->bridge_dev == info->br)
+		return true;
+
+	if (dst->index == info->tree_index && ds->index == info->sw_index)
+		return dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port);
+
+	return false;
+}
+
+static int dsa_switch_mc_disabled(struct dsa_switch *ds,
+				  struct dsa_notifier_mc_disabled_info *info)
+{
+	bool mc_disabled = info->mc_disabled;
+	int port, err;
+
+	for (port = 0; port < ds->num_ports; port++) {
+		if (dsa_switch_mc_disabled_match(ds, port, info)) {
+			err = dsa_port_multicast_toggle(ds, port, mc_disabled);
+			if (err)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
 static int dsa_switch_event(struct notifier_block *nb,
 			    unsigned long event, void *info)
 {
@@ -374,6 +407,9 @@ static int dsa_switch_event(struct notifier_block *nb,
 	case DSA_NOTIFIER_MTU:
 		err = dsa_switch_mtu(ds, info);
 		break;
+	case DSA_NOTIFIER_MC_DISABLED:
+		err = dsa_switch_mc_disabled(ds, info);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
-- 
2.25.1

Powered by blists - more mailing lists