Re: [PATCH net-next] selftests/net: mctp: Roundtrip tun tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 3/22/22 1:19 AM, Matt Johnston wrote:
Tests MCTP messages between two tun devices forwarding between
each other.

Tests are:
- Round trip message and reply
- MCTP_NET_ANY and MCTP_ADDR_ANY listeners
- Message fragmentation
- Message key expiry timeout are dropped
- Duplicate replies are dropped

Requires "mctp" userspace utility to configure addresses and routes,
tests will be skipped if it is not available.


Are you adding this test to main Makefile? If so you will need to chane
selftests/Makefile

Signed-off-by: Matt Johnston <matt@xxxxxxxxxxxxxxxxxxxx>
---
  tools/testing/selftests/net/mctp/Makefile    |  11 +
  tools/testing/selftests/net/mctp/config      |   2 +
  tools/testing/selftests/net/mctp/mctp-defs.h |   5 +
  tools/testing/selftests/net/mctp/mctp-tun.c  | 422 +++++++++++++++++++
  4 files changed, 440 insertions(+)
  create mode 100644 tools/testing/selftests/net/mctp/Makefile
  create mode 100644 tools/testing/selftests/net/mctp/config
  create mode 100644 tools/testing/selftests/net/mctp/mctp-defs.h
  create mode 100644 tools/testing/selftests/net/mctp/mctp-tun.c

Add .gitignore for the generated executables.


diff --git a/tools/testing/selftests/net/mctp/Makefile b/tools/testing/selftests/net/mctp/Makefile
new file mode 100644
index 000000000000..baee43d5b048
--- /dev/null
+++ b/tools/testing/selftests/net/mctp/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CFLAGS =  -Wall -Wl,--no-as-needed -O2 -g
+CFLAGS += -I../../../../../usr/include/
+
+TEST_PROGS := mctp-tun

TEST_PROGS are shell scripts. TEST_GEN_PROGS is the correct variable
to use for this test execuatble.

+include ../../lib.mk
+
+mctp-tun: LDLIBS += -lpthread
+
+all: $(TEST_PROGS)
diff --git a/tools/testing/selftests/net/mctp/config b/tools/testing/selftests/net/mctp/config
new file mode 100644
index 000000000000..92c0b8c79ac7
--- /dev/null
+++ b/tools/testing/selftests/net/mctp/config
@@ -0,0 +1,2 @@
+CONFIG_TUN=y
+CONFIG_MCTP=y
diff --git a/tools/testing/selftests/net/mctp/mctp-defs.h b/tools/testing/selftests/net/mctp/mctp-defs.h
new file mode 100644
index 000000000000..fcb1f5250ca9
--- /dev/null
+++ b/tools/testing/selftests/net/mctp/mctp-defs.h
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Protocol numbers are not exposed through uapi */
+#ifndef AF_MCTP
+#define AF_MCTP 45
+#endif
diff --git a/tools/testing/selftests/net/mctp/mctp-tun.c b/tools/testing/selftests/net/mctp/mctp-tun.c
new file mode 100644
index 000000000000..ce289b10d23f
--- /dev/null
+++ b/tools/testing/selftests/net/mctp/mctp-tun.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021,2022 Code Construct
+ */
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/random.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/mctp.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <assert.h>
+
+#include "mctp-defs.h"
+
+#include "../../kselftest_harness.h"
+
+static const int BUF_SIZE = 70000;
+
+static void *tun_start(void *arg);
+static int create_tun(struct __test_metadata *_metadata,
+	const char *name);
+
+FIXTURE(TUNPAIR) {
+	pthread_t pth;
+	int fd[2];
+	volatile bool running;
+	bool skip;
+};
+
+const size_t default_mtu = 70;
+const size_t default_msglen = 4;
+
+FIXTURE_VARIANT(TUNPAIR) {
+	/* some defaults above */
+	size_t msglen;
+	size_t mtu;
+	bool listen_addr_any;
+	bool listen_net_any;
+	bool expire_key;
+	bool duplicate_reply;
+};
+
+FIXTURE_VARIANT_ADD(TUNPAIR, len4) {
+	.listen_addr_any = true,
+	.listen_net_any = false,
+};
+
+FIXTURE_VARIANT_ADD(TUNPAIR, len300) {
+	.msglen = 300,
+	.mtu = 500,
+	.listen_addr_any = true,
+};
+
+/* With fragmentation */
+FIXTURE_VARIANT_ADD(TUNPAIR, len300frag) {
+	.msglen = 300,
+	.listen_addr_any = true,
+};
+
+FIXTURE_VARIANT_ADD(TUNPAIR, net_any) {
+	.listen_addr_any = true,
+	.listen_net_any = true,
+};
+
+FIXTURE_VARIANT_ADD(TUNPAIR, addr_specific) {
+	.listen_net_any = true,
+};
+
+FIXTURE_VARIANT_ADD(TUNPAIR, expire_key) {
+	.expire_key = true,
+};
+
+FIXTURE_VARIANT_ADD(TUNPAIR, duplicate_reply) {
+	.duplicate_reply = true,
+};
+
+FIXTURE_SETUP(TUNPAIR)
+{
+	int rc;
+	char str[1000];
+	int mtu;
+
+	memset(self, 0, sizeof(*self));
+
+	/* Check requirements first */
+	/* TODO: cap_get() instead? */
+	if (getuid() != 0) {
+		self->skip = 1;
+		TH_LOG("Not root, skipping");

Change this message to "Run this test as root. Skipping"

+		return;
+	}
+
+	rc = system("sh -c \"which mctp 2> /dev/null > /dev/null\"");
+	if (rc != 0) {
+		self->skip = 1;
+		TH_LOG("No mctp utility installed, skipping");

Change this message to "Install mctp utility to run this test. Skipping"

+		return;
+	}
+
+	rc = socket(AF_MCTP, SOCK_DGRAM, 0);
+	if (rc == -1 && errno == EAFNOSUPPORT) {
+		self->skip = 1;
+		TH_LOG("No AF_MCTP support, skipping");

Change the message to "AF_MCTP support is required to run this test. Skipping"

I assume this servers as a check for CONFIG_TUN and CONFIG_MCTP?

+		return;
+	}
+	close(rc);
+
+	/* Pair of netdevs */
+	self->fd[0] = create_tun(_metadata, "mctptun1");
+	self->fd[1] = create_tun(_metadata, "mctptun2");
+
+	mtu = variant->mtu ?: default_mtu;
+
+	/* Forward packets between devices */
+	self->running = true;
+	rc = pthread_create(&self->pth, NULL, tun_start, self);
+	ASSERT_EQ(rc, 0);
+
+	/* Set up devices. All in one command to avoid
+	 * slow shell invocations
+	 */
+	rc = snprintf(str, sizeof(str),
+		/* net needs to be set before routes */
+		"mctp link set mctptun1 up net 31 mtu %d && "
+		"mctp link set mctptun2 up net 32 mtu %d && "
+		"mctp addr add 131 dev mctptun1 && "
+		"mctp addr add 132 dev mctptun2 && "
+		"mctp route add 132 via mctptun1 && "
+		"mctp route add 131 via mctptun2 && "
+		"true",
+		mtu, mtu);
+	ASSERT_LT(rc, sizeof(str));
+	rc = system(str);
+	ASSERT_EQ(rc, 0);
+}
+
+FIXTURE_TEARDOWN(TUNPAIR)
+{
+	int rc;
+
+	if (self->skip)
+		return;
+
+	self->running = false;
+	rc = pthread_join(self->pth, NULL);
+	ASSERT_EQ(rc, 0);
+	ASSERT_EQ(close(self->fd[0]), 0);
+	ASSERT_EQ(close(self->fd[1]), 0);
+
+	/* mctp addr/routes go away with devices */
+}
+
+/* Creates a socket, binds.
+ * Always returns success, asserts on failure
+ */
+static int mctp_bind(struct __test_metadata *_metadata,
+	int net, int eid, int type)
+{
+	struct sockaddr_mctp addr = {0};
+	int sd, rc;
+
+	sd = socket(AF_MCTP, SOCK_DGRAM, 0);
+	ASSERT_GE(sd, 0);
+	addr.smctp_family = AF_MCTP;
+	addr.smctp_network = net;
+	addr.smctp_addr.s_addr = eid;
+	addr.smctp_type = type;
+	addr.smctp_tag = MCTP_TAG_OWNER;
+	rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
+	ASSERT_EQ(rc, 0);
+	return sd;
+}
+
+/* Creates a socket, sends on it.
+ * Always returns success, asserts on failure.
+ * Returns the socket sd.
+ */
+static int mctp_send(struct __test_metadata *_metadata,
+	int net, int eid, int type, void *buf, size_t len)
+{
+	struct sockaddr_mctp addr = {0};
+	ssize_t sent;
+	int sd;
+
+	addr.smctp_family = AF_MCTP;
+	addr.smctp_network = net;
+	addr.smctp_addr.s_addr = eid;
+	addr.smctp_type = type;
+	addr.smctp_tag = MCTP_TAG_OWNER;
+	sd = socket(AF_MCTP, SOCK_DGRAM, 0);
+	ASSERT_GE(sd, 0);
+	sent = sendto(sd, buf, len, 0,
+			(struct sockaddr *)&addr, sizeof(addr));
+	ASSERT_EQ(sent, len);
+	return sd;
+}
+
+/* Helper to test key expiry */
+static void test_expire_key(struct __test_metadata *_metadata,
+	int sock_listen, int sd, struct sockaddr_mctp *addr)
+{
+	struct pollfd pf = { .fd = sd, .events = POLLIN };
+	int val1 = 0x11223344;
+	ssize_t len;
+	int rc;
+
+	/* Wait for timeout, > mctp_key_lifetime = 6 secs */
+	TH_LOG("Waiting 7 seconds for key expiry...");
+	sleep(7);
+
+	addr->smctp_tag &= ~MCTP_TAG_OWNER;
+	len = sendto(sock_listen, &val1, sizeof(val1),
+		0, (struct sockaddr *)addr, sizeof(*addr));
+	ASSERT_EQ(len, sizeof(val1));
+
+	/* Wait 100ms */
+	rc = poll(&pf, 1, 100);
+	/* We should time out, packet is never received */
+	ASSERT_EQ(rc, 0);
+}
+
+/* Helper to test duplicate replies */
+static void test_duplicate_reply(struct __test_metadata *_metadata,
+	int sock_listen, int sd, struct sockaddr_mctp *addr)
+{
+	struct pollfd pf = { .fd = sd, .events = POLLIN };
+	int val1 = 0x11223344;
+	ssize_t len;
+	int rc;
+
+	/* Send first reply */
+	addr->smctp_tag &= ~MCTP_TAG_OWNER;
+	len = sendto(sock_listen, &val1, sizeof(val1),
+		0, (struct sockaddr *)addr, sizeof(*addr));
+	ASSERT_EQ(len, sizeof(val1));
+
+	/* Receive reply */
+	len = recvfrom(sd, NULL, 0, MSG_TRUNC, NULL, NULL);
+	EXPECT_EQ(len, sizeof(val1));
+
+	/* Try a second reply */
+	len = sendto(sock_listen, &val1, sizeof(val1),
+		0, (struct sockaddr *)addr, sizeof(*addr));
+	ASSERT_EQ(len, sizeof(val1));
+
+	/* Wait 100ms */
+	rc = poll(&pf, 1, 100);
+	/* We should time out, packet is never received */
+	ASSERT_EQ(rc, 0);
+}
+
+TEST_F(TUNPAIR, roundtrip)
+{
+	const size_t msglen = variant->msglen ?: default_msglen;
+	struct sockaddr_mctp addr = {0};
+	int sock_listen, sd;
+	void *buf1, *buf2;
+	socklen_t addrlen;
+	int net, eid;
+	ssize_t len;
+
+	if (self->skip)
+		SKIP(return, "Skipping");
+
+	buf1 = malloc(msglen);
+	buf2 = malloc(msglen);
+
+	/* Listen on mctptun2 (eid 132 net 32) */
+	if (variant->listen_net_any)
+		net = MCTP_NET_ANY;
+	else
+		net = 32;
+
+	if (variant->listen_addr_any)
+		eid = MCTP_ADDR_ANY;
+	else
+		eid = 132;
+
+	sock_listen = mctp_bind(_metadata, net, eid, 1);
+
+	/* Send mctptun1 -> mctptun2 */
+	getrandom(buf1, msglen, 0);
+	sd = mctp_send(_metadata, 31, 132, 1, buf1, msglen);
+
+	/* Receive it */
+	addrlen = sizeof(addr);
+	len = recvfrom(sock_listen, buf2, msglen, MSG_TRUNC,
+		(struct sockaddr *)&addr, &addrlen);
+	EXPECT_EQ(len, msglen);
+	EXPECT_EQ(memcmp(buf1, buf2, msglen), 0);
+	TH_LOG("addr family %d ", addr.smctp_family);
+
+	if (variant->expire_key) {
+		test_expire_key(_metadata, sock_listen, sd, &addr);
+	} else if (variant->duplicate_reply) {
+		test_duplicate_reply(_metadata, sock_listen, sd, &addr);
+	} else {
+		/* Reply mctptun2 -> mctptun1 */
+		getrandom(buf1, msglen, 0);
+		addr.smctp_tag &= ~MCTP_TAG_OWNER;
+		len = sendto(sock_listen, buf1, msglen,
+			0, (struct sockaddr *)&addr, sizeof(addr));
+		ASSERT_EQ(len, msglen);
+
+		/* Receive reply */
+		addrlen = sizeof(addr);
+		len = recvfrom(sd, buf2, msglen, MSG_TRUNC,
+			(struct sockaddr *)&addr, &addrlen);
+		EXPECT_EQ(len, msglen);
+		EXPECT_EQ(memcmp(buf1, buf2, msglen), 0);
+	}
+
+	close(sd);
+	close(sock_listen);
+	free(buf2);
+	free(buf1);
+}
+
+/* Returns file descriptor.
+ * Asserts on failure
+ */
+static int create_tun(struct __test_metadata *_metadata,
+	const char *name)
+{
+	struct ifreq ifr = {0};
+	int rc;
+	int fd;
+
+	fd = open("/dev/net/tun", O_RDWR);
+	ASSERT_GE(fd, 0);
+
+	assert(strlen(name) < IFNAMSIZ);
+	strcpy(ifr.ifr_name, name);
+	ifr.ifr_flags = IFF_TUN;
+
+	rc = ioctl(fd, TUNSETIFF, &ifr);
+	ASSERT_EQ(rc, 0) TH_LOG("tun ioctl failed: %s",
+		strerror(errno));
+	return fd;
+}
+
+static int atomic_write(int fd, void *buffer, size_t len)
+{
+	while (len) {
+		ssize_t wlen = write(fd, buffer, len);
+
+		if (wlen == -1 && errno == EINTR)
+			continue;
+
+		if (wlen < 0)
+			return wlen;
+
+		len -= wlen;
+		buffer += wlen;
+	}
+	return 0;
+}
+
+
+/* Thread to forward messages between FDs */
+static void *tun_start(void *arg)
+{
+	struct _test_data_TUNPAIR *fix = arg;
+	struct pollfd pf[2] = {
+		{ .fd = fix->fd[0], .events = POLLIN },
+		{ .fd = fix->fd[1], .events = POLLIN },
+	};
+	char *buffer = NULL;
+	ssize_t len;
+	int rc;
+
+	buffer = malloc(BUF_SIZE);
+	if (!buffer) {
+		fprintf(stderr, "malloc failed");
+		goto out;
+	}
+
+	while (fix->running) {
+		rc = poll(pf, 2, 100);
+		if (rc == 0 || (rc == -1 && errno == EINTR)) {
+			continue;
+		}
+		if (rc < 0) {
+			fprintf(stderr, "poll error: %s", strerror(errno));
+			goto out;
+		}
+
+		for (int i = 0; i < 2; i++) {
+			if (pf[i].revents & POLLIN) {
+				len = read(pf[i].fd, buffer, BUF_SIZE);
+				if (len == -1 && errno == EINTR) {
+					continue;
+				}
+				if (len == 0) {
+					fprintf(stderr, "EOF from tun");
+					goto out;
+				}
+				if (len < 0) {
+					fprintf(stderr, "error from tun read: %s",
+						strerror(errno));
+					goto out;
+				}
+
+				rc = atomic_write(pf[(i+1) % 2].fd, buffer, len);
+				if (rc < 0) {
+					fprintf(stderr, "error from tun write: %s",
+						strerror(errno));
+					goto out;
+				}
+			}
+		}
+	}
+out:
+	return NULL;
+}
+
+TEST_HARNESS_MAIN


Couple of thoughts. If you were to write wrapper shell script,
you could check for requirements: configs, mctp presence from
the shell script and then run mctp-tun from the shell scripts.
This is a just a suggestion if adding shell script makes things
easier especially checking the configs.

With the comments addressed:

Reviewed-by: Shuah Khan <skhan@xxxxxxxxxxxxxxxxxxx>

thanks,
-- Shuah





[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux