From 1de4e73e6d62bd90d2533f6333483f7643982119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Fri, 1 Sep 2023 11:24:51 +0200 Subject: [PATCH] tests: net: sockets: add tests for SO_REUSEPORT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds unit tests for the SO_REUSEPORT sockets option. It also fixes some bugs in other tests triggered by the new features. Signed-off-by: Tobias Frauenschläger --- tests/net/socket/misc/src/main.c | 4 +- tests/net/socket/reuseaddr/src/main.c | 473 -------- .../CMakeLists.txt | 0 .../prj.conf | 1 + .../net/socket/reuseaddr_reuseport/src/main.c | 1011 +++++++++++++++++ .../testcase.yaml | 0 6 files changed, 1015 insertions(+), 474 deletions(-) delete mode 100644 tests/net/socket/reuseaddr/src/main.c rename tests/net/socket/{reuseaddr => reuseaddr_reuseport}/CMakeLists.txt (100%) rename tests/net/socket/{reuseaddr => reuseaddr_reuseport}/prj.conf (97%) create mode 100644 tests/net/socket/reuseaddr_reuseport/src/main.c rename tests/net/socket/{reuseaddr => reuseaddr_reuseport}/testcase.yaml (100%) diff --git a/tests/net/socket/misc/src/main.c b/tests/net/socket/misc/src/main.c index c8f31435fd8..384eafe6126 100644 --- a/tests/net/socket/misc/src/main.c +++ b/tests/net/socket/misc/src/main.c @@ -286,6 +286,8 @@ void test_so_bindtodevice(int sock_c, int sock_s, struct sockaddr *peer_addr, zassert_equal(ret, 0, "close failed, %d", errno); ret = close(sock_s); zassert_equal(ret, 0, "close failed, %d", errno); + + k_sleep(K_MSEC(CONFIG_NET_TCP_TIME_WAIT_DELAY)); } void test_ipv4_so_bindtodevice(void) @@ -423,7 +425,7 @@ void test_getpeername(int family) ret = close(sock_s); zassert_equal(ret, 0, "close failed, %d", errno); - k_sleep(K_MSEC(CONFIG_NET_TCP_TIME_WAIT_DELAY)); + k_sleep(K_MSEC(2 * CONFIG_NET_TCP_TIME_WAIT_DELAY)); } diff --git a/tests/net/socket/reuseaddr/src/main.c b/tests/net/socket/reuseaddr/src/main.c deleted file mode 100644 index 1dc41fb72d1..00000000000 --- a/tests/net/socket/reuseaddr/src/main.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Copyright (c) 2019 Linaro Limited - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); - -#include -#include -#include -#include - -#include -#include - -#include "../../socket_helpers.h" - -#define TEST_IPV4_ANY_ADDR "0.0.0.0" -#define TEST_MY_IPV4_ADDR "192.0.2.1" -#define TEST_MY_IPV4_ADDR2 "192.0.2.2" - -#define TEST_IPV6_ANY_ADDR "::" -#define TEST_MY_IPV6_ADDR "2001:db8::1" -#define TEST_MY_IPV6_ADDR2 "2001:db8::2" - -#define LOCAL_PORT 4242 - -#define TCP_TEARDOWN_TIMEOUT K_SECONDS(3) - -#define SHOULD_SUCCEED true -#define SHOULD_FAIL false - -static inline void prepare_sock_tcp(sa_family_t family, const char *addr, uint16_t port, - int *sock, struct sockaddr *sockaddr) -{ - if (family == AF_INET) { - prepare_sock_tcp_v4(addr, - port, - sock, - (struct sockaddr_in *) sockaddr); - } else if (family == AF_INET6) { - prepare_sock_tcp_v6(addr, - port, - sock, - (struct sockaddr_in6 *) sockaddr); - } -} - -static void test_getsocketopt_reuseaddr(int sock, void *optval, socklen_t *optlen) -{ - zassert_equal(getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, optval, optlen), - 0, - "getsocketopt() failed with error %d", errno); -} - -static void test_setsocketopt_reuseaddr(int sock, void *optval, socklen_t optlen) -{ - zassert_equal(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, optval, optlen), - 0, - "setsocketopt() failed with error %d", errno); -} - -static void test_enable_reuseaddr(int sock) -{ - int value = 1; - - test_setsocketopt_reuseaddr(sock, &value, sizeof(value)); -} - -static void test_add_local_ip_address(sa_family_t family, const char *ip) -{ - if (family == AF_INET) { - struct sockaddr_in addr; - - zsock_inet_pton(AF_INET, ip, &addr.sin_addr); - - zassert_not_null(net_if_ipv4_addr_add(net_if_get_default(), - &addr.sin_addr, - NET_ADDR_MANUAL, - 0), - "Cannot add IPv4 address %s", ip); - } else if (family == AF_INET6) { - struct sockaddr_in6 addr; - - zsock_inet_pton(AF_INET6, ip, &addr.sin6_addr); - - zassert_not_null(net_if_ipv6_addr_add(net_if_get_default(), - &addr.sin6_addr, - NET_ADDR_MANUAL, - 0), - "Cannot add IPv6 address %s", ip); - } -} - -static void test_bind_success(int sock, const struct sockaddr * addr, socklen_t addrlen) -{ - zassert_equal(bind(sock, addr, addrlen), - 0, - "bind() failed with error %d", errno); -} - -static void test_bind_fail(int sock, const struct sockaddr * addr, socklen_t addrlen) -{ - zassert_equal(bind(sock, addr, addrlen), - -1, - "bind() succeeded incorrectly"); - - zassert_equal(errno, EADDRINUSE, "bind() returned unexpected errno (%d)", errno); -} - -static void test_listen(int sock) -{ - zassert_equal(listen(sock, 0), - 0, - "listen() failed with error %d", errno); -} - -static void test_connect(int sock, const struct sockaddr * addr, socklen_t addrlen) -{ - zassert_equal(connect(sock, addr, addrlen), - 0, - "connect() failed with error %d", errno); - - if (IS_ENABLED(CONFIG_NET_TC_THREAD_PREEMPTIVE)) { - /* Let the connection proceed */ - k_msleep(50); - } -} - -static int test_accept(int sock, struct sockaddr * addr, socklen_t * addrlen) -{ - int new_sock = accept(sock, addr, addrlen); - - zassert_not_equal(new_sock, -1, "accept() failed with error %d", errno); - - return new_sock; -} - -static void calc_net_context(struct net_context *context, void *user_data) -{ - int *count = user_data; - - (*count)++; -} - -/* Wait until the number of TCP contexts reaches a certain level - * exp_num_contexts : The number of contexts to wait for - * timeout : The time to wait for - */ -int wait_for_n_tcp_contexts(int exp_num_contexts, k_timeout_t timeout) -{ - uint32_t start_time = k_uptime_get_32(); - uint32_t time_diff; - int context_count = 0; - - /* After the client socket closing, the context count should be 1 less */ - net_context_foreach(calc_net_context, &context_count); - - time_diff = k_uptime_get_32() - start_time; - - /* Eventually the client socket should be cleaned up */ - while (context_count != exp_num_contexts) { - context_count = 0; - net_context_foreach(calc_net_context, &context_count); - k_sleep(K_MSEC(50)); - time_diff = k_uptime_get_32() - start_time; - - if (K_MSEC(time_diff).ticks > timeout.ticks) { - return -ETIMEDOUT; - } - } - - return 0; -} - -static void test_context_cleanup(void) -{ - zassert_equal(wait_for_n_tcp_contexts(0, TCP_TEARDOWN_TIMEOUT), - 0, - "Not all TCP contexts properly cleaned up"); -} - - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_enable_disable) -{ - int server_sock = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - zassert_true(server_sock >= 0, "socket open failed"); - - int value = -1; - size_t value_size = sizeof(int); - - /* Read initial value */ - test_getsocketopt_reuseaddr(server_sock, (void *)&value, (socklen_t *)&value_size); - zassert_equal(value_size, sizeof(int), "incorrect value size returned by getsocketopt()"); - zassert_equal(value, (int) false, "SO_REUSEADDR incorrectly set (expected false)"); - - /* Enable option */ - value = 1; - test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); - test_getsocketopt_reuseaddr(server_sock, (void *)&value, (socklen_t *)&value_size); - zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); - - /* Enable option (with other value as linux takes any int here) */ - value = 2; - test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); - test_getsocketopt_reuseaddr(server_sock, (void *)&value, (socklen_t *)&value_size); - zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); - - /* Enable option (with other value as linux takes any int here) */ - value = 0x100; - test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); - test_getsocketopt_reuseaddr(server_sock, (void *)&value, (socklen_t *)&value_size); - zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); - - /* Enable option (with other value as linux takes any int here) */ - value = -1; - test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); - test_getsocketopt_reuseaddr(server_sock, (void *)&value, (socklen_t *)&value_size); - zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); - - close(server_sock); - - test_context_cleanup(); -} - - -static void test_reuseaddr_unspecified_specified_common(sa_family_t family, - char const *first_ip, - char const *second_ip, - bool should_succeed) -{ - int server_sock1 = -1; - int server_sock2 = -1; - - struct sockaddr bind_addr1; - struct sockaddr bind_addr2; - - /* Create the sockets */ - prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock1, &bind_addr1); - prepare_sock_tcp(family, second_ip, LOCAL_PORT, &server_sock2, &bind_addr2); - - /* Bind the first socket */ - test_bind_success(server_sock1, &bind_addr1, sizeof(bind_addr1)); - - /* Try to bind the second socket, should fail */ - test_bind_fail(server_sock2, &bind_addr2, sizeof(bind_addr2)); - - /* Enable SO_REUSEADDR option for the second socket */ - test_enable_reuseaddr(server_sock2); - - /* Try to bind the second socket again */ - if (should_succeed) { - test_bind_success(server_sock2, &bind_addr2, sizeof(bind_addr2)); - } else { - test_bind_fail(server_sock2, &bind_addr2, sizeof(bind_addr2)); - } - - close(server_sock1); - close(server_sock2); - - test_context_cleanup(); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv4_first_unspecified) -{ - test_reuseaddr_unspecified_specified_common(AF_INET, - TEST_IPV4_ANY_ADDR, - TEST_MY_IPV4_ADDR, - SHOULD_SUCCEED); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv6_first_unspecified) -{ - test_reuseaddr_unspecified_specified_common(AF_INET6, - TEST_IPV6_ANY_ADDR, - TEST_MY_IPV6_ADDR, - SHOULD_SUCCEED); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv4_second_unspecified) -{ - test_reuseaddr_unspecified_specified_common(AF_INET, - TEST_MY_IPV4_ADDR, - TEST_IPV4_ANY_ADDR, - SHOULD_SUCCEED); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv6_second_unspecified) -{ - test_reuseaddr_unspecified_specified_common(AF_INET6, - TEST_MY_IPV6_ADDR, - TEST_IPV6_ANY_ADDR, - SHOULD_SUCCEED); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv4_both_unspecified) -{ - test_reuseaddr_unspecified_specified_common(AF_INET, - TEST_IPV4_ANY_ADDR, - TEST_IPV4_ANY_ADDR, - SHOULD_FAIL); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv6_both_unspecified) -{ - test_reuseaddr_unspecified_specified_common(AF_INET6, - TEST_IPV6_ANY_ADDR, - TEST_IPV6_ANY_ADDR, - SHOULD_FAIL); -} - - -static void test_reuseaddr_tcp_listening_common(sa_family_t family, - char const *first_ip, - char const *second_ip) -{ - int server_sock1 = -1; - int server_sock2 = -1; - - struct sockaddr bind_addr1; - struct sockaddr bind_addr2; - - /* Create the sockets */ - prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock1, &bind_addr1); - prepare_sock_tcp(family, second_ip, LOCAL_PORT, &server_sock2, &bind_addr2); - - /* Bind the first socket */ - test_bind_success(server_sock1, &bind_addr1, sizeof(bind_addr1)); - - /* Set the first socket to LISTEN state */ - test_listen(server_sock1); - - /* Enable SO_REUSEADDR option for the second socket */ - test_enable_reuseaddr(server_sock2); - - /* Try to bind the second socket, should fail */ - test_bind_fail(server_sock2, (struct sockaddr *) &bind_addr2, sizeof(bind_addr2)); - - close(server_sock1); - close(server_sock2); - - test_context_cleanup(); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv4_tcp_unspecified_listening) -{ - test_reuseaddr_tcp_listening_common(AF_INET, - TEST_IPV4_ANY_ADDR, - TEST_MY_IPV4_ADDR); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv6_tcp_unspecified_listening) -{ - test_reuseaddr_tcp_listening_common(AF_INET6, - TEST_IPV6_ANY_ADDR, - TEST_MY_IPV6_ADDR); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv4_tcp_specified_listening) -{ - test_reuseaddr_tcp_listening_common(AF_INET, - TEST_MY_IPV4_ADDR, - TEST_IPV4_ANY_ADDR); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv6_tcp_specified_listening) -{ - test_reuseaddr_tcp_listening_common(AF_INET6, - TEST_MY_IPV6_ADDR, - TEST_IPV6_ANY_ADDR); -} - - -static void test_reuseaddr_tcp_tcp_time_wait_common(sa_family_t family, - char const *first_ip, - char const *second_ip) -{ - int server_sock = -1; - int client_sock = -1; - int accept_sock = -1; - - struct sockaddr bind_addr; - struct sockaddr conn_addr; - - struct sockaddr accept_addr; - socklen_t accept_addrlen = sizeof(accept_addr); - - prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock, &bind_addr); - prepare_sock_tcp(family, second_ip, LOCAL_PORT, &client_sock, &conn_addr); - - /* Bind the server socket */ - test_bind_success(server_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)); - - /* Start listening on the server socket */ - test_listen(server_sock); - - /* Connect the client */ - test_connect(client_sock, &conn_addr, sizeof(conn_addr)); - - /* Accept the client */ - accept_sock = test_accept(server_sock, &accept_addr, &accept_addrlen); - - /* Close the server socket */ - close(server_sock); - - /* Close the accepted socket */ - close(accept_sock); - - /* Wait a short time for the accept socket to enter TIME_WAIT state*/ - k_msleep(50); - - /* Recreate the server socket */ - prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock, &bind_addr); - - /* Bind the server socket, should fail */ - test_bind_fail(server_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)); - - /* Enable SO_REUSEADDR option for the new server socket */ - test_enable_reuseaddr(server_sock); - - /* Try to bind the new server socket again, should work now */ - test_bind_success(server_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)); - - close(client_sock); - close(server_sock); - - test_context_cleanup(); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv4_tcp_time_wait_unspecified) -{ - test_reuseaddr_tcp_tcp_time_wait_common(AF_INET, - TEST_IPV4_ANY_ADDR, - TEST_MY_IPV4_ADDR); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv6_tcp_time_wait_unspecified) -{ - test_reuseaddr_tcp_tcp_time_wait_common(AF_INET6, - TEST_IPV6_ANY_ADDR, - TEST_MY_IPV6_ADDR); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv4_tcp_time_wait_specified) -{ - test_reuseaddr_tcp_tcp_time_wait_common(AF_INET, - TEST_MY_IPV4_ADDR, - TEST_MY_IPV4_ADDR); -} - -ZTEST_USER(socket_reuseaddr_reuseport_test_suite, test_reuseaddr_ipv6_tcp_time_wait_specified) -{ - test_reuseaddr_tcp_tcp_time_wait_common(AF_INET6, - TEST_MY_IPV6_ADDR, - TEST_MY_IPV6_ADDR); -} - - -static void *setup(void) -{ - /* Make sure that both the specified IPv4 and IPv6 addresses are - * added to the network interface. - */ - test_add_local_ip_address(AF_INET, TEST_MY_IPV4_ADDR); - test_add_local_ip_address(AF_INET6, TEST_MY_IPV6_ADDR); - - return NULL; -} - - -ZTEST_SUITE(socket_reuseaddr_reuseport_test_suite, NULL, setup, NULL, NULL, NULL); diff --git a/tests/net/socket/reuseaddr/CMakeLists.txt b/tests/net/socket/reuseaddr_reuseport/CMakeLists.txt similarity index 100% rename from tests/net/socket/reuseaddr/CMakeLists.txt rename to tests/net/socket/reuseaddr_reuseport/CMakeLists.txt diff --git a/tests/net/socket/reuseaddr/prj.conf b/tests/net/socket/reuseaddr_reuseport/prj.conf similarity index 97% rename from tests/net/socket/reuseaddr/prj.conf rename to tests/net/socket/reuseaddr_reuseport/prj.conf index c820fefa199..7ff751c8785 100644 --- a/tests/net/socket/reuseaddr/prj.conf +++ b/tests/net/socket/reuseaddr_reuseport/prj.conf @@ -24,6 +24,7 @@ CONFIG_NET_BUF_RX_COUNT=64 CONFIG_NET_BUF_TX_COUNT=64 CONFIG_NET_CONTEXT_REUSEADDR=y +CONFIG_NET_CONTEXT_REUSEPORT=y CONFIG_NET_HOSTNAME_ENABLE=y CONFIG_NET_HOSTNAME="ztest_hostname" diff --git a/tests/net/socket/reuseaddr_reuseport/src/main.c b/tests/net/socket/reuseaddr_reuseport/src/main.c new file mode 100644 index 00000000000..db047ac9cf4 --- /dev/null +++ b/tests/net/socket/reuseaddr_reuseport/src/main.c @@ -0,0 +1,1011 @@ +/* + * Copyright (c) 2019 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +#include +#include +#include +#include + +#include +#include + +#include "../../socket_helpers.h" + +#define TEST_IPV4_ANY_ADDR "0.0.0.0" +#define TEST_MY_IPV4_ADDR "192.0.2.1" +#define TEST_MY_IPV4_ADDR2 "192.0.2.2" + +#define TEST_IPV6_ANY_ADDR "::" +#define TEST_MY_IPV6_ADDR "2001:db8::1" +#define TEST_MY_IPV6_ADDR2 "2001:db8::2" + +#define LOCAL_PORT 4242 + +#define TCP_TEARDOWN_TIMEOUT K_SECONDS(3) + +#define SHOULD_SUCCEED true +#define SHOULD_FAIL false + +static void test_add_local_ip_address(sa_family_t family, const char *ip) +{ + if (family == AF_INET) { + struct sockaddr_in addr; + + zsock_inet_pton(AF_INET, ip, &addr.sin_addr); + + zassert_not_null(net_if_ipv4_addr_add(net_if_get_default(), + &addr.sin_addr, + NET_ADDR_MANUAL, + 0), + "Cannot add IPv4 address %s", ip); + } else if (family == AF_INET6) { + struct sockaddr_in6 addr; + + zsock_inet_pton(AF_INET6, ip, &addr.sin6_addr); + + zassert_not_null(net_if_ipv6_addr_add(net_if_get_default(), + &addr.sin6_addr, + NET_ADDR_MANUAL, + 0), + "Cannot add IPv6 address %s", ip); + } +} + +static void *setup(void) +{ + /* Make sure that both the specified IPv4 and IPv6 addresses are + * added to the network interface. + */ + test_add_local_ip_address(AF_INET, TEST_MY_IPV4_ADDR); + test_add_local_ip_address(AF_INET6, TEST_MY_IPV6_ADDR); + + return NULL; +} + +static inline void prepare_sock_tcp(sa_family_t family, const char *ip, uint16_t port, + int *sock, struct sockaddr *addr) +{ + if (family == AF_INET) { + prepare_sock_tcp_v4(ip, + port, + sock, + (struct sockaddr_in *) addr); + } else if (family == AF_INET6) { + prepare_sock_tcp_v6(ip, + port, + sock, + (struct sockaddr_in6 *) addr); + } +} + +static inline void prepare_sock_udp(sa_family_t family, const char *ip, uint16_t port, + int *sock, struct sockaddr *addr) +{ + if (family == AF_INET) { + prepare_sock_udp_v4(ip, + port, + sock, + (struct sockaddr_in *) addr); + } else if (family == AF_INET6) { + prepare_sock_udp_v6(ip, + port, + sock, + (struct sockaddr_in6 *) addr); + } +} + +static void test_getsocketopt_reuseaddr(int sock, void *optval, socklen_t *optlen) +{ + zassert_equal(getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, optval, optlen), + 0, + "getsocketopt() failed with error %d", errno); +} + +static void test_setsocketopt_reuseaddr(int sock, void *optval, socklen_t optlen) +{ + zassert_equal(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, optval, optlen), + 0, + "setsocketopt() failed with error %d", errno); +} + +static void test_enable_reuseaddr(int sock) +{ + int value = 1; + + test_setsocketopt_reuseaddr(sock, &value, sizeof(value)); +} + +static void test_getsocketopt_reuseport(int sock, void *optval, socklen_t *optlen) +{ + zassert_equal(getsockopt(sock, SOL_SOCKET, SO_REUSEPORT, optval, optlen), + 0, + "getsocketopt() failed with error %d", errno); +} + +static void test_setsocketopt_reuseport(int sock, void *optval, socklen_t optlen) +{ + zassert_equal(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, optval, optlen), + 0, + "setsocketopt() failed with error %d", errno); +} + +static void test_enable_reuseport(int sock) +{ + int value = 1; + + test_setsocketopt_reuseport(sock, &value, sizeof(value)); +} + +static void test_bind_success(int sock, const struct sockaddr *addr, socklen_t addrlen) +{ + zassert_equal(bind(sock, addr, addrlen), + 0, + "bind() failed with error %d", errno); +} + +static void test_bind_fail(int sock, const struct sockaddr *addr, socklen_t addrlen) +{ + zassert_equal(bind(sock, addr, addrlen), + -1, + "bind() succeeded incorrectly"); + + zassert_equal(errno, EADDRINUSE, "bind() returned unexpected errno (%d)", errno); +} + +static void test_listen(int sock) +{ + zassert_equal(listen(sock, 0), + 0, + "listen() failed with error %d", errno); +} + +static void test_connect_success(int sock, const struct sockaddr *addr, socklen_t addrlen) +{ + zassert_equal(connect(sock, addr, addrlen), + 0, + "connect() failed with error %d", errno); + + if (IS_ENABLED(CONFIG_NET_TC_THREAD_PREEMPTIVE)) { + /* Let the connection proceed */ + k_msleep(50); + } +} + +static void test_connect_fail(int sock, const struct sockaddr *addr, socklen_t addrlen) +{ + zassert_equal(connect(sock, addr, addrlen), + -1, + "connect() succeeded incorrectly"); + + zassert_equal(errno, EADDRINUSE, "connect() returned unexpected errno (%d)", errno); +} + +static int test_accept(int sock, struct sockaddr *addr, socklen_t *addrlen) +{ + int new_sock = accept(sock, addr, addrlen); + + zassert_not_equal(new_sock, -1, "accept() failed with error %d", errno); + + return new_sock; +} + +static void test_sendto(int sock, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + zassert_equal(sendto(sock, buf, len, flags, dest_addr, addrlen), + len, + "sendto failed with error %d", errno); +} + +static void test_recvfrom_success(int sock, void *buf, size_t max_len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen) +{ + zassert_equal(recvfrom(sock, buf, max_len, flags, src_addr, addrlen), + max_len, + "recvfrom failed with error %d", errno); +} + +static void test_recvfrom_fail(int sock, void *buf, size_t max_len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen) +{ + zassert_equal(recvfrom(sock, buf, max_len, flags, src_addr, addrlen), + -1, + "recvfrom succeeded incorrectly"); + + zassert_equal(errno, EAGAIN, "recvfrom() returned unexpected errno (%d)", errno); +} + +static void test_recv_success(int sock, void *buf, size_t max_len, int flags) +{ + zassert_equal(recv(sock, buf, max_len, flags), + max_len, + "recv failed with error %d", errno); +} + +static void test_recv_fail(int sock, void *buf, size_t max_len, int flags) +{ + zassert_equal(recv(sock, buf, max_len, flags), + -1, + "recvfrom succeeded incorrectly"); + + zassert_equal(errno, EAGAIN, "recvfrom() returned unexpected errno (%d)", errno); +} + +static void calc_net_context(struct net_context *context, void *user_data) +{ + int *count = user_data; + + (*count)++; +} + +/* Wait until the number of TCP contexts reaches a certain level + * exp_num_contexts : The number of contexts to wait for + * timeout : The time to wait for + */ +int wait_for_n_tcp_contexts(int exp_num_contexts, k_timeout_t timeout) +{ + uint32_t start_time = k_uptime_get_32(); + uint32_t time_diff; + int context_count = 0; + + /* After the client socket closing, the context count should be 1 less */ + net_context_foreach(calc_net_context, &context_count); + + time_diff = k_uptime_get_32() - start_time; + + /* Eventually the client socket should be cleaned up */ + while (context_count != exp_num_contexts) { + context_count = 0; + net_context_foreach(calc_net_context, &context_count); + k_sleep(K_MSEC(50)); + time_diff = k_uptime_get_32() - start_time; + + if (K_MSEC(time_diff).ticks > timeout.ticks) { + return -ETIMEDOUT; + } + } + + return 0; +} + +static void test_context_cleanup(void) +{ + zassert_equal(wait_for_n_tcp_contexts(0, TCP_TEARDOWN_TIMEOUT), + 0, + "Not all TCP contexts properly cleaned up"); +} + + +ZTEST_USER(socket_reuseaddr_test_suite, test_enable_disable) +{ + int server_sock = -1; + int value = -1; + socklen_t value_size = sizeof(int); + + server_sock = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + zassert_true(server_sock >= 0, "socket open failed"); + + /* Read initial value */ + test_getsocketopt_reuseaddr(server_sock, (void *)&value, &value_size); + zassert_equal(value_size, sizeof(int), "incorrect value size returned by getsocketopt()"); + zassert_equal(value, (int) false, "SO_REUSEADDR incorrectly set (expected false)"); + + /* Enable option */ + value = 1; + test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseaddr(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); + + /* Enable option (with other value as linux takes any int here) */ + value = 2; + test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseaddr(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); + + /* Enable option (with other value as linux takes any int here) */ + value = 0x100; + test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseaddr(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); + + /* Enable option (with other value as linux takes any int here) */ + value = -1; + test_setsocketopt_reuseaddr(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseaddr(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEADDR not correctly set, returned %d", value); + + close(server_sock); + + test_context_cleanup(); +} + + +static void test_reuseaddr_unspecified_specified_common(sa_family_t family, + char const *first_ip, + char const *second_ip, + bool should_succeed) +{ + int server_sock1 = -1; + int server_sock2 = -1; + + struct sockaddr bind_addr1; + struct sockaddr bind_addr2; + + /* Create the sockets */ + prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock1, &bind_addr1); + prepare_sock_tcp(family, second_ip, LOCAL_PORT, &server_sock2, &bind_addr2); + + /* Bind the first socket */ + test_bind_success(server_sock1, &bind_addr1, sizeof(bind_addr1)); + + /* Try to bind the second socket, should fail */ + test_bind_fail(server_sock2, &bind_addr2, sizeof(bind_addr2)); + + /* Enable SO_REUSEADDR option for the second socket */ + test_enable_reuseaddr(server_sock2); + + /* Try to bind the second socket again */ + if (should_succeed) { + test_bind_success(server_sock2, &bind_addr2, sizeof(bind_addr2)); + } else { + test_bind_fail(server_sock2, &bind_addr2, sizeof(bind_addr2)); + } + + close(server_sock1); + close(server_sock2); + + test_context_cleanup(); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv4_first_unspecified) +{ + test_reuseaddr_unspecified_specified_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_MY_IPV4_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv6_first_unspecified) +{ + test_reuseaddr_unspecified_specified_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_MY_IPV6_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv4_second_unspecified) +{ + test_reuseaddr_unspecified_specified_common(AF_INET, + TEST_MY_IPV4_ADDR, + TEST_IPV4_ANY_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv6_second_unspecified) +{ + test_reuseaddr_unspecified_specified_common(AF_INET6, + TEST_MY_IPV6_ADDR, + TEST_IPV6_ANY_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv4_both_unspecified) +{ + test_reuseaddr_unspecified_specified_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_IPV4_ANY_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv6_both_unspecified) +{ + test_reuseaddr_unspecified_specified_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_IPV6_ANY_ADDR, + SHOULD_FAIL); +} + + +static void test_reuseaddr_tcp_listening_common(sa_family_t family, + char const *first_ip, + char const *second_ip) +{ + int server_sock1 = -1; + int server_sock2 = -1; + + struct sockaddr bind_addr1; + struct sockaddr bind_addr2; + + /* Create the sockets */ + prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock1, &bind_addr1); + prepare_sock_tcp(family, second_ip, LOCAL_PORT, &server_sock2, &bind_addr2); + + /* Bind the first socket */ + test_bind_success(server_sock1, &bind_addr1, sizeof(bind_addr1)); + + /* Set the first socket to LISTEN state */ + test_listen(server_sock1); + + /* Enable SO_REUSEADDR option for the second socket */ + test_enable_reuseaddr(server_sock2); + + /* Try to bind the second socket, should fail */ + test_bind_fail(server_sock2, (struct sockaddr *) &bind_addr2, sizeof(bind_addr2)); + + close(server_sock1); + close(server_sock2); + + test_context_cleanup(); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv4_tcp_unspecified_listening) +{ + test_reuseaddr_tcp_listening_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_MY_IPV4_ADDR); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv6_tcp_unspecified_listening) +{ + test_reuseaddr_tcp_listening_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_MY_IPV6_ADDR); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv4_tcp_specified_listening) +{ + test_reuseaddr_tcp_listening_common(AF_INET, + TEST_MY_IPV4_ADDR, + TEST_IPV4_ANY_ADDR); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv6_tcp_specified_listening) +{ + test_reuseaddr_tcp_listening_common(AF_INET6, + TEST_MY_IPV6_ADDR, + TEST_IPV6_ANY_ADDR); +} + + +static void test_reuseaddr_tcp_tcp_time_wait_common(sa_family_t family, + char const *first_ip, + char const *second_ip) +{ + int server_sock = -1; + int client_sock = -1; + int accept_sock = -1; + + struct sockaddr bind_addr; + struct sockaddr conn_addr; + + struct sockaddr accept_addr; + socklen_t accept_addrlen = sizeof(accept_addr); + + prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock, &bind_addr); + prepare_sock_tcp(family, second_ip, LOCAL_PORT, &client_sock, &conn_addr); + + /* Bind the server socket */ + test_bind_success(server_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)); + + /* Start listening on the server socket */ + test_listen(server_sock); + + /* Connect the client */ + test_connect_success(client_sock, &conn_addr, sizeof(conn_addr)); + + /* Accept the client */ + accept_sock = test_accept(server_sock, &accept_addr, &accept_addrlen); + + /* Close the server socket */ + close(server_sock); + + /* Close the accepted socket */ + close(accept_sock); + + /* Wait a short time for the accept socket to enter TIME_WAIT state*/ + k_msleep(50); + + /* Recreate the server socket */ + prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock, &bind_addr); + + /* Bind the server socket, should fail */ + test_bind_fail(server_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)); + + /* Enable SO_REUSEADDR option for the new server socket */ + test_enable_reuseaddr(server_sock); + + /* Try to bind the new server socket again, should work now */ + test_bind_success(server_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)); + + close(client_sock); + close(server_sock); + + test_context_cleanup(); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv4_tcp_time_wait_unspecified) +{ + test_reuseaddr_tcp_tcp_time_wait_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_MY_IPV4_ADDR); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv6_tcp_time_wait_unspecified) +{ + test_reuseaddr_tcp_tcp_time_wait_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_MY_IPV6_ADDR); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv4_tcp_time_wait_specified) +{ + test_reuseaddr_tcp_tcp_time_wait_common(AF_INET, + TEST_MY_IPV4_ADDR, + TEST_MY_IPV4_ADDR); +} + +ZTEST_USER(socket_reuseaddr_test_suite, test_ipv6_tcp_time_wait_specified) +{ + test_reuseaddr_tcp_tcp_time_wait_common(AF_INET6, + TEST_MY_IPV6_ADDR, + TEST_MY_IPV6_ADDR); +} + + +ZTEST_SUITE(socket_reuseaddr_test_suite, NULL, setup, NULL, NULL, NULL); + + +ZTEST_USER(socket_reuseport_test_suite, test_enable_disable) +{ + int server_sock = -1; + + int value = -1; + socklen_t value_size = sizeof(int); + + server_sock = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + zassert_true(server_sock >= 0, "socket open failed"); + + /* Read initial value */ + test_getsocketopt_reuseport(server_sock, (void *)&value, &value_size); + zassert_equal(value_size, sizeof(int), "incorrect value size returned by getsocketopt()"); + zassert_equal(value, (int) false, "SO_REUSEPORT incorrectly set (expected false)"); + + /* Enable option */ + value = 1; + test_setsocketopt_reuseport(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseport(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEPORT not correctly set, returned %d", value); + + /* Enable option (with other value as linux takes any int here) */ + value = 2; + test_setsocketopt_reuseport(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseport(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEPORT not correctly set, returned %d", value); + + /* Enable option (with other value as linux takes any int here) */ + value = 0x100; + test_setsocketopt_reuseport(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseport(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEPORT not correctly set, returned %d", value); + + /* Enable option (with other value as linux takes any int here) */ + value = -1; + test_setsocketopt_reuseport(server_sock, (void *)&value, sizeof(value)); + test_getsocketopt_reuseport(server_sock, (void *)&value, &value_size); + zassert_equal(value, (int) true, "SO_REUSEPORT not correctly set, returned %d", value); + + close(server_sock); + + test_context_cleanup(); +} + + +static void test_reuseport_unspecified_specified_common(sa_family_t family, + char const *first_ip, + char const *second_ip, + bool should_succeed) +{ + int server_sock1 = -1; + int server_sock2 = -1; + + struct sockaddr bind_addr1; + struct sockaddr bind_addr2; + + /* Create the sockets */ + prepare_sock_tcp(family, first_ip, LOCAL_PORT, &server_sock1, &bind_addr1); + prepare_sock_tcp(family, second_ip, LOCAL_PORT, &server_sock2, &bind_addr2); + + /* Depending on the expected result, we enable SO_REUSEPORT for the first socket */ + if (should_succeed) { + test_enable_reuseport(server_sock1); + } + + /* Bind the first socket */ + test_bind_success(server_sock1, &bind_addr1, sizeof(bind_addr1)); + + /* Try to bind the second socket, should fail */ + test_bind_fail(server_sock2, &bind_addr2, sizeof(bind_addr2)); + + /* Enable SO_REUSEPORT option for the second socket */ + test_enable_reuseport(server_sock2); + + /* Try to bind the second socket again */ + if (should_succeed) { + test_bind_success(server_sock2, &bind_addr2, sizeof(bind_addr2)); + } else { + test_bind_fail(server_sock2, &bind_addr2, sizeof(bind_addr2)); + } + + close(server_sock1); + close(server_sock2); + + test_context_cleanup(); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_both_unspecified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_IPV4_ANY_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_both_unspecified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_IPV6_ANY_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_both_unspecified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_IPV4_ANY_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_both_unspecified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_IPV6_ANY_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_both_specified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_MY_IPV4_ADDR, + TEST_MY_IPV4_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_both_specified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_MY_IPV6_ADDR, + TEST_MY_IPV6_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_both_specified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_MY_IPV4_ADDR, + TEST_MY_IPV4_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_both_specified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_MY_IPV6_ADDR, + TEST_MY_IPV6_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_first_unspecified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_MY_IPV4_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_first_unspecified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_MY_IPV6_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_first_unspecified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_MY_IPV4_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_first_unspecified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_MY_IPV6_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_second_unspecified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_MY_IPV4_ADDR, + TEST_IPV4_ANY_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_second_unspecified_bad) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_MY_IPV6_ADDR, + TEST_IPV6_ANY_ADDR, + SHOULD_FAIL); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_second_unspecified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET, + TEST_MY_IPV4_ADDR, + TEST_IPV4_ANY_ADDR, + SHOULD_SUCCEED); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_second_unspecified_good) +{ + test_reuseport_unspecified_specified_common(AF_INET6, + TEST_MY_IPV6_ADDR, + TEST_IPV6_ANY_ADDR, + SHOULD_SUCCEED); +} + + +enum sockets_reuseport_enabled { + NONE_SET = 0, + FIRST_SET, + SECOND_SET, + BOTH_SET +}; + +static void test_reuseport_udp_server_client_common(sa_family_t family, + char const *ip, + enum sockets_reuseport_enabled setup) +{ + int server_sock = -1; + int client_sock = -1; + int accept_sock = 1; + + struct sockaddr server_addr; + struct sockaddr client_addr; + + struct sockaddr accept_addr; + socklen_t accept_addr_len = sizeof(accept_addr); + + char tx_buf = 0x55; + char rx_buf; + + /* Create sockets */ + prepare_sock_udp(family, ip, LOCAL_PORT, &server_sock, &server_addr); + prepare_sock_udp(family, ip, 0, &client_sock, &client_addr); + + /* Make sure we can bind to the address:port */ + if (setup == FIRST_SET || setup == BOTH_SET) { + test_enable_reuseport(server_sock); + } + + /* Bind server socket */ + test_bind_success(server_sock, (struct sockaddr *) &server_addr, sizeof(server_addr)); + + /* Bind client socket (on a random port) */ + test_bind_success(client_sock, (struct sockaddr *) &client_addr, sizeof(client_addr)); + + /* Send message from client to server */ + test_sendto(client_sock, &tx_buf, sizeof(tx_buf), 0, &server_addr, sizeof(server_addr)); + + /* Give the packet a chance to go through the net stack */ + k_msleep(50); + + /* Receive data from the client */ + rx_buf = 0; + test_recvfrom_success(server_sock, &rx_buf, sizeof(rx_buf), MSG_DONTWAIT, + &accept_addr, &accept_addr_len); + zassert_equal(rx_buf, tx_buf, "wrong data"); + + /* Create a more specific socket to have a direct connection to the new client */ + accept_sock = socket(family, SOCK_DGRAM, IPPROTO_UDP); + zassert_true(accept_sock >= 0, "socket open failed"); + + /* Make sure we can bind to the address:port */ + if (setup == SECOND_SET || setup == BOTH_SET) { + test_enable_reuseport(accept_sock); + } + + /* Try to bind new client socket */ + if (setup == BOTH_SET) { + /* Should succeed */ + test_bind_success(accept_sock, (struct sockaddr *) &server_addr, + sizeof(server_addr)); + } else { + /* Should fail */ + test_bind_fail(accept_sock, (struct sockaddr *) &server_addr, + sizeof(server_addr)); + } + + /* Connect the client to set remote address and remote port */ + test_connect_success(accept_sock, &accept_addr, sizeof(accept_addr)); + + /* Send another message from client to server */ + test_sendto(client_sock, &tx_buf, sizeof(tx_buf), 0, &server_addr, sizeof(server_addr)); + + /* Give the packet a chance to go through the net stack */ + k_msleep(50); + + /* Receive the data */ + if (setup == BOTH_SET) { + /* We should receive data on the new specific socket, not on the general one */ + rx_buf = 0; + test_recvfrom_fail(server_sock, &rx_buf, sizeof(rx_buf), MSG_DONTWAIT, + &accept_addr, &accept_addr_len); + + rx_buf = 0; + test_recv_success(accept_sock, &rx_buf, sizeof(rx_buf), MSG_DONTWAIT); + } else { + /* We should receive data on the general server socket */ + rx_buf = 0; + test_recvfrom_success(server_sock, &rx_buf, sizeof(rx_buf), MSG_DONTWAIT, + &accept_addr, &accept_addr_len); + + rx_buf = 0; + test_recv_fail(accept_sock, &rx_buf, sizeof(rx_buf), MSG_DONTWAIT); + } + + close(accept_sock); + close(client_sock); + close(server_sock); + + test_context_cleanup(); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_udp_bad_both_not_set) +{ + test_reuseport_udp_server_client_common(AF_INET, + TEST_MY_IPV4_ADDR, + NONE_SET); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_udp_bad_both_not_set) +{ + test_reuseport_udp_server_client_common(AF_INET6, + TEST_MY_IPV6_ADDR, + NONE_SET); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_udp_bad_first_not_set) +{ + test_reuseport_udp_server_client_common(AF_INET, + TEST_MY_IPV4_ADDR, + SECOND_SET); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_udp_bad_first_not_set) +{ + test_reuseport_udp_server_client_common(AF_INET6, + TEST_MY_IPV6_ADDR, + SECOND_SET); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_udp_bad_second_not_set) +{ + test_reuseport_udp_server_client_common(AF_INET, + TEST_MY_IPV4_ADDR, + FIRST_SET); +} + + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_udp_bad_second_not_set) +{ + test_reuseport_udp_server_client_common(AF_INET6, + TEST_MY_IPV6_ADDR, + FIRST_SET); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_udp_good) +{ + test_reuseport_udp_server_client_common(AF_INET, + TEST_MY_IPV4_ADDR, + BOTH_SET); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_udp_good) +{ + test_reuseport_udp_server_client_common(AF_INET6, + TEST_MY_IPV6_ADDR, + BOTH_SET); +} + + +static void test_reuseport_tcp_identical_clients_common(sa_family_t family, + char const *server_ip, + char const *client_ip) +{ + int server_sock = -1; + int client_sock1 = -1; + int client_sock2 = -1; + int accept_sock = 1; + + struct sockaddr server_addr; + struct sockaddr client_addr; + struct sockaddr connect_addr; + + struct sockaddr accept_addr; + socklen_t accept_addr_len = sizeof(accept_addr); + + /* Create sockets */ + prepare_sock_tcp(family, server_ip, LOCAL_PORT, &server_sock, &server_addr); + prepare_sock_tcp(family, client_ip, LOCAL_PORT + 1, &client_sock1, &client_addr); + prepare_sock_tcp(family, client_ip, LOCAL_PORT, &client_sock2, &connect_addr); + + /* Enable SO_REUSEPORT option for the two sockets */ + test_enable_reuseport(client_sock1); + test_enable_reuseport(client_sock2); + + /* Bind server socket */ + test_bind_success(server_sock, &server_addr, sizeof(server_addr)); + + /* Start listening on the server socket */ + test_listen(server_sock); + + /* Bind the client sockets */ + test_bind_success(client_sock1, &client_addr, sizeof(client_addr)); + test_bind_success(client_sock2, &client_addr, sizeof(client_addr)); + + /* Connect the first client */ + test_connect_success(client_sock1, &connect_addr, sizeof(connect_addr)); + + /* Accept the first client */ + accept_sock = test_accept(server_sock, &accept_addr, &accept_addr_len); + + /* Connect the second client, should fail */ + test_connect_fail(client_sock2, (struct sockaddr *)&connect_addr, sizeof(connect_addr)); + + close(accept_sock); + close(client_sock1); + close(client_sock2); + close(server_sock); + + test_context_cleanup(); +} + +ZTEST_USER(socket_reuseport_test_suite, test_ipv4_tcp_identical_clients) +{ + test_reuseport_tcp_identical_clients_common(AF_INET, + TEST_IPV4_ANY_ADDR, + TEST_MY_IPV4_ADDR); +} + + +ZTEST_USER(socket_reuseport_test_suite, test_ipv6_tcp_identical_clients) +{ + test_reuseport_tcp_identical_clients_common(AF_INET6, + TEST_IPV6_ANY_ADDR, + TEST_MY_IPV6_ADDR); +} + +ZTEST_SUITE(socket_reuseport_test_suite, NULL, setup, NULL, NULL, NULL); diff --git a/tests/net/socket/reuseaddr/testcase.yaml b/tests/net/socket/reuseaddr_reuseport/testcase.yaml similarity index 100% rename from tests/net/socket/reuseaddr/testcase.yaml rename to tests/net/socket/reuseaddr_reuseport/testcase.yaml