From 4cea2b13b221bed66023254dd3356f2548a08df4 Mon Sep 17 00:00:00 2001 From: Javier Smooth Date: Mon, 23 Nov 2015 17:34:55 +0000 Subject: [PATCH] Add IP blocking for misbehaving nodes (adapted from Boolberry) With minor cleanup and fixes (spelling, indent) by moneromooo --- .../epee/include/net/abstract_tcp_server2.inl | 4 +- src/cryptonote_config.h | 5 ++ .../cryptonote_protocol_handler.inl | 4 ++ src/p2p/net_node.h | 19 +++++- src/p2p/net_node.inl | 68 +++++++++++++++++++ src/p2p/net_node_common.h | 10 +++ src/p2p/net_peerlist.h | 3 +- 7 files changed, 108 insertions(+), 5 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 612e2b41..b75a9eca 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -899,9 +899,7 @@ POP_WARNINGS boost::bind(&boosted_tcp_server::handle_accept, this, boost::asio::placeholders::error)); - bool r = conn->start(true, 1 < m_threads_count); - if (!r) - _erro("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sock_count); + conn->start(true, 1 < m_threads_count); conn->save_dbg_log(); }else { diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6053387a..33005660 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -105,6 +105,11 @@ #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 +#define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour +#define P2P_IP_BLOCKTIME (60*60*24) //24 hour +#define P2P_IP_FAILS_BEFORE_BLOCK 10 +#define P2P_IDLE_CONNECTION_KILL_INTERVAL (5*60) //5 minutes + #define ALLOW_DEBUG_COMMANDS #define CRYPTONOTE_NAME "bitmonero" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 83c7233b..a6761101 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -571,6 +571,7 @@ namespace cryptonote { LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); m_core.cleanup_handle_incoming_blocks(); return 1; } @@ -578,6 +579,7 @@ namespace cryptonote { LOG_PRINT_CCONTEXT_L1("Block received at sync phase was marked as orphaned, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); m_core.cleanup_handle_incoming_blocks(); return 1; } @@ -728,6 +730,7 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } @@ -736,6 +739,7 @@ namespace cryptonote LOG_ERROR_CCONTEXT("sent m_block_ids starting from unknown id: " << epee::string_tools::pod_to_hex(arg.m_block_ids.front()) << " , dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 3eb12520..4aaac813 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "cryptonote_config.h" #include "warnings.h" @@ -66,7 +67,8 @@ namespace nodetool template class node_server: public epee::levin::levin_commands_handler >, - public i_p2p_endpoint + public i_p2p_endpoint, + public epee::net_utils::i_connection_filter { struct by_conn_id{}; struct by_peer_id{}; @@ -169,6 +171,10 @@ namespace nodetool virtual bool drop_connection(const epee::net_utils::connection_context_base& context); virtual void request_callback(const epee::net_utils::connection_context_base& context); virtual void for_each_connection(std::function f); + virtual bool block_ip(uint32_t adress); + virtual bool add_ip_fail(uint32_t address); + //----------------- i_connection_filter -------------------------------------------------------- + virtual bool is_remote_ip_allowed(uint32_t adress); //----------------------------------------------------------------------------------------------- bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line( @@ -196,6 +202,8 @@ namespace nodetool template bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool make_expected_connections_count(bool white_list, size_t expected_connections); + void cache_connect_fail_info(const net_address& addr); + bool is_addr_recently_failed(const net_address& addr); bool is_priority_node(const net_address& na); template @@ -282,6 +290,15 @@ namespace nodetool //keep connections to initiate some interactions net_server m_net_server; boost::uuids::uuid m_network_id; + + std::map m_conn_fails_cache; + epee::critical_section m_conn_fails_cache_lock; + + epee::critical_section m_blocked_ips_lock; + std::map m_blocked_ips; + + epee::critical_section m_ip_fails_score_lock; + std::map m_ip_fails_score; }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 371cbc46..11df7ee4 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -28,6 +28,8 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +// IP blocking adapted from Boolberry + #pragma once #include @@ -161,6 +163,22 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::is_remote_ip_allowed(uint32_t addr) + { + CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + auto it = m_blocked_ips.find(addr); + if(it == m_blocked_ips.end()) + return true; + if(time(nullptr) - it->second > P2P_IP_BLOCKTIME ) + { + m_blocked_ips.erase(it); + LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(addr) << "is unblocked.", LOG_LEVEL_0); + return true; + } + return false; + } + //----------------------------------------------------------------------------------- + template bool node_server::make_default_config() { m_config.m_peer_id = crypto::rand(); @@ -168,6 +186,31 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::block_ip(uint32_t addr) + { + CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + m_blocked_ips[addr] = time(nullptr); + LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(addr) << " blocked.", LOG_LEVEL_0); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::add_ip_fail(uint32_t address) + { + CRITICAL_REGION_LOCAL(m_ip_fails_score_lock); + uint64_t fails = ++m_ip_fails_score[address]; + LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(address) << " fail score=" << fails, LOG_LEVEL_1); + if(fails > P2P_IP_FAILS_BEFORE_BLOCK) + { + auto it = m_ip_fails_score.find(address); + CHECK_AND_ASSERT_MES(it != m_ip_fails_score.end(), false, "internal error"); + it->second = P2P_IP_FAILS_BEFORE_BLOCK/2; + block_ip(address); + } + return true; + } + //----------------------------------------------------------------------------------- + template bool node_server::parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr) { return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); @@ -428,6 +471,7 @@ namespace nodetool m_net_server.set_threads_prefix("P2P"); m_net_server.get_config_object().m_pcommands_handler = this; m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; + m_net_server.set_connection_filter(this); //try to bind LOG_PRINT_L0("Binding on " << m_bind_ip << ":" << m_port); @@ -624,6 +668,7 @@ namespace nodetool if(!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); + add_ip_fail(context.m_remote_ip); return; } hsh_result = true; @@ -685,6 +730,7 @@ namespace nodetool { LOG_ERROR_CCONTEXT("COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); m_net_server.get_config_object().close(context.m_connection_id ); + add_ip_fail(context.m_remote_ip); } if(!context.m_is_income) m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); @@ -829,6 +875,20 @@ namespace nodetool #undef LOG_PRINT_CC_PRIORITY_NODE + //----------------------------------------------------------------------------------- + template + bool node_server::is_addr_recently_failed(const net_address& addr) + { + CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); + auto it = m_conn_fails_cache.find(addr); + if(it == m_conn_fails_cache.end()) + return false; + + if(time(NULL) - it->second > P2P_FAILED_ADDR_FORGET_SECONDS) + return false; + else + return true; + } //----------------------------------------------------------------------------------- template bool node_server::make_new_connection_from_peerlist(bool use_white_list) @@ -866,6 +926,12 @@ namespace nodetool continue; } + if(!is_remote_ip_allowed(pe.adr.ip)) + continue; + + if(is_addr_recently_failed(pe.adr)) + continue; + LOG_PRINT_L2("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << "[white=" << use_white_list @@ -1270,6 +1336,7 @@ namespace nodetool LOG_PRINT_CCONTEXT_L1("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); drop_connection(context); + add_ip_fail(context.m_remote_ip); return 1; } @@ -1277,6 +1344,7 @@ namespace nodetool { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came not from incoming connection"); drop_connection(context); + add_ip_fail(context.m_remote_ip); return 1; } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index a7b8bf6f..93b29deb 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -50,6 +50,8 @@ namespace nodetool virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; virtual uint64_t get_connections_count()=0; virtual void for_each_connection(std::function f)=0; + virtual bool block_ip(uint32_t adress)=0; + virtual bool add_ip_fail(uint32_t adress)=0; }; template @@ -84,5 +86,13 @@ namespace nodetool { return false; } + virtual bool block_ip(uint32_t adress) + { + return true; + } + virtual bool add_ip_fail(uint32_t adress) + { + return true; + } }; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index f738c68f..3d8b08ce 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -53,6 +53,7 @@ #include "net_peerlist_boost_serialization.h" +#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 4 namespace nodetool { @@ -394,4 +395,4 @@ namespace nodetool //-------------------------------------------------------------------------------------------------- } -BOOST_CLASS_VERSION(nodetool::peerlist_manager, 4) +BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER)