blob: c5e6cb4633f3d4abb4ce9ef737f98ee955c71552 [file] [log] [blame]
/*
* Copyright (c) 2019 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/ztest_assert.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/net_ip.h>
#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(zsock_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(zsock_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(zsock_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(zsock_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(zsock_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(zsock_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(zsock_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(zsock_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(zsock_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 = zsock_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(zsock_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(zsock_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(zsock_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(zsock_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(zsock_recv(sock, buf, max_len, flags),
-1,
"recvfrom succeeded incorrectly");
zassert_equal(errno, EAGAIN, "recv() returned unexpected errno (%d)", errno);
}
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);
zsock_close(server_sock);
}
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));
}
zsock_close(server_sock1);
zsock_close(server_sock2);
}
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));
zsock_close(server_sock1);
zsock_close(server_sock2);
}
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 */
zsock_close(server_sock);
/* Close the accepted socket */
zsock_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));
zsock_close(client_sock);
zsock_close(server_sock);
/* Connection is in TIME_WAIT state, context will be released
* after K_MSEC(CONFIG_NET_TCP_TIME_WAIT_DELAY), so wait for it.
*/
k_sleep(K_MSEC(CONFIG_NET_TCP_TIME_WAIT_DELAY));
}
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);
zsock_close(server_sock);
}
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));
}
zsock_close(server_sock1);
zsock_close(server_sock2);
}
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), ZSOCK_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 = zsock_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), ZSOCK_MSG_DONTWAIT,
&accept_addr, &accept_addr_len);
rx_buf = 0;
test_recv_success(accept_sock, &rx_buf, sizeof(rx_buf), ZSOCK_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), ZSOCK_MSG_DONTWAIT,
&accept_addr, &accept_addr_len);
rx_buf = 0;
test_recv_fail(accept_sock, &rx_buf, sizeof(rx_buf), ZSOCK_MSG_DONTWAIT);
}
zsock_close(accept_sock);
zsock_close(client_sock);
zsock_close(server_sock);
}
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));
zsock_close(accept_sock);
zsock_close(client_sock1);
zsock_close(client_sock2);
zsock_close(server_sock);
/* Connection is in TIME_WAIT state, context will be released
* after K_MSEC(CONFIG_NET_TCP_TIME_WAIT_DELAY), so wait for it.
*/
k_sleep(K_MSEC(CONFIG_NET_TCP_TIME_WAIT_DELAY));
}
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);