Skip to content

Commit

Permalink
Timeouts silent incoming connections
Browse files Browse the repository at this point in the history
Timeouts silent incoming connections
Adds new stat counters:
- tcp_silent_connection_drop
- tcp_io_timeout_drop
  • Loading branch information
clemahieu authored and Thiago Silva committed Nov 18, 2021
1 parent d41789d commit 62fbc45
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 2 deletions.
28 changes: 28 additions & 0 deletions nano/core_test/socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,34 @@ TEST (socket, disabled_max_peers_per_ip)
node->stop ();
}

TEST (socket, disconnection_of_silent_connections)
{
nano::system system;
auto node = system.add_node ();
auto socket = std::make_shared<nano::socket> (*node);
// Classify the socket type as real-time as the disconnections are done only for this connection type.
socket->type_set (nano::socket::type_t::realtime);
// Silent connections are connections open by external peers that don't contribute with any data.
socket->set_silent_connection_tolerance_time (std::chrono::seconds{ 5 });
auto bootstrap_endpoint = node->bootstrap.endpoint ();
std::atomic<bool> connected{ false };
// Opening a connection that will be closed because it remains silent during the tolerance time.
socket->async_connect (bootstrap_endpoint, [socket, &connected] (boost::system::error_code const & ec) {
ASSERT_FALSE (ec);
connected = true;
});
ASSERT_TIMELY (4s, connected);
// Checking the connection was closed.
ASSERT_TIMELY (10s, socket->is_closed ());

auto get_tcp_silent_connection_drops = [&node] () {
return node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_silent_connection_drop, nano::stat::dir::in);
};
ASSERT_EQ (1, get_tcp_silent_connection_drops ());

node->stop ();
}

TEST (socket, drop_policy)
{
auto node_flags = nano::inactive_node_flag_defaults ();
Expand Down
2 changes: 2 additions & 0 deletions nano/lib/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class network_constants
request_interval_ms = is_dev_network () ? 20 : 500;
cleanup_period = is_dev_network () ? std::chrono::seconds (1) : std::chrono::seconds (60);
idle_timeout = is_dev_network () ? cleanup_period * 15 : cleanup_period * 2;
silent_connection_tolerance_time = std::chrono::seconds (120);
syn_cookie_cutoff = std::chrono::seconds (5);
bootstrap_interval = std::chrono::seconds (15 * 60);
max_peers_per_ip = is_dev_network () ? 10 : 5;
Expand Down Expand Up @@ -189,6 +190,7 @@ class network_constants
}
/** Default maximum idle time for a socket before it's automatically closed */
std::chrono::seconds idle_timeout;
std::chrono::seconds silent_connection_tolerance_time;
std::chrono::seconds syn_cookie_cutoff;
std::chrono::seconds bootstrap_interval;
/** Maximum number of peers per IP. It is also the max number of connections per IP */
Expand Down
2 changes: 1 addition & 1 deletion nano/node/bootstrap/bootstrap_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,5 +749,5 @@ bool nano::bootstrap_server::is_bootstrap_connection ()

bool nano::bootstrap_server::is_realtime_connection ()
{
return socket->type () == nano::socket::type_t::realtime || socket->type () == nano::socket::type_t::realtime_response_server;
return socket->is_realtime_connection ();
}
29 changes: 28 additions & 1 deletion nano/node/socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ nano::socket::socket (nano::node & node_a) :
node{ node_a },
next_deadline{ std::numeric_limits<uint64_t>::max () },
last_completion_time{ 0 },
io_timeout{ node_a.config.tcp_io_timeout }
last_receive_time{ 0 },
io_timeout{ node_a.config.tcp_io_timeout },
silent_connection_tolerance_time{ node_a.network_params.network.silent_connection_tolerance_time }
{
}

Expand Down Expand Up @@ -58,6 +60,7 @@ void nano::socket::async_read (std::shared_ptr<std::vector<uint8_t>> const & buf
[this_l, buffer_a, callback_a] (boost::system::error_code const & ec, std::size_t size_a) {
this_l->node.stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::in, size_a);
this_l->stop_timer ();
this_l->update_last_receive_time ();
callback_a (ec, size_a);
}));
}));
Expand Down Expand Up @@ -124,14 +127,30 @@ void nano::socket::stop_timer ()
last_completion_time = nano::seconds_since_epoch ();
}

void nano::socket::update_last_receive_time ()
{
last_receive_time = nano::seconds_since_epoch ();
}

void nano::socket::checkup ()
{
std::weak_ptr<nano::socket> this_w (shared_from_this ());
node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (2), [this_w] () {
if (auto this_l = this_w.lock ())
{
uint64_t now (nano::seconds_since_epoch ());
auto condition_to_disconnect{ false };
if (this_l->is_realtime_connection () && now - this_l->last_receive_time > this_l->silent_connection_tolerance_time.count ())
{
this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_silent_connection_drop, nano::stat::dir::in);
condition_to_disconnect = true;
}
if (this_l->next_deadline != std::numeric_limits<uint64_t>::max () && now - this_l->last_completion_time > this_l->next_deadline)
{
this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_io_timeout_drop, nano::stat::dir::in);
condition_to_disconnect = true;
}
if (condition_to_disconnect)
{
if (this_l->node.config.logging.network_timeout_logging ())
{
Expand Down Expand Up @@ -164,6 +183,14 @@ void nano::socket::timeout_set (std::chrono::seconds io_timeout_a)
io_timeout = io_timeout_a;
}

void nano::socket::set_silent_connection_tolerance_time (std::chrono::seconds tolerance_time_a)
{
auto this_l (shared_from_this ());
boost::asio::dispatch (strand, boost::asio::bind_executor (strand, [this_l, tolerance_time_a] () {
this_l->silent_connection_tolerance_time = tolerance_time_a;
}));
}

void nano::socket::close ()
{
auto this_l (shared_from_this ());
Expand Down
12 changes: 12 additions & 0 deletions nano/node/socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class socket : public std::enable_shared_from_this<nano::socket>
/** This can be called to change the maximum idle time, e.g. based on the type of traffic detected. */
void timeout_set (std::chrono::seconds io_timeout_a);
void start_timer (std::chrono::seconds deadline_a);
void set_silent_connection_tolerance_time (std::chrono::seconds tolerance_time_a);
bool max () const
{
return queue_size >= queue_size_max;
Expand All @@ -88,6 +89,14 @@ class socket : public std::enable_shared_from_this<nano::socket>
{
type_m = type_a;
}
bool is_realtime_connection ()
{
return type () == nano::socket::type_t::realtime || type () == nano::socket::type_t::realtime_response_server;
}
bool is_closed ()
{
return closed;
}

protected:
/** Holds the buffer and callback for queued writes */
Expand All @@ -107,8 +116,10 @@ class socket : public std::enable_shared_from_this<nano::socket>

std::atomic<uint64_t> next_deadline;
std::atomic<uint64_t> last_completion_time;
std::atomic<uint64_t> last_receive_time;
std::atomic<bool> timed_out{ false };
std::atomic<std::chrono::seconds> io_timeout;
std::chrono::seconds silent_connection_tolerance_time;
std::atomic<std::size_t> queue_size{ 0 };

/** Set by close() - completion handlers must check this. This is more reliable than checking
Expand All @@ -117,6 +128,7 @@ class socket : public std::enable_shared_from_this<nano::socket>
void close_internal ();
void start_timer ();
void stop_timer ();
void update_last_receive_time ();
void checkup ();

private:
Expand Down

0 comments on commit 62fbc45

Please sign in to comment.