blob: 47fcb8608b33316086bb5a814165befb9f7ba622 [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 <zephyr/ztest_assert.h>
#include <zephyr/net/socket.h>
#include <zephyr/sys/fdtable.h>
#include "../../socket_helpers.h"
#define BUF_AND_SIZE(buf) buf, sizeof(buf) - 1
#define STRLEN(buf) (sizeof(buf) - 1)
#define TEST_STR_SMALL "test"
#define MY_IPV6_ADDR "::1"
#define ANY_PORT 0
#define SERVER_PORT 4242
#define CLIENT_PORT 9898
/* On QEMU, poll() which waits takes +10ms from the requested time. */
#define FUZZ 10
ZTEST(net_socket_poll, test_poll)
{
int res;
int c_sock;
int s_sock;
int c_sock_tcp;
int s_sock_tcp;
struct sockaddr_in6 c_addr;
struct sockaddr_in6 s_addr;
struct pollfd pollfds[2];
struct pollfd pollout[1];
uint32_t tstamp;
ssize_t len;
char buf[10];
prepare_sock_udp_v6(MY_IPV6_ADDR, CLIENT_PORT, &c_sock, &c_addr);
prepare_sock_udp_v6(MY_IPV6_ADDR, SERVER_PORT, &s_sock, &s_addr);
prepare_sock_tcp_v6(MY_IPV6_ADDR, CLIENT_PORT, &c_sock_tcp, &c_addr);
prepare_sock_tcp_v6(MY_IPV6_ADDR, SERVER_PORT, &s_sock_tcp, &s_addr);
res = bind(s_sock, (struct sockaddr *)&s_addr, sizeof(s_addr));
zassert_equal(res, 0, "bind failed");
res = connect(c_sock, (struct sockaddr *)&s_addr, sizeof(s_addr));
zassert_equal(res, 0, "connect failed");
memset(pollfds, 0, sizeof(pollfds));
pollfds[0].fd = c_sock;
pollfds[0].events = POLLIN;
pollfds[1].fd = s_sock;
pollfds[1].events = POLLIN;
/* Poll non-ready fd's with timeout of 0 */
tstamp = k_uptime_get_32();
res = poll(pollfds, ARRAY_SIZE(pollfds), 0);
zassert_true(k_uptime_get_32() - tstamp <= FUZZ, "");
zassert_equal(res, 0, "");
zassert_equal(pollfds[0].fd, c_sock, "");
zassert_equal(pollfds[0].events, POLLIN, "");
zassert_equal(pollfds[0].revents, 0, "");
zassert_equal(pollfds[1].fd, s_sock, "");
zassert_equal(pollfds[1].events, POLLIN, "");
zassert_equal(pollfds[1].revents, 0, "");
/* Poll non-ready fd's with timeout of 30 */
tstamp = k_uptime_get_32();
res = poll(pollfds, ARRAY_SIZE(pollfds), 30);
tstamp = k_uptime_get_32() - tstamp;
zassert_true(tstamp >= 30U && tstamp <= 30 + FUZZ * 2, "tstamp %d",
tstamp);
zassert_equal(res, 0, "");
/* Send pkt for s_sock and poll with timeout of 10 */
len = send(c_sock, BUF_AND_SIZE(TEST_STR_SMALL), 0);
zassert_equal(len, STRLEN(TEST_STR_SMALL), "invalid send len");
tstamp = k_uptime_get_32();
res = poll(pollfds, ARRAY_SIZE(pollfds), 30);
tstamp = k_uptime_get_32() - tstamp;
zassert_true(tstamp <= FUZZ, "");
zassert_equal(res, 1, "");
zassert_equal(pollfds[0].fd, c_sock, "");
zassert_equal(pollfds[0].events, POLLIN, "");
zassert_equal(pollfds[0].revents, 0, "");
zassert_equal(pollfds[1].fd, s_sock, "");
zassert_equal(pollfds[1].events, POLLIN, "");
zassert_equal(pollfds[1].revents, POLLIN, "");
/* Recv pkt from s_sock and ensure no poll events happen */
len = recv(s_sock, BUF_AND_SIZE(buf), 0);
zassert_equal(len, STRLEN(TEST_STR_SMALL), "invalid recv len");
tstamp = k_uptime_get_32();
res = poll(pollfds, ARRAY_SIZE(pollfds), 0);
zassert_true(k_uptime_get_32() - tstamp <= FUZZ, "");
zassert_equal(res, 0, "");
zassert_equal(pollfds[1].revents, 0, "");
/* Make sure that POLLOUT does not wait if not really needed */
memset(pollout, 0, sizeof(pollout));
pollout[0].fd = c_sock;
pollout[0].events = POLLOUT;
res = connect(c_sock, (const struct sockaddr *)&s_addr,
sizeof(s_addr));
zassert_equal(res, 0, "");
tstamp = k_uptime_get_32();
res = poll(pollout, ARRAY_SIZE(pollout), 200);
zassert_true(k_uptime_get_32() - tstamp < 100, "");
zassert_equal(res, 1, "");
zassert_equal(pollout[0].revents, POLLOUT, "");
/* First test that TCP POLLOUT will not wait if there is enough
* room in TCP window
*/
memset(pollout, 0, sizeof(pollout));
pollout[0].fd = c_sock_tcp;
pollout[0].events = POLLOUT;
res = bind(s_sock_tcp, (struct sockaddr *)&s_addr, sizeof(s_addr));
zassert_equal(res, 0, "");
res = listen(s_sock_tcp, 0);
zassert_equal(res, 0, "");
res = connect(c_sock_tcp, (const struct sockaddr *)&s_addr,
sizeof(s_addr));
zassert_equal(res, 0, "");
tstamp = k_uptime_get_32();
res = poll(pollout, ARRAY_SIZE(pollout), 200);
zassert_true(k_uptime_get_32() - tstamp < 100, "");
zassert_equal(res, 1, "");
zassert_equal(pollout[0].revents, POLLOUT, "");
/* Let the network stack run */
k_msleep(10);
res = close(c_sock_tcp);
zassert_equal(res, 0, "close failed");
res = close(s_sock_tcp);
zassert_equal(res, 0, "close failed");
/* Close one socket and ensure POLLNVAL happens */
res = close(c_sock);
zassert_equal(res, 0, "close failed");
tstamp = k_uptime_get_32();
res = poll(pollfds, ARRAY_SIZE(pollfds), 0);
zassert_true(k_uptime_get_32() - tstamp <= FUZZ, "");
zassert_equal(res, 1, "");
zassert_equal(pollfds[0].revents, POLLNVAL, "");
zassert_equal(pollfds[1].revents, 0, "");
res = close(s_sock);
zassert_equal(res, 0, "close failed");
}
#define TEST_SNDBUF_SIZE CONFIG_NET_TCP_MAX_RECV_WINDOW_SIZE
ZTEST(net_socket_poll, test_pollout_tcp)
{
int res;
int c_sock;
int s_sock;
int new_sock;
struct sockaddr_in6 c_addr;
struct sockaddr_in6 s_addr;
struct pollfd pollout[1];
char buf[TEST_SNDBUF_SIZE] = { };
prepare_sock_tcp_v6(MY_IPV6_ADDR, CLIENT_PORT, &c_sock, &c_addr);
prepare_sock_tcp_v6(MY_IPV6_ADDR, SERVER_PORT, &s_sock, &s_addr);
res = bind(s_sock, (struct sockaddr *)&s_addr, sizeof(s_addr));
zassert_equal(res, 0, "");
res = listen(s_sock, 0);
zassert_equal(res, 0, "");
res = connect(c_sock, (const struct sockaddr *)&s_addr,
sizeof(s_addr));
zassert_equal(res, 0, "");
new_sock = accept(s_sock, NULL, NULL);
zassert_true(new_sock >= 0, "");
k_msleep(10);
/* POLLOUT should be reported after connecting */
memset(pollout, 0, sizeof(pollout));
pollout[0].fd = c_sock;
pollout[0].events = POLLOUT;
res = poll(pollout, ARRAY_SIZE(pollout), 10);
zassert_equal(res, 1, "");
zassert_equal(pollout[0].revents, POLLOUT, "");
/* POLLOUT should not be reported after filling the window */
res = send(c_sock, buf, sizeof(buf), 0);
zassert_equal(res, sizeof(buf), "");
memset(pollout, 0, sizeof(pollout));
pollout[0].fd = c_sock;
pollout[0].events = POLLOUT;
res = poll(pollout, ARRAY_SIZE(pollout), 10);
zassert_equal(res, 0, "%d", pollout[0].revents);
zassert_equal(pollout[0].revents, 0, "");
/* POLLOUT should be reported again after consuming the data server
* side.
*/
res = recv(new_sock, buf, sizeof(buf), 0);
zassert_equal(res, sizeof(buf), "");
memset(pollout, 0, sizeof(pollout));
pollout[0].fd = c_sock;
pollout[0].events = POLLOUT;
/* Wait longer this time to give TCP stack a chance to send ZWP. */
res = poll(pollout, ARRAY_SIZE(pollout), 500);
zassert_equal(res, 1, "");
zassert_equal(pollout[0].revents, POLLOUT, "");
k_msleep(10);
/* Finalize the test */
res = close(c_sock);
zassert_equal(res, 0, "close failed");
res = close(s_sock);
zassert_equal(res, 0, "close failed");
res = close(new_sock);
zassert_equal(res, 0, "close failed");
}
ZTEST_SUITE(net_socket_poll, NULL, NULL, NULL, NULL, NULL);