zephyr/tests/net/socket/poll/src/main.c
Sjors Hettinga a28a656aa0 net: tcp: Avoid double acknowlegding retransmitted data
In the FIN_WAIT_1 any incoming data is dropped, but anknowledged. Add a
check to see if the data is already acknowleged to prevent double
acknowledging of the data and bringing the acknowlegde counter out of
sync. When the acknowledge counter gets out of sync, the connection will
never properly terminate any more.

Signed-off-by: Sjors Hettinga <s.a.hettinga@gmail.com>
2023-05-15 15:25:51 +02:00

256 lines
7.1 KiB
C

/*
* 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
#define TCP_TEARDOWN_TIMEOUT K_SECONDS(3)
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");
k_sleep(TCP_TEARDOWN_TIMEOUT);
}
#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);