diff --git a/tests/net/socket/tcp/src/main.c b/tests/net/socket/tcp/src/main.c index 205c038b9df..b619494d3a2 100644 --- a/tests/net/socket/tcp/src/main.c +++ b/tests/net/socket/tcp/src/main.c @@ -771,6 +771,106 @@ void test_connect_timeout(void) "net_context still in use"); } +#define TCP_CLOSE_FAILURE_TIMEOUT 60000 + +void test_close_obstructed(void) +{ + /* Test if socket closing even when there is not communication + * possible any more + */ + uint32_t start_time, time_diff; + int count_before = 0, count_after = 0; + struct sockaddr_in c_saddr; + struct sockaddr_in s_saddr; + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int c_sock; + int s_sock; + int new_sock; + int dropped_packets_before = 0; + int dropped_packets_after = 0; + + 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); + + zassert_equal(connect(c_sock, (struct sockaddr *)&s_saddr, + sizeof(s_saddr)), + 0, "connect not succeed"); + test_accept(s_sock, &new_sock, &addr, &addrlen); + + /* We should have two contexts open now */ + net_context_foreach(calc_net_context, &count_before); + + /* Break the communication */ + loopback_set_packet_drop_ratio(1.0f); + + dropped_packets_before = loopback_get_num_dropped_packets(); + + test_close(c_sock); + + start_time = k_uptime_get_32(); + + /* After the client socket closing, the context count should be 1 less */ + net_context_foreach(calc_net_context, &count_after); + + time_diff = k_uptime_get_32() - start_time; + + /* Eventually the client socket should be cleaned up */ + while ((count_before == count_after) && (time_diff < TCP_CLOSE_FAILURE_TIMEOUT)) { + count_after = 0; + net_context_foreach(calc_net_context, &count_after); + k_sleep(K_MSEC(50)); + time_diff = k_uptime_get_32() - start_time; + } + + zassert_equal(count_before - 1, count_after, + "net_context still in use (before %d vs after %d)", + count_before - 1, count_after); + + dropped_packets_after = loopback_get_num_dropped_packets(); + int dropped_packets = dropped_packets_after - dropped_packets_before; + + /* At least some packet should have been dropped */ + zassert_equal(dropped_packets, + CONFIG_NET_TCP_RETRY_COUNT + 1, + "Incorrect number of FIN retries, got %i, expected %i", + dropped_packets, CONFIG_NET_TCP_RETRY_COUNT+1); + + test_close(new_sock); + test_close(s_sock); + + start_time = k_uptime_get_32(); + + k_sleep(TCP_TEARDOWN_TIMEOUT); + + /* After the client socket closing, the context count should be 0 */ + count_after = 0; + net_context_foreach(calc_net_context, &count_after); + + /* Eventually the client socket should be cleaned up */ + while ((count_after > 0) && (time_diff < TCP_CLOSE_FAILURE_TIMEOUT)) { + count_after = 0; + net_context_foreach(calc_net_context, &count_after); + k_sleep(K_MSEC(50)); + time_diff = k_uptime_get_32() - start_time; + } + + zassert_equal(count_after, 0, "net_context still in use"); + + /* After everything is closed, we expect no more dropped packets */ + dropped_packets_before = loopback_get_num_dropped_packets(); + k_sleep(K_SECONDS(2)); + dropped_packets_after = loopback_get_num_dropped_packets(); + + zassert_equal(dropped_packets_before, dropped_packets_after, + "packets after close"); +} + void test_v4_accept_timeout(void) { /* Test if accept() will timeout properly */ @@ -1336,6 +1436,8 @@ void test_main(void) ztest_unit_test(test_open_close_immediately), ztest_unit_test_setup_teardown(test_connect_timeout, restore_packet_loss_ratio, restore_packet_loss_ratio), + ztest_unit_test_setup_teardown(test_close_obstructed, + restore_packet_loss_ratio, restore_packet_loss_ratio), ztest_user_unit_test(test_v4_accept_timeout), ztest_unit_test(test_so_type), ztest_unit_test(test_so_protocol),