From 32c0f908cd753f9319909e2f43d2b4657ebaf664 Mon Sep 17 00:00:00 2001 From: Erik de Castro Lopo Date: Sun, 21 Jan 2018 08:44:23 +1100 Subject: [PATCH] Allow the number of incoming connections to be limited It was already possible to limit outgoing connections. One might want to do this on home network connections with high bandwidth but low usage caps. --- .../net/levin_protocol_handler_async.h | 78 +++++++++++-------- src/daemon/command_parser_executor.cpp | 17 ++++ src/daemon/command_parser_executor.h | 4 +- src/daemon/command_server.cpp | 6 ++ src/daemon/rpc_command_executor.cpp | 32 ++++++++ src/daemon/rpc_command_executor.h | 4 +- src/p2p/net_node.cpp | 1 + src/p2p/net_node.h | 6 ++ src/p2p/net_node.inl | 57 +++++++++++++- src/p2p/p2p_protocol_defs.h | 2 + src/rpc/core_rpc_server.cpp | 12 +++ src/rpc/core_rpc_server.h | 2 + src/rpc/core_rpc_server_commands_defs.h | 24 +++++- 13 files changed, 205 insertions(+), 40 deletions(-) diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index ee64da5d8..de270bfd0 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -77,6 +77,8 @@ class async_protocol_handler_config levin_commands_handler* m_pcommands_handler; void (*m_pcommands_handler_destroy)(levin_commands_handler*); + void delete_connections (size_t count, bool incoming); + public: typedef t_connection_context connection_context; uint64_t m_max_packet_size; @@ -101,6 +103,7 @@ public: {} ~async_protocol_handler_config() { set_handler(NULL, NULL); } void del_out_connections(size_t count); + void del_in_connections(size_t count); }; @@ -731,41 +734,50 @@ void async_protocol_handler_config::del_connection(async_p } //------------------------------------------------------------------------------------------ template +void async_protocol_handler_config::delete_connections(size_t count, bool incoming) +{ + std::vector connections; + CRITICAL_REGION_BEGIN(m_connects_lock); + for (auto& c: m_connects) + { + if (c.second->m_connection_context.m_is_income == incoming) + connections.push_back(c.first); + } + + // close random connections from the provided set + // TODO or better just keep removing random elements (performance) + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + shuffle(connections.begin(), connections.end(), std::default_random_engine(seed)); + while (count > 0 && connections.size() > 0) + { + try + { + auto i = connections.end() - 1; + async_protocol_handler *conn = m_connects.at(*i); + del_connection(conn); + close(*i); + connections.erase(i); + } + catch (const std::out_of_range &e) + { + MWARNING("Connection not found in m_connects, continuing"); + } + --count; + } + + CRITICAL_REGION_END(); +} +//------------------------------------------------------------------------------------------ +template void async_protocol_handler_config::del_out_connections(size_t count) { - std::vector out_connections; - CRITICAL_REGION_BEGIN(m_connects_lock); - for (auto& c: m_connects) - { - if (!c.second->m_connection_context.m_is_income) - out_connections.push_back(c.first); - } - - if (out_connections.size() == 0) - return; - - // close random out connections - // TODO or better just keep removing random elements (performance) - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); - shuffle(out_connections.begin(), out_connections.end(), std::default_random_engine(seed)); - while (count > 0 && out_connections.size() > 0) - { - try - { - auto i = out_connections.end() - 1; - async_protocol_handler *conn = m_connects.at(*i); - del_connection(conn); - close(*i); - out_connections.erase(i); - } - catch (const std::out_of_range &e) - { - MWARNING("Connection not found in m_connects, continuing"); - } - --count; - } - - CRITICAL_REGION_END(); + delete_connections(count, false); +} +//------------------------------------------------------------------------------------------ +template +void async_protocol_handler_config::del_in_connections(size_t count) +{ + delete_connections(count, true); } //------------------------------------------------------------------------------------------ template diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 3ec74ff79..09e425dd1 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -428,6 +428,23 @@ bool t_command_parser_executor::out_peers(const std::vector& args) return m_executor.out_peers(limit); } +bool t_command_parser_executor::in_peers(const std::vector& args) +{ + if (args.empty()) return false; + + unsigned int limit; + try { + limit = std::stoi(args[0]); + } + + catch(const std::exception& ex) { + _erro("stoi exception"); + return false; + } + + return m_executor.in_peers(limit); +} + bool t_command_parser_executor::start_save_graph(const std::vector& args) { if (!args.empty()) return false; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 37e900b8f..2c09a4748 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -108,7 +108,9 @@ public: bool set_limit_down(const std::vector& args); bool out_peers(const std::vector& args); - + + bool in_peers(const std::vector& args); + bool start_save_graph(const std::vector& args); bool stop_save_graph(const std::vector& args); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 1f8981fa2..a50dbea69 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -196,6 +196,12 @@ t_command_server::t_command_server( , "out_peers " , "Set the of out peers." ); + m_command_lookup.set_handler( + "in_peers" + , std::bind(&t_command_parser_executor::in_peers, &m_parser, p::_1) + , "in_peers " + , "Set the of in peers." + ); m_command_lookup.set_handler( "start_save_graph" , std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 2da4f3e6e..af9c05bc7 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1281,6 +1281,38 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) return true; } +bool t_rpc_command_executor::in_peers(uint64_t limit) +{ + cryptonote::COMMAND_RPC_IN_PEERS::request req; + cryptonote::COMMAND_RPC_IN_PEERS::response res; + + epee::json_rpc::error error_resp; + + req.in_peers = limit; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "in_peers", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_in_peers(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + std::cout << "Max number of in peers set to " << limit << std::endl; + + return true; +} + bool t_rpc_command_executor::start_save_graph() { cryptonote::COMMAND_RPC_START_SAVE_GRAPH::request req; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index f0781180a..fa83d8988 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -122,7 +122,9 @@ public: bool set_limit(int64_t limit_down, int64_t limit_up); bool out_peers(uint64_t limit); - + + bool in_peers(uint64_t limit); + bool start_save_graph(); bool stop_save_graph(); diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 121e72392..994941168 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -55,6 +55,7 @@ namespace nodetool const command_line::arg_descriptor arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; const command_line::arg_descriptor arg_out_peers = {"out-peers", "set max number of out peers", -1}; + const command_line::arg_descriptor arg_in_peers = {"in-peers", "set max number of in peers", -1}; const command_line::arg_descriptor arg_tos_flag = {"tos-flag", "set TOS flag", -1}; const command_line::arg_descriptor arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", -1}; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 2f26695a2..568c650cc 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -81,6 +81,7 @@ namespace nodetool node_server(t_payload_net_handler& payload_handler) :m_payload_handler(payload_handler), m_current_number_of_out_peers(0), + m_current_number_of_in_peers(0), m_allow_local_ip(false), m_hide_my_port(false), m_no_igd(false), @@ -117,8 +118,10 @@ namespace nodetool bool log_connections(); virtual uint64_t get_connections_count(); size_t get_outgoing_connections_count(); + size_t get_incoming_connections_count(); peerlist_manager& get_peerlist_manager(){return m_peerlist;} void delete_out_connections(size_t count); + void delete_in_connections(size_t count); virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME); virtual bool unblock_host(const epee::net_utils::network_address &address); virtual std::map get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } @@ -230,6 +233,7 @@ namespace nodetool bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor > & arg, Container& container); bool set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max); + bool set_max_in_peers(const boost::program_options::variables_map& vm, int64_t max); bool set_tos_flag(const boost::program_options::variables_map& vm, int limit); bool set_rate_up_limit(const boost::program_options::variables_map& vm, int64_t limit); @@ -271,6 +275,7 @@ namespace nodetool public: config m_config; // TODO was private, add getters? std::atomic m_current_number_of_out_peers; + std::atomic m_current_number_of_in_peers; void set_save_graph(bool save_graph) { @@ -345,6 +350,7 @@ namespace nodetool extern const command_line::arg_descriptor arg_no_igd; extern const command_line::arg_descriptor arg_offline; extern const command_line::arg_descriptor arg_out_peers; + extern const command_line::arg_descriptor arg_in_peers; extern const command_line::arg_descriptor arg_tos_flag; extern const command_line::arg_descriptor arg_limit_rate_up; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 5f00e97f0..3445674f6 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -85,6 +85,7 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_out_peers); + command_line::add_arg(desc, arg_in_peers); command_line::add_arg(desc, arg_tos_flag); command_line::add_arg(desc, arg_limit_rate_up); command_line::add_arg(desc, arg_limit_rate_down); @@ -315,6 +316,9 @@ namespace nodetool if ( !set_max_out_peers(vm, command_line::get_arg(vm, arg_out_peers) ) ) return false; + if ( !set_max_in_peers(vm, command_line::get_arg(vm, arg_in_peers) ) ) + return false; + if ( !set_tos_flag(vm, command_line::get_arg(vm, arg_tos_flag) ) ) return false; @@ -565,14 +569,23 @@ namespace nodetool while (!is_closing && !m_net_server.is_stop_signal_sent()) { // main loop of thread //number_of_peers = m_net_server.get_config_object().get_connections_count(); - unsigned int number_of_peers = 0; + unsigned int number_of_in_peers = 0; + unsigned int number_of_out_peers = 0; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if (!cntxt.m_is_income) ++number_of_peers; + if (cntxt.m_is_income) + { + ++number_of_in_peers; + } + else + { + ++number_of_out_peers; + } return true; }); // lambda - m_current_number_of_out_peers = number_of_peers; + m_current_number_of_in_peers = number_of_in_peers; + m_current_number_of_out_peers = number_of_out_peers; boost::this_thread::sleep_for(boost::chrono::seconds(1)); } // main loop of thread @@ -1253,6 +1266,20 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + size_t node_server::get_incoming_connections_count() + { + size_t count = 0; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.m_is_income) + ++count; + return true; + }); + + return count; + } + //----------------------------------------------------------------------------------- + template bool node_server::idle_worker() { m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server::peer_sync_idle_maker, this)); @@ -1618,6 +1645,13 @@ namespace nodetool return 1; } + if (m_current_number_of_in_peers >= m_config.m_net_config.max_in_connection_count) // in peers limit + { + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but already have max incoming connections, so dropping this one."); + drop_connection(context); + return 1; + } + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); @@ -1786,12 +1820,29 @@ namespace nodetool return true; } + template + bool node_server::set_max_in_peers(const boost::program_options::variables_map& vm, int64_t max) + { + if(max == -1) { + m_config.m_net_config.max_in_connection_count = -1; + return true; + } + m_config.m_net_config.max_in_connection_count = max; + return true; + } + template void node_server::delete_out_connections(size_t count) { m_net_server.get_config_object().del_out_connections(count); } + template + void node_server::delete_in_connections(size_t count) + { + m_net_server.get_config_object().del_in_connections(count); + } + template bool node_server::set_tos_flag(const boost::program_options::variables_map& vm, int flag) { diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 3fa71713d..e793e19b6 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -132,12 +132,14 @@ namespace nodetool { BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(max_out_connection_count) + KV_SERIALIZE(max_in_connection_count) KV_SERIALIZE(handshake_interval) KV_SERIALIZE(packet_max_size) KV_SERIALIZE(config_id) END_KV_SERIALIZE_MAP() uint32_t max_out_connection_count; + uint32_t max_in_connection_count; uint32_t connection_timeout; uint32_t ping_connection_timeout; uint32_t handshake_interval; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 2bae6352e..80d9823d4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1603,6 +1603,18 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res) + { + PERF_TIMER(on_in_peers); + size_t n_connections = m_p2p.get_incoming_connections_count(); + size_t n_delete = (n_connections > req.in_peers) ? n_connections - req.in_peers : 0; + m_p2p.m_config.m_net_config.max_in_connection_count = req.in_peers; + if (n_delete) + m_p2p.delete_in_connections(n_delete); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res) { PERF_TIMER(on_start_save_graph); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 0c7028719..2b0d54bef 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -114,6 +114,7 @@ namespace cryptonote MAP_URI_AUTO_JON2("/get_limit", on_get_limit, COMMAND_RPC_GET_LIMIT) MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) + MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted) MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) @@ -183,6 +184,7 @@ namespace cryptonote bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res); bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res); + bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res); bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 9b9a8f949..3a878c4cc 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1678,8 +1678,28 @@ namespace cryptonote struct response { - std::string status; - + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_IN_PEERS + { + struct request + { + uint64_t in_peers; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(in_peers) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) END_KV_SERIALIZE_MAP()