tests: socket: socketpair: tests for socketpair(2) syscall
Tests for issue #24366
Signed-off-by: Christopher Friedt <chrisfriedt@gmail.com>
diff --git a/CODEOWNERS b/CODEOWNERS
index aab64d1..499e7f2 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -498,6 +498,7 @@
/tests/net/lib/http_header_fields/ @jukkar @tbursztyka
/tests/net/lib/mqtt_packet/ @jukkar @tbursztyka
/tests/net/lib/coap/ @rveerama1
+/tests/net/socket/socketpair/ @cfriedt
/tests/net/socket/ @jukkar @tbursztyka @pfalcon
/tests/subsys/fs/ @nashif @wentongwu
/tests/subsys/settings/ @nvlsianpu
diff --git a/subsys/net/lib/sockets/socketpair.c b/subsys/net/lib/sockets/socketpair.c
index a37bd09..5cec71b 100644
--- a/subsys/net/lib/sockets/socketpair.c
+++ b/subsys/net/lib/sockets/socketpair.c
@@ -568,7 +568,7 @@
*/
static ssize_t spair_read(void *obj, void *buffer, size_t count)
{
- ssize_t res;
+ int res;
bool is_connected;
size_t avail;
@@ -783,7 +783,7 @@
if (pfd->events & ZSOCK_POLLOUT) {
if (!sock_is_connected(spair)) {
pfd->revents |= ZSOCK_POLLHUP;
- goto check_pollin;
+ goto pollout_done;
}
remote = z_get_fd_obj(spair->remote,
@@ -850,7 +850,6 @@
(*pev)++;
-out:
if (remote != NULL && have_remote_sem) {
k_sem_give(&remote->sem);
}
diff --git a/tests/net/socket/socketpair/CMakeLists.txt b/tests/net/socket/socketpair/CMakeLists.txt
new file mode 100644
index 0000000..27b977b
--- /dev/null
+++ b/tests/net/socket/socketpair/CMakeLists.txt
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.13.1)
+find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
+project(socketpair)
+
+FILE(GLOB app_sources src/*.c)
+target_sources(app PRIVATE ${app_sources})
diff --git a/tests/net/socket/socketpair/prj.conf b/tests/net/socket/socketpair/prj.conf
new file mode 100644
index 0000000..2d43936
--- /dev/null
+++ b/tests/net/socket/socketpair/prj.conf
@@ -0,0 +1,34 @@
+CONFIG_MP_NUM_CPUS=1
+
+# General config
+CONFIG_NEWLIB_LIBC=y
+
+# Networking config
+CONFIG_NETWORKING=y
+CONFIG_NET_TEST=y
+CONFIG_NET_LOOPBACK=y
+CONFIG_NET_IPV4=y
+CONFIG_NET_IPV6=y
+CONFIG_NET_SOCKETS=y
+CONFIG_NET_SOCKETPAIR=y
+CONFIG_NET_SOCKETPAIR_BUFFER_SIZE=64
+CONFIG_NET_SOCKETS_POSIX_NAMES=y
+# Defines fd_set size
+CONFIG_POSIX_MAX_FDS=33
+
+# Network driver config
+CONFIG_TEST_RANDOM_GENERATOR=y
+
+# Network address config
+CONFIG_NET_CONFIG_SETTINGS=y
+CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
+CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
+
+CONFIG_MAIN_STACK_SIZE=2048
+CONFIG_ZTEST=y
+
+# User mode requirements
+CONFIG_TEST_USERSPACE=y
+CONFIG_HEAP_MEM_POOL_SIZE=2048
+
+CONFIG_QEMU_TICKLESS_WORKAROUND=y
diff --git a/tests/net/socket/socketpair/src/main.c b/tests/net/socket/socketpair/src/main.c
new file mode 100644
index 0000000..046f17b
--- /dev/null
+++ b/tests/net/socket/socketpair/src/main.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <ztest.h>
+
+/* in happy_path.c */
+extern void test_socketpair_AF_LOCAL__SOCK_STREAM__0(void);
+extern void test_socketpair_AF_UNIX__SOCK_STREAM__0(void);
+
+/* in nonblock.c */
+extern void test_socketpair_write_nonblock(void);
+extern void test_socketpair_read_nonblock(void);
+
+/* in block.c */
+extern void test_socketpair_write_block(void);
+extern void test_socketpair_read_block(void);
+
+/* in closed_ends.c */
+extern void test_socketpair_close_one_end_and_write_to_the_other(void);
+extern void test_socketpair_close_one_end_and_read_from_the_other(void);
+
+/* in expected_failures.c */
+extern void test_socketpair_expected_failures(void);
+
+/* in unsupported_calls.c */
+extern void test_socketpair_unsupported_calls(void);
+
+/* in fcntl.c */
+extern void test_socketpair_fcntl(void);
+
+/* in poll.c */
+extern void test_socketpair_poll_timeout(void);
+extern void test_socketpair_poll_timeout_nonblocking(void);
+extern void test_socketpair_poll_close_remote_end_POLLIN(void);
+extern void test_socketpair_poll_close_remote_end_POLLOUT(void);
+extern void test_socketpair_poll_immediate_data(void);
+extern void test_socketpair_poll_delayed_data(void);
+
+void test_main(void)
+{
+ k_thread_system_pool_assign(k_current_get());
+
+ ztest_test_suite(
+ socketpair,
+ ztest_user_unit_test(test_socketpair_AF_LOCAL__SOCK_STREAM__0),
+ ztest_user_unit_test(test_socketpair_AF_UNIX__SOCK_STREAM__0),
+
+ ztest_user_unit_test(test_socketpair_write_nonblock),
+ ztest_user_unit_test(test_socketpair_read_nonblock),
+
+ ztest_user_unit_test(
+ test_socketpair_close_one_end_and_write_to_the_other),
+ ztest_user_unit_test(
+ test_socketpair_close_one_end_and_read_from_the_other),
+
+ ztest_user_unit_test(test_socketpair_expected_failures),
+
+ ztest_user_unit_test(test_socketpair_unsupported_calls),
+
+ ztest_user_unit_test(test_socketpair_fcntl),
+
+ ztest_user_unit_test(test_socketpair_poll_timeout),
+ ztest_user_unit_test(
+ test_socketpair_poll_timeout_nonblocking),
+ ztest_user_unit_test(test_socketpair_poll_immediate_data)
+ );
+
+ ztest_run_test_suite(socketpair);
+
+/* 20200509: @cfriedt: experiencing some issues with userspace thread
+ * / memory permissions that will require some sorting out. Currently
+ * these tests succeeed for native_posix_64.
+ *
+ * This feature is experimental at present.
+ */
+#if 0
+ ztest_test_suite(
+ socketpair_only_kernel,
+ ztest_user_unit_test(test_socketpair_write_block),
+ ztest_user_unit_test(test_socketpair_read_block),
+ ztest_user_unit_test(test_socketpair_poll_delayed_data),
+ ztest_user_unit_test(
+ test_socketpair_poll_close_remote_end_POLLIN),
+ ztest_user_unit_test(
+ test_socketpair_poll_close_remote_end_POLLOUT)
+ );
+
+ ztest_run_test_suite(socketpair_only_kernel);
+#endif
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_block.c b/tests/net/socket/socketpair/src/test_socketpair_block.c
new file mode 100644
index 0000000..b5a9cad
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_block.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <fcntl.h>
+#include <string.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <net/socket.h>
+#include <sys/util.h>
+#include <posix/unistd.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+#define TIMEOUT K_FOREVER
+
+struct ctx {
+ /* true if test is write_block(), false if test is read_block() */
+ bool write;
+ /* the secondary-side socket of the socketpair */
+ int fd;
+ /* the count of the main thread */
+ atomic_t m;
+ /* the count of the secondary thread */
+ size_t n;
+ /* true when secondary thread is done */
+ bool done;
+ /* true if both main and secondary thread should immediately quit */
+ bool fail;
+ /* thread id of the secondary thread */
+ k_tid_t tid;
+};
+ZTEST_BMEM struct ctx ctx;
+#define STACK_SIZE 512
+/* thread structure for secondary thread */
+ZTEST_BMEM struct k_thread th;
+/* stack for the secondary thread */
+static K_THREAD_STACK_DEFINE(th_stack, STACK_SIZE);
+
+static void th_fun(void *arg0, void *arg1, void *arg2)
+{
+ (void) arg0;
+ (void) arg1;
+ (void) arg2;
+
+ int res;
+ char c = '\0';
+
+ LOG_DBG("secondary thread running");
+
+ while (true) {
+ if (ctx.write) {
+ LOG_DBG("ctx.m: %u", ctx.m);
+ if (atomic_get(&ctx.m)
+ < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE) {
+ continue;
+ }
+ LOG_DBG("ready to read!");
+ } else {
+ LOG_DBG("sleeping for 100ms..");
+ k_sleep(K_MSEC(100));
+ LOG_DBG("ready to write!");
+ }
+ break;
+ }
+
+ LOG_DBG("%sing 1 byte %s fd %d", ctx.write ? "read" : "write",
+ ctx.write ? "from" : "to", ctx.fd);
+ if (ctx.write) {
+ res = read(ctx.fd, &c, 1);
+ } else {
+ res = write(ctx.fd, "x", 1);
+ }
+ if (-1 == res || 1 != res) {
+ LOG_DBG("%s(2) failed: %d", ctx.write ? "read" : "write",
+ errno);
+ goto out;
+ }
+ LOG_DBG("%s 1 byte", ctx.write ? "read" : "wrote");
+ ctx.n = 1;
+
+out:
+ ctx.done = true;
+
+ LOG_DBG("terminating..");
+}
+
+void test_socketpair_write_block(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+
+ LOG_DBG("creating socketpair..");
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
+
+ for (size_t i = 0; i < 2; ++i) {
+
+ LOG_DBG("data direction %d -> %d", sv[i], sv[(!i) & 1]);
+
+ LOG_DBG("setting up context");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.write = true;
+ ctx.fd = sv[(!i) & 1];
+
+ LOG_DBG("creating secondary thread");
+ ctx.tid = k_thread_create(&th, th_stack,
+ STACK_SIZE, th_fun,
+ NULL, NULL, NULL,
+ CONFIG_MAIN_THREAD_PRIORITY,
+ K_INHERIT_PERMS, K_NO_WAIT);
+ LOG_DBG("created secondary thread %p", ctx.tid);
+
+ /* fill up the buffer */
+ for (ctx.m = 0; atomic_get(&ctx.m)
+ < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE;) {
+
+ res = write(sv[i], "x", 1);
+ zassert_not_equal(res, -1, "write(2) failed: %d",
+ errno);
+ zassert_equal(res, 1, "wrote %d bytes instead of 1",
+ res);
+
+ atomic_inc(&ctx.m);
+ LOG_DBG("have written %u bytes", ctx.m);
+ }
+
+ /* try to write one more byte */
+ LOG_DBG("writing to fd %d", sv[i]);
+ res = write(sv[i], "x", 1);
+ zassert_not_equal(res, -1, "write(2) failed: %d", errno);
+ zassert_equal(res, 1, "wrote %d bytes instead of 1", res);
+
+ LOG_DBG("success!");
+
+ k_thread_join(&th, K_MSEC(1000));
+ }
+
+ close(sv[0]);
+ close(sv[1]);
+}
+
+void test_socketpair_read_block(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+ char x;
+
+ LOG_DBG("creating socketpair..");
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
+
+ for (size_t i = 0; i < 2; ++i) {
+
+ LOG_DBG("data direction %d -> %d", sv[i], sv[(!i) & 1]);
+
+ LOG_DBG("setting up context");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.write = false;
+ ctx.fd = sv[(!i) & 1];
+
+ LOG_DBG("creating secondary thread");
+ ctx.tid = k_thread_create(&th, th_stack,
+ STACK_SIZE, th_fun,
+ NULL, NULL, NULL,
+ CONFIG_MAIN_THREAD_PRIORITY,
+ K_INHERIT_PERMS, K_NO_WAIT);
+ LOG_DBG("created secondary thread %p", ctx.tid);
+
+ /* try to read one byte */
+ LOG_DBG("reading from fd %d", sv[i]);
+ x = '\0';
+ res = read(sv[i], &x, 1);
+ zassert_not_equal(res, -1, "read(2) failed: %d", errno);
+ zassert_equal(res, 1, "read %d bytes instead of 1", res);
+
+ LOG_DBG("success!");
+
+ k_thread_join(&th, K_MSEC(1000));
+ }
+
+ close(sv[0]);
+ close(sv[1]);
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_closed_ends.c b/tests/net/socket/socketpair/src/test_socketpair_closed_ends.c
new file mode 100644
index 0000000..163f659
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_closed_ends.c
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ */
+#include <fcntl.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <stdio.h>
+#include <string.h>
+#include <net/socket.h>
+#include <sys/util.h>
+#include <posix/unistd.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+void test_socketpair_close_one_end_and_write_to_the_other(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+
+ for (size_t i = 0; i < 2; ++i) {
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
+
+ res = close(sv[i]);
+ zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
+
+ res = write(sv[(!i) & 1], "x", 1);
+ zassert_equal(res, -1, "expected write(2) to fail");
+ zassert_equal(res, -1, "errno: expected: EPIPE actual: %d",
+ errno);
+
+ res = close(sv[(!i) & 1]);
+ zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
+ }
+}
+
+void test_socketpair_close_one_end_and_read_from_the_other(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+ char xx[16];
+
+ for (size_t i = 0; i < 2; ++i) {
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
+
+ /* We want to write some bytes to the closing end of the
+ * socket before it gets shut down, so that we can prove that
+ * reading is possible from the other end still and that data
+ * is not lost.
+ */
+ res = write(sv[i], "xx", 2);
+ zassert_not_equal(res, -1, "write(2) failed: %d", errno);
+ zassert_equal(res, 2, "write(2) failed to write 2 bytes");
+
+ res = close(sv[i]);
+ zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
+
+ memset(xx, 0, sizeof(xx));
+ res = read(sv[(!i) & 1], xx, sizeof(xx));
+ zassert_not_equal(res, -1, "read(2) failed: %d", errno);
+ zassert_equal(res, 2, "expected to read 2 bytes but read %d",
+ res);
+
+ res = read(sv[(!i) & 1], xx, sizeof(xx));
+ zassert_equal(res, 0,
+ "expected read(2) to succeed but read 0 bytes");
+
+ res = close(sv[(!i) & 1]);
+ zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
+ }
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_expected_failures.c b/tests/net/socket/socketpair/src/test_socketpair_expected_failures.c
new file mode 100644
index 0000000..cf8ce6e
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_expected_failures.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <fcntl.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <stdio.h>
+#include <string.h>
+#include <net/socket.h>
+#include <sys/util.h>
+#include <posix/unistd.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+void test_socketpair_expected_failures(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+
+ /* Use invalid values in fields starting from left to right */
+
+ res = socketpair(AF_INET, SOCK_STREAM, 0, sv);
+ if (res != -1) {
+ close(sv[0]);
+ close(sv[1]);
+ }
+ zassert_equal(res, -1, "socketpair with fail with bad address family");
+ zassert_equal(errno, EAFNOSUPPORT,
+ "errno should be EAFNOSUPPORT with bad adddress family");
+
+ res = socketpair(AF_UNIX, 42, 0, sv);
+ if (res != -1) {
+ close(sv[0]);
+ close(sv[1]);
+ }
+ zassert_equal(res, -1,
+ "socketpair should fail with unsupported socket type");
+ zassert_equal(errno, EPROTOTYPE,
+ "errno should be EPROTOTYPE with bad socket type");
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, IPPROTO_TLS_1_0, sv);
+ if (res != -1) {
+ close(sv[0]);
+ close(sv[1]);
+ }
+ zassert_equal(res, -1,
+ "socketpair should fail with unsupported protocol");
+ zassert_equal(errno, EPROTONOSUPPORT,
+ "errno should be EPROTONOSUPPORT with bad protocol");
+
+ /* This is not a POSIX requirement, but should be safe */
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, NULL);
+ if (res != -1) {
+ close(sv[0]);
+ close(sv[1]);
+ }
+ zassert_equal(res, -1,
+ "socketpair should fail with invalid socket vector");
+ zassert_equal(errno, EFAULT,
+ "errno should be EFAULT with bad socket vector");
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_fcntl.c b/tests/net/socket/socketpair/src/test_socketpair_fcntl.c
new file mode 100644
index 0000000..4839eeb
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_fcntl.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <fcntl.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <stdio.h>
+#include <string.h>
+#include <net/socket.h>
+#include <sys/util.h>
+#include <posix/unistd.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+void test_socketpair_fcntl(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+ int flags;
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0,
+ "socketpair(AF_UNIX, SOCK_STREAM, 0, sv) failed");
+
+ res = fcntl(sv[0], F_GETFL, 0);
+ zassert_not_equal(res, -1,
+ "fcntl(sv[0], F_GETFL) failed. errno: %d", errno);
+
+ flags = res;
+ zassert_equal(res & O_NONBLOCK, 0,
+ "socketpair should block by default");
+
+ res = fcntl(sv[0], F_SETFL, flags | O_NONBLOCK);
+ zassert_not_equal(res, -1,
+ "fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) failed. errno: %d",
+ errno);
+
+ res = fcntl(sv[0], F_GETFL, 0);
+ zassert_equal(res ^ flags, O_NONBLOCK, "expected O_NONBLOCK set");
+
+ close(sv[0]);
+ close(sv[1]);
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_happy_path.c b/tests/net/socket/socketpair/src/test_socketpair_happy_path.c
new file mode 100644
index 0000000..9922600
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_happy_path.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <fcntl.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <string.h>
+#include <net/socket.h>
+#include <sys/util.h>
+#include <posix/unistd.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+static void happy_path(
+ const int family, const char *family_s,
+ const int type, const char *type_s,
+ const int proto, const char *proto_s
+)
+{
+ int res;
+ int sv[2] = {-1, -1};
+
+ const char *expected_msg = "Hello, socketpair(2) world!";
+ const unsigned int expected_msg_len = strlen(expected_msg);
+ char actual_msg[32];
+ size_t actual_msg_len;
+ struct iovec iovec;
+ struct msghdr msghdr;
+
+ LOG_DBG("calling socketpair(%u, %u, %u, %p)", family, type, proto, sv);
+ res = socketpair(family, type, proto, sv);
+ zassert_true(res == -1 || res == 0,
+ "socketpair returned an unspecified value");
+ zassert_equal(res, 0, "socketpair failed");
+ LOG_DBG("sv: {%d, %d}", sv[0], sv[1]);
+
+ socklen_t len;
+
+ /* sockets are bidirectional. test functions from both ends */
+ for (int i = 0; i < 2; ++i) {
+
+ /*
+ * Test with write(2) / read(2)
+ */
+
+ LOG_DBG("calling write(%d, '%s', %u)", sv[i], expected_msg,
+ expected_msg_len);
+ res = write(sv[i], expected_msg, expected_msg_len);
+
+ zassert_not_equal(res, -1, "write(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "did not write entire message");
+
+ memset(actual_msg, 0, sizeof(actual_msg));
+
+ LOG_DBG("calling read(%d, %p, %u)", sv[i], actual_msg,
+ (unsigned int)sizeof(actual_msg));
+ res = read(sv[(!i) & 1], actual_msg, sizeof(actual_msg));
+
+ zassert_not_equal(res, -1, "read(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "wrong return value");
+
+ zassert_true(strncmp(expected_msg, actual_msg,
+ actual_msg_len) == 0,
+ "the wrong message was passed through the socketpair");
+
+ /*
+ * Test with send(2) / recv(2)
+ */
+
+ res = send(sv[i], expected_msg, expected_msg_len, 0);
+
+ zassert_not_equal(res, -1, "send(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "did not send entire message");
+
+ memset(actual_msg, 0, sizeof(actual_msg));
+
+ res = recv(sv[(!i) & 1], actual_msg, sizeof(actual_msg), 0);
+
+ zassert_not_equal(res, -1, "recv(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "wrong return value");
+
+ zassert_true(strncmp(expected_msg, actual_msg,
+ actual_msg_len) == 0,
+ "the wrong message was passed through the socketpair");
+
+ /*
+ * Test with sendto(2) / recvfrom(2)
+ */
+
+ res = sendto(sv[i], expected_msg, expected_msg_len, 0, NULL, 0);
+
+ zassert_not_equal(res, -1, "sendto(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "did not sendto entire message");
+
+ memset(actual_msg, 0, sizeof(actual_msg));
+
+ res = recvfrom(sv[(!i) & 1], actual_msg, sizeof(actual_msg), 0,
+ NULL, &len);
+
+ zassert_not_equal(res, -1, "recvfrom(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "wrong return value");
+
+ zassert_true(strncmp(expected_msg, actual_msg,
+ actual_msg_len) == 0,
+ "the wrong message was passed through the socketpair");
+
+ /*
+ * Test with sendmsg(2) / recv(2) - Zephyr lacks recvmsg atm
+ */
+
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+ iovec.iov_base = (void *)expected_msg;
+ iovec.iov_len = expected_msg_len;
+
+ res = sendmsg(sv[i], &msghdr, 0);
+
+ zassert_not_equal(res, -1, "sendmsg(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "did not sendmsg entire message");
+
+ res = read(sv[(!i) & 1], actual_msg, sizeof(actual_msg));
+
+ zassert_not_equal(res, -1, "read(2) failed: %d", errno);
+ actual_msg_len = res;
+ zassert_equal(actual_msg_len, expected_msg_len,
+ "wrong return value");
+
+ zassert_true(strncmp(expected_msg, actual_msg,
+ actual_msg_len) == 0,
+ "the wrong message was passed through the socketpair");
+ }
+
+ res = close(sv[0]);
+ zassert_equal(res, 0, "close failed");
+
+ res = close(sv[1]);
+ zassert_equal(res, 0, "close failed");
+}
+
+void test_socketpair_AF_LOCAL__SOCK_STREAM__0(void)
+{
+ happy_path(
+ AF_LOCAL, "AF_LOCAL",
+ SOCK_STREAM, "SOCK_STREAM",
+ 0, "0"
+ );
+}
+
+void test_socketpair_AF_UNIX__SOCK_STREAM__0(void)
+{
+ happy_path(
+ AF_UNIX, "AF_UNIX",
+ SOCK_STREAM, "SOCK_STREAM",
+ 0, "0"
+ );
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_nonblock.c b/tests/net/socket/socketpair/src/test_socketpair_nonblock.c
new file mode 100644
index 0000000..b055cd1
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_nonblock.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <fcntl.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <stdio.h>
+#include <string.h>
+#include <net/socket.h>
+#include <sys/util.h>
+#include <posix/unistd.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+void test_socketpair_write_nonblock(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
+
+ for (size_t i = 0; i < 2; ++i) {
+ /* first, fill up the buffer */
+ for (size_t k = 0; k < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE;
+ ++k) {
+ res = write(sv[i], "x", 1);
+ zassert_equal(res, 1, "write(2) failed: %d", errno);
+ }
+
+ /* then set the O_NONBLOCK flag */
+ res = fcntl(sv[i], F_GETFL, 0);
+ zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
+
+ res = fcntl(sv[i], F_SETFL, res | O_NONBLOCK);
+ zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
+
+ /* then, try to write one more byte */
+ res = write(sv[i], "x", 1);
+ zassert_equal(res, -1, "expected write to fail");
+ zassert_equal(errno, EAGAIN, "errno: exected: EAGAIN "
+ "actual: %d", errno);
+ }
+
+ close(sv[0]);
+ close(sv[1]);
+}
+
+void test_socketpair_read_nonblock(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+ char c;
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
+
+ for (size_t i = 0; i < 2; ++i) {
+ /* set the O_NONBLOCK flag */
+ res = fcntl(sv[i], F_GETFL, 0);
+ zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
+
+ res = fcntl(sv[i], F_SETFL, res | O_NONBLOCK);
+ zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
+
+ /* then, try to read one byte */
+ res = read(sv[i], &c, 1);
+ zassert_equal(res, -1, "expected read to fail");
+ zassert_equal(errno, EAGAIN, "errno: exected: EAGAIN "
+ "actual: %d", errno);
+ }
+
+ close(sv[0]);
+ close(sv[1]);
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_poll.c b/tests/net/socket/socketpair/src/test_socketpair_poll.c
new file mode 100644
index 0000000..2b3b9c7
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_poll.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <fcntl.h>
+#include <string.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <net/socket.h>
+#include <posix/unistd.h>
+#include <sys/util.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+#define STACK_SIZE 512
+/* stack for the secondary thread */
+static K_THREAD_STACK_DEFINE(th_stack, STACK_SIZE);
+static struct k_thread th;
+static k_tid_t tid;
+
+/*
+ * Timeout should work the same for blocking & non-blocking threads
+ *
+ * - no bytes available to read after timeout, r: 0 (timeout)
+ * - no bytes available to write after timeout, r: 0 (timeout)
+ */
+
+static void test_socketpair_poll_timeout_common(int sv[2])
+{
+ int res;
+
+ struct pollfd fds[1];
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLIN;
+ res = poll(fds, 1, 1);
+ zassert_equal(res, 0, "poll: expected: 0 actual: %d", res);
+
+ for (size_t i = 0; i < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE; ++i) {
+ res = write(sv[0], "x", 1);
+ zassert_equal(res, 1, "write failed: %d", res);
+ }
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLOUT;
+ res = poll(fds, 1, 1);
+ zassert_equal(res, 0, "poll: expected: 0 actual: %d", res);
+
+ close(sv[0]);
+ close(sv[1]);
+}
+
+void test_socketpair_poll_timeout(void)
+{
+ int sv[2] = {-1, -1};
+ int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+
+ zassert_not_equal(res, -1, "socketpair failed: %d", errno);
+
+ test_socketpair_poll_timeout_common(sv);
+}
+
+/* O_NONBLOCK should have no affect on poll(2) */
+void test_socketpair_poll_timeout_nonblocking(void)
+{
+ int sv[2] = {-1, -1};
+ int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+
+ zassert_not_equal(res, -1, "socketpair failed: %d", errno);
+
+ res = fcntl(sv[0], F_GETFL, 0);
+ zassert_not_equal(res, -1, "fcntl failed: %d", errno);
+
+ int flags = res;
+
+ res = fcntl(sv[0], F_SETFL, O_NONBLOCK | flags);
+ zassert_not_equal(res, -1, "fcntl failed: %d", errno);
+
+ res = fcntl(sv[1], F_SETFL, O_NONBLOCK | flags);
+ zassert_not_equal(res, -1, "fcntl failed: %d", errno);
+
+ test_socketpair_poll_timeout_common(sv);
+}
+
+static void close_fun(void *arg1, void *arg2, void *arg3)
+{
+ (void)arg2;
+ (void)arg3;
+
+ const int *const fd = (const int *)arg1;
+
+ k_sleep(K_MSEC(1000));
+
+ LOG_DBG("about to close fd %d", *fd);
+ close(*fd);
+}
+
+/*
+ * Hangup should cause the following behaviour
+ * - close remote fd while the local fd is blocking in poll. r: 1,
+ * POLLIN, read -> r: 0, errno: 0 -> EOF
+ * - close remote fd while the local fd is blocking in poll. r: 1,
+ * POLLOUT, write -> r: -1, errno: EPIPE.
+ */
+void test_socketpair_poll_close_remote_end_POLLIN(void)
+{
+ int res;
+ char c;
+ struct pollfd fds[1];
+
+ int sv[2] = {-1, -1};
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
+
+ /*
+ * poll until there are bytes to read.
+ * But rather than writing, close the other end of the channel
+ */
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLIN;
+
+ tid = k_thread_create(&th, th_stack,
+ K_THREAD_STACK_SIZEOF(th_stack), close_fun,
+ &sv[1], NULL, NULL,
+ CONFIG_MAIN_THREAD_PRIORITY + 1,
+ K_USER, K_NO_WAIT);
+
+ res = poll(fds, 1, -1);
+ zassert_equal(res, 1, "poll(2) failed: %d", res);
+ zassert_equal(fds[0].revents & POLLIN, POLLIN, "POLLIN not set");
+
+ res = k_thread_join(&th, K_MSEC(5000));
+ zassert_false(res < 0, "k_thread_join failed: %d", res);
+
+ res = read(sv[0], &c, 1);
+ zassert_equal(res, 0, "read did not return EOF");
+
+ close(sv[0]);
+}
+
+void test_socketpair_poll_close_remote_end_POLLOUT(void)
+{
+ int res;
+ struct pollfd fds[1];
+
+ int sv[2] = {-1, -1};
+
+ /*
+ * Fill up the remote q and then poll until write space is available.
+ * But rather than reading, close the other end of the channel
+ */
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
+
+ for (size_t i = 0; i < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE; ++i) {
+ res = write(sv[0], "x", 1);
+ zassert_equal(res, 1, "write failed: %d", res);
+ }
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLOUT;
+
+ tid = k_thread_create(&th, th_stack,
+ K_THREAD_STACK_SIZEOF(th_stack), close_fun,
+ &sv[1], NULL, NULL,
+ CONFIG_MAIN_THREAD_PRIORITY + 1,
+ K_USER, K_NO_WAIT);
+
+ res = poll(fds, 1, -1);
+ zassert_equal(res, 1, "poll(2) failed: %d", res);
+ zassert_equal(fds[0].revents & POLLHUP, POLLHUP, "POLLHUP not set");
+
+ res = k_thread_join(&th, K_MSEC(5000));
+ zassert_false(res < 0, "k_thread_join failed: %d", res);
+
+ res = write(sv[0], "x", 1);
+ zassert_equal(res, -1, "write(2): expected: -1 actual: %d", res);
+ zassert_equal(errno, EPIPE, "errno: expected: EPIPE actual: %d", errno);
+
+ close(sv[0]);
+}
+
+/*
+ * Data available immediately
+ * - even with a timeout value of 0 us, poll should return immediately with
+ * a value of 1 (for either read or write cases)
+ * - even with a timeout value of 0us, poll should return immediately with
+ * a value of 2 if both read and write are available
+ */
+void test_socketpair_poll_immediate_data(void)
+{
+ int sv[2] = {-1, -1};
+ int res;
+
+ struct pollfd fds[2];
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLOUT;
+ res = poll(fds, 1, 0);
+ zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
+ zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
+ zassert_not_equal(fds[0].revents & POLLOUT, 0, "POLLOUT not set");
+
+ res = write(sv[0], "x", 1);
+ zassert_not_equal(res, -1, "write(2) failed: %d", errno);
+ zassert_equal(res, 1, "write(2): expected: 1 actual: %d", res);
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[1];
+ fds[0].events |= POLLIN;
+ res = poll(fds, 1, 0);
+ zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
+ zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
+ zassert_not_equal(fds[0].revents & POLLIN, 0, "POLLIN not set");
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLOUT;
+ fds[1].fd = sv[1];
+ fds[1].events |= POLLIN;
+ res = poll(fds, 2, 0);
+ zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
+ zassert_equal(res, 2, "poll(2): expected: 1 actual: %d", res);
+ zassert_not_equal(fds[0].revents & POLLOUT, 0, "POLLOUT not set");
+ zassert_not_equal(fds[1].revents & POLLIN, 0, "POLLIN not set");
+
+ close(sv[0]);
+ close(sv[1]);
+}
+
+static void rw_fun(void *arg1, void *arg2, void *arg3)
+{
+ (void)arg3;
+
+ const bool *const should_write = (const bool *) arg1;
+ const int *const fd = (const int *)arg2;
+
+ int res;
+ char c;
+
+ k_sleep(K_MSEC(1000));
+
+ if (*should_write) {
+ LOG_DBG("about to write 1 byte");
+ res = write(*fd, "x", 1);
+ if (-1 == res) {
+ LOG_DBG("write(2) failed: %d", errno);
+ } else {
+ LOG_DBG("wrote 1 byte");
+ }
+ } else {
+ LOG_DBG("about to read 1 byte");
+ res = read(*fd, &c, 1);
+ if (-1 == res) {
+ LOG_DBG("read(2) failed: %d", errno);
+ } else {
+ LOG_DBG("read 1 byte");
+ }
+ }
+}
+
+/*
+ * Data only available but after some short period
+ * - say there is a timeout value of 5 s, poll should return immediately
+ * with the a value of 1 (for either read or write cases)
+ */
+void test_socketpair_poll_delayed_data(void)
+{
+ int sv[2] = {-1, -1};
+ int res;
+
+ bool should_write;
+
+ struct pollfd fds[1];
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLIN;
+ should_write = true;
+
+ tid = k_thread_create(&th, th_stack,
+ K_THREAD_STACK_SIZEOF(th_stack), rw_fun,
+ &should_write, &sv[1], NULL,
+ CONFIG_MAIN_THREAD_PRIORITY + 1,
+ K_USER, K_NO_WAIT);
+
+ res = poll(fds, 1, 5000);
+ zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
+ zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
+ zassert_not_equal(fds[0].revents & POLLIN, 0, "POLLIN not set");
+ k_thread_join(&th, K_FOREVER);
+
+ for (size_t i = 0; i < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE; ++i) {
+ res = write(sv[0], "x", 1);
+ zassert_equal(res, 1, "write failed: %d", res);
+ }
+
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = sv[0];
+ fds[0].events |= POLLOUT;
+ should_write = false;
+
+ tid = k_thread_create(&th, th_stack,
+ K_THREAD_STACK_SIZEOF(th_stack), rw_fun,
+ &should_write, &sv[1], NULL,
+ CONFIG_MAIN_THREAD_PRIORITY + 1,
+ K_USER, K_NO_WAIT);
+
+ res = poll(fds, 1, 5000);
+ zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
+ zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
+ zassert_not_equal(fds[0].revents & POLLOUT, 0, "POLLOUT was not set");
+ k_thread_join(&th, K_FOREVER);
+
+ close(sv[0]);
+ close(sv[1]);
+}
diff --git a/tests/net/socket/socketpair/src/test_socketpair_unsupported_calls.c b/tests/net/socket/socketpair/src/test_socketpair_unsupported_calls.c
new file mode 100644
index 0000000..3a0852d
--- /dev/null
+++ b/tests/net/socket/socketpair/src/test_socketpair_unsupported_calls.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <fcntl.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
+
+#include <stdio.h>
+#include <string.h>
+#include <net/socket.h>
+#include <sys/util.h>
+#include <posix/unistd.h>
+
+#include <ztest_assert.h>
+
+#undef read
+#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
+
+#undef write
+#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
+
+void test_socketpair_unsupported_calls(void)
+{
+ int res;
+ int sv[2] = {-1, -1};
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
+ socklen_t len = sizeof(addr);
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ zassert_equal(res, 0,
+ "socketpair(AF_UNIX, SOCK_STREAM, 0, sv) failed");
+
+
+ for (size_t i = 0; i < 2; ++i) {
+
+ res = bind(sv[i], (struct sockaddr *)&addr, len);
+ zassert_equal(res, -1,
+ "bind should fail on a socketpair endpoint");
+ zassert_equal(errno, EISCONN,
+ "bind should set errno to EISCONN");
+
+ res = connect(sv[i], (struct sockaddr *)&addr, len);
+ zassert_equal(res, -1,
+ "connect should fail on a socketpair endpoint");
+ zassert_equal(errno, EISCONN,
+ "connect should set errno to EISCONN");
+
+ res = listen(sv[i], 1);
+ zassert_equal(res, -1,
+ "listen should fail on a socketpair endpoint");
+ zassert_equal(errno, EINVAL,
+ "listen should set errno to EINVAL");
+
+ res = accept(sv[i], (struct sockaddr *)&addr, &len);
+ zassert_equal(res, -1,
+ "accept should fail on a socketpair endpoint");
+ zassert_equal(errno, EOPNOTSUPP,
+ "accept should set errno to EOPNOTSUPP");
+ }
+
+ res = close(sv[0]);
+ zassert_equal(res, 0, "close failed");
+
+ res = close(sv[1]);
+ zassert_equal(res, 0, "close failed");
+}
diff --git a/tests/net/socket/socketpair/testcase.yaml b/tests/net/socket/socketpair/testcase.yaml
new file mode 100644
index 0000000..1da7159
--- /dev/null
+++ b/tests/net/socket/socketpair/testcase.yaml
@@ -0,0 +1,5 @@
+common:
+ tags: net socket userspace
+tests:
+ net.socket.socketpair:
+ min_ram: 21