tests: net: tcp2: Add tests for TCP recv data queueing
Make sure that received and out-of-order TCP segments are queued until we receive proper segments. Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
ef801886b6
commit
408a6ceff3
3 changed files with 209 additions and 16 deletions
|
@ -12,10 +12,10 @@ CONFIG_NET_IPV4=y
|
|||
CONFIG_NET_BUF=y
|
||||
|
||||
CONFIG_MAIN_STACK_SIZE=2048
|
||||
CONFIG_NET_PKT_RX_COUNT=20
|
||||
CONFIG_NET_PKT_TX_COUNT=20
|
||||
CONFIG_NET_BUF_RX_COUNT=20
|
||||
CONFIG_NET_BUF_TX_COUNT=20
|
||||
CONFIG_NET_PKT_RX_COUNT=30
|
||||
CONFIG_NET_PKT_TX_COUNT=30
|
||||
CONFIG_NET_BUF_RX_COUNT=30
|
||||
CONFIG_NET_BUF_TX_COUNT=30
|
||||
|
||||
CONFIG_NET_MAX_CONTEXTS=10
|
||||
CONFIG_NET_LOG=y
|
||||
|
@ -50,3 +50,5 @@ CONFIG_NET_LOG=y
|
|||
#CONFIG_NET_IPV4_LOG_LEVEL_DBG=y
|
||||
#CONFIG_NET_IPV6_LOG_LEVEL_DBG=y
|
||||
#CONFIG_NET_CORE_LOG_LEVEL_DBG=y
|
||||
|
||||
CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT=1000
|
||||
|
|
|
@ -32,6 +32,29 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_TCP_LOG_LEVEL);
|
|||
#define MY_PORT 4242
|
||||
#define PEER_PORT 4242
|
||||
|
||||
/* Data (1280 bytes) to be sent */
|
||||
static const char lorem_ipsum[] =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris id "
|
||||
"sodales lacus. Proin vel rhoncus sapien. Morbi semper, enim in "
|
||||
"ullamcorper luctus, urna mi efficitur ex, laoreet eleifend massa "
|
||||
"felis ac dui. Duis ut magna convallis, tristique leo eu, ornare "
|
||||
"eros. Mauris a neque dictum, lobortis quam ut, rutrum erat. Vivamus "
|
||||
"vulputate neque vel auctor porta. Duis consectetur justo ac molestie "
|
||||
"tristique. In hac habitasse platea dictumst. Cras augue metus, "
|
||||
"aliquet sodales elit eget suscipit rutrum tellus. Nulla ante purus, "
|
||||
"dictum id tellus at, mattis cursus lectus. Mauris fringilla eros "
|
||||
"lorem, in auctor erat consequat nec. Ut pharetra sollicitudin dolor "
|
||||
"vel laoreet. Praesent eu lectus a dolor fringilla aliquet varius "
|
||||
"eget erat. Ut vitae mauris commodo, feugiat arcu non vehicula nunc. "
|
||||
"Nam ac enim elit. Praesent sit amet erat massa. Suspendisse potenti. "
|
||||
"Etiam diam justo, tempus vel lobortis tincidunt, scelerisque vitae "
|
||||
"mauris. Aenean vestibulum venenatis dapibus. Curabitur id ullamcorper"
|
||||
" diam. Ut eu turpis mauris. Aliquam et ligula est. Proin a velit non "
|
||||
"velit interdum vulputate. Proin vehicula eleifend suscipit. Cras "
|
||||
"condimentum non massa egestas tempor. Donec quis scelerisque est, id "
|
||||
"suscipit neque. Ut lobortis cursus ultrices. Aenean malesuada, nibh "
|
||||
"ut laoreet.";
|
||||
|
||||
static struct in_addr my_addr = { { { 192, 0, 2, 1 } } };
|
||||
static struct sockaddr_in my_addr_s = {
|
||||
.sin_family = AF_INET,
|
||||
|
@ -95,12 +118,15 @@ static void handle_server_test(sa_family_t af, struct tcphdr *th);
|
|||
static void handle_syn_resend(void);
|
||||
static void handle_client_fin_wait_2_test(sa_family_t af, struct tcphdr *th);
|
||||
static void handle_client_closing_test(sa_family_t af, struct tcphdr *th);
|
||||
static void handle_server_recv_out_of_order(struct net_pkt *pkt);
|
||||
|
||||
static void verify_flags(struct tcphdr *th, uint8_t flags,
|
||||
const char *fun, int line)
|
||||
{
|
||||
if (!(th && FL(&th->th_flags, ==, flags))) {
|
||||
zassert_true(false, "%s:%d flags mismatch", fun, line);
|
||||
zassert_true(false,
|
||||
"%s:%d flags mismatch (0x%04x vs 0x%04x)",
|
||||
fun, line, th->th_flags, flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,8 +208,10 @@ static uint8_t tcp_options[20] = {
|
|||
0x03, 0x03, 0x07 /* Win scale*/ };
|
||||
|
||||
static struct net_pkt *tester_prepare_tcp_pkt(sa_family_t af,
|
||||
uint16_t src_port, uint16_t dst_port,
|
||||
uint8_t flags, uint8_t *data,
|
||||
uint16_t src_port,
|
||||
uint16_t dst_port,
|
||||
uint8_t flags,
|
||||
const uint8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct tcphdr);
|
||||
|
@ -302,7 +330,8 @@ static struct net_pkt *prepare_ack_packet(sa_family_t af, uint16_t src_port,
|
|||
}
|
||||
|
||||
static struct net_pkt *prepare_data_packet(sa_family_t af, uint16_t src_port,
|
||||
uint16_t dst_port, uint8_t *data,
|
||||
uint16_t dst_port,
|
||||
const uint8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
return tester_prepare_tcp_pkt(af, src_port, dst_port, PSH | ACK, data,
|
||||
|
@ -382,6 +411,9 @@ static int tester_send(const struct device *dev, struct net_pkt *pkt)
|
|||
case 8:
|
||||
handle_client_closing_test(net_pkt_family(pkt), &th);
|
||||
break;
|
||||
case 9:
|
||||
handle_server_recv_out_of_order(pkt);
|
||||
break;
|
||||
default:
|
||||
zassert_true(false, "Undefined test case");
|
||||
}
|
||||
|
@ -603,8 +635,6 @@ static void handle_server_test(sa_family_t af, struct tcphdr *th)
|
|||
|
||||
switch (t_state) {
|
||||
case T_SYN:
|
||||
seq = 0U;
|
||||
ack = 0U;
|
||||
reply = prepare_syn_packet(af, htons(MY_PORT),
|
||||
htons(PEER_PORT));
|
||||
t_state = T_SYN_ACK;
|
||||
|
@ -1172,14 +1202,16 @@ static void test_client_closing_ipv6(void)
|
|||
k_sleep(K_MSEC(CONFIG_NET_TCP_TIME_WAIT_DELAY));
|
||||
}
|
||||
|
||||
static struct net_context *create_server_socket(void)
|
||||
static struct net_context *create_server_socket(uint32_t my_seq,
|
||||
uint32_t my_ack)
|
||||
{
|
||||
struct net_context *ctx;
|
||||
int ret;
|
||||
|
||||
t_state = T_SYN;
|
||||
test_case_no = 5;
|
||||
seq = ack = 0;
|
||||
seq = my_seq;
|
||||
ack = my_ack;
|
||||
|
||||
ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, &ctx);
|
||||
if (ret < 0) {
|
||||
|
@ -1248,7 +1280,7 @@ static void check_rst_succeed(struct net_context *ctx,
|
|||
|
||||
/* Make sure that various other corner cases work */
|
||||
if (ctx == NULL) {
|
||||
ctx = create_server_socket();
|
||||
ctx = create_server_socket(0, 0);
|
||||
}
|
||||
|
||||
/* Another valid seq in the RST packet */
|
||||
|
@ -1279,7 +1311,7 @@ static void test_client_invalid_rst(void)
|
|||
struct tcp *conn;
|
||||
uint16_t wnd;
|
||||
|
||||
ctx = create_server_socket();
|
||||
ctx = create_server_socket(0, 0);
|
||||
|
||||
conn = ctx->tcp;
|
||||
wnd = conn->recv_win;
|
||||
|
@ -1292,6 +1324,158 @@ static void test_client_invalid_rst(void)
|
|||
check_rst_succeed(ctx, wnd - 1);
|
||||
check_rst_succeed(NULL, 0);
|
||||
check_rst_succeed(NULL, 1);
|
||||
|
||||
net_tcp_put(ctx);
|
||||
}
|
||||
|
||||
#define MAX_DATA 100
|
||||
static uint32_t expected_ack = MAX_DATA + 1 - 15;
|
||||
static struct net_context *ooo_ctx;
|
||||
|
||||
static void handle_server_recv_out_of_order(struct net_pkt *pkt)
|
||||
{
|
||||
struct tcphdr th;
|
||||
int ret;
|
||||
|
||||
ret = read_tcp_header(pkt, &th);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Verify that we received all the queued data */
|
||||
zassert_equal(expected_ack, ntohl(th.th_ack),
|
||||
"Not all pending data received. "
|
||||
"Expected ACK %u but got %u",
|
||||
expected_ack, ntohl(th.th_ack));
|
||||
|
||||
test_sem_give();
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
zassert_true(false, "%s failed", __func__);
|
||||
net_pkt_unref(pkt);
|
||||
}
|
||||
|
||||
static void test_server_recv_out_of_order_data(void)
|
||||
{
|
||||
const uint8_t *data = lorem_ipsum + 10;
|
||||
struct net_pkt *pkt;
|
||||
int ret, i;
|
||||
|
||||
/* Only run the tests if queueing is enabled */
|
||||
if (CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Start the sequence numbering so that we will wrap it (just for
|
||||
* testing purposes)
|
||||
*/
|
||||
ooo_ctx = create_server_socket(-15U, -15U);
|
||||
|
||||
/* This will force the packet to be routed to our checker func
|
||||
* handle_server_recv_out_of_order()
|
||||
*/
|
||||
test_case_no = 9;
|
||||
|
||||
/* First packet will be out-of-order */
|
||||
seq += MAX_DATA - 20;
|
||||
pkt = prepare_data_packet(AF_INET6, htons(MY_PORT), htons(PEER_PORT),
|
||||
&data[seq], 10);
|
||||
zassert_not_null(pkt, "Cannot create pkt");
|
||||
|
||||
ret = net_recv_data(iface, pkt);
|
||||
zassert_true(ret == 0, "recv data failed (%d)", ret);
|
||||
|
||||
/* Let the IP stack to process the packet properly */
|
||||
k_msleep(1);
|
||||
|
||||
/* Then we send a packet that is after the previous packet */
|
||||
seq += 10;
|
||||
|
||||
pkt = prepare_data_packet(AF_INET6, htons(MY_PORT), htons(PEER_PORT),
|
||||
&data[seq], 10);
|
||||
zassert_not_null(pkt, "Cannot create pkt");
|
||||
|
||||
ret = net_recv_data(iface, pkt);
|
||||
zassert_true(ret == 0, "recv data failed (%d)", ret);
|
||||
|
||||
k_msleep(1);
|
||||
|
||||
/* Then send packets that are before the first packet. The final packet
|
||||
* will flush the receive queue as the seq will be 1
|
||||
*/
|
||||
for (i = MAX_DATA - 10; i > 0; i -= 10) {
|
||||
seq -= 10;
|
||||
|
||||
pkt = prepare_data_packet(AF_INET6, htons(MY_PORT),
|
||||
htons(PEER_PORT),
|
||||
&data[i], 10);
|
||||
zassert_not_null(pkt, "Cannot create pkt");
|
||||
|
||||
ret = net_recv_data(iface, pkt);
|
||||
zassert_true(ret == 0, "recv data failed (%d)", ret);
|
||||
|
||||
k_msleep(1);
|
||||
}
|
||||
|
||||
/* Then the final packet that will flush the receive queue */
|
||||
pkt = prepare_data_packet(AF_INET6, htons(MY_PORT),
|
||||
htons(PEER_PORT),
|
||||
&data[i], 10);
|
||||
zassert_not_null(pkt, "Cannot create pkt");
|
||||
|
||||
ret = net_recv_data(iface, pkt);
|
||||
zassert_true(ret == 0, "recv data failed (%d)", ret);
|
||||
|
||||
/* Peer will release the semaphone after it sends proper ACK to the
|
||||
* queued data.
|
||||
*/
|
||||
test_sem_take(K_MSEC(1000), __LINE__);
|
||||
}
|
||||
|
||||
/* This test expects that the system is in correct state after a call to
|
||||
* test_server_recv_out_of_order_data(), so this test must be run after that
|
||||
* test.
|
||||
*/
|
||||
static void test_server_timeout_out_of_order_data(void)
|
||||
{
|
||||
const uint8_t *data = lorem_ipsum + 10;
|
||||
struct net_pkt *pkt;
|
||||
int ret, i;
|
||||
|
||||
if (CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
k_sem_reset(&test_sem);
|
||||
|
||||
/* The +1 will cause the seq to be not sequential thus we should
|
||||
* get a timeout.
|
||||
*/
|
||||
seq = expected_ack + MAX_DATA + 1;
|
||||
|
||||
/* Then special handling to send out-of-order TCP segments */
|
||||
for (i = MAX_DATA; i > 10; i -= 10) {
|
||||
seq -= 10;
|
||||
|
||||
pkt = prepare_data_packet(AF_INET6, htons(MY_PORT),
|
||||
htons(PEER_PORT),
|
||||
&data[i], 10);
|
||||
zassert_not_null(pkt, "Cannot create pkt");
|
||||
|
||||
ret = net_recv_data(iface, pkt);
|
||||
zassert_true(ret == 0, "recv data failed (%d)", ret);
|
||||
}
|
||||
|
||||
/* Because the pending seq values are not sequential,
|
||||
* the recv queue in tcp2 should timeout.
|
||||
*/
|
||||
ret = k_sem_take(&test_sem,
|
||||
K_MSEC(CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT + 10));
|
||||
zassert_equal(ret, -EAGAIN, "semaphore did not time out (%d)", ret);
|
||||
|
||||
net_tcp_put(ooo_ctx);
|
||||
}
|
||||
|
||||
/** Test case main entry */
|
||||
|
@ -1307,7 +1491,9 @@ void test_main(void)
|
|||
ztest_unit_test(test_client_syn_resend),
|
||||
ztest_unit_test(test_client_fin_wait_2_ipv4),
|
||||
ztest_unit_test(test_client_closing_ipv6),
|
||||
ztest_unit_test(test_client_invalid_rst)
|
||||
ztest_unit_test(test_client_invalid_rst),
|
||||
ztest_unit_test(test_server_recv_out_of_order_data),
|
||||
ztest_unit_test(test_server_timeout_out_of_order_data)
|
||||
);
|
||||
|
||||
ztest_run_test_suite(test_tcp_fn);
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
common:
|
||||
depends_on: netif
|
||||
tags: net tcp2
|
||||
tests:
|
||||
net.tcp2.simple:
|
||||
tags: net tcp2
|
||||
extra_configs:
|
||||
- CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT=1000
|
||||
net.tcp2.no_recv_queue:
|
||||
extra_configs:
|
||||
- CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT=0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue