aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2015-11-24 23:04:36 +0200
committerRiccardo Spagni <ric@spagni.net>2015-11-24 23:04:51 +0200
commit4061a32082554f3b18429b85b35398469b061faa (patch)
tree45c6d2da44341a4192771a1e913fda77fb91cbc0 /src
parentMerge pull request #500 (diff)
parenthardfork: fix more major/minor issues (diff)
downloadmonero-4061a32082554f3b18429b85b35398469b061faa.tar.xz
Merge pull request #501
d887c18 hardfork: fix more major/minor issues (moneromooo-monero) 3b47ca2 hardfork: fix rescan on load (moneromooo-monero) 4cea2b1 Add IP blocking for misbehaving nodes (adapted from Boolberry) (Javier Smooth) 9c64b12 quiet down p2p logging a bit (Javier Smooth) 53c75ab blockchain: log versions as numbers, not characters (moneromooo-monero) edade8d hardfork: fix actual/voting confusion (moneromooo-monero)
Diffstat (limited to 'src')
-rw-r--r--src/cryptonote_config.h5
-rw-r--r--src/cryptonote_core/blockchain.cpp6
-rw-r--r--src/cryptonote_core/hardfork.cpp44
-rw-r--r--src/cryptonote_core/hardfork.h11
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl4
-rw-r--r--src/p2p/net_node.h19
-rw-r--r--src/p2p/net_node.inl70
-rw-r--r--src/p2p/net_node_common.h10
-rw-r--r--src/p2p/net_peerlist.h3
9 files changed, 142 insertions, 30 deletions
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 6053387ac..33005660a 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_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 8bb63ad09..af7713972 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -257,13 +257,13 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet)
if (testnet) {
m_hardfork = new HardFork(*db, 1, testnet_hard_fork_version_1_till);
for (size_t n = 0; n < sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]); ++n)
- m_hardfork->add(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].threshold, testnet_hard_forks[n].time);
+ m_hardfork->add_fork(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].threshold, testnet_hard_forks[n].time);
}
else
{
m_hardfork = new HardFork(*db, 1, mainnet_hard_fork_version_1_till);
for (size_t n = 0; n < sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]); ++n)
- m_hardfork->add(mainnet_hard_forks[n].version, mainnet_hard_forks[n].height, mainnet_hard_forks[n].threshold, mainnet_hard_forks[n].time);
+ m_hardfork->add_fork(mainnet_hard_forks[n].version, mainnet_hard_forks[n].height, mainnet_hard_forks[n].threshold, mainnet_hard_forks[n].time);
}
m_hardfork->init();
@@ -2338,7 +2338,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
// this is a cheap test
if (!m_hardfork->check(bl))
{
- LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version: " << bl.major_version << std::endl << "current: " << m_hardfork->get_current_version());
+ LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version: " << (unsigned)bl.major_version << std::endl << "current: " << (unsigned)m_hardfork->get_current_version());
return false;
}
diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp
index edc2f33a9..2a2e25635 100644
--- a/src/cryptonote_core/hardfork.cpp
+++ b/src/cryptonote_core/hardfork.cpp
@@ -46,6 +46,11 @@ static uint8_t get_block_vote(const cryptonote::block &b)
return b.minor_version;
}
+static uint8_t get_block_version(const cryptonote::block &b)
+{
+ return b.major_version;
+}
+
HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, uint64_t original_version_till_height, time_t forked_time, time_t update_time, uint64_t window_size, uint8_t default_threshold_percent):
db(db),
original_version(original_version),
@@ -61,7 +66,7 @@ HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, uint6
throw "default_threshold_percent needs to be between 0 and 100";
}
-bool HardFork::add(uint8_t version, uint64_t height, uint8_t threshold, time_t time)
+bool HardFork::add_fork(uint8_t version, uint64_t height, uint8_t threshold, time_t time)
{
CRITICAL_REGION_LOCAL(lock);
@@ -82,42 +87,43 @@ bool HardFork::add(uint8_t version, uint64_t height, uint8_t threshold, time_t t
return true;
}
-bool HardFork::add(uint8_t version, uint64_t height, time_t time)
+bool HardFork::add_fork(uint8_t version, uint64_t height, time_t time)
{
- return add(version, height, default_threshold_percent, time);
+ return add_fork(version, height, default_threshold_percent, time);
}
-uint8_t HardFork::get_effective_version(uint8_t version) const
+uint8_t HardFork::get_effective_version(uint8_t voting_version) const
{
if (!heights.empty()) {
uint8_t max_version = heights.back().version;
- if (version > max_version)
- version = max_version;
+ if (voting_version > max_version)
+ voting_version = max_version;
}
- return version;
+ return voting_version;
}
-bool HardFork::do_check(uint8_t version) const
+bool HardFork::do_check(uint8_t block_version, uint8_t voting_version) const
{
- return version >= heights[current_fork_index].version;
+ return block_version >= heights[current_fork_index].version
+ && voting_version >= heights[current_fork_index].version;
}
bool HardFork::check(const cryptonote::block &block) const
{
CRITICAL_REGION_LOCAL(lock);
- return do_check(get_block_vote(block));
+ return do_check(::get_block_version(block), ::get_block_vote(block));
}
-bool HardFork::add(uint8_t block_version, uint64_t height)
+bool HardFork::add(uint8_t block_version, uint8_t voting_version, uint64_t height)
{
CRITICAL_REGION_LOCAL(lock);
- if (!do_check(block_version))
+ if (!do_check(block_version, voting_version))
return false;
db.set_hard_fork_version(height, heights[current_fork_index].version);
- const uint8_t version = get_effective_version(block_version);
+ voting_version = get_effective_version(voting_version);
while (versions.size() >= window_size) {
const uint8_t old_version = versions.front();
@@ -126,8 +132,8 @@ bool HardFork::add(uint8_t block_version, uint64_t height)
versions.pop_front();
}
- last_versions[version]++;
- versions.push_back(version);
+ last_versions[voting_version]++;
+ versions.push_back(voting_version);
uint8_t voted = get_voted_fork_index(height + 1);
if (voted > current_fork_index) {
@@ -143,7 +149,7 @@ bool HardFork::add(uint8_t block_version, uint64_t height)
bool HardFork::add(const cryptonote::block &block, uint64_t height)
{
- return add(get_block_vote(block), height);
+ return add(::get_block_version(block), ::get_block_vote(block), height);
}
void HardFork::init()
@@ -185,7 +191,7 @@ uint8_t HardFork::get_block_version(uint64_t height) const
return original_version;
const cryptonote::block &block = db.get_block_from_height(height);
- return get_block_vote(block);
+ return ::get_block_version(block);
}
bool HardFork::reorganize_from_block_height(uint64_t height)
@@ -225,7 +231,7 @@ bool HardFork::reorganize_from_block_height(uint64_t height)
const uint64_t bc_height = db.height();
for (uint64_t h = height + 1; h < bc_height; ++h) {
- add(get_block_version(h), h);
+ add(db.get_block_from_height(h), h);
}
db.batch_stop();
@@ -258,7 +264,7 @@ bool HardFork::rescan_from_block_height(uint64_t height)
versions.push_back(v);
}
- uint8_t lastv = db.get_hard_fork_version(height);
+ uint8_t lastv = db.get_hard_fork_version(db.height() - 1);
current_fork_index = 0;
while (current_fork_index + 1 < heights.size() && heights[current_fork_index].version != lastv)
++current_fork_index;
diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h
index 6800749da..6d2a3c55b 100644
--- a/src/cryptonote_core/hardfork.h
+++ b/src/cryptonote_core/hardfork.h
@@ -71,7 +71,7 @@ namespace cryptonote
* @param threshold The threshold of votes needed for this fork (0-100)
* @param time Approximate time of the hardfork (seconds since epoch)
*/
- bool add(uint8_t version, uint64_t height, uint8_t threshold, time_t time);
+ bool add_fork(uint8_t version, uint64_t height, uint8_t threshold, time_t time);
/**
* @brief add a new hardfork height
@@ -79,10 +79,11 @@ namespace cryptonote
* returns true if no error, false otherwise
*
* @param version the major block version for the fork
+ * @param voting_version the minor block version for the fork, used for voting
* @param height The height the hardfork takes effect
* @param time Approximate time of the hardfork (seconds since epoch)
*/
- bool add(uint8_t version, uint64_t height, time_t time);
+ bool add_fork(uint8_t version, uint64_t height, time_t time);
/**
* @brief initialize the object
@@ -203,10 +204,10 @@ namespace cryptonote
private:
uint8_t get_block_version(uint64_t height) const;
- bool do_check(uint8_t version) const;
+ bool do_check(uint8_t block_version, uint8_t voting_version) const;
int get_voted_fork_index(uint64_t height) const;
- uint8_t get_effective_version(uint8_t version) const;
- bool add(uint8_t block_version, uint64_t height);
+ uint8_t get_effective_version(uint8_t voting_version) const;
+ bool add(uint8_t block_version, uint8_t voting_version, uint64_t height);
bool rescan_from_block_height(uint64_t height);
bool rescan_from_chain_height(uint64_t height);
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 83c7233b1..a6761101f 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 3eb125208..4aaac813e 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -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;
};
}
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index d92b4bb2a..11df7ee49 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 <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);
@@ -831,6 +877,20 @@ namespace nodetool
//-----------------------------------------------------------------------------------
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)
{
size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count();
@@ -866,7 +926,13 @@ namespace nodetool
continue;
}
- LOG_PRINT_L1("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip)
+ 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
<< "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never"));
@@ -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 a7b8bf6f3..93b29deb2 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<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;
+ }
};
}
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index f738c68f6..3d8b08ce6 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)