Add IP blocking for misbehaving nodes (adapted from Boolberry)
With minor cleanup and fixes (spelling, indent) by moneromooo
This commit is contained in:
parent
9c64b12320
commit
4cea2b13b2
7 changed files with 108 additions and 5 deletions
|
@ -899,9 +899,7 @@ POP_WARNINGS
|
|||
boost::bind(&boosted_tcp_server<t_protocol_handler>::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
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/serialization/map.hpp>
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "warnings.h"
|
||||
|
@ -66,7 +67,8 @@ namespace nodetool
|
|||
|
||||
template<class t_payload_net_handler>
|
||||
class node_server: public epee::levin::levin_commands_handler<p2p_connection_context_t<typename t_payload_net_handler::connection_context> >,
|
||||
public i_p2p_endpoint<typename t_payload_net_handler::connection_context>
|
||||
public i_p2p_endpoint<typename t_payload_net_handler::connection_context>,
|
||||
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<bool(typename t_payload_net_handler::connection_context&, peerid_type)> 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<class t_callback>
|
||||
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 <class Container>
|
||||
|
@ -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<net_address, time_t> m_conn_fails_cache;
|
||||
epee::critical_section m_conn_fails_cache_lock;
|
||||
|
||||
epee::critical_section m_blocked_ips_lock;
|
||||
std::map<uint32_t, time_t> m_blocked_ips;
|
||||
|
||||
epee::critical_section m_ip_fails_score_lock;
|
||||
std::map<uint32_t, uint64_t> m_ip_fails_score;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <algorithm>
|
||||
|
@ -161,6 +163,22 @@ namespace nodetool
|
|||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::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<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::make_default_config()
|
||||
{
|
||||
m_config.m_peer_id = crypto::rand<uint64_t>();
|
||||
|
@ -168,6 +186,31 @@ namespace nodetool
|
|||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::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<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::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<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::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<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::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<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::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<std::string>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<bool(t_connection_context&, peerid_type)> f)=0;
|
||||
virtual bool block_ip(uint32_t adress)=0;
|
||||
virtual bool add_ip_fail(uint32_t adress)=0;
|
||||
};
|
||||
|
||||
template<class t_connection_context>
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue