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: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 29 Apr 2020 19:19:52 +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,
        netdev@...r.kernel.org, leoyang.li@....com,
        nikolay@...ulusnetworks.com
Subject: [PATCH net-next 4/4] net: dsa: sja1105: implement cross-chip bridging operations

From: Vladimir Oltean <vladimir.oltean@....com>

sja1105 uses dsa_8021q for DSA tagging, a format which is VLAN at heart
and which is compatible with cascading. A complete description of this
tagging format is in net/dsa/tag_8021q.c, but a quick summary is that
each external-facing port tags incoming frames with a unique pvid, and
this special VLAN is transmitted as tagged towards the inside of the
system, and as untagged towards the exterior. The tag encodes the switch
id and the source port index.

This means that cross-chip bridging for dsa_8021q only entails adding
the dsa_8021q pvids of one switch to the RX filter of the other
switches. Everything else falls naturally into place, as long as the
bottom-end of ports (the leaves in the tree) is comprised exclusively of
dsa_8021q-compatible (i.e. sja1105 switches). Otherwise, there would be
a chance that a front-panel switch transmits a packet tagged with a
dsa_8021q header, header which it wouldn't be able to remove, and which
would hence "leak" out.

The only use case I tested (due to lack of board availability) was when
the sja1105 switches are part of disjoint trees (however, this doesn't
change the fact that multiple sja1105 switches still need unique switch
identifiers in such a system). But in principle, even "true" single-tree
setups (with DSA links) should work just as fine, except for a small
change which I can't test: dsa_towards_port should be used instead of
dsa_upstream_port (I made the assumption that the routing port that any
sja1105 should use towards its neighbours is the CPU port. That might
not hold true in other setups).

Signed-off-by: Vladimir Oltean <vladimir.oltean@....com>
---
 drivers/net/dsa/sja1105/sja1105_main.c | 122 +++++++++++++++++++++++++
 1 file changed, 122 insertions(+)

diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index 472f4eb20c49..32a05a2424a3 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -25,6 +25,8 @@
 #include "sja1105_sgmii.h"
 #include "sja1105_tas.h"
 
+static const struct dsa_switch_ops sja1105_switch_ops;
+
 static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len,
 			     unsigned int startup_delay)
 {
@@ -1934,6 +1936,124 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+static int sja1105_crosschip_bridge_member(struct dsa_switch *ds,
+					   int tree_index, int sw_index,
+					   int other_port,
+					   struct net_device *br, bool enabled)
+{
+	struct dsa_switch *other_ds = dsa_switch_find(tree_index, sw_index);
+	struct sja1105_private *priv = ds->priv;
+	struct sja1105_private *other_priv;
+	u16 rx_vid, other_rx_vid;
+	int cpu = -1, other_cpu;
+	int port, rc;
+
+	if (other_ds->ops != &sja1105_switch_ops)
+		return 0;
+
+	other_cpu = dsa_upstream_port(other_ds, other_port);
+	other_priv = other_ds->priv;
+
+	/* Make traffic from all local ports enslaved to @br be received
+	 * by @other_ds. This means that our @rx_vid needs to be installed
+	 * on @other_ds's CPU port and user ports. The user ports should be
+	 * egress-untagged so that the can pop the dsa_8021q VLAN. But the
+	 * @other_cpu can be either egress-tagged or untagged: it doesn't
+	 * matter, since it should never egress a frame having our @rx_vid.
+	 */
+	for (port = 0; port < SJA1105_NUM_PORTS; port++) {
+		if (!dsa_is_user_port(ds, port))
+			continue;
+		if (dsa_to_port(ds, port)->bridge_dev != br)
+			continue;
+
+		cpu = dsa_upstream_port(ds, port);
+
+		rx_vid = dsa_8021q_rx_vid(ds, port);
+
+		/* The VLANs on @other_cpu port should be refcounted, because
+		 * we don't want to remove our @rx_vid just yet when a single
+		 * remote port leaves. But that's tricky to implement, so just
+		 * keep it simple and leave the VLANs installed on the CPU
+		 * ports when leaving. Forwarding will still be denied, as it
+		 * should be, because the @rx_vid has been removed from the
+		 * remote switch's external port.
+		 */
+		if (enabled) {
+			/* @rx_vid of local @ds port @port goes to @other_cpu
+			 * of @other_ds
+			 */
+			rc = sja1105_vlan_apply(other_priv, other_cpu, rx_vid,
+						enabled, false);
+			if (rc < 0)
+				return rc;
+		}
+
+		/* @rx_vid of local @ds port @port goes to @other_port of
+		 * @other_ds
+		 */
+		rc = sja1105_vlan_apply(other_priv, other_port,
+					rx_vid, enabled, true);
+		if (rc < 0)
+			return rc;
+	}
+
+	/* Shorthand way of saying that we had no ports enslaved to @br. Also a
+	 * convenient way of getting a reference to the CPU port via
+	 * dsa_upstream_port(), which needs a valid user port as argument.
+	 */
+	if (cpu == -1)
+		return 0;
+
+	/* Make traffic from the remote @other_ds switch's @other_port be
+	 * accepted by the local @ds. This means that its @rx_vid needs to be
+	 * installed on our local CPU port as well as local ports that are
+	 * members of @br.
+	 */
+	other_rx_vid = dsa_8021q_rx_vid(other_ds, other_port);
+
+	for (port = 0; port < SJA1105_NUM_PORTS; port++) {
+		if (!dsa_is_user_port(ds, port))
+			continue;
+		if (dsa_to_port(ds, port)->bridge_dev != br)
+			continue;
+
+		/* @other_rx_vid of @other_ds port @other_port goes to port
+		 * @port of local @ds.
+		 */
+		rc = sja1105_vlan_apply(priv, port, other_rx_vid, enabled,
+					true);
+		if (rc < 0)
+			return rc;
+	}
+
+	/* The comment above regarding VLAN refcounting applies here too */
+	if (!enabled)
+		return 0;
+
+	/* @other_rx_vid of @other_ds port @other_port goes to port @cpu of
+	 * local @ds
+	 */
+	return sja1105_vlan_apply(priv, cpu, other_rx_vid, enabled, false);
+}
+
+static int sja1105_crosschip_bridge_join(struct dsa_switch *ds,
+					 int tree_index, int sw_index,
+					 int other_port, struct net_device *br)
+{
+	return sja1105_crosschip_bridge_member(ds, tree_index, sw_index,
+					       other_port, br, true);
+}
+
+static void sja1105_crosschip_bridge_leave(struct dsa_switch *ds,
+					   int tree_index, int sw_index,
+					   int other_port,
+					   struct net_device *br)
+{
+	sja1105_crosschip_bridge_member(ds, tree_index, sw_index, other_port,
+					br, false);
+}
+
 /* The programming model for the SJA1105 switch is "all-at-once" via static
  * configuration tables. Some of these can be dynamically modified at runtime,
  * but not the xMII mode parameters table.
@@ -2359,6 +2479,8 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
 	.port_policer_del	= sja1105_port_policer_del,
 	.cls_flower_add		= sja1105_cls_flower_add,
 	.cls_flower_del		= sja1105_cls_flower_del,
+	.crosschip_bridge_join	= sja1105_crosschip_bridge_join,
+	.crosschip_bridge_leave	= sja1105_crosschip_bridge_leave,
 };
 
 static int sja1105_check_device_id(struct sja1105_private *priv)
-- 
2.17.1

Powered by blists - more mailing lists