aboutsummaryrefslogtreecommitdiff
path: root/src/p2p
diff options
context:
space:
mode:
Diffstat (limited to 'src/p2p')
-rw-r--r--src/p2p/net_node.h13
-rw-r--r--src/p2p/net_node.inl174
-rw-r--r--src/p2p/net_node_common.h11
-rw-r--r--src/p2p/net_peerlist.h20
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h10
5 files changed, 189 insertions, 39 deletions
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 4eec3bcad..50f8fe744 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -247,7 +247,11 @@ namespace nodetool
void change_max_in_public_peers(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<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
+ virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME);
+ virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet);
+ virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return !is_remote_host_allowed(address, seconds); }
+ virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
+ virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_subnets; }
virtual void add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
virtual void remove_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
@@ -318,7 +322,7 @@ namespace nodetool
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f);
virtual bool add_host_fail(const epee::net_utils::network_address &address);
//----------------- i_connection_filter --------------------------------------------------------
- virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address);
+ virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL);
//-----------------------------------------------------------------------------------------------
bool parse_peer_from_string(epee::net_utils::network_address& pe, const std::string& node_addr, uint16_t default_port = 0);
bool handle_command_line(
@@ -453,8 +457,9 @@ namespace nodetool
std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache;
epee::critical_section m_conn_fails_cache_lock;
- epee::critical_section m_blocked_hosts_lock;
- std::map<std::string, time_t> m_blocked_hosts;
+ epee::critical_section m_blocked_hosts_lock; // for both hosts and subnets
+ std::map<epee::net_utils::network_address, time_t> m_blocked_hosts;
+ std::map<epee::net_utils::ipv4_network_subnet, time_t> m_blocked_subnets;
epee::critical_section m_host_fails_score_lock;
std::map<std::string, uint64_t> m_host_fails_score;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 64011cd06..b39f133da 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -154,19 +154,55 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address)
+ bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t)
{
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
- auto it = m_blocked_hosts.find(address.host_str());
- if(it == m_blocked_hosts.end())
- return true;
- if(time(nullptr) >= it->second)
+
+ const time_t now = time(nullptr);
+
+ // look in the hosts list
+ auto it = m_blocked_hosts.find(address);
+ if (it != m_blocked_hosts.end())
{
- m_blocked_hosts.erase(it);
- MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked.");
- return true;
+ if (now >= it->second)
+ {
+ m_blocked_hosts.erase(it);
+ MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked.");
+ it = m_blocked_hosts.end();
+ }
+ else
+ {
+ if (t)
+ *t = it->second - now;
+ return false;
+ }
}
- return false;
+
+ // manually loop in subnets
+ if (address.get_type_id() == epee::net_utils::address_type::ipv4)
+ {
+ auto ipv4_address = address.template as<epee::net_utils::ipv4_network_address>();
+ std::map<epee::net_utils::ipv4_network_subnet, time_t>::iterator it;
+ for (it = m_blocked_subnets.begin(); it != m_blocked_subnets.end(); )
+ {
+ if (now >= it->second)
+ {
+ it = m_blocked_subnets.erase(it);
+ MCLOG_CYAN(el::Level::Info, "global", "Subnet " << it->first.host_str() << " unblocked.");
+ continue;
+ }
+ if (it->first.matches(ipv4_address))
+ {
+ if (t)
+ *t = it->second - now;
+ return false;
+ }
+ ++it;
+ }
+ }
+
+ // not found in hosts or subnets, allowed
+ return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@@ -183,7 +219,7 @@ namespace nodetool
limit = std::numeric_limits<time_t>::max();
else
limit = now + seconds;
- m_blocked_hosts[addr.host_str()] = limit;
+ m_blocked_hosts[addr] = limit;
// drop any connection to that address. This should only have to look into
// the zone related to the connection, but really make sure everything is
@@ -213,7 +249,7 @@ namespace nodetool
bool node_server<t_payload_net_handler>::unblock_host(const epee::net_utils::network_address &address)
{
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
- auto i = m_blocked_hosts.find(address.host_str());
+ auto i = m_blocked_hosts.find(address);
if (i == m_blocked_hosts.end())
return false;
m_blocked_hosts.erase(i);
@@ -222,6 +258,58 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
+ bool node_server<t_payload_net_handler>::block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds)
+ {
+ const time_t now = time(nullptr);
+
+ CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
+ time_t limit;
+ if (now > std::numeric_limits<time_t>::max() - seconds)
+ limit = std::numeric_limits<time_t>::max();
+ else
+ limit = now + seconds;
+ m_blocked_subnets[subnet] = limit;
+
+ // drop any connection to that subnet. This should only have to look into
+ // the zone related to the connection, but really make sure everything is
+ // swept ...
+ std::vector<boost::uuids::uuid> conns;
+ for(auto& zone : m_network_zones)
+ {
+ zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
+ {
+ if (cntxt.m_remote_address.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id())
+ return true;
+ auto ipv4_address = cntxt.m_remote_address.template as<epee::net_utils::ipv4_network_address>();
+ if (subnet.matches(ipv4_address))
+ {
+ conns.push_back(cntxt.m_connection_id);
+ }
+ return true;
+ });
+ for (const auto &c: conns)
+ zone.second.m_net_server.get_config_object().close(c);
+
+ conns.clear();
+ }
+
+ MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " blocked.");
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
+ bool node_server<t_payload_net_handler>::unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet)
+ {
+ CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
+ auto i = m_blocked_subnets.find(subnet);
+ if (i == m_blocked_subnets.end())
+ return false;
+ m_blocked_subnets.erase(i);
+ MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " unblocked.");
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address)
{
if(!address.is_blockable())
@@ -938,7 +1026,10 @@ namespace nodetool
}
if(!context.m_is_income)
m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port);
- m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false);
+ if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false))
+ {
+ m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id );
+ }
});
if(!r)
@@ -1084,6 +1175,7 @@ namespace nodetool
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer "
<< na.str()
/*<< ", try " << try_count*/);
+ zone.m_net_server.get_config_object().close(con->m_connection_id);
return false;
}
@@ -1143,7 +1235,7 @@ namespace nodetool
bool is_priority = is_priority_node(na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str());
-
+ zone.m_net_server.get_config_object().close(con->m_connection_id);
return false;
}
@@ -1220,19 +1312,53 @@ namespace nodetool
size_t random_index;
const uint32_t next_needed_pruning_stripe = m_payload_handler.get_next_needed_pruning_stripe().second;
+ // build a set of all the /16 we're connected to, and prefer a peer that's not in that set
+ std::set<uint32_t> classB;
+ if (&zone == &m_network_zones.at(epee::net_utils::zone::public_)) // at returns reference, not copy
+ {
+ zone.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
+ {
+ if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+
+ const epee::net_utils::network_address na = cntxt.m_remote_address;
+ const uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ classB.insert(actual_ip & 0x0000ffff);
+ }
+ return true;
+ });
+ }
+
std::deque<size_t> filtered;
const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max();
- size_t idx = 0;
- zone.m_peerlist.foreach (use_white_list, [&filtered, &idx, limit, next_needed_pruning_stripe](const peerlist_entry &pe){
- if (filtered.size() >= limit)
- return false;
- if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0)
- filtered.push_back(idx);
- else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed))
- filtered.push_front(idx);
- ++idx;
- return true;
- });
+ size_t idx = 0, skipped = 0;
+ for (int step = 0; step < 2; ++step)
+ {
+ bool skip_duplicate_class_B = step == 0;
+ zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){
+ if (filtered.size() >= limit)
+ return false;
+ bool skip = false;
+ if (skip_duplicate_class_B && pe.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ const epee::net_utils::network_address na = pe.adr;
+ uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ skip = classB.find(actual_ip & 0x0000ffff) != classB.end();
+ }
+ if (skip)
+ ++skipped;
+ else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0)
+ filtered.push_back(idx);
+ else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed))
+ filtered.push_front(idx);
+ ++idx;
+ return true;
+ });
+ if (skipped == 0 || !filtered.empty())
+ break;
+ if (skipped)
+ MGINFO("Skipping " << skipped << " possible peers as they share a class B with existing peers");
+ }
if (filtered.empty())
{
MDEBUG("No available peer in " << (use_white_list ? "white" : "gray") << " list filtered by " << next_needed_pruning_stripe);
diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h
index 26451b333..34d151f5f 100644
--- a/src/p2p/net_node_common.h
+++ b/src/p2p/net_node_common.h
@@ -56,7 +56,8 @@ namespace nodetool
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0;
virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0;
virtual bool unblock_host(const epee::net_utils::network_address &address)=0;
- virtual std::map<std::string, time_t> get_blocked_hosts()=0;
+ virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()=0;
+ virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()=0;
virtual bool add_host_fail(const epee::net_utils::network_address &address)=0;
virtual void add_used_stripe_peer(const t_connection_context &context)=0;
virtual void remove_used_stripe_peer(const t_connection_context &context)=0;
@@ -112,9 +113,13 @@ namespace nodetool
{
return true;
}
- virtual std::map<std::string, time_t> get_blocked_hosts()
+ virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()
{
- return std::map<std::string, time_t>();
+ return std::map<epee::net_utils::network_address, time_t>();
+ }
+ virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()
+ {
+ return std::map<epee::net_utils::ipv4_network_subnet, time_t>();
}
virtual bool add_host_fail(const epee::net_utils::network_address &address)
{
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index f4fa921e2..883997fd6 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -344,8 +344,14 @@ namespace nodetool
trim_white_peerlist();
}else
{
- //update record in white list
- m_peers_white.replace(by_addr_it_wt, ple);
+ //update record in white list
+ peerlist_entry new_ple = ple;
+ if (by_addr_it_wt->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
+ new_ple.pruning_seed = by_addr_it_wt->pruning_seed;
+ if (by_addr_it_wt->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
+ new_ple.rpc_port = by_addr_it_wt->rpc_port;
+ new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
+ m_peers_white.replace(by_addr_it_wt, new_ple);
}
//remove from gray list, if need
auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr);
@@ -379,8 +385,14 @@ namespace nodetool
trim_gray_peerlist();
}else
{
- //update record in white list
- m_peers_gray.replace(by_addr_it_gr, ple);
+ //update record in gray list
+ peerlist_entry new_ple = ple;
+ if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
+ new_ple.pruning_seed = by_addr_it_gr->pruning_seed;
+ if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
+ new_ple.rpc_port = by_addr_it_gr->rpc_port;
+ new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
+ m_peers_gray.replace(by_addr_it_gr, new_ple);
}
return true;
CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false);
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 40ef2ebcd..32f30adca 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -134,10 +134,11 @@ namespace boost
a & port;
a & length;
- if (length > net::tor_address::buffer_size())
+ const size_t buffer_size = net::tor_address::buffer_size();
+ if (length > buffer_size)
MONERO_THROW(net::error::invalid_tor_address, "Tor address too long");
- char host[net::tor_address::buffer_size()] = {0};
+ char host[buffer_size] = {0};
a.load_binary(host, length);
host[sizeof(host) - 1] = 0;
@@ -155,10 +156,11 @@ namespace boost
a & port;
a & length;
- if (length > net::i2p_address::buffer_size())
+ const size_t buffer_size = net::i2p_address::buffer_size();
+ if (length > buffer_size)
MONERO_THROW(net::error::invalid_i2p_address, "i2p address too long");
- char host[net::i2p_address::buffer_size()] = {0};
+ char host[buffer_size] = {0};
a.load_binary(host, length);
host[sizeof(host) - 1] = 0;