Skip to content

Commit

Permalink
Connect handshake v2 messages to the node.
Browse files Browse the repository at this point in the history
  • Loading branch information
pwojcikdev authored and clemahieu committed Mar 8, 2023
1 parent 427fded commit cff1dbd
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 41 deletions.
3 changes: 2 additions & 1 deletion nano/core_test/telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ TEST (telemetry, ongoing_broadcasts)
ASSERT_TIMELY (5s, node2.stats.count (nano::stat::type::telemetry, nano::stat::detail::process) >= 3)
}

TEST (telemetry, mismatched_genesis)
// TODO: With handshake V2, nodes with mismatched genesis will refuse to connect while setting up the system
TEST (telemetry, DISABLED_mismatched_genesis)
{
// Only second node will broadcast telemetry
nano::test::system system;
Expand Down
7 changes: 7 additions & 0 deletions nano/lib/stats_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum class type : uint8_t
unchecked,
election_scheduler,
optimistic_scheduler,
handshake,

_last // Must be the last enum
};
Expand All @@ -53,6 +54,7 @@ enum class detail : uint8_t
all = 0,

// common
ok,
loop,
total,
process,
Expand Down Expand Up @@ -273,6 +275,11 @@ enum class detail : uint8_t
insert_priority_success,
erase_oldest,

// handshake
invalid_node_id,
missing_cookie,
invalid_genesis,

_last // Must be the last enum
};

Expand Down
62 changes: 62 additions & 0 deletions nano/node/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,68 @@ void nano::network::exclude (std::shared_ptr<nano::transport::channel> const & c
erase (*channel);
}

bool nano::network::verify_handshake_response (const nano::node_id_handshake::response_payload & response, const nano::endpoint & remote_endpoint)
{
// Prevent connection with ourselves
if (response.node_id == node.node_id.pub)
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_node_id);
return false; // Fail
}

// Prevent mismatched genesis
if (response.v2 && response.v2->genesis != node.network_params.ledger.genesis->hash ())
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_genesis);
return false; // Fail
}

auto cookie = syn_cookies.cookie (remote_endpoint);
if (!cookie)
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::missing_cookie);
return false; // Fail
}

if (!response.validate (*cookie))
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_signature);
return false; // Fail
}

node.stats.inc (nano::stat::type::handshake, nano::stat::detail::ok);
return true; // OK
}

std::optional<nano::node_id_handshake::query_payload> nano::network::prepare_handshake_query (const nano::endpoint & remote_endpoint)
{
if (auto cookie = syn_cookies.assign (remote_endpoint); cookie)
{
nano::node_id_handshake::query_payload query{ *cookie };
return query;
}
return std::nullopt;
}

nano::node_id_handshake::response_payload nano::network::prepare_handshake_response (const nano::node_id_handshake::query_payload & query, bool v2) const
{
nano::node_id_handshake::response_payload response{};
response.node_id = node.node_id.pub;
if (v2)
{
nano::node_id_handshake::response_payload::v2_payload response_v2{};
response_v2.salt = nano::random_pool::generate<uint256_union> ();
response_v2.genesis = node.network_params.ledger.genesis->hash ();
response.v2 = response_v2;
}
response.sign (query.cookie, node.node_id);
return response;
}

/*
* tcp_message_manager
*/

nano::tcp_message_manager::tcp_message_manager (unsigned incoming_connections_max_a) :
max_entries (incoming_connections_max_a * nano::tcp_message_manager::max_entries_per_connection + 1)
{
Expand Down
5 changes: 5 additions & 0 deletions nano/node/network.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ class network final
/** Disconnects and adds peer to exclusion list */
void exclude (std::shared_ptr<nano::transport::channel> const & channel);

/** Verifies that handshake response matches our query. @returns true if OK */
bool verify_handshake_response (nano::node_id_handshake::response_payload const & response, nano::endpoint const & remote_endpoint);
std::optional<nano::node_id_handshake::query_payload> prepare_handshake_query (nano::endpoint const & remote_endpoint);
nano::node_id_handshake::response_payload prepare_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) const;

static std::string to_string (nano::networks);

private:
Expand Down
1 change: 1 addition & 0 deletions nano/node/transport/socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class socket : public std::enable_shared_from_this<nano::transport::socket>
{
friend class server_socket;
friend class tcp_server;
friend class tcp_channels;

public:
enum class type_t
Expand Down
51 changes: 27 additions & 24 deletions nano/node/transport/tcp.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <nano/lib/stats.hpp>
#include <nano/node/node.hpp>
#include <nano/node/transport/message_deserializer.hpp>
#include <nano/node/transport/tcp.hpp>

#include <boost/format.hpp>
Expand Down Expand Up @@ -544,14 +545,7 @@ void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a
if (!ec && channel)
{
// TCP node ID handshake

std::optional<nano::node_id_handshake::query_payload> query;
if (auto cookie = node_l->network.syn_cookies.assign (endpoint_a); cookie)
{
nano::node_id_handshake::query_payload pld{ *cookie };
query = pld;
}

auto query = node_l->network.prepare_handshake_query (endpoint_a);
nano::node_id_handshake message{ node_l->network_params.network, query };

if (node_l->config.logging.network_node_id_handshake_logging ())
Expand Down Expand Up @@ -619,7 +613,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
}
};

socket_l->async_read (receive_buffer_a, 8 + sizeof (nano::account) + sizeof (nano::account) + sizeof (nano::signature), [node_w, channel_a, endpoint_a, receive_buffer_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) {
auto message_deserializer = std::make_shared<nano::transport::message_deserializer> (node.network_params.network, node.network.publish_filter, node.block_uniquer, node.vote_uniquer,
[socket_l] (std::shared_ptr<std::vector<uint8_t>> const & data_a, size_t size_a, std::function<void (boost::system::error_code const &, std::size_t)> callback_a) {
debug_assert (socket_l != nullptr);
socket_l->read_impl (data_a, size_a, callback_a);
});
message_deserializer->read ([node_w, socket_l, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code ec, std::unique_ptr<nano::message> message) {
auto node_l = node_w.lock ();
if (!node_l)
{
Expand All @@ -636,10 +635,9 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
}
node_l->stats.inc (nano::stat::type::message, nano::stat::detail::node_id_handshake, nano::stat::dir::in);
auto error (false);
nano::bufferstream stream (receive_buffer_a->data (), size_a);
nano::message_header header (error, stream);

// the header type should in principle be checked after checking the network bytes and the version numbers, I will not change it here since the benefits do not outweight the difficulties
if (error || header.type != nano::message_type::node_id_handshake)
if (error || message->type () != nano::message_type::node_id_handshake)
{
if (node_l->config.logging.network_node_id_handshake_logging ())
{
Expand All @@ -648,10 +646,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
cleanup_node_id_handshake_socket (endpoint_a);
return;
}
if (header.network != node_l->network_params.network.current_network || header.version_using < node_l->network_params.network.protocol_version_min)
auto & handshake = static_cast<nano::node_id_handshake &> (*message);

if (message->header.network != node_l->network_params.network.current_network || message->header.version_using < node_l->network_params.network.protocol_version_min)
{
// error handling, either the networks bytes or the version is wrong
if (header.network == node_l->network_params.network.current_network)
if (message->header.network == node_l->network_params.network.current_network)
{
node_l->stats.inc (nano::stat::type::message, nano::stat::detail::invalid_network);
}
Expand All @@ -668,8 +668,8 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
}
return;
}
nano::node_id_handshake message (error, stream, header);
if (error || !message.response || !message.query)

if (error || !handshake.response || !handshake.query)
{
if (node_l->config.logging.network_node_id_handshake_logging ())
{
Expand All @@ -678,15 +678,16 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
cleanup_node_id_handshake_socket (endpoint_a);
return;
}
channel_a->set_network_version (header.version_using);
channel_a->set_network_version (handshake.header.version_using);

debug_assert (handshake.query);
debug_assert (handshake.response);

debug_assert (message.query);
debug_assert (message.response);
auto const node_id = handshake.response->node_id;

auto node_id = message.response->node_id;
bool process = (!node_l->network.syn_cookies.validate (endpoint_a, node_id, message.response->signature) && node_id != node_l->node_id.pub);
if (!process)
if (!node_l->network.verify_handshake_response (*handshake.response, endpoint_a))
{
cleanup_node_id_handshake_socket (endpoint_a);
return;
}

Expand All @@ -695,18 +696,20 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
auto existing_channel (node_l->network.tcp_channels.find_node_id (node_id));
if (existing_channel && !existing_channel->temporary)
{
cleanup_node_id_handshake_socket (endpoint_a);
return;
}

channel_a->set_node_id (node_id);
channel_a->set_last_packet_received (std::chrono::steady_clock::now ());

nano::node_id_handshake::response_payload response{ node_l->node_id.pub, nano::sign_message (node_l->node_id.prv, node_l->node_id.pub, message.query->cookie) };
debug_assert (handshake.query);
auto response = node_l->network.prepare_handshake_response (*handshake.query, handshake.is_v2 ());
nano::node_id_handshake handshake_response (node_l->network_params.network, std::nullopt, response);

if (node_l->config.logging.network_node_id_handshake_logging ())
{
node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % message.query->cookie.to_string ()));
node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % handshake.query->cookie.to_string ()));
}

channel_a->send (handshake_response, [node_w, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) {
Expand Down
32 changes: 17 additions & 15 deletions nano/node/transport/tcp_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

#include <memory>

/*
* tcp_listener
*/

nano::transport::tcp_listener::tcp_listener (uint16_t port_a, nano::node & node_a) :
node (node_a),
port (port_a)
Expand Down Expand Up @@ -123,6 +127,10 @@ std::unique_ptr<nano::container_info_component> nano::transport::collect_contain
return composite;
}

/*
* tcp_server
*/

nano::transport::tcp_server::tcp_server (std::shared_ptr<nano::transport::socket> socket_a, std::shared_ptr<nano::node> node_a, bool allow_bootstrap_a) :
socket{ std::move (socket_a) },
node{ std::move (node_a) },
Expand Down Expand Up @@ -315,6 +323,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
{
server->node->logger.try_log (boost::str (boost::format ("Disabled realtime TCP for handshake %1%") % server->remote_endpoint));
}
// Stop invalid handshake
server->stop ();
return;
}
Expand All @@ -325,6 +334,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
{
server->node->logger.try_log (boost::str (boost::format ("Detected multiple node_id_handshake query from %1%") % server->remote_endpoint));
}
// Stop invalid handshake
server->stop ();
return;
}
Expand All @@ -338,37 +348,29 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (

if (message.query)
{
server->send_handshake_response (*message.query);
server->send_handshake_response (*message.query, message.is_v2 ());
}
if (message.response)
{
nano::account const & node_id = message.response->node_id;
if (!server->node->network.syn_cookies.validate (nano::transport::map_tcp_to_endpoint (server->remote_endpoint), node_id, message.response->signature) && node_id != server->node->node_id.pub)
if (server->node->network.verify_handshake_response (*message.response, nano::transport::map_tcp_to_endpoint (server->remote_endpoint)))
{
server->to_realtime_connection (node_id);
server->to_realtime_connection (message.response->node_id);
}
else
{
// Stop invalid handshake
server->stop ();
return;
}
}

process = true;
}

void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query)
void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2)
{
nano::node_id_handshake::response_payload response{ node->node_id.pub, nano::sign_message (node->node_id.prv, node->node_id.pub, query.cookie) };
debug_assert (!nano::validate_message (response.node_id, query.cookie, response.signature));

std::optional<nano::node_id_handshake::query_payload> own_query;
if (auto own_cookie = node->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (remote_endpoint)); own_cookie)
{
nano::node_id_handshake::query_payload pld{ *own_cookie };
own_query = pld;
}

auto response = node->network.prepare_handshake_response (query, v2);
auto own_query = node->network.prepare_handshake_query (nano::transport::map_tcp_to_endpoint (remote_endpoint));
nano::node_id_handshake handshake_response{ node->network_params.network, own_query, response };

// TODO: Use channel
Expand Down
2 changes: 1 addition & 1 deletion nano/node/transport/tcp_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class tcp_server final : public std::enable_shared_from_this<tcp_server>
std::chrono::steady_clock::time_point last_telemetry_req{};

private:
void send_handshake_response (nano::node_id_handshake::query_payload const & query);
void send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2);

void receive_message ();
void received_message (std::unique_ptr<nano::message> message);
Expand Down

0 comments on commit cff1dbd

Please # to comment.