Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a dedicated thread for tcp listener #4523

Merged
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8c32228
Make target interval more explicit
pwojcikdev Mar 24, 2024
ba115cb
Refactor `socket_type` and `socket_endpoint` enums
pwojcikdev Mar 21, 2024
a4a96ed
Add `peer_exclusion::check` helper overload
pwojcikdev Mar 22, 2024
2dcf07b
Add `is_same_ip` & `is_same_subnetwork` functions
pwojcikdev Mar 25, 2024
32a6548
Additional test logging
pwojcikdev Mar 22, 2024
9b4e16e
Convert tcp listener to use a dedicated thread
pwojcikdev Mar 25, 2024
01ac3d5
Fix tests
pwojcikdev Mar 22, 2024
ef1af6b
Move `is_temporary_error` function
pwojcikdev Mar 22, 2024
cdba6e3
Remove public counters and replace with getters
pwojcikdev Mar 22, 2024
7e02c0d
Store component as unique_ptr inside the node
pwojcikdev Mar 22, 2024
037c350
Move tests for network functions to a separate file
pwojcikdev Mar 25, 2024
b652bf0
Add tests for ip address manipulation functions
pwojcikdev Mar 25, 2024
3f0ba6c
Improvements
pwojcikdev Mar 26, 2024
4bc4545
Fix socket tests
pwojcikdev Mar 27, 2024
3f9a2c5
Use async accept
pwojcikdev Mar 26, 2024
7b65d9f
Use `asio` namespace alias
pwojcikdev Apr 1, 2024
89196fb
Locking
pwojcikdev Apr 1, 2024
75fce7e
Unused callback
pwojcikdev Apr 1, 2024
4064223
Fixes
pwojcikdev Apr 9, 2024
560f3b4
Merge branch 'develop' into networking-fixes/tcp-listener-2
pwojcikdev Apr 9, 2024
4463ba7
Merge branch 'develop' into networking-fixes/tcp-listener-2
pwojcikdev Apr 17, 2024
4920edd
Merge branch 'develop' into networking-fixes/tcp-listener-2
pwojcikdev Apr 19, 2024
e1c9c97
Use single threaded io context in tests
pwojcikdev Apr 19, 2024
e93f243
Fix `socket.max_connections`
pwojcikdev Apr 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nano/core_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ add_executable(
memory_pool.cpp
network.cpp
network_filter.cpp
network_functions.cpp
node.cpp
object_stream.cpp
optimistic_scheduler.cpp
Expand Down
54 changes: 27 additions & 27 deletions nano/core_test/bootstrap.cpp

Large diffs are not rendered by default.

86 changes: 15 additions & 71 deletions nano/core_test/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ TEST (network, construction_with_specified_port)
auto const node = system.add_node (nano::node_config{ port });
EXPECT_EQ (port, node->network.port);
EXPECT_EQ (port, node->network.endpoint ().port ());
EXPECT_EQ (port, node->tcp_listener->endpoint ().port ());
EXPECT_EQ (port, node->tcp_listener.endpoint ().port ());
}

TEST (network, construction_without_specified_port)
Expand All @@ -75,7 +75,7 @@ TEST (network, construction_without_specified_port)
auto const port = node->network.port.load ();
EXPECT_NE (0, port);
EXPECT_EQ (port, node->network.endpoint ().port ());
EXPECT_EQ (port, node->tcp_listener->endpoint ().port ());
EXPECT_EQ (port, node->tcp_listener.endpoint ().port ());
}

TEST (network, send_node_id_handshake_tcp)
Expand Down Expand Up @@ -540,62 +540,6 @@ TEST (network, endpoint_bad_fd)
ASSERT_TIMELY_EQ (10s, system.nodes[0]->network.endpoint ().port (), 0);
}

TEST (network, reserved_address)
{
nano::test::system system (1);
// 0 port test
ASSERT_TRUE (nano::transport::reserved_address (nano::endpoint (boost::asio::ip::make_address_v6 ("2001::"), 0)));
// Valid address test
ASSERT_FALSE (nano::transport::reserved_address (nano::endpoint (boost::asio::ip::make_address_v6 ("2001::"), 1)));
nano::endpoint loopback (boost::asio::ip::make_address_v6 ("::1"), 1);
ASSERT_FALSE (nano::transport::reserved_address (loopback));
nano::endpoint private_network_peer (boost::asio::ip::make_address_v6 ("::ffff:10.0.0.0"), 1);
ASSERT_TRUE (nano::transport::reserved_address (private_network_peer, false));
ASSERT_FALSE (nano::transport::reserved_address (private_network_peer, true));
}

TEST (network, ipv6_bind_subnetwork)
{
auto address1 (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"));
auto subnet1 (boost::asio::ip::make_network_v6 (address1, 48));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298::"), subnet1.network ());
auto address1_subnet (nano::transport::ipv4_address_or_ipv6_subnet (address1));
ASSERT_EQ (subnet1.network (), address1_subnet);
// Ipv4 should return initial address
auto address2 (boost::asio::ip::make_address_v6 ("::ffff:192.168.1.1"));
auto address2_subnet (nano::transport::ipv4_address_or_ipv6_subnet (address2));
ASSERT_EQ (address2, address2_subnet);
}

TEST (network, network_range_ipv6)
{
auto address1 (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"));
auto subnet1 (boost::asio::ip::make_network_v6 (address1, 58));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298:cf40::"), subnet1.network ());
auto address2 (boost::asio::ip::make_address_v6 ("520d:2402:3d:5e65:11:f8:7c54:3f"));
auto subnet2 (boost::asio::ip::make_network_v6 (address2, 33));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("520d:2402:0::"), subnet2.network ());
// Default settings test
auto address3 (boost::asio::ip::make_address_v6 ("a719:0f12:536e:d88a:1331:ba53:4598:04e5"));
auto subnet3 (boost::asio::ip::make_network_v6 (address3, 32));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("a719:0f12::"), subnet3.network ());
auto address3_subnet (nano::transport::map_address_to_subnetwork (address3));
ASSERT_EQ (subnet3.network (), address3_subnet);
}

TEST (network, network_range_ipv4)
{
auto address1 (boost::asio::ip::make_address_v6 ("::ffff:192.168.1.1"));
auto subnet1 (boost::asio::ip::make_network_v6 (address1, 96 + 16));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("::ffff:192.168.0.0"), subnet1.network ());
// Default settings test
auto address2 (boost::asio::ip::make_address_v6 ("::ffff:80.67.148.225"));
auto subnet2 (boost::asio::ip::make_network_v6 (address2, 96 + 24));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("::ffff:80.67.148.0"), subnet2.network ());
auto address2_subnet (nano::transport::map_address_to_subnetwork (address2));
ASSERT_EQ (subnet2.network (), address2_subnet);
}

TEST (node, port_mapping)
{
nano::test::system system (1);
Expand All @@ -614,7 +558,7 @@ TEST (tcp_listener, tcp_node_id_handshake)
{
nano::test::system system (1);
auto socket (std::make_shared<nano::transport::socket> (*system.nodes[0]));
auto bootstrap_endpoint (system.nodes[0]->tcp_listener->endpoint ());
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 };
Expand Down Expand Up @@ -653,7 +597,7 @@ TEST (tcp_listener, DISABLED_tcp_listener_timeout_empty)
auto node0 (system.nodes[0]);
auto socket (std::make_shared<nano::transport::socket> (*node0));
std::atomic<bool> connected (false);
socket->async_connect (node0->tcp_listener->endpoint (), [&connected] (boost::system::error_code const & ec) {
socket->async_connect (node0->tcp_listener.endpoint (), [&connected] (boost::system::error_code const & ec) {
ASSERT_FALSE (ec);
connected = true;
});
Expand All @@ -662,7 +606,7 @@ TEST (tcp_listener, DISABLED_tcp_listener_timeout_empty)
system.deadline_set (std::chrono::seconds (6));
while (!disconnected)
{
disconnected = node0->tcp_listener->connection_count () == 0;
disconnected = node0->tcp_listener.connection_count () == 0;
ASSERT_NO_ERROR (system.poll ());
}
}
Expand All @@ -672,24 +616,24 @@ TEST (tcp_listener, tcp_listener_timeout_node_id_handshake)
nano::test::system system (1);
auto node0 (system.nodes[0]);
auto socket (std::make_shared<nano::transport::socket> (*node0));
auto cookie (node0->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (node0->tcp_listener->endpoint ())));
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<nano::transport::channel_tcp> (*node0, socket);
socket->async_connect (node0->tcp_listener->endpoint (), [&node_id_handshake, channel] (boost::system::error_code const & ec) {
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);
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;
disconnected = node0->tcp_listener.connection_count () == 0;
ASSERT_NO_ERROR (system.poll ());
}
}
Expand All @@ -715,7 +659,7 @@ TEST (network, peer_max_tcp_attempts)
}
ASSERT_EQ (0, node->network.size ());
ASSERT_FALSE (node->network.tcp_channels.track_reachout (nano::endpoint (node->network.endpoint ().address (), system.get_available_port ())));
ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_ip, nano::stat::dir::out));
ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::max_per_ip, nano::stat::dir::out));
}
#endif

Expand All @@ -737,9 +681,9 @@ namespace transport
ASSERT_TRUE (node->network.tcp_channels.track_reachout (endpoint));
}
ASSERT_EQ (0, node->network.size ());
ASSERT_EQ (0, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::out));
ASSERT_EQ (0, node->stats.count (nano::stat::type::tcp, nano::stat::detail::max_per_subnetwork, nano::stat::dir::out));
ASSERT_FALSE (node->network.tcp_channels.track_reachout (nano::endpoint (boost::asio::ip::make_address_v6 ("::ffff:127.0.0.1"), system.get_available_port ())));
ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::out));
ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::max_per_subnetwork, nano::stat::dir::out));
}
}
}
Expand Down Expand Up @@ -910,7 +854,7 @@ TEST (peer_exclusion, validate)
}
}

TEST (network, tcp_no_connect_excluded_peers)
TEST (network, tcp_no_accept_excluded_peers)
{
nano::test::system system (1);
auto node0 (system.nodes[0]);
Expand All @@ -923,9 +867,9 @@ TEST (network, tcp_no_connect_excluded_peers)
{
node0->network.excluded_peers.add (endpoint1_tcp);
}
ASSERT_EQ (0, node0->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_excluded));
ASSERT_EQ (0, node0->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::excluded));
node1->network.merge_peer (node0->network.endpoint ());
ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_excluded) >= 1);
ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::tcp_listener, nano::stat::detail::excluded) >= 1);
ASSERT_EQ (nullptr, node0->network.find_node_id (node1->get_node_id ()));

// Should not actively reachout to excluded peers
Expand Down
144 changes: 144 additions & 0 deletions nano/core_test/network_functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include <nano/node/transport/transport.hpp>
#include <nano/test_common/testutil.hpp>

#include <gtest/gtest.h>

TEST (network_functions, reserved_address)
{
// 0 port test
ASSERT_TRUE (nano::transport::reserved_address (nano::endpoint (boost::asio::ip::make_address_v6 ("2001::"), 0)));
// Valid address test
ASSERT_FALSE (nano::transport::reserved_address (nano::endpoint (boost::asio::ip::make_address_v6 ("2001::"), 1)));
nano::endpoint loopback (boost::asio::ip::make_address_v6 ("::1"), 1);
ASSERT_FALSE (nano::transport::reserved_address (loopback));
nano::endpoint private_network_peer (boost::asio::ip::make_address_v6 ("::ffff:10.0.0.0"), 1);
ASSERT_TRUE (nano::transport::reserved_address (private_network_peer, false));
ASSERT_FALSE (nano::transport::reserved_address (private_network_peer, true));
}

TEST (network_functions, ipv6_bind_subnetwork)
{
auto address1 (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"));
auto subnet1 (boost::asio::ip::make_network_v6 (address1, 48));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298::"), subnet1.network ());
auto address1_subnet (nano::transport::ipv4_address_or_ipv6_subnet (address1));
ASSERT_EQ (subnet1.network (), address1_subnet);
// Ipv4 should return initial address
auto address2 (boost::asio::ip::make_address_v6 ("::ffff:192.168.1.1"));
auto address2_subnet (nano::transport::ipv4_address_or_ipv6_subnet (address2));
ASSERT_EQ (address2, address2_subnet);
}

TEST (network_functions, network_range_ipv6)
{
auto address1 (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298:cf45:672e:bd1a:e7fb:f713"));
auto subnet1 (boost::asio::ip::make_network_v6 (address1, 58));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("a41d:b7b2:8298:cf40::"), subnet1.network ());
auto address2 (boost::asio::ip::make_address_v6 ("520d:2402:3d:5e65:11:f8:7c54:3f"));
auto subnet2 (boost::asio::ip::make_network_v6 (address2, 33));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("520d:2402:0::"), subnet2.network ());
// Default settings test
auto address3 (boost::asio::ip::make_address_v6 ("a719:0f12:536e:d88a:1331:ba53:4598:04e5"));
auto subnet3 (boost::asio::ip::make_network_v6 (address3, 32));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("a719:0f12::"), subnet3.network ());
auto address3_subnet (nano::transport::map_address_to_subnetwork (address3));
ASSERT_EQ (subnet3.network (), address3_subnet);
}

TEST (network_functions, network_range_ipv4)
{
auto address1 (boost::asio::ip::make_address_v6 ("::ffff:192.168.1.1"));
auto subnet1 (boost::asio::ip::make_network_v6 (address1, 96 + 16));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("::ffff:192.168.0.0"), subnet1.network ());
// Default settings test
auto address2 (boost::asio::ip::make_address_v6 ("::ffff:80.67.148.225"));
auto subnet2 (boost::asio::ip::make_network_v6 (address2, 96 + 24));
ASSERT_EQ (boost::asio::ip::make_address_v6 ("::ffff:80.67.148.0"), subnet2.network ());
auto address2_subnet (nano::transport::map_address_to_subnetwork (address2));
ASSERT_EQ (subnet2.network (), address2_subnet);
}

TEST (network_functions, ipv4_address_or_ipv6_subnet)
{
// IPv4 mapped as IPv6 address should return the original IPv4 address
boost::asio::ip::address addr1 = boost::asio::ip::address::from_string ("192.168.1.1");
boost::asio::ip::address addr2 = boost::asio::ip::address::from_string ("::ffff:192.168.1.1");
ASSERT_EQ (nano::transport::ipv4_address_or_ipv6_subnet (addr1), addr2);

// IPv6 address within the same /48 subnet should return the network prefix
boost::asio::ip::address addr3 = boost::asio::ip::address::from_string ("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
boost::asio::ip::address addr4 = boost::asio::ip::address::from_string ("2001:0db8:85a3::");
ASSERT_EQ (nano::transport::ipv4_address_or_ipv6_subnet (addr3), addr4);

// Different IPv6 address outside the /48 subnet should not match
boost::asio::ip::address addr5 = boost::asio::ip::address::from_string ("2001:0db8:85a4:0001:0000:8a2e:0370:7334");
ASSERT_NE (nano::transport::ipv4_address_or_ipv6_subnet (addr3), nano::transport::ipv4_address_or_ipv6_subnet (addr5));
}

TEST (network_functions, is_same_ip)
{
// Same IPv4 addresses
boost::asio::ip::address ipv4_addr1 = boost::asio::ip::address::from_string ("192.168.1.1");
ASSERT_TRUE (nano::transport::is_same_ip (ipv4_addr1, ipv4_addr1));

// IPv4 and its IPv6 mapped form
boost::asio::ip::address ipv6_mapped_ipv4 = boost::asio::ip::address::from_string ("::ffff:192.168.1.1");
ASSERT_TRUE (nano::transport::is_same_ip (ipv4_addr1, ipv6_mapped_ipv4));
}

TEST (network_functions, is_same_ipv4)
{
// Same IPv4 addresses
boost::asio::ip::address ipv4_addr1 = boost::asio::ip::address::from_string ("192.168.1.1");
ASSERT_TRUE (nano::transport::is_same_ip (ipv4_addr1, ipv4_addr1));

// IPv4 and its IPv6 mapped form
boost::asio::ip::address ipv6_mapped_ipv4 = boost::asio::ip::address::from_string ("::ffff:192.168.1.1");
ASSERT_TRUE (nano::transport::is_same_ip (ipv4_addr1, ipv6_mapped_ipv4));
}

TEST (network_functions, is_same_ipv6)
{
// Two different IPv6 addresses within the same /48 subnet
boost::asio::ip::address ipv6_addr1 = boost::asio::ip::address::from_string ("2001:db8::1");
boost::asio::ip::address ipv6_addr2 = boost::asio::ip::address::from_string ("2001:db8::2");
ASSERT_TRUE (nano::transport::is_same_ip (ipv6_addr1, ipv6_addr2));

// Two different IPv6 addresses in different /48 subnets
boost::asio::ip::address ipv6_addr3 = boost::asio::ip::address::from_string ("2001:db8:1::1");
ASSERT_FALSE (nano::transport::is_same_ip (ipv6_addr1, ipv6_addr3));
}

TEST (network_functions, is_different_ip_family)
{
boost::asio::ip::address addr1 = boost::asio::ip::address::from_string ("192.168.1.1");
boost::asio::ip::address addr2 = boost::asio::ip::address::from_string ("::1");
ASSERT_FALSE (nano::transport::is_same_ip (addr1, addr2));
}

TEST (network_functions, is_same_ip_v4_mapped)
{
boost::asio::ip::address addr1 = boost::asio::ip::address::from_string ("::ffff:192.168.1.1");
boost::asio::ip::address addr2 = boost::asio::ip::address::from_string ("192.168.1.1");
ASSERT_TRUE (nano::transport::is_same_ip (addr1, addr2));

boost::asio::ip::address addr3 = boost::asio::ip::address::from_string ("10.0.0.1");
ASSERT_FALSE (nano::transport::is_same_ip (addr1, addr3));
}

TEST (network_functions, map_ipv4_address_to_subnetwork)
{
boost::asio::ip::address addr = boost::asio::ip::address::from_string ("192.168.1.100");
auto subnetwork = nano::transport::map_address_to_subnetwork (addr);
// Assuming a /24 subnet mask for IPv4, all addresses in 192.168.1.x should map to the same network
// Automatically maps to IPv6
ASSERT_EQ (subnetwork.to_string (), "::ffff:192.168.1.0");
}

TEST (network_functions, map_ipv6_address_to_subnetwork)
{
boost::asio::ip::address addr = boost::asio::ip::address::from_string ("2001:db8:abcd:0012::0");
auto subnetwork = nano::transport::map_address_to_subnetwork (addr);
// Assuming a /32 subnet mask for IPv6
ASSERT_EQ (subnetwork.to_string (), "2001:db8::");
}
3 changes: 2 additions & 1 deletion nano/core_test/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ TEST (node, null_account)
TEST (node, stop)
{
nano::test::system system (1);
system.io_guard.reset ();
dsiganos marked this conversation as resolved.
Show resolved Hide resolved
ASSERT_NE (system.nodes[0]->wallets.items.end (), system.nodes[0]->wallets.items.begin ());
ASSERT_TRUE (true);
}
Expand Down Expand Up @@ -2819,7 +2820,7 @@ TEST (node, peers)
node2->start ();
ASSERT_TIMELY (10s, !node2->network.empty () && !node1->network.empty ())
// Wait to finish TCP node ID handshakes
ASSERT_TIMELY (10s, node1->tcp_listener->realtime_count != 0 && node2->tcp_listener->realtime_count != 0);
ASSERT_TIMELY (10s, node1->tcp_listener.realtime_count () != 0 && node2->tcp_listener.realtime_count () != 0);
// Confirm that the peers match with the endpoints we are expecting
ASSERT_EQ (1, node1->network.size ());
auto list1 (node1->network.list (2));
Expand Down
Loading
Loading