diff --git a/CODEOWNERS b/CODEOWNERS index aab64d1558f..499e7f23601 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 a37bd09cb15..5cec71bdd43 100644 --- a/subsys/net/lib/sockets/socketpair.c +++ b/subsys/net/lib/sockets/socketpair.c @@ -568,7 +568,7 @@ out: */ 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 @@ static int zsock_poll_update_ctx(struct spair *const spair, 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 @@ pollin_done: (*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 00000000000..27b977b095b --- /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 00000000000..2d43936f3b0 --- /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 00000000000..046f17b0d17 --- /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 + +/* 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 00000000000..b5a9cada0a6 --- /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 +#include + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include + +#include + +#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 00000000000..163f65904c0 --- /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 + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include + +#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 00000000000..cf8ce6edbdb --- /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 + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include + +#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 00000000000..4839eeba212 --- /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 + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include + +#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 00000000000..992260031b6 --- /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 + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include +#include + +#include + +#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 00000000000..b055cd1b975 --- /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 + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include + +#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 00000000000..2b3b9c7364f --- /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 +#include + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include + +#include + +#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 00000000000..3a0852d29be --- /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 + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include + +#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 00000000000..1da7159174d --- /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