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  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 22 Feb 2021 14:02:48 +0200
From:   Vladimir Oltean <olteanv@...il.com>
To:     netdev@...r.kernel.org
Cc:     Andrew Lunn <andrew@...n.ch>,
        Florian Fainelli <f.fainelli@...il.com>,
        Vivien Didelot <vivien.didelot@...il.com>,
        DENG Qingfang <dqfext@...il.com>,
        Tobias Waldekranz <tobias@...dekranz.com>,
        George McCollister <george.mccollister@...il.com>,
        Horatiu Vultur <horatiu.vultur@...rochip.com>,
        Kurt Kanzenbach <kurt@...utronix.de>,
        Marek Behun <marek.behun@....cz>
Subject: [RFC PATCH net-next] selftests: net: dsa: add a test for ports matching on notifiers

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

DSA supports cross-chip setups, and this means that when an operation is
performed on a port, the other switches in the fabric are also notified,
to get a chance to implement something similar which might be needed
when sending/receiving a packet to/from that port.

However we find that the DSA notifier match functions are written up
pretty much randomly, so let's add a test that is easy to run even
without cross-chip hardware, in order to better visualize the ports on
which the notifier will match.

[.../selftests/net/dsa] $ make
gcc     test_dsa_notifier_match.c  -o test_dsa_notifier_match
[.../selftests/net/dsa] $ ./test_dsa_notifier_match
Heat map for test notifier emitted on sw2p1:

   sw0p0     sw0p1     sw0p2     sw0p3     sw0p4
[  cpu  ] [  user ] [  user ] [  user ] [  dsa  ]
[   x   ] [       ] [       ] [       ] [   x   ]

   sw1p0     sw1p1     sw1p2     sw1p3     sw1p4
[  user ] [  user ] [  user ] [  user ] [  dsa  ]
[       ] [       ] [       ] [       ] [   x   ]

   sw2p0     sw2p1     sw2p2     sw2p3     sw2p4
[  user ] [  user ] [  user ] [  user ] [  dsa  ]
[       ] [   x   ] [       ] [       ] [       ]

Heat map for test notifier emitted on sw0p0:

   sw0p0     sw0p1     sw0p2     sw0p3     sw0p4
[  cpu  ] [  user ] [  user ] [  user ] [  dsa  ]
[   x   ] [       ] [       ] [       ] [       ]

   sw1p0     sw1p1     sw1p2     sw1p3     sw1p4
[  user ] [  user ] [  user ] [  user ] [  dsa  ]
[       ] [       ] [       ] [       ] [   x   ]

   sw2p0     sw2p1     sw2p2     sw2p3     sw2p4
[  user ] [  user ] [  user ] [  user ] [  dsa  ]
[       ] [       ] [       ] [       ] [   x   ]

Signed-off-by: Vladimir Oltean <vladimir.oltean@....com>
---
 MAINTAINERS                                   |   1 +
 tools/testing/selftests/net/dsa/Makefile      |   6 +
 .../net/dsa/test_dsa_notifier_match.c         | 446 ++++++++++++++++++
 3 files changed, 453 insertions(+)
 create mode 100644 tools/testing/selftests/net/dsa/Makefile
 create mode 100644 tools/testing/selftests/net/dsa/test_dsa_notifier_match.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 986a8eef8633..0b501750065a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12397,6 +12397,7 @@ F:	include/linux/dsa/
 F:	include/linux/platform_data/dsa.h
 F:	include/net/dsa.h
 F:	net/dsa/
+F:	tools/testing/selftests/net/dsa/
 
 NETWORKING [GENERAL]
 M:	"David S. Miller" <davem@...emloft.net>
diff --git a/tools/testing/selftests/net/dsa/Makefile b/tools/testing/selftests/net/dsa/Makefile
new file mode 100644
index 000000000000..650d99bd6dab
--- /dev/null
+++ b/tools/testing/selftests/net/dsa/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TEST_PROGS := test_dsa_notifier_match
+TEST_GEN_FILES := test_dsa_notifier_match
+
+include ../../lib.mk
diff --git a/tools/testing/selftests/net/dsa/test_dsa_notifier_match.c b/tools/testing/selftests/net/dsa/test_dsa_notifier_match.c
new file mode 100644
index 000000000000..8118e97c6294
--- /dev/null
+++ b/tools/testing/selftests/net/dsa/test_dsa_notifier_match.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2008-2009 Marvell Semiconductor
+ * Copyright (c) 2013 Florian Fainelli <florian@...nwrt.org>
+ * Copyright (c) 2016 Andrew Lunn <andrew@...n.ch>
+ * Copyright (c) 2017 Savoir-faire Linux Inc.
+ *	Vivien Didelot <vivien.didelot@...oirfairelinux.com>
+ * Copyright (c) 2021 Vladimir Oltean <vladimir.oltean@....com>
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+
+struct dsa_port {
+	enum {
+		DSA_PORT_TYPE_UNUSED = 0,
+		DSA_PORT_TYPE_CPU,
+		DSA_PORT_TYPE_DSA,
+		DSA_PORT_TYPE_USER,
+	} type;
+
+	struct dsa_switch	*ds;
+	unsigned int		index;
+	struct dsa_port		*cpu_dp;
+
+	STAILQ_ENTRY(dsa_port)	list;
+};
+
+/* TODO: ideally DSA ports would have a single dp->link_dp member,
+ * and no dst->rtable nor this struct dsa_link would be needed,
+ * but this would require some more complex tree walking,
+ * so keep it stupid at the moment and list them all.
+ */
+struct dsa_link {
+	struct dsa_port		*dp;
+	struct dsa_port		*link_dp;
+	STAILQ_ENTRY(dsa_link)	list;
+};
+
+struct dsa_switch_tree {
+	/* List of switch ports */
+	STAILQ_HEAD(ports_head, dsa_port) ports;
+
+	/* List of switches (for notifiers) */
+	STAILQ_HEAD(switches_head, dsa_switch) switches;
+
+	/* List of DSA links composing the routing table */
+	STAILQ_HEAD(rtable_head, dsa_link) rtable;
+};
+
+struct dsa_switch {
+	/*
+	 * Parent switch tree, and switch index.
+	 */
+	struct dsa_switch_tree		*dst;
+	unsigned int			index;
+
+	STAILQ_ENTRY(dsa_switch)	list;
+
+	bool				*heat_map;
+
+	size_t				num_ports;
+};
+
+static const char dsa_port_type[][16] = {
+	[DSA_PORT_TYPE_UNUSED]	= "unused ",
+	[DSA_PORT_TYPE_CPU]	= "  cpu  ",
+	[DSA_PORT_TYPE_DSA]	= "  dsa  ",
+	[DSA_PORT_TYPE_USER]	= "  user ",
+};
+
+static struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p)
+{
+	struct dsa_switch_tree *dst = ds->dst;
+	struct dsa_port *dp;
+
+	STAILQ_FOREACH(dp, &dst->ports, list)
+		if (dp->ds == ds && dp->index == p)
+			return dp;
+
+	return NULL;
+}
+
+static bool dsa_is_unused_port(struct dsa_switch *ds, int p)
+{
+	return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED;
+}
+
+static bool dsa_is_cpu_port(struct dsa_switch *ds, int p)
+{
+	return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_CPU;
+}
+
+static bool dsa_is_dsa_port(struct dsa_switch *ds, int p)
+{
+	return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_DSA;
+}
+
+static bool dsa_is_user_port(struct dsa_switch *ds, int p)
+{
+	return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER;
+}
+
+static int dsa_register_switch(struct dsa_switch_tree *dst, int index,
+			       int num_ports)
+{
+	struct dsa_switch *ds;
+	struct dsa_port *dp;
+	int err, port;
+
+	ds = calloc(1, sizeof(*ds));
+	if (!ds)
+		return -ENOMEM;
+
+	ds->heat_map = calloc(num_ports, sizeof(bool));
+	if (!ds->heat_map) {
+		free(ds);
+		return -ENOMEM;
+	}
+
+	ds->num_ports = num_ports;
+	ds->index = index;
+	ds->dst = dst;
+
+	for (port = 0; port < num_ports; port++) {
+		dp = calloc(1, sizeof(*dp));
+		if (!dp) {
+			err = -ENOMEM;
+			goto out_free_ports;
+		}
+
+		dp->ds = ds;
+		dp->index = port;
+		STAILQ_INSERT_TAIL(&dst->ports, dp, list);
+	}
+
+	STAILQ_INSERT_TAIL(&dst->switches, ds, list);
+
+	return 0;
+
+out_free_ports:
+	while ((dp = STAILQ_FIRST(&dst->ports))) {
+		STAILQ_REMOVE_HEAD(&dst->ports, list);
+		free(dp);
+	}
+
+	free(ds);
+	return err;
+}
+
+static void dsa_tree_teardown(struct dsa_switch_tree *dst)
+{
+	struct dsa_switch *ds;
+	struct dsa_port *dp;
+	struct dsa_link *dl;
+
+	while (dl = STAILQ_FIRST(&dst->rtable)) {
+		STAILQ_REMOVE_HEAD(&dst->rtable, list);
+		free(dl);
+	}
+
+	while (dp = STAILQ_FIRST(&dst->ports)) {
+		STAILQ_REMOVE_HEAD(&dst->ports, list);
+		free(dp);
+	}
+
+	while (ds = STAILQ_FIRST(&dst->switches)) {
+		STAILQ_REMOVE_HEAD(&dst->switches, list);
+		free(ds->heat_map);
+		free(ds);
+	}
+}
+
+static struct dsa_port *dsa_tree_find_port_by_index(struct dsa_switch_tree *dst,
+						    int sw_index, int port)
+{
+	struct dsa_port *dp;
+
+	STAILQ_FOREACH(dp, &dst->ports, list)
+		if (dp->ds->index == sw_index && dp->index == port)
+			return dp;
+
+	return NULL;
+}
+
+static int dsa_setup_link(struct dsa_switch_tree *dst, int from_sw_index,
+			  int from_port, int to_sw_index, int to_port)
+{
+	struct dsa_port *dp, *link_dp;
+	struct dsa_link *dl;
+
+	dp = dsa_tree_find_port_by_index(dst, from_sw_index, from_port);
+	if (!dp) {
+		fprintf(stderr, "failed to find sw%dp%d\n", from_sw_index,
+			from_port);
+		return -ENODEV;
+	}
+
+	link_dp = dsa_tree_find_port_by_index(dst, to_sw_index, to_port);
+	if (!link_dp) {
+		fprintf(stderr, "failed to find sw%dp%d\n", to_sw_index,
+			to_port);
+		return -ENODEV;
+	}
+
+	dl = calloc(1, sizeof(*dl));
+	if (!dl)
+		return -ENOMEM;
+
+	STAILQ_INSERT_HEAD(&dst->rtable, dl, list);
+	dp->type = DSA_PORT_TYPE_DSA;
+	link_dp->type = DSA_PORT_TYPE_DSA;
+
+	return 0;
+}
+
+static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst,
+				      int sw_index, int port)
+{
+	struct dsa_port *dp, *cpu_dp;
+
+	cpu_dp = dsa_tree_find_port_by_index(dst, sw_index, port);
+	if (!cpu_dp) {
+		fprintf(stderr, "failed to find sw%dp%d\n", sw_index, port);
+		return -ENODEV;
+	}
+
+	cpu_dp->type = DSA_PORT_TYPE_CPU;
+
+	STAILQ_FOREACH(dp, &dst->ports, list)
+		if (dsa_is_user_port(dp->ds, dp->index) ||
+		    dsa_is_dsa_port(dp->ds, dp->index))
+			dp->cpu_dp = cpu_dp;
+
+	return 0;
+}
+
+static void
+dsa_tree_convert_all_unused_ports_to_user(struct dsa_switch_tree *dst)
+{
+	struct dsa_port *dp;
+
+	STAILQ_FOREACH(dp, &dst->ports, list)
+		if (dsa_is_unused_port(dp->ds, dp->index))
+			dp->type = DSA_PORT_TYPE_USER;
+}
+
+/*  CPU
+ *   |
+ * sw0p0 sw0p1 sw0p2 sw0p3 sw0p4
+ *                           | DSA link
+ * sw1p0 sw1p1 sw1p2 sw1p3 sw1p4
+ *                           | DSA link
+ * sw2p0 sw2p1 sw2p2 sw2p3 sw2p4
+ */
+static int dsa_setup_tree(struct dsa_switch_tree *dst)
+{
+	int err;
+
+	STAILQ_INIT(&dst->ports);
+	STAILQ_INIT(&dst->switches);
+	STAILQ_INIT(&dst->rtable);
+
+	err = dsa_register_switch(dst, 0, 5);
+	if (err)
+		return err;
+
+	err = dsa_register_switch(dst, 1, 5);
+	if (err)
+		return err;
+
+	err = dsa_register_switch(dst, 2, 5);
+	if (err)
+		return err;
+
+	/* sw0p4 to sw1p4 */
+	err = dsa_setup_link(dst, 0, 4, 1, 4);
+	if (err)
+		return err;
+
+	/* sw0p4 to sw2p4 */
+	err = dsa_setup_link(dst, 0, 4, 2, 4);
+	if (err)
+		return err;
+
+	/* sw1p4 to sw0p4 */
+	err = dsa_setup_link(dst, 1, 4, 0, 4);
+	if (err)
+		return err;
+
+	/* sw1p4 to sw2p4 */
+	err = dsa_setup_link(dst, 1, 4, 2, 4);
+	if (err)
+		return err;
+
+	/* sw2p4 to sw0p4 */
+	err = dsa_setup_link(dst, 2, 4, 0, 4);
+	if (err)
+		return err;
+
+	/* sw2p4 to sw1p4 */
+	err = dsa_setup_link(dst, 2, 4, 1, 4);
+	if (err)
+		return err;
+
+	dsa_tree_convert_all_unused_ports_to_user(dst);
+
+	err = dsa_tree_setup_default_cpu(dst, 0, 0);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+enum {
+	DSA_NOTIFIER_TEST,
+};
+
+struct dsa_notifier_test_info {
+	int sw_index;
+	int port;
+};
+
+static int dsa_switch_test_match(struct dsa_switch *ds, int port,
+				 struct dsa_notifier_test_info *info)
+{
+	if (ds->index == info->sw_index)
+		return port == info->port;
+
+	return dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port);
+}
+
+static int dsa_switch_test(struct dsa_switch *ds,
+			   struct dsa_notifier_test_info *info)
+{
+	int port;
+
+	for (port = 0; port < ds->num_ports; port++)
+		if (dsa_switch_test_match(ds, port, info))
+			ds->heat_map[port] = true;
+
+	return 0;
+}
+
+static int dsa_switch_event(struct dsa_switch *ds, unsigned long event,
+			    void *info)
+{
+	int err;
+
+	switch (event) {
+	case DSA_NOTIFIER_TEST:
+		err = dsa_switch_test(ds, info);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v)
+{
+	struct dsa_switch *ds;
+	int err;
+
+	STAILQ_FOREACH(ds, &dst->switches, list) {
+		err = dsa_switch_event(ds, e, v);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
+{
+	return dsa_tree_notify(dp->ds->dst, e, v);
+}
+
+static int dsa_test_notify(struct dsa_switch_tree *dst, int sw_index, int port)
+{
+	struct dsa_notifier_test_info info = {
+		.sw_index = sw_index,
+		.port = port,
+	};
+	struct dsa_switch *ds;
+	struct dsa_port *dp;
+	int err;
+
+	dp = dsa_tree_find_port_by_index(dst, sw_index, port);
+	if (!dp) {
+		fprintf(stderr, "failed to find sw%dp%d\n", sw_index, port);
+		return -ENODEV;
+	}
+
+	err = dsa_port_notify(dp, DSA_NOTIFIER_TEST, &info);
+	if (err)
+		return err;
+
+	printf("Heat map for test notifier emitted on sw%dp%d:\n\n",
+	       sw_index, port);
+
+	STAILQ_FOREACH(ds, &dst->switches, list) {
+		for (port = 0; port < ds->num_ports; port++)
+			printf("   sw%dp%d  ", ds->index, port);
+		printf("\n");
+		for (port = 0; port < ds->num_ports; port++)
+			printf("[%s] ", dsa_port_type[dsa_to_port(ds, port)->type]);
+		printf("\n");
+		for (port = 0; port < ds->num_ports; port++) {
+			if (ds->heat_map[port])
+				printf("[   x   ] ");
+			else
+				printf("[       ] ");
+		}
+		printf("\n\n");
+		memset(ds->heat_map, 0, sizeof(bool) * ds->num_ports);
+	}
+}
+
+int main(void)
+{
+	struct dsa_switch_tree dst = {0};
+	struct dsa_port *dp;
+	int err;
+
+	err = dsa_setup_tree(&dst);
+	if (err)
+		goto out;
+
+	err = dsa_test_notify(&dst, 2, 1);
+	if (err)
+		goto out;
+
+	err = dsa_test_notify(&dst, 0, 0);
+	if (err)
+		goto out;
+
+out:
+	dsa_tree_teardown(&dst);
+
+	return err;
+}
-- 
2.25.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ