diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index f007ccc76..9599df9d7 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -83,6 +83,12 @@ class async_protocol_handler_config // struct invoke_remote_command2_state_machine*); + + void delete_connections (size_t count, bool incoming); + void delete_connections (const std::vector &connections); + public: typedef t_connection_context connection_context; levin_commands_handler* m_pcommands_handler; @@ -106,6 +112,7 @@ class async_protocol_handler_config async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) {} void del_out_connections(size_t count); + void del_in_connections(size_t count); }; @@ -386,10 +393,14 @@ class async_protocol_handler is_continue = false; if(cb >= MIN_BYTES_WANTED && !m_invoke_response_handlers.empty()) { - //async call scenario - boost::shared_ptr response_handler = m_invoke_response_handlers.front(); - response_handler->reset_timer(); - MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb); + CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock); + if (!m_invoke_response_handlers.empty()) + { + //async call scenario + boost::shared_ptr response_handler = m_invoke_response_handlers.front(); + response_handler->reset_timer(); + MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb); + } } break; } @@ -733,32 +744,72 @@ 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); + close(*i); + // TODO: check if connection, connection_context and async_protocol_handler deleted + del_connection(conn); + 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::delete_connections(const std::vector &connections) +{ + CRITICAL_REGION_BEGIN(m_connects_lock); + for (const auto &conn_id: connections) { + try { + async_protocol_handler *conn = m_connects.at(conn_id); + close(conn_id); + // TODO: check if connection, connection_context and async_protocol_handler deleted + del_connection(conn); + } + catch (const std::out_of_range &e) + { + MWARNING("Connection not found in m_connects, continuing"); + } + } + 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) - { - close(*out_connections.begin()); - del_connection(m_connects.at(*out_connections.begin())); - out_connections.erase(out_connections.begin()); - --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/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h index 433038647..8411b5e71 100644 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -142,7 +142,7 @@ namespace epee } if(resp_t.error.code || resp_t.error.message.size()) { - LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); + LOG_ERROR("RPC call of \"" << req_t.method << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); return false; } result_struct = resp_t.result; diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index 297020ef2..67d377041 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -72,10 +72,10 @@ namespace tools fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - ok = ok && epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); + ok = epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { - fail_msg_writer() << "Daemon request failed"; + fail_msg_writer() << "basic_json_rpc_request: Daemon request failed"; return false; } else @@ -95,15 +95,15 @@ namespace tools t_http_connection connection(&m_http_client); bool ok = connection.is_open(); - ok = ok && epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + ok = epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); + if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? { - fail_msg_writer() << fail_msg << " -- " << res.status; + fail_msg_writer() << fail_msg << " -- json_rpc_request: " << res.status; return false; } else @@ -123,15 +123,15 @@ namespace tools t_http_connection connection(&m_http_client); bool ok = connection.is_open(); - ok = ok && epee::net_utils::invoke_http_json(relative_url, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + ok = epee::net_utils::invoke_http_json(relative_url, req, res, m_http_client, t_http_connection::TIMEOUT()); + if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? { - fail_msg_writer() << fail_msg << " -- " << res.status; + fail_msg_writer() << fail_msg << "-- rpc_request: " << res.status; return false; } else diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index d949a57b1..3a11dbb44 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -402,6 +402,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 f301ef14a..75579d0c6 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -108,7 +108,9 @@ class t_command_parser_executor final 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 79f81971c..e91d46e2a 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -189,6 +189,11 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::out_peers, &m_parser, p::_1) , "Set max number of out peers" ); + m_command_lookup.set_handler( + "in_peers" + , std::bind(&t_command_parser_executor::in_peers, &m_parser, p::_1) + , "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 0423da03a..30cd9fb69 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1187,7 +1187,7 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) if (m_is_rpc) { - if (!m_rpc_client->json_rpc_request(req, res, "out_peers", fail_message.c_str())) + if (!m_rpc_client->rpc_request(req, res, "/out_peers", fail_message.c_str())) { return true; } @@ -1206,6 +1206,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->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 fc0b39654..1ca4ce007 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -126,7 +126,9 @@ class t_rpc_command_executor final { bool set_limit_down(int limit); 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 new file mode 100644 index 000000000..be8c4f9b7 --- /dev/null +++ b/src/p2p/net_node.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "common/command_line.h" +#include "net_node.h" + +namespace nodetool +{ + const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; + const command_line::arg_descriptor arg_p2p_bind_port = { + "p2p-bind-port" + , "Port for p2p network protocol" + , std::to_string(config::P2P_DEFAULT_PORT) + }; + const command_line::arg_descriptor arg_testnet_p2p_bind_port = { + "testnet-p2p-bind-port" + , "Port for testnet p2p network protocol" + , std::to_string(config::testnet::P2P_DEFAULT_PORT) + }; + const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; + const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; + const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; + const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; + const command_line::arg_descriptor > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only." + " If this option is given the options add-priority-node and seed-node are ignored"}; + const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; + const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + + const command_line::arg_descriptor arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; + const command_line::arg_descriptor arg_offline = {"offline", "Do not listen for peers, nor connect to any"}; + const command_line::arg_descriptor arg_out_peers = {"out-peers", "set max number of out peers", 200}; + const command_line::arg_descriptor arg_in_peers = {"in-peers", "set max number of in peers", 100}; + 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}; + const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", -1}; + const command_line::arg_descriptor arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; + + const command_line::arg_descriptor arg_save_graph = {"save-graph", "Save data for dr monero", false}; + const command_line::arg_descriptor arg_p2p_net_id = {"net-id", "The way to replace hardcoded NETWORK_ID. Effective only with --testnet, ex.: 'net-id = 54686520-4172-7420-6f77-205761722037'", Uuid()}; + +} diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index e2ed24ca6..cc2d4c3f0 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -79,6 +79,8 @@ namespace nodetool bool m_in_timedsync; }; + + template class node_server: public epee::levin::levin_commands_handler >, public i_p2p_endpoint, @@ -99,6 +101,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), @@ -135,8 +138,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_connections(size_t count); + 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; } @@ -168,10 +173,7 @@ namespace nodetool private: const std::vector m_seed_nodes_list = - { "seeds.moneroseeds.se" - , "seeds.moneroseeds.ae.org" - , "seeds.moneroseeds.ch" - , "seeds.moneroseeds.li" + { }; bool islimitup=false; @@ -323,6 +325,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); @@ -365,6 +368,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) { @@ -406,6 +410,9 @@ namespace nodetool bool notify_peer_list(int command, const std::string& buf, const std::vector& peers_to_send, bool try_connect = false); + private: + void close_stale_rta_connections(bool incoming); + bool rta_housekeeping(); private: std::multimap m_supernode_requests_timestamps; @@ -471,6 +478,31 @@ namespace nodetool bool m_testnet; }; + const int64_t default_limit_up = 2048; + const int64_t default_limit_down = 8192; + extern const command_line::arg_descriptor arg_p2p_bind_ip; + extern const command_line::arg_descriptor arg_p2p_bind_port; + extern const command_line::arg_descriptor arg_testnet_p2p_bind_port; + extern const command_line::arg_descriptor arg_p2p_external_port; + extern const command_line::arg_descriptor arg_p2p_allow_local_ip; + extern const command_line::arg_descriptor > arg_p2p_add_peer; + extern const command_line::arg_descriptor > arg_p2p_add_priority_node; + extern const command_line::arg_descriptor > arg_p2p_add_exclusive_node; + extern const command_line::arg_descriptor > arg_p2p_seed_node; + extern const command_line::arg_descriptor arg_p2p_hide_my_port; + + 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; + extern const command_line::arg_descriptor arg_limit_rate_down; + extern const command_line::arg_descriptor arg_limit_rate; + + extern const command_line::arg_descriptor arg_save_graph; + extern const command_line::arg_descriptor arg_p2p_net_id; } #include "net_node.inl" diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 80435d42f..4d14e3c2b 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -79,42 +79,7 @@ namespace nodetool { - namespace - { - const int64_t default_limit_up = 2048; - const int64_t default_limit_down = 8192; - const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; - const command_line::arg_descriptor arg_p2p_bind_port = { - "p2p-bind-port" - , "Port for p2p network protocol" - , std::to_string(config::P2P_DEFAULT_PORT) - }; - const command_line::arg_descriptor arg_testnet_p2p_bind_port = { - "testnet-p2p-bind-port" - , "Port for testnet p2p network protocol" - , std::to_string(config::testnet::P2P_DEFAULT_PORT) - }; - const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; - const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; - const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; - const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; - const command_line::arg_descriptor > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only." - " If this option is given the options add-priority-node and seed-node are ignored"}; - const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect. Effective only with --testnet, ex.: 'seed-node = 10.12.1.2:8080'"}; - const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; - - const command_line::arg_descriptor arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; - const command_line::arg_descriptor arg_offline = {"offline", "Do not listen for peers, nor connect to any"}; - const command_line::arg_descriptor arg_out_peers = {"out-peers", "set max number of out 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}; - const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", -1}; - const command_line::arg_descriptor arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; - - const command_line::arg_descriptor arg_save_graph = {"save-graph", "Save data for dr monero", false}; - const command_line::arg_descriptor arg_p2p_net_id = {"net-id", "The way to replace hardcoded NETWORK_ID. Effective only with --testnet, ex.: 'net-id = 54686520-4172-7420-6f77-205761722037'"}; - } + //----------------------------------------------------------------------------------- template void node_server::init_options(boost::program_options::options_description& desc) @@ -132,6 +97,7 @@ namespace nodetool command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_offline); 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); @@ -363,6 +329,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; @@ -697,14 +666,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 @@ -716,6 +694,7 @@ namespace nodetool m_net_server.add_idle_handler(boost::bind(&node_server::idle_worker, this), 1000); m_net_server.add_idle_handler(boost::bind(&t_payload_net_handler::on_idle, &m_payload_handler), 1000); + m_net_server.add_idle_handler(boost::bind(&node_server::rta_housekeeping, this), 5000); boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); @@ -829,7 +808,58 @@ namespace nodetool MDEBUG("P2P Request: notify_peer_list: end notify"); return true; } + //----------------------------------------------------------------------------------- + template + void node_server::close_stale_rta_connections(bool incoming) + { + size_t connections_limit = incoming ? m_config.m_net_config.max_in_connection_count : m_config.m_net_config.max_out_connection_count; + if (connections_limit < 0) { + MDEBUG("connections limit is not set"); + return; + } + MTRACE("starting rta connections cleanup: total connection count: " << m_net_server.get_config_object().get_connections_count()); + // read all connections into container + using ConnectionInfo = std::tuple; + std::deque tmp_connections; + m_net_server.get_config_object().foreach_connection([&](p2p_connection_context &ctx) { + MTRACE("checking connection : " << ctx.m_connection_id + << ", m_is_income: " << ctx.m_is_income << ", m_last_send: " << ctx.m_last_send << ", m_last_recv: " << ctx.m_last_recv + << ", m_state: " << ctx.m_state); + // all RTA connections will be in "state_before_handshake" state + // TODO: explicit flag/type for RTA connections? + if (ctx.m_is_income == incoming && ctx.m_state == cryptonote::cryptonote_connection_context::state_before_handshake) { + ConnectionInfo conn = std::make_tuple(ctx.m_connection_id, time(nullptr) - std::max(ctx.m_last_send, ctx.m_last_recv)); + MTRACE("considering to delete: " << std::get<0>(conn) << ", idle time: " << std::get<1>(conn)); + tmp_connections.push_back(conn); + } + return true; // loop over all connections + }); + // sort container by idle time + std::sort(tmp_connections.begin(), tmp_connections.end(), [](const ConnectionInfo &a, const ConnectionInfo &b) { + return std::get<1>(a) < std::get<1>(b); + }); + + std::vector stale_connections; + while (tmp_connections.size() > connections_limit) { + const ConnectionInfo &c = tmp_connections.back(); + MTRACE("deleting connection: " << std::get<0>(c) << ", idle_time: " << std::get<1>(c)); + stale_connections.push_back(std::get<0>(c)); + tmp_connections.pop_back(); + } + m_net_server.get_config_object().delete_connections(stale_connections); + return; + } + //----------------------------------------------------------------------------------- + template + bool node_server::rta_housekeeping() + { + // clean incoming connections + close_stale_rta_connections(true); + // clean outgoing rta connections + close_stale_rta_connections(false); + return true; + } //----------------------------------------------------------------------------------- template bool node_server::multicast_send(int command, const string &data, const std::list &addresses, @@ -1423,11 +1453,11 @@ namespace nodetool template bool node_server::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) { - if (m_current_number_of_out_peers == m_config.m_net_config.connections_count) // out peers limit + if (m_current_number_of_out_peers == m_config.m_net_config.max_out_connection_count) // out peers limit { return false; } - else if (m_current_number_of_out_peers > m_config.m_net_config.connections_count) + else if (m_current_number_of_out_peers > m_config.m_net_config.max_out_connection_count) { m_net_server.get_config_object().del_out_connections(1); m_current_number_of_out_peers --; // atomic variable, update time = 1s @@ -1730,10 +1760,10 @@ namespace nodetool if (!connect_to_peerlist(m_priority_peers)) return false; - size_t expected_white_connections = (m_config.m_net_config.connections_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + size_t expected_white_connections = (m_config.m_net_config.max_out_connection_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; size_t conn_count = get_outgoing_connections_count(); - if(conn_count < m_config.m_net_config.connections_count) + if(conn_count < m_config.m_net_config.max_out_connection_count) { if(conn_count < expected_white_connections) { @@ -1744,20 +1774,20 @@ namespace nodetool if(!make_expected_connections_count(white, expected_white_connections)) return false; //then do grey list - if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.max_out_connection_count)) return false; }else { //start from grey list - if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.max_out_connection_count)) return false; //and then do white list - if(!make_expected_connections_count(white, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(white, m_config.m_net_config.max_out_connection_count)) return false; } } - if (start_conn_count == get_outgoing_connections_count() && start_conn_count < m_config.m_net_config.connections_count) + if (start_conn_count == get_outgoing_connections_count() && start_conn_count < m_config.m_net_config.max_out_connection_count) { MINFO("Failed to connect to any, trying seeds"); if (!connect_to_seed()) @@ -1819,6 +1849,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)); @@ -2190,6 +2234,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."); @@ -2637,19 +2688,36 @@ namespace nodetool bool node_server::set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max) { if(max == -1) { - m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; + m_config.m_net_config.max_out_connection_count = P2P_DEFAULT_CONNECTIONS_COUNT; return true; } - m_config.m_net_config.connections_count = max; + m_config.m_net_config.max_out_connection_count = max; return true; } template - void node_server::delete_connections(size_t count) + 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 b04d8e609..b7db78a43 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -130,13 +130,15 @@ namespace nodetool struct network_config { BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(connections_count) + 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 connections_count; + 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 e5017aff5..ddffd46ca 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1532,21 +1532,24 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res) { - // TODO - /*if (m_p2p.get_outgoing_connections_count() > req.out_peers) - { - m_p2p.m_config.m_net_config.connections_count = req.out_peers; - if (m_p2p.get_outgoing_connections_count() > req.out_peers) - { - int count = m_p2p.get_outgoing_connections_count() - req.out_peers; - m_p2p.delete_connections(count); - } - } - - else - m_p2p.m_config.m_net_config.connections_count = req.out_peers; - */ - return true; + size_t n_connections = m_p2p.get_outgoing_connections_count(); + size_t n_delete = (n_connections > req.out_peers) ? n_connections - req.out_peers : 0; + m_p2p.m_config.m_net_config.max_out_connection_count = req.out_peers; + if (n_delete) + m_p2p.delete_out_connections(n_delete); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res) + { + 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) diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 297ac5cd7..5a4589a26 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -98,6 +98,7 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) 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) @@ -165,6 +166,7 @@ namespace cryptonote bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res); bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::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 bea4f0ad0..cce367be5 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1266,8 +1266,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()