Message ID | 20220322071934.2655827-1-matt@codeconstruct.com.au |
---|---|
State | New |
Headers | show |
Series | [net-next] selftests/net: mctp: Roundtrip tun tests | expand |
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@codeconstruct.com.au> > --- > 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@linuxfoundation.org> thanks, -- Shuah
Hi Shuah, Thanks for the review. On Tue, 2022-03-22 at 17:28 -0600, Shuah Khan wrote: > Are you adding this test to main Makefile? If so you will need to chane > selftests/Makefile Given most test-running systems won't have the "mctp" binary requirement I'm not sure it should be added to the main Makefile? The tests are intended more for future MCTP developers. I can add it if you think it's worthwhile though. > 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? > ... > 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. I think it's better kept in the compiled mctp-tun - it's often used cross- compiled so a single binary is simpler to manage. I'll move the requirements checks out to a separate function for clarity. Cheers, Matt
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 +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"); + 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"); + return; + } + + rc = socket(AF_MCTP, SOCK_DGRAM, 0); + if (rc == -1 && errno == EAFNOSUPPORT) { + self->skip = 1; + TH_LOG("No AF_MCTP support, skipping"); + 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
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. Signed-off-by: Matt Johnston <matt@codeconstruct.com.au> --- 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