diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index be9fc2a2..14d94728 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -54,7 +54,7 @@ namespace epee { namespace net_utils { struct i_connection_filter { - virtual bool is_remote_ip_allowed(uint32_t adress) = 0; + virtual bool is_remote_ip_allowed(uint32_t adress, bool is_incoming) = 0; protected: virtual ~i_connection_filter() diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 3f5aa31b..c087b2f4 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -146,7 +146,7 @@ bool connection::start(bool is_income, bool is_multithreaded LOG_PRINT_L3("[sock " << socket_.native_handle() << "] new connection, remote end_point: " << print_connection_context_short(context) << " local end_point: " << local_ep.address().to_string() << ':' << local_ep.port() << ", total sockets objects " << m_ref_sockets_count); - if(is_income && m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip)) { + if(is_income && m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip, is_income)) { LOG_PRINT_L0("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection"); close(); return false; diff --git a/src/currency_core/currency_config.h b/src/currency_core/currency_config.h index f621d83b..c5782f6e 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -239,6 +239,7 @@ #define P2P_NET_DATA_FILENAME "p2pstate.bin" +#define P2P_MANUAL_CONFIG_FILENAME "p2p_manual_config.json" #define MINER_CONFIG_FILENAME "miner_conf.json" #define GUI_SECURE_CONFIG_FILENAME "gui_secure_conf.bin" #define GUI_CONFIG_FILENAME "gui_settings.json" diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index f9e7edc3..c87f92dc 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -81,6 +81,7 @@ public: m_cmd_binder.set_handler("debug_remote_node_mode", boost::bind(&daemon_commands_handler::debug_remote_node_mode, this, ph::_1), " - If node got connected put node into 'debug mode' i.e. no sync process of other communication except ping responses, maintenance secrete key will be requested"); m_cmd_binder.set_handler("full_db_cache_warmup", boost::bind(&daemon_commands_handler::full_db_cache_warmup, this, ph::_1), "(Experimental) Perform full DB loading to RAM cache(make sense only with big numbers passed to --db-cache-l2 option)"); m_cmd_binder.set_handler("print_cache_state", boost::bind(&daemon_commands_handler::print_cache_state, this, ph::_1), "Print db l2 cache state"); + m_cmd_binder.set_handler("reload_p2p_manual_config", boost::bind(&daemon_commands_handler::reload_p2p_manual_config, this, ph::_1), "Reload manual p2p config from 'p2p_manual_config.json'"); #ifdef _DEBUG m_cmd_binder.set_handler("debug_set_time_adj", boost::bind(&daemon_commands_handler::debug_set_time_adj, this, ph::_1), "DEBUG: set core time adjustment"); #endif @@ -901,6 +902,13 @@ private: return true; } //-------------------------------------------------------------------------------- + bool reload_p2p_manual_config(const std::vector& args) + { + m_srv.reload_p2p_manual_config(false); + + return true; + } + //-------------------------------------------------------------------------------- bool print_pool(const std::vector& args) { LOG_PRINT_L0("Pool state: " << ENDL << m_srv.get_payload_object().get_core().print_pool(false)); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 6cb58abb..b4a004ba 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -121,10 +121,12 @@ 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;} bool handle_maintainers_entry(const maintainers_entry& me); bool get_maintainers_info(maintainers_info_external& me); void get_ip_block_list(std::map& blocklist); + bool reload_p2p_manual_config(bool silent = true); typedef COMMAND_REQUEST_STAT_INFO_T COMMAND_REQUEST_STAT_INFO; private: @@ -184,9 +186,10 @@ namespace nodetool virtual bool add_ip_fail(uint32_t address); virtual bool is_stop_signal_sent(); //----------------- i_connection_filter -------------------------------------------------------- - virtual bool is_remote_ip_allowed(uint32_t adress); + virtual bool is_remote_ip_allowed(uint32_t adress, bool is_incoming); //----------------------------------------------------------------------------------------------- bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); + bool parse_peerlist(const std::vector& perrs_str, std::list& peers); bool handle_command_line(const boost::program_options::variables_map& vm); bool idle_worker(); bool handle_remote_peerlist(const std::list& peerlist, time_t local_time, const net_utils::connection_context_base& context); @@ -298,9 +301,16 @@ namespace nodetool critical_section m_blocked_ips_lock; std::map m_blocked_ips; + //critical_section m_permanently_blocked_ips_lock; + std::set m_permanently_blocked_ips; + + critical_section m_ip_fails_score_lock; std::map m_ip_fails_score; + critical_section m_p2p_manual_config_lock; + p2p_config_data m_p2p_manual_config; + }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 43024815..5ba43f4a 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -106,8 +106,11 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::is_remote_ip_allowed(uint32_t addr) + bool node_server::is_remote_ip_allowed(uint32_t addr, bool is_incoming) { + if (is_incoming && m_p2p_manual_config.incoming_connections_limit && *m_p2p_manual_config.incoming_connections_limit >= get_incoming_connections_count()) + return false; + if (m_offline_mode) return false; @@ -132,6 +135,13 @@ namespace nodetool bool node_server::is_ip_in_blacklist(uint32_t addr) { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + + //first check permanently blocked ip + if (m_permanently_blocked_ips.find(addr) != m_permanently_blocked_ips.end()) + return true; + + + //check temporary blocked auto it = m_blocked_ips.find(addr); if (it == m_blocked_ips.end()) return false; @@ -147,6 +157,77 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::reload_p2p_manual_config(bool silent) + { + std::set new_block_list; + std::list new_priority_peers; + { + CRITICAL_REGION_LOCAL(m_p2p_manual_config_lock); + p2p_config_data config = AUTO_VAL_INIT(config); + std::string p2p_config_file_path = m_config_folder + "/" + P2P_MANUAL_CONFIG_FILENAME; + bool r = epee::serialization::load_t_from_json_file(config, p2p_config_file_path); + if (!r) + { + if (!silent) + { + LOG_PRINT_L0("P2P network config file is missing or not loaded"); + } + return true; + } + //loaded json, let's try to load blacklist + for (const std::string& ip_str_entry : config.ip_black_list) + { + uint32_t ip = 0; + if (!string_tools::get_ip_int32_from_string(ip, ip_str_entry)) + { + LOG_ERROR("P2P network config file loading error: failed to convert '" << ip_str_entry << "' to ip address"); + return false; + } + new_block_list.insert(ip); + } + + if (config.ip_priority_list.size()) + { + bool r = parse_peerlist(config.ip_priority_list, new_priority_peers); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse priority peers"); + } + + m_p2p_manual_config = config; + + + //override some of the settings + if (m_p2p_manual_config.use_only_priority_peers) + { + m_use_only_priority_peers = *m_p2p_manual_config.use_only_priority_peers; + } + + m_priority_peers = new_priority_peers; + + + if (m_p2p_manual_config.incoming_connections_limit && *m_p2p_manual_config.incoming_connections_limit == 0) + { + m_hide_my_port = true; + } + + if (m_p2p_manual_config.outgoing_connections_limit && *m_p2p_manual_config.outgoing_connections_limit == 0) + { + //TODO: consider if need to do this + //m_offline_mode = true; + } + + } + + + //TODO: this command when passed to running daemon via command line option might be multi-thread-unsafe, subject for refactoring + //CRITICAL_REGION_LOCAL(m_permanently_blocked_ips_lock); + m_permanently_blocked_ips.clear(); + m_permanently_blocked_ips = new_block_list; + + LOG_PRINT_L0("P2P network manual config file loaded(some of the p2p cpommand line options might be overridden by '" << P2P_MANUAL_CONFIG_FILENAME << "')."); + return true; + } + //----------------------------------------------------------------------------------- + template bool node_server::block_ip(uint32_t addr) { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); @@ -188,6 +269,19 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::parse_peerlist(const std::vector& perrs_str, std::list& peers) + { + for (const std::string& pr_str : perrs_str) + { + nodetool::net_address na = AUTO_VAL_INIT(na); + bool r = parse_peer_from_string(na, pr_str); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); + peers.push_back(na); + } + return true; + } + //----------------------------------------------------------------------------------- + template bool node_server::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); @@ -236,13 +330,8 @@ namespace nodetool if (command_line::has_arg(vm, arg_p2p_add_priority_node)) { std::vector perrs = command_line::get_arg(vm, arg_p2p_add_priority_node); - for(const std::string& pr_str: perrs) - { - nodetool::net_address na = AUTO_VAL_INIT(na); - bool r = parse_peer_from_string(na, pr_str); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - m_priority_peers.push_back(na); - } + bool r = parse_peerlist(perrs, m_priority_peers); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse priority peers"); } if(command_line::has_arg(vm, arg_p2p_use_only_priority_nodes)) m_use_only_priority_peers = true; @@ -383,6 +472,10 @@ namespace nodetool LOG_PRINT_GREEN("Net service binded on " << m_bind_ip << ":" << m_listenning_port, LOG_LEVEL_0); if(m_external_port) LOG_PRINT_L0("External port defined as " << m_external_port); + + + reload_p2p_manual_config(true); + return res; } //----------------------------------------------------------------------------------- @@ -842,7 +935,7 @@ namespace nodetool continue; } - if (!is_remote_ip_allowed(pe.adr.ip)) + if (!is_remote_ip_allowed(pe.adr.ip, false)) { ++peer_index; continue; @@ -913,7 +1006,19 @@ namespace nodetool size_t expected_white_connections = (m_config.m_net_config.connections_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) + bool need_more_connections = false; + if (m_p2p_manual_config.outgoing_connections_limit) + { + // m_p2p_manual_config always override default settings from m_config.m_net_config + need_more_connections = conn_count < *m_p2p_manual_config.outgoing_connections_limit; + } + else + { + // use default policy + need_more_connections = conn_count < m_config.m_net_config.connections_count; + } + + if(need_more_connections) { if(conn_count < expected_white_connections) { @@ -966,6 +1071,20 @@ namespace nodetool return true; }); + return count; + } + //----------------------------------------------------------------------------------- + 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; } //----------------------------------------------------------------------------------- diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index fe1225f4..6ac82b2e 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -289,6 +289,29 @@ namespace nodetool }; }; + + + struct p2p_config_data + { + std::optional incoming_connections_limit; + std::optional outgoing_connections_limit; + std::vector ip_black_list; + std::optional use_only_priority_peers = false; //allow to make outgoing connections only to the peers listed in ip_priority_list + std::vector ip_priority_list; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(incoming_connections_limit) + KV_SERIALIZE(outgoing_connections_limit) + KV_SERIALIZE(ip_black_list) + KV_SERIALIZE(use_only_priority_peers) + KV_SERIALIZE(ip_priority_list) + END_KV_SERIALIZE_MAP() + }; + + + + + #ifdef ALLOW_DEBUG_COMMANDS //These commands are considered as insecure, and made in debug purposes for a limited lifetime. diff --git a/src/serialization/stl_containers.h b/src/serialization/stl_containers.h index e347328a..f6cdbd49 100644 --- a/src/serialization/stl_containers.h +++ b/src/serialization/stl_containers.h @@ -9,6 +9,10 @@ #include #include + +#define SANITY_CHECK_MAX_RESERVATION_RAM 10000000 + + //#include "serialization.h" template