blob: 72903cb0ee9666582e1e184cc89781b60045735b [file] [log] [blame]
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
#include <ztest_assert.h>
#include <fcntl.h>
#include <net/socket.h>
#include "../../socket_helpers.h"
#define TEST_STR_SMALL "test"
#define ANY_PORT 0
#define SERVER_PORT 4242
#define MAX_CONNS 5
#define TCP_TEARDOWN_TIMEOUT K_SECONDS(1)
static void test_bind(int sock, struct sockaddr *addr, socklen_t addrlen)
{
zassert_equal(bind(sock, addr, addrlen),
0,
"bind failed");
}
static void test_listen(int sock)
{
zassert_equal(listen(sock, MAX_CONNS),
0,
"listen failed");
}
static void test_connect(int sock, struct sockaddr *addr, socklen_t addrlen)
{
zassert_equal(connect(sock, addr, addrlen),
0,
"connect failed");
}
static void test_send(int sock, const void *buf, size_t len, int flags)
{
zassert_equal(send(sock, buf, len, flags),
len,
"send failed");
}
static void test_sendto(int sock, const void *buf, size_t len, int flags,
const struct sockaddr *addr, socklen_t addrlen)
{
zassert_equal(sendto(sock, buf, len, flags, addr, addrlen),
len,
"send failed");
}
static void test_accept(int sock, int *new_sock, struct sockaddr *addr,
socklen_t *addrlen)
{
zassert_not_null(new_sock, "null newsock");
*new_sock = accept(sock, addr, addrlen);
zassert_true(*new_sock >= 0, "accept failed");
}
static void test_accept_timeout(int sock, int *new_sock, struct sockaddr *addr,
socklen_t *addrlen)
{
zassert_not_null(new_sock, "null newsock");
*new_sock = accept(sock, addr, addrlen);
zassert_equal(*new_sock, -1, "accept succeed");
zassert_equal(errno, EAGAIN, "");
}
static void test_fcntl(int sock, int cmd, int val)
{
zassert_equal(fcntl(sock, cmd, val), 0, "fcntl failed");
}
static void test_recv(int sock, int flags)
{
ssize_t recved = 0;
char rx_buf[30] = {0};
recved = recv(sock, rx_buf, sizeof(rx_buf), flags);
zassert_equal(recved,
strlen(TEST_STR_SMALL),
"unexpected received bytes");
zassert_equal(strncmp(rx_buf, TEST_STR_SMALL, strlen(TEST_STR_SMALL)),
0,
"unexpected data");
}
static void test_recvfrom(int sock,
int flags,
struct sockaddr *addr,
socklen_t *addrlen)
{
ssize_t recved = 0;
char rx_buf[30] = {0};
recved = recvfrom(sock,
rx_buf,
sizeof(rx_buf),
flags,
addr,
addrlen);
zassert_equal(recved,
strlen(TEST_STR_SMALL),
"unexpected received bytes");
zassert_equal(strncmp(rx_buf, TEST_STR_SMALL, strlen(TEST_STR_SMALL)),
0,
"unexpected data");
}
static void test_close(int sock)
{
zassert_equal(close(sock),
0,
"close failed");
}
/* Test that EOF handling works correctly. Should be called with socket
* whose peer socket was closed.
*/
static void test_eof(int sock)
{
char rx_buf[1];
ssize_t recved;
/* Test that EOF properly detected. */
recved = recv(sock, rx_buf, sizeof(rx_buf), 0);
zassert_equal(recved, 0, "");
/* Calling again should be OK. */
recved = recv(sock, rx_buf, sizeof(rx_buf), 0);
zassert_equal(recved, 0, "");
/* Calling when TCP connection is fully torn down should be still OK. */
k_sleep(TCP_TEARDOWN_TIMEOUT);
recved = recv(sock, rx_buf, sizeof(rx_buf), 0);
zassert_equal(recved, 0, "");
}
void test_v4_send_recv(void)
{
/* Test if send() and recv() work on a ipv4 stream socket. */
int c_sock;
int s_sock;
int new_sock;
struct sockaddr_in c_saddr;
struct sockaddr_in s_saddr;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, ANY_PORT,
&c_sock, &c_saddr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_send(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0);
test_accept(s_sock, &new_sock, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in), "wrong addrlen");
test_recv(new_sock, MSG_PEEK);
test_recv(new_sock, 0);
test_close(c_sock);
test_eof(new_sock);
test_close(new_sock);
test_close(s_sock);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
void test_v6_send_recv(void)
{
/* Test if send() and recv() work on a ipv6 stream socket. */
int c_sock;
int s_sock;
int new_sock;
struct sockaddr_in6 c_saddr;
struct sockaddr_in6 s_saddr;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
prepare_sock_tcp_v6(CONFIG_NET_CONFIG_MY_IPV6_ADDR, ANY_PORT,
&c_sock, &c_saddr);
prepare_sock_tcp_v6(CONFIG_NET_CONFIG_MY_IPV6_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_send(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0);
test_accept(s_sock, &new_sock, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in6), "wrong addrlen");
test_recv(new_sock, MSG_PEEK);
test_recv(new_sock, 0);
test_close(c_sock);
test_eof(new_sock);
test_close(new_sock);
test_close(s_sock);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
void test_v4_sendto_recvfrom(void)
{
int c_sock;
int s_sock;
int new_sock;
struct sockaddr_in c_saddr;
struct sockaddr_in s_saddr;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, ANY_PORT,
&c_sock, &c_saddr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
(struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_accept(s_sock, &new_sock, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in), "wrong addrlen");
test_recvfrom(new_sock, MSG_PEEK, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in), "wrong addrlen");
test_recvfrom(new_sock, 0, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in), "wrong addrlen");
test_close(new_sock);
test_close(s_sock);
test_close(c_sock);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
void test_v6_sendto_recvfrom(void)
{
int c_sock;
int s_sock;
int new_sock;
struct sockaddr_in6 c_saddr;
struct sockaddr_in6 s_saddr;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
prepare_sock_tcp_v6(CONFIG_NET_CONFIG_MY_IPV6_ADDR, ANY_PORT,
&c_sock, &c_saddr);
prepare_sock_tcp_v6(CONFIG_NET_CONFIG_MY_IPV6_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
(struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_accept(s_sock, &new_sock, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in6), "wrong addrlen");
test_recvfrom(new_sock, MSG_PEEK, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in6), "wrong addrlen");
test_recvfrom(new_sock, 0, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in6), "wrong addrlen");
test_close(new_sock);
test_close(s_sock);
test_close(c_sock);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
void test_v4_sendto_recvfrom_null_dest(void)
{
/* For a stream socket, sendto() should ignore NULL dest address */
int c_sock;
int s_sock;
int new_sock;
struct sockaddr_in c_saddr;
struct sockaddr_in s_saddr;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, ANY_PORT,
&c_sock, &c_saddr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
(struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_accept(s_sock, &new_sock, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in), "wrong addrlen");
test_recvfrom(new_sock, 0, NULL, NULL);
test_close(new_sock);
test_close(s_sock);
test_close(c_sock);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
void test_v6_sendto_recvfrom_null_dest(void)
{
/* For a stream socket, sendto() should ignore NULL dest address */
int c_sock;
int s_sock;
int new_sock;
struct sockaddr_in6 c_saddr;
struct sockaddr_in6 s_saddr;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
prepare_sock_tcp_v6(CONFIG_NET_CONFIG_MY_IPV6_ADDR, ANY_PORT,
&c_sock, &c_saddr);
prepare_sock_tcp_v6(CONFIG_NET_CONFIG_MY_IPV6_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
(struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_accept(s_sock, &new_sock, &addr, &addrlen);
zassert_equal(addrlen, sizeof(struct sockaddr_in6), "wrong addrlen");
test_recvfrom(new_sock, 0, NULL, NULL);
test_close(new_sock);
test_close(s_sock);
test_close(c_sock);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
static void calc_net_context(struct net_context *context, void *user_data)
{
int *count = user_data;
(*count)++;
}
void test_open_close_immediately(void)
{
/* Test if socket closing works if done immediately after
* receiving SYN.
*/
int count_before = 0, count_after = 0;
struct sockaddr_in c_saddr;
struct sockaddr_in s_saddr;
int c_sock;
int s_sock;
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, ANY_PORT,
&c_sock, &c_saddr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
/* We should have two contexts open now */
net_context_foreach(calc_net_context, &count_before);
/* Try to connect to a port that is not accepting connections.
* The end result should be that we do not leak net_context.
*/
s_saddr.sin_port = htons(SERVER_PORT + 1);
zassert_not_equal(connect(c_sock, (struct sockaddr *)&s_saddr,
sizeof(s_saddr)),
0, "connect succeed");
test_close(c_sock);
/* After the client socket closing, the context count should be 1 */
net_context_foreach(calc_net_context, &count_after);
test_close(s_sock);
zassert_equal(count_before - 1, count_after,
"net_context still in use (before %d vs after %d)",
count_before - 1, count_after);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
void test_v4_accept_timeout(void)
{
/* Test if accept() will timeout properly */
int s_sock;
int new_sock;
u32_t tstamp;
struct sockaddr_in s_saddr;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
prepare_sock_tcp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, SERVER_PORT,
&s_sock, &s_saddr);
test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
test_listen(s_sock);
test_fcntl(s_sock, F_SETFL, O_NONBLOCK);
tstamp = k_uptime_get_32();
test_accept_timeout(s_sock, &new_sock, &addr, &addrlen);
zassert_true(k_uptime_get_32() - tstamp <= 100, "");
test_close(s_sock);
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
void test_main(void)
{
ztest_test_suite(
socket_tcp,
ztest_user_unit_test(test_v4_send_recv),
ztest_user_unit_test(test_v6_send_recv),
ztest_user_unit_test(test_v4_sendto_recvfrom),
ztest_user_unit_test(test_v6_sendto_recvfrom),
ztest_user_unit_test(test_v4_sendto_recvfrom_null_dest),
ztest_user_unit_test(test_v6_sendto_recvfrom_null_dest),
ztest_unit_test(test_open_close_immediately),
ztest_user_unit_test(test_v4_accept_timeout));
ztest_run_test_suite(socket_tcp);
}