From 3da2dbdce4c7395eb2fb6aa1aa98b17b705c32cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:54:09 +0100 Subject: [PATCH 1/4] Socket tests renaming --- nano/core_test/socket.cpp | 120 +++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index fc19e26863..6a224daf30 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -19,7 +19,63 @@ using namespace std::chrono_literals; -TEST (socket, max_connections) +TEST (socket_functions, limited_subnet_address) +{ + auto address = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); + auto network = nano::transport::socket_functions::get_ipv6_subnet_address (address.to_v6 (), 32); // network prefix = 32. + ASSERT_EQ ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713/32", network.to_string ()); + ASSERT_EQ ("a41d:b7b2::/32", network.canonical ().to_string ()); +} + +TEST (socket_functions, first_ipv6_subnet_address) +{ + auto address = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); + auto first_address = nano::transport::socket_functions::first_ipv6_subnet_address (address.to_v6 (), 32); // network prefix = 32. + ASSERT_EQ ("a41d:b7b2::", first_address.to_string ()); +} + +TEST (socket_functions, last_ipv6_subnet_address) +{ + auto address = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); + auto last_address = nano::transport::socket_functions::last_ipv6_subnet_address (address.to_v6 (), 32); // network prefix = 32. + ASSERT_EQ ("a41d:b7b2:ffff:ffff:ffff:ffff:ffff:ffff", last_address.to_string ()); +} + +TEST (socket_functions, count_subnetwork_connections) +{ + nano::test::system system; + auto node = system.add_node (); + + auto address0 = boost::asio::ip::make_address ("a41d:b7b1:ffff:ffff:ffff:ffff:ffff:ffff"); // out of network prefix + auto address1 = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); // referece address + auto address2 = boost::asio::ip::make_address ("a41d:b7b2::"); // start of the network range + auto address3 = boost::asio::ip::make_address ("a41d:b7b2::1"); + auto address4 = boost::asio::ip::make_address ("a41d:b7b2:ffff:ffff:ffff:ffff:ffff:ffff"); // end of the network range + auto address5 = boost::asio::ip::make_address ("a41d:b7b3::"); // out of the network prefix + auto address6 = boost::asio::ip::make_address ("a41d:b7b3::1"); // out of the network prefix + + auto connection0 = std::make_shared (*node); + auto connection1 = std::make_shared (*node); + auto connection2 = std::make_shared (*node); + auto connection3 = std::make_shared (*node); + auto connection4 = std::make_shared (*node); + auto connection5 = std::make_shared (*node); + auto connection6 = std::make_shared (*node); + + nano::transport::address_socket_mmap connections_per_address; + connections_per_address.emplace (address0, connection0); + connections_per_address.emplace (address1, connection1); + connections_per_address.emplace (address2, connection2); + connections_per_address.emplace (address3, connection3); + connections_per_address.emplace (address4, connection4); + connections_per_address.emplace (address5, connection5); + connections_per_address.emplace (address6, connection6); + + // Asserts it counts only the connections for the specified address and its network prefix. + ASSERT_EQ (4, nano::transport::socket_functions::count_subnetwork_connections (connections_per_address, address1.to_v6 (), 32)); +} + +TEST (tcp_listener, max_connections) { nano::test::system system; @@ -90,7 +146,7 @@ TEST (socket, max_connections) ASSERT_TIMELY_EQ (5s, connection_attempts, 8); // connections initiated by the client } -TEST (socket, max_connections_per_ip) +TEST (tcp_listener, max_connections_per_ip) { nano::test::system system; @@ -128,63 +184,7 @@ TEST (socket, max_connections_per_ip) ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1); } -TEST (socket, limited_subnet_address) -{ - auto address = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); - auto network = nano::transport::socket_functions::get_ipv6_subnet_address (address.to_v6 (), 32); // network prefix = 32. - ASSERT_EQ ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713/32", network.to_string ()); - ASSERT_EQ ("a41d:b7b2::/32", network.canonical ().to_string ()); -} - -TEST (socket, first_ipv6_subnet_address) -{ - auto address = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); - auto first_address = nano::transport::socket_functions::first_ipv6_subnet_address (address.to_v6 (), 32); // network prefix = 32. - ASSERT_EQ ("a41d:b7b2::", first_address.to_string ()); -} - -TEST (socket, last_ipv6_subnet_address) -{ - auto address = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); - auto last_address = nano::transport::socket_functions::last_ipv6_subnet_address (address.to_v6 (), 32); // network prefix = 32. - ASSERT_EQ ("a41d:b7b2:ffff:ffff:ffff:ffff:ffff:ffff", last_address.to_string ()); -} - -TEST (socket, count_subnetwork_connections) -{ - nano::test::system system; - auto node = system.add_node (); - - auto address0 = boost::asio::ip::make_address ("a41d:b7b1:ffff:ffff:ffff:ffff:ffff:ffff"); // out of network prefix - auto address1 = boost::asio::ip::make_address ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"); // referece address - auto address2 = boost::asio::ip::make_address ("a41d:b7b2::"); // start of the network range - auto address3 = boost::asio::ip::make_address ("a41d:b7b2::1"); - auto address4 = boost::asio::ip::make_address ("a41d:b7b2:ffff:ffff:ffff:ffff:ffff:ffff"); // end of the network range - auto address5 = boost::asio::ip::make_address ("a41d:b7b3::"); // out of the network prefix - auto address6 = boost::asio::ip::make_address ("a41d:b7b3::1"); // out of the network prefix - - auto connection0 = std::make_shared (*node); - auto connection1 = std::make_shared (*node); - auto connection2 = std::make_shared (*node); - auto connection3 = std::make_shared (*node); - auto connection4 = std::make_shared (*node); - auto connection5 = std::make_shared (*node); - auto connection6 = std::make_shared (*node); - - nano::transport::address_socket_mmap connections_per_address; - connections_per_address.emplace (address0, connection0); - connections_per_address.emplace (address1, connection1); - connections_per_address.emplace (address2, connection2); - connections_per_address.emplace (address3, connection3); - connections_per_address.emplace (address4, connection4); - connections_per_address.emplace (address5, connection5); - connections_per_address.emplace (address6, connection6); - - // Asserts it counts only the connections for the specified address and its network prefix. - ASSERT_EQ (4, nano::transport::socket_functions::count_subnetwork_connections (connections_per_address, address1.to_v6 (), 32)); -} - -TEST (socket, max_connections_per_subnetwork) +TEST (tcp_listener, max_connections_per_subnetwork) { nano::test::system system; @@ -225,7 +225,7 @@ TEST (socket, max_connections_per_subnetwork) ASSERT_TIMELY_EQ (5s, connection_attempts, max_subnetwork_connections + 1); } -TEST (socket, disabled_max_peers_per_ip) +TEST (tcp_listener, max_peers_per_ip) { nano::test::system system; From c6c12566c84bc955f3189ae4d9779047bd439b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:58:50 +0100 Subject: [PATCH 2/4] Move tcp_listener test into a single file --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/network.cpp | 84 --------- nano/core_test/socket.cpp | 190 --------------------- nano/core_test/tcp_listener.cpp | 291 ++++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+), 274 deletions(-) create mode 100644 nano/core_test/tcp_listener.cpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 0e197344c7..6f7c402ae0 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -57,6 +57,7 @@ add_executable( signal_manager.cpp socket.cpp system.cpp + tcp_listener.cpp telemetry.cpp thread_pool.cpp throttle.cpp diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 916c8dbf4d..bedc3aaf2a 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -547,90 +547,6 @@ TEST (network, endpoint_bad_fd) ASSERT_TIMELY_EQ (10s, system.nodes[0]->network.endpoint ().port (), 0); } -TEST (tcp_listener, tcp_node_id_handshake) -{ - nano::test::system system (1); - auto socket (std::make_shared (*system.nodes[0])); - auto bootstrap_endpoint (system.nodes[0]->tcp_listener.endpoint ()); - auto cookie (system.nodes[0]->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (bootstrap_endpoint))); - ASSERT_TRUE (cookie); - nano::node_id_handshake::query_payload query{ *cookie }; - nano::node_id_handshake node_id_handshake{ nano::dev::network_params.network, query }; - auto input (node_id_handshake.to_shared_const_buffer ()); - std::atomic write_done (false); - socket->async_connect (bootstrap_endpoint, [&input, socket, &write_done] (boost::system::error_code const & ec) { - ASSERT_FALSE (ec); - socket->async_write (input, [&input, &write_done] (boost::system::error_code const & ec, size_t size_a) { - ASSERT_FALSE (ec); - ASSERT_EQ (input.size (), size_a); - write_done = true; - }); - }); - - ASSERT_TIMELY (5s, write_done); - - nano::node_id_handshake::response_payload response_zero{ 0 }; - nano::node_id_handshake node_id_handshake_response{ nano::dev::network_params.network, std::nullopt, response_zero }; - auto output (node_id_handshake_response.to_bytes ()); - std::atomic done (false); - socket->async_read (output, output->size (), [&output, &done] (boost::system::error_code const & ec, size_t size_a) { - ASSERT_FALSE (ec); - ASSERT_EQ (output->size (), size_a); - done = true; - }); - ASSERT_TIMELY (5s, done); -} - -// Test disabled because it's failing intermittently. -// PR in which it got disabled: https://github.com/nanocurrency/nano-node/pull/3611 -// Issue for investigating it: https://github.com/nanocurrency/nano-node/issues/3615 -TEST (tcp_listener, DISABLED_tcp_listener_timeout_empty) -{ - nano::test::system system (1); - auto node0 (system.nodes[0]); - auto socket (std::make_shared (*node0)); - std::atomic connected (false); - socket->async_connect (node0->tcp_listener.endpoint (), [&connected] (boost::system::error_code const & ec) { - ASSERT_FALSE (ec); - connected = true; - }); - ASSERT_TIMELY (5s, connected); - bool disconnected (false); - system.deadline_set (std::chrono::seconds (6)); - while (!disconnected) - { - disconnected = node0->tcp_listener.connection_count () == 0; - ASSERT_NO_ERROR (system.poll ()); - } -} - -TEST (tcp_listener, tcp_listener_timeout_node_id_handshake) -{ - nano::test::system system (1); - auto node0 (system.nodes[0]); - auto socket (std::make_shared (*node0)); - auto cookie (node0->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (node0->tcp_listener.endpoint ()))); - ASSERT_TRUE (cookie); - nano::node_id_handshake::query_payload query{ *cookie }; - nano::node_id_handshake node_id_handshake{ nano::dev::network_params.network, query }; - auto channel = std::make_shared (*node0, socket); - socket->async_connect (node0->tcp_listener.endpoint (), [&node_id_handshake, channel] (boost::system::error_code const & ec) { - ASSERT_FALSE (ec); - channel->send (node_id_handshake, [] (boost::system::error_code const & ec, size_t size_a) { - ASSERT_FALSE (ec); - }); - }); - ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::tcp_server, nano::stat::detail::node_id_handshake) != 0); - ASSERT_EQ (node0->tcp_listener.connection_count (), 1); - bool disconnected (false); - system.deadline_set (std::chrono::seconds (20)); - while (!disconnected) - { - disconnected = node0->tcp_listener.connection_count () == 0; - ASSERT_NO_ERROR (system.poll ()); - } -} - // Test disabled because it's failing repeatedly for Windows + LMDB. // PR in which it got disabled: https://github.com/nanocurrency/nano-node/pull/3622 // Issue for investigating it: https://github.com/nanocurrency/nano-node/issues/3621 diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index 6a224daf30..89066f87c0 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -75,196 +75,6 @@ TEST (socket_functions, count_subnetwork_connections) ASSERT_EQ (4, nano::transport::socket_functions::count_subnetwork_connections (connections_per_address, address1.to_v6 (), 32)); } -TEST (tcp_listener, max_connections) -{ - nano::test::system system; - - nano::node_flags node_flags; - nano::node_config node_config = system.default_config (); - node_config.tcp.max_inbound_connections = 2; - auto node = system.add_node (node_config, node_flags); - - // client side connection tracking - std::atomic connection_attempts = 0; - auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { - ASSERT_EQ (ec_a.value (), 0); - ++connection_attempts; - }; - - // start 3 clients, 2 will persist but 1 will be dropped - auto client1 = std::make_shared (*node); - client1->async_connect (node->network.endpoint (), connect_handler); - - auto client2 = std::make_shared (*node); - client2->async_connect (node->network.endpoint (), connect_handler); - - auto client3 = std::make_shared (*node); - client3->async_connect (node->network.endpoint (), connect_handler); - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 2); - ASSERT_ALWAYS_EQ (1s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 2); - ASSERT_TIMELY_EQ (5s, connection_attempts, 3); - - // create space for one socket and fill the connections table again - { - auto sockets1 = node->tcp_listener.sockets (); - ASSERT_EQ (sockets1.size (), 2); - sockets1[0]->close (); - } - ASSERT_TIMELY_EQ (10s, node->tcp_listener.sockets ().size (), 1); - - auto client4 = std::make_shared (*node); - client4->async_connect (node->network.endpoint (), connect_handler); - - auto client5 = std::make_shared (*node); - client5->async_connect (node->network.endpoint (), connect_handler); - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 3); - ASSERT_ALWAYS_EQ (1s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 3); - ASSERT_TIMELY_EQ (5s, connection_attempts, 5); - - // close all existing sockets and fill the connections table again - { - auto sockets2 = node->tcp_listener.sockets (); - ASSERT_EQ (sockets2.size (), 2); - sockets2[0]->close (); - sockets2[1]->close (); - } - ASSERT_TIMELY_EQ (10s, node->tcp_listener.sockets ().size (), 0); - - auto client6 = std::make_shared (*node); - client6->async_connect (node->network.endpoint (), connect_handler); - - auto client7 = std::make_shared (*node); - client7->async_connect (node->network.endpoint (), connect_handler); - - auto client8 = std::make_shared (*node); - client8->async_connect (node->network.endpoint (), connect_handler); - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 5); - ASSERT_ALWAYS_EQ (1s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 5); - ASSERT_TIMELY_EQ (5s, connection_attempts, 8); // connections initiated by the client -} - -TEST (tcp_listener, max_connections_per_ip) -{ - nano::test::system system; - - nano::node_flags node_flags; - nano::node_config node_config = system.default_config (); - node_config.network.max_peers_per_ip = 3; - auto node = system.add_node (node_config, node_flags); - ASSERT_FALSE (node->flags.disable_max_peers_per_ip); - - auto server_port = system.get_available_port (); - - const auto max_ip_connections = node->config.network.max_peers_per_ip; - ASSERT_GE (max_ip_connections, 1); - - // client side connection tracking - std::atomic connection_attempts = 0; - auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { - ASSERT_EQ (ec_a.value (), 0); - ++connection_attempts; - }; - - // start n clients, n-1 will persist but 1 will be dropped, where n == max_ip_connections - std::vector> client_list; - client_list.reserve (max_ip_connections + 1); - - for (auto idx = 0; idx < max_ip_connections + 1; ++idx) - { - auto client = std::make_shared (*node); - client->async_connect (node->network.endpoint (), connect_handler); - client_list.push_back (client); - } - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), max_ip_connections); - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener_rejected, nano::stat::detail::max_per_ip), 1); - ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1); -} - -TEST (tcp_listener, max_connections_per_subnetwork) -{ - nano::test::system system; - - nano::node_flags node_flags; - // disabling IP limit because it will be used the same IP address to check they come from the same subnetwork. - node_flags.disable_max_peers_per_ip = true; - node_flags.disable_max_peers_per_subnetwork = false; - nano::node_config node_config = system.default_config (); - node_config.network.max_peers_per_subnetwork = 3; - auto node = system.add_node (node_config, node_flags); - - ASSERT_TRUE (node->flags.disable_max_peers_per_ip); - ASSERT_FALSE (node->flags.disable_max_peers_per_subnetwork); - - const auto max_subnetwork_connections = node->config.network.max_peers_per_subnetwork; - ASSERT_GE (max_subnetwork_connections, 1); - - // client side connection tracking - std::atomic connection_attempts = 0; - auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { - ASSERT_EQ (ec_a.value (), 0); - ++connection_attempts; - }; - - // start n clients, n-1 will persist but 1 will be dropped, where n == max_subnetwork_connections - std::vector> client_list; - client_list.reserve (max_subnetwork_connections + 1); - - for (auto idx = 0; idx < max_subnetwork_connections + 1; ++idx) - { - auto client = std::make_shared (*node); - client->async_connect (node->network.endpoint (), connect_handler); - client_list.push_back (client); - } - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), max_subnetwork_connections); - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener_rejected, nano::stat::detail::max_per_subnetwork), 1); - ASSERT_TIMELY_EQ (5s, connection_attempts, max_subnetwork_connections + 1); -} - -TEST (tcp_listener, max_peers_per_ip) -{ - nano::test::system system; - - nano::node_flags node_flags; - node_flags.disable_max_peers_per_ip = true; - nano::node_config node_config = system.default_config (); - node_config.network.max_peers_per_ip = 3; - auto node = system.add_node (node_config, node_flags); - - ASSERT_TRUE (node->flags.disable_max_peers_per_ip); - - auto server_port = system.get_available_port (); - - const auto max_ip_connections = node->config.network.max_peers_per_ip; - ASSERT_GE (max_ip_connections, 1); - - // client side connection tracking - std::atomic connection_attempts = 0; - auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { - ASSERT_EQ (ec_a.value (), 0); - ++connection_attempts; - }; - - // start n clients, n-1 will persist but 1 will be dropped, where n == max_ip_connections - std::vector> client_list; - client_list.reserve (max_ip_connections + 1); - - for (auto idx = 0; idx < max_ip_connections + 1; ++idx) - { - auto client = std::make_shared (*node); - client->async_connect (node->network.endpoint (), connect_handler); - client_list.push_back (client); - } - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), max_ip_connections + 1); - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener_rejected, nano::stat::detail::max_per_ip), 0); - ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1); -} - TEST (socket, disconnection_of_silent_connections) { nano::test::system system; diff --git a/nano/core_test/tcp_listener.cpp b/nano/core_test/tcp_listener.cpp new file mode 100644 index 0000000000..9173806f46 --- /dev/null +++ b/nano/core_test/tcp_listener.cpp @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +TEST (tcp_listener, max_connections) +{ + nano::test::system system; + + nano::node_flags node_flags; + nano::node_config node_config = system.default_config (); + node_config.tcp.max_inbound_connections = 2; + auto node = system.add_node (node_config, node_flags); + + // client side connection tracking + std::atomic connection_attempts = 0; + auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { + ASSERT_EQ (ec_a.value (), 0); + ++connection_attempts; + }; + + // start 3 clients, 2 will persist but 1 will be dropped + auto client1 = std::make_shared (*node); + client1->async_connect (node->network.endpoint (), connect_handler); + + auto client2 = std::make_shared (*node); + client2->async_connect (node->network.endpoint (), connect_handler); + + auto client3 = std::make_shared (*node); + client3->async_connect (node->network.endpoint (), connect_handler); + + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 2); + ASSERT_ALWAYS_EQ (1s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 2); + ASSERT_TIMELY_EQ (5s, connection_attempts, 3); + + // create space for one socket and fill the connections table again + { + auto sockets1 = node->tcp_listener.sockets (); + ASSERT_EQ (sockets1.size (), 2); + sockets1[0]->close (); + } + ASSERT_TIMELY_EQ (10s, node->tcp_listener.sockets ().size (), 1); + + auto client4 = std::make_shared (*node); + client4->async_connect (node->network.endpoint (), connect_handler); + + auto client5 = std::make_shared (*node); + client5->async_connect (node->network.endpoint (), connect_handler); + + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 3); + ASSERT_ALWAYS_EQ (1s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 3); + ASSERT_TIMELY_EQ (5s, connection_attempts, 5); + + // close all existing sockets and fill the connections table again + { + auto sockets2 = node->tcp_listener.sockets (); + ASSERT_EQ (sockets2.size (), 2); + sockets2[0]->close (); + sockets2[1]->close (); + } + ASSERT_TIMELY_EQ (10s, node->tcp_listener.sockets ().size (), 0); + + auto client6 = std::make_shared (*node); + client6->async_connect (node->network.endpoint (), connect_handler); + + auto client7 = std::make_shared (*node); + client7->async_connect (node->network.endpoint (), connect_handler); + + auto client8 = std::make_shared (*node); + client8->async_connect (node->network.endpoint (), connect_handler); + + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 5); + ASSERT_ALWAYS_EQ (1s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), 5); + ASSERT_TIMELY_EQ (5s, connection_attempts, 8); // connections initiated by the client +} + +TEST (tcp_listener, max_connections_per_ip) +{ + nano::test::system system; + + nano::node_flags node_flags; + nano::node_config node_config = system.default_config (); + node_config.network.max_peers_per_ip = 3; + auto node = system.add_node (node_config, node_flags); + ASSERT_FALSE (node->flags.disable_max_peers_per_ip); + + auto server_port = system.get_available_port (); + + const auto max_ip_connections = node->config.network.max_peers_per_ip; + ASSERT_GE (max_ip_connections, 1); + + // client side connection tracking + std::atomic connection_attempts = 0; + auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { + ASSERT_EQ (ec_a.value (), 0); + ++connection_attempts; + }; + + // start n clients, n-1 will persist but 1 will be dropped, where n == max_ip_connections + std::vector> client_list; + client_list.reserve (max_ip_connections + 1); + + for (auto idx = 0; idx < max_ip_connections + 1; ++idx) + { + auto client = std::make_shared (*node); + client->async_connect (node->network.endpoint (), connect_handler); + client_list.push_back (client); + } + + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), max_ip_connections); + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener_rejected, nano::stat::detail::max_per_ip), 1); + ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1); +} + +TEST (tcp_listener, max_connections_per_subnetwork) +{ + nano::test::system system; + + nano::node_flags node_flags; + // disabling IP limit because it will be used the same IP address to check they come from the same subnetwork. + node_flags.disable_max_peers_per_ip = true; + node_flags.disable_max_peers_per_subnetwork = false; + nano::node_config node_config = system.default_config (); + node_config.network.max_peers_per_subnetwork = 3; + auto node = system.add_node (node_config, node_flags); + + ASSERT_TRUE (node->flags.disable_max_peers_per_ip); + ASSERT_FALSE (node->flags.disable_max_peers_per_subnetwork); + + const auto max_subnetwork_connections = node->config.network.max_peers_per_subnetwork; + ASSERT_GE (max_subnetwork_connections, 1); + + // client side connection tracking + std::atomic connection_attempts = 0; + auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { + ASSERT_EQ (ec_a.value (), 0); + ++connection_attempts; + }; + + // start n clients, n-1 will persist but 1 will be dropped, where n == max_subnetwork_connections + std::vector> client_list; + client_list.reserve (max_subnetwork_connections + 1); + + for (auto idx = 0; idx < max_subnetwork_connections + 1; ++idx) + { + auto client = std::make_shared (*node); + client->async_connect (node->network.endpoint (), connect_handler); + client_list.push_back (client); + } + + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), max_subnetwork_connections); + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener_rejected, nano::stat::detail::max_per_subnetwork), 1); + ASSERT_TIMELY_EQ (5s, connection_attempts, max_subnetwork_connections + 1); +} + +TEST (tcp_listener, max_peers_per_ip) +{ + nano::test::system system; + + nano::node_flags node_flags; + node_flags.disable_max_peers_per_ip = true; + nano::node_config node_config = system.default_config (); + node_config.network.max_peers_per_ip = 3; + auto node = system.add_node (node_config, node_flags); + + ASSERT_TRUE (node->flags.disable_max_peers_per_ip); + + auto server_port = system.get_available_port (); + + const auto max_ip_connections = node->config.network.max_peers_per_ip; + ASSERT_GE (max_ip_connections, 1); + + // client side connection tracking + std::atomic connection_attempts = 0; + auto connect_handler = [&connection_attempts] (boost::system::error_code const & ec_a) { + ASSERT_EQ (ec_a.value (), 0); + ++connection_attempts; + }; + + // start n clients, n-1 will persist but 1 will be dropped, where n == max_ip_connections + std::vector> client_list; + client_list.reserve (max_ip_connections + 1); + + for (auto idx = 0; idx < max_ip_connections + 1; ++idx) + { + auto client = std::make_shared (*node); + client->async_connect (node->network.endpoint (), connect_handler); + client_list.push_back (client); + } + + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::accept_success), max_ip_connections + 1); + ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::tcp_listener_rejected, nano::stat::detail::max_per_ip), 0); + ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1); +} + +TEST (tcp_listener, tcp_node_id_handshake) +{ + nano::test::system system (1); + auto socket (std::make_shared (*system.nodes[0])); + auto bootstrap_endpoint (system.nodes[0]->tcp_listener.endpoint ()); + auto cookie (system.nodes[0]->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (bootstrap_endpoint))); + ASSERT_TRUE (cookie); + nano::node_id_handshake::query_payload query{ *cookie }; + nano::node_id_handshake node_id_handshake{ nano::dev::network_params.network, query }; + auto input (node_id_handshake.to_shared_const_buffer ()); + std::atomic write_done (false); + socket->async_connect (bootstrap_endpoint, [&input, socket, &write_done] (boost::system::error_code const & ec) { + ASSERT_FALSE (ec); + socket->async_write (input, [&input, &write_done] (boost::system::error_code const & ec, size_t size_a) { + ASSERT_FALSE (ec); + ASSERT_EQ (input.size (), size_a); + write_done = true; + }); + }); + + ASSERT_TIMELY (5s, write_done); + + nano::node_id_handshake::response_payload response_zero{ 0 }; + nano::node_id_handshake node_id_handshake_response{ nano::dev::network_params.network, std::nullopt, response_zero }; + auto output (node_id_handshake_response.to_bytes ()); + std::atomic done (false); + socket->async_read (output, output->size (), [&output, &done] (boost::system::error_code const & ec, size_t size_a) { + ASSERT_FALSE (ec); + ASSERT_EQ (output->size (), size_a); + done = true; + }); + ASSERT_TIMELY (5s, done); +} + +// Test disabled because it's failing intermittently. +// PR in which it got disabled: https://github.com/nanocurrency/nano-node/pull/3611 +// Issue for investigating it: https://github.com/nanocurrency/nano-node/issues/3615 +TEST (tcp_listener, DISABLED_tcp_listener_timeout_empty) +{ + nano::test::system system (1); + auto node0 (system.nodes[0]); + auto socket (std::make_shared (*node0)); + std::atomic connected (false); + socket->async_connect (node0->tcp_listener.endpoint (), [&connected] (boost::system::error_code const & ec) { + ASSERT_FALSE (ec); + connected = true; + }); + ASSERT_TIMELY (5s, connected); + bool disconnected (false); + system.deadline_set (std::chrono::seconds (6)); + while (!disconnected) + { + disconnected = node0->tcp_listener.connection_count () == 0; + ASSERT_NO_ERROR (system.poll ()); + } +} + +TEST (tcp_listener, tcp_listener_timeout_node_id_handshake) +{ + nano::test::system system (1); + auto node0 (system.nodes[0]); + auto socket (std::make_shared (*node0)); + auto cookie (node0->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (node0->tcp_listener.endpoint ()))); + ASSERT_TRUE (cookie); + nano::node_id_handshake::query_payload query{ *cookie }; + nano::node_id_handshake node_id_handshake{ nano::dev::network_params.network, query }; + auto channel = std::make_shared (*node0, socket); + socket->async_connect (node0->tcp_listener.endpoint (), [&node_id_handshake, channel] (boost::system::error_code const & ec) { + ASSERT_FALSE (ec); + channel->send (node_id_handshake, [] (boost::system::error_code const & ec, size_t size_a) { + ASSERT_FALSE (ec); + }); + }); + ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::tcp_server, nano::stat::detail::node_id_handshake) != 0); + ASSERT_EQ (node0->tcp_listener.connection_count (), 1); + bool disconnected (false); + system.deadline_set (std::chrono::seconds (20)); + while (!disconnected) + { + disconnected = node0->tcp_listener.connection_count () == 0; + ASSERT_NO_ERROR (system.poll ()); + } +} \ No newline at end of file From cfc1c14cacc1050ec624e084a0bae43137abffc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:04:54 +0100 Subject: [PATCH 3/4] IWYU --- nano/node/transport/tcp_listener.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/nano/node/transport/tcp_listener.hpp b/nano/node/transport/tcp_listener.hpp index 66644665d1..e8c86732fe 100644 --- a/nano/node/transport/tcp_listener.hpp +++ b/nano/node/transport/tcp_listener.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include From 3db853d40d1468d3349b663ec962cd25d2a12ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:17:09 +0100 Subject: [PATCH 4/4] Cleanup --- nano/node/transport/tcp_listener.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/transport/tcp_listener.hpp b/nano/node/transport/tcp_listener.hpp index e8c86732fe..94fc9f12ae 100644 --- a/nano/node/transport/tcp_listener.hpp +++ b/nano/node/transport/tcp_listener.hpp @@ -86,7 +86,7 @@ class tcp_listener final nano::container_info container_info () const; public: // Events - using connection_accepted_event_t = nano::observer_set const &, std::shared_ptr>; + using connection_accepted_event_t = nano::observer_set, std::shared_ptr>; connection_accepted_event_t connection_accepted; private: // Dependencies