p2p: group outbound IPv6 peers by /32

This commit is contained in:
selsta
2026-07-30 23:36:30 +02:00
parent a2c40926a9
commit 5a199af7a6
2 changed files with 53 additions and 3 deletions
+32 -3
View File
@@ -38,6 +38,7 @@
#include <boost/thread/thread.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/algorithm/string.hpp>
#include <array>
#include <atomic>
#include <functional>
#include <limits>
@@ -126,6 +127,21 @@ namespace nodetool
}
}
using ipv6_peer_group = std::array<unsigned char, 4>;
inline boost::optional<ipv6_peer_group> get_ipv6_peer_group(const epee::net_utils::network_address& address)
{
if (address.get_type_id() != epee::net_utils::ipv6_network_address::get_type_id())
return boost::none;
const boost::asio::ip::address_v6 ip = address.as<const epee::net_utils::ipv6_network_address>().ip();
if (ip.is_v4_mapped())
return boost::none;
const boost::asio::ip::address_v6::bytes_type bytes = ip.to_bytes();
return ipv6_peer_group{{bytes[0], bytes[1], bytes[2], bytes[3]}};
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
node_server<t_payload_net_handler>::~node_server()
{
@@ -1687,9 +1703,10 @@ namespace nodetool
const uint32_t next_needed_pruning_stripe = m_payload_handler.get_next_needed_pruning_stripe().second;
// Build a list of all distinct /24 subnets we are connected to now right now; to catch
// Build a list of all distinct IPv4 /24 and IPv6 /32 groups we are connected to right now; to catch
// any connection changes, re-build the list for every outer try loop pass
std::set<uint32_t> connected_subnets;
std::set<ipv6_peer_group> connected_ipv6_groups;
const uint32_t subnet_mask = ntohl(0xffffff00);
const bool is_public_zone = &zone == &m_network_zones.at(epee::net_utils::zone::public_);
if (is_public_zone)
@@ -1713,6 +1730,12 @@ namespace nodetool
memcpy(&actual_ipv4, v4ip.to_bytes().data(), sizeof(actual_ipv4));
connected_subnets.insert(actual_ipv4 & subnet_mask);
}
else
{
const boost::optional<ipv6_peer_group> group = get_ipv6_peer_group(na);
if (group)
connected_ipv6_groups.insert(*group);
}
}
return true;
});
@@ -1742,8 +1765,9 @@ namespace nodetool
std::shuffle(shuffled_indexes.begin(), shuffled_indexes.end(), crypto::random_device{});
// Step 2: Deduplicate by only taking 1 candidate from each /24 subnet that occurs, the FIRST
// candidate seen from each subnet within the now random order
// candidate seen from each subnet within the now random order. Native IPv6 peers use /32 groups.
std::set<uint32_t> subnets = connected_subnets;
std::set<ipv6_peer_group> ipv6_groups = connected_ipv6_groups;
for (size_t index : shuffled_indexes)
{
const peerlist_entry &peer = peers.at(index);
@@ -1772,7 +1796,12 @@ namespace nodetool
if (take)
subnets.insert(subnet);
}
// else 'take' stays true, we will take an IPv6 address that is not V4 mapped
else
{
const boost::optional<ipv6_peer_group> group = get_ipv6_peer_group(na);
if (group)
take = ipv6_groups.insert(*group).second;
}
}
if (take)
subnet_peers.push_back(peer);
+21
View File
@@ -119,6 +119,27 @@ public:
typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_core>> Server;
TEST(node_server, ipv6_peer_group)
{
const auto make_ipv6 = [](const char* ip) {
return epee::net_utils::network_address{
epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6(ip), 18080}
};
};
const boost::optional<nodetool::ipv6_peer_group> first = nodetool::get_ipv6_peer_group(make_ipv6("2001:db8:1::1"));
const boost::optional<nodetool::ipv6_peer_group> same = nodetool::get_ipv6_peer_group(make_ipv6("2001:db8:ffff::1"));
const boost::optional<nodetool::ipv6_peer_group> different = nodetool::get_ipv6_peer_group(make_ipv6("2001:db9::1"));
ASSERT_TRUE(first);
ASSERT_TRUE(same);
ASSERT_TRUE(different);
EXPECT_EQ(*first, *same);
EXPECT_NE(*first, *different);
EXPECT_FALSE(nodetool::get_ipv6_peer_group(make_ipv6("::ffff:192.0.2.1")));
EXPECT_FALSE(nodetool::get_ipv6_peer_group(MAKE_IPV4_ADDRESS(192, 0, 2, 1)));
}
static bool is_blocked(Server &server, const epee::net_utils::network_address &address, time_t *t = NULL)
{
std::map<std::string, time_t> hosts = server.get_blocked_hosts();