p2p, rpc: group public IPv6 connection limits by /64
What changed, and why it matters
This commit tightens Monero's connection limits for public IPv6 addresses. Previously, limits were applied per individual IP address, which is ineffective on IPv6 because a single attacker can easily obtain a huge block of addresses (a /64 subnet). The change groups all public IPv6 addresses within the same /64 subnet together for both P2P and RPC connection limits, so they share a single connection allowance. Private, loopback, link-local, and similar special IPv6 addresses are still treated individually.
Deploy as part of normal hardening; no emergency response required. Operators running public nodes on IPv6 should verify that the updated limits match their capacity planning, since a /64 now shares one limit instead of each address having its own. Review whether the default of 1 connection per /64 for P2P and RPC is appropriate for your deployment.
Security signals we found
Rate-limit bypass mitigation: per-address limits are ineffective against IPv6 /64 subnets, which are trivially enumerable by an attacker.
P2P and RPC connection limits now share a counter across a public IPv6 /64 prefix.
Special IPv6 scopes (loopback, link-local, unique-local, site-local, multicast, unspecified, IPv4-mapped) are excluded from grouping to avoid over-aggregating legitimate local traffic.
No authentication, cryptographic, or memory-safety changes; this is a network-layer resource-control hardening patch.
Evidence from the diff
The patch changes connection-counting keys from per-host strings to /64-subnet keys for global unicast public IPv6 addresses. New helpers should_group_ipv6_by_prefix() and get_ipv6_subnet_address() are added in epee’s net utilities. get_rpc_connection_limit_key() uses these to return <subnet>/64 for public IPv6, otherwise host_str(). The HTTP protocol handler, HTTP server limit check, and P2P net_node inbound connection counting are updated to use these keys. CLI help text for max-connections-per-ip and rpc-max-connections-per-public-ip is updated accordingly. Unit tests verify grouping behavior and exemptions for loopback, link-local, unique-local, site-local, multicast, unspecified, and IPv4-mapped addresses.
Changed components
contrib/epee/include/net/http_protocol_handler.hcontrib/epee/include/net/http_protocol_handler.inlcontrib/epee/include/net/http_server_impl_base.hcontrib/epee/include/net/net_utils_base.hcontrib/epee/src/CMakeLists.txtcontrib/epee/src/http_protocol_handler.cppcontrib/epee/src/net_utils_base.cppsrc/p2p/net_node.cppsrc/p2p/net_node.inlsrc/rpc/core_rpc_server.cppsrc/wallet/wallet_rpc_server.cpptests/unit_tests/http.cpptests/unit_tests/node_server.cppInspect captured patch +275 / −8
diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h
index 8b73964..8f1ddd1 100644
--- a/contrib/epee/include/net/http_protocol_handler.h
+++ b/contrib/epee/include/net/http_protocol_handler.h
@@ -36,6 +36,7 @@
#include "net_utils_base.h"
#include "http_auth.h"
#include "http_base.h"
+#include "syncobj.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.http"
@@ -65,6 +66,9 @@ namespace net_utils
critical_section m_lock;
};
+ // RPC limits groupable IPv6 clients by /64 to avoid per-address limit bypasses.
+ std::string get_rpc_connection_limit_key(const net_utils::network_address& address);
+
/************************************************************************/
/* */
/************************************************************************/
diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl
index 47a3c1d..a908c46 100644
--- a/contrib/epee/include/net/http_protocol_handler.inl
+++ b/contrib/epee/include/net/http_protocol_handler.inl
@@ -225,7 +225,7 @@ namespace net_utils
CRITICAL_REGION_LOCAL(m_config.m_lock);
if (m_config.m_connection_count)
--m_config.m_connection_count;
- auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str());
+ auto elem = m_config.m_connections.find(get_rpc_connection_limit_key(m_conn_context.m_remote_address));
if (elem != m_config.m_connections.end())
{
if (elem->second == 1 || elem->second == 0)
@@ -243,7 +243,7 @@ namespace net_utils
bool simple_http_connection_handler<t_connection_context>::after_init_connection()
{
CRITICAL_REGION_LOCAL(m_config.m_lock);
- ++m_config.m_connections[m_conn_context.m_remote_address.host_str()];
+ ++m_config.m_connections[get_rpc_connection_limit_key(m_conn_context.m_remote_address)];
++m_config.m_connection_count;
m_initialized = true;
return true;
diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h
index a3173e4..f5ee5c9 100644
--- a/contrib/epee/include/net/http_server_impl_base.h
+++ b/contrib/epee/include/net/http_server_impl_base.h
@@ -155,7 +155,7 @@ namespace epee
return true;
const bool is_private = na.is_loopback() || na.is_local();
- const auto elem = config.m_connections.find(na.host_str());
+ const auto elem = config.m_connections.find(net_utils::http::get_rpc_connection_limit_key(na));
if (elem != config.m_connections.end())
{
if (is_private)
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
index bed3b42..8dfdc5e 100644
--- a/contrib/epee/include/net/net_utils_base.h
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -33,6 +33,7 @@
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/address_v6.hpp>
#include <boost/optional/optional.hpp>
+#include <cstddef>
#include <stdexcept>
#include <typeinfo>
#include <type_traits>
@@ -214,6 +215,9 @@ namespace net_utils
END_KV_SERIALIZE_MAP()
};
+ bool should_group_ipv6_by_prefix(const boost::asio::ip::address_v6& ip);
+ boost::asio::ip::address_v6 get_ipv6_subnet_address(const boost::asio::ip::address_v6& ip, const std::size_t prefix_bits);
+
inline bool operator==(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
{ return lhs.equal(rhs); }
inline bool operator!=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt
index 1dc1573..eb841ef 100644
--- a/contrib/epee/src/CMakeLists.txt
+++ b/contrib/epee/src/CMakeLists.txt
@@ -31,7 +31,7 @@ set(EPEE_INCLUDE_DIR_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../include")
# Add headers to the file list, to be able to search for them and autosave in IDEs.
monero_find_all_headers(EPEE_HEADERS_PUBLIC "${EPEE_INCLUDE_DIR_BASE}")
-monero_add_library(epee byte_slice.cpp byte_stream.cpp hex.cpp abstract_http_client.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp parserse_base_utils.cpp
+monero_add_library(epee byte_slice.cpp byte_stream.cpp hex.cpp abstract_http_client.cpp http_auth.cpp http_protocol_handler.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp parserse_base_utils.cpp
wipeable_string.cpp levin_base.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp
int-util.cpp portable_storage.cpp
misc_language.cpp
diff --git a/contrib/epee/src/http_protocol_handler.cpp b/contrib/epee/src/http_protocol_handler.cpp
new file mode 100644
index 0000000..1fd3c3c
--- /dev/null
+++ b/contrib/epee/src/http_protocol_handler.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2026, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "net/http_protocol_handler.h"
+
+namespace epee { namespace net_utils { namespace http
+{
+ std::string get_rpc_connection_limit_key(const net_utils::network_address& address)
+ {
+ if (address.get_type_id() == net_utils::ipv6_network_address::get_type_id())
+ {
+ const boost::asio::ip::address_v6 ip = address.as<const net_utils::ipv6_network_address>().ip();
+ if (net_utils::should_group_ipv6_by_prefix(ip))
+ return net_utils::get_ipv6_subnet_address(ip, 64).to_string() + "/64";
+ }
+
+ return address.host_str();
+ }
+}}}
diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp
index 36b5592..3010227 100644
--- a/contrib/epee/src/net_utils_base.cpp
+++ b/contrib/epee/src/net_utils_base.cpp
@@ -1,6 +1,8 @@
#include "net/net_utils_base.h"
+#include <algorithm>
+
#include <boost/asio/ip/address_v4.hpp>
#include <boost/uuid/uuid_io.hpp>
@@ -53,6 +55,34 @@ namespace epee { namespace net_utils
return ipv4_network_address{SWAP32BE(ipv4.to_uint()), address.port()};
}
+ static bool is_ipv6_unique_local(const boost::asio::ip::address_v6& ip)
+ {
+ const boost::asio::ip::address_v6::bytes_type bytes = ip.to_bytes();
+ return (bytes[0] & 0xfe) == 0xfc;
+ }
+
+ bool should_group_ipv6_by_prefix(const boost::asio::ip::address_v6& ip)
+ {
+ return !ip.is_unspecified() && !ip.is_loopback() && !ip.is_multicast() &&
+ !ip.is_link_local() && !ip.is_site_local() && !is_ipv6_unique_local(ip) &&
+ !ip.is_v4_mapped();
+ }
+
+ boost::asio::ip::address_v6 get_ipv6_subnet_address(const boost::asio::ip::address_v6& ip, const std::size_t prefix_bits)
+ {
+ boost::asio::ip::address_v6::bytes_type bytes = ip.to_bytes();
+ const std::size_t bits = std::min(prefix_bits, std::size_t{128});
+ if (bits < 128)
+ {
+ const std::size_t full_bytes = bits / 8;
+ const std::size_t remainder_bits = bits % 8;
+ if (remainder_bits != 0)
+ bytes[full_bytes] &= uint8_t(0xffu << (8 - remainder_bits));
+ std::fill(bytes.begin() + full_bytes + (remainder_bits != 0), bytes.end(), 0);
+ }
+ return boost::asio::ip::address_v6(bytes);
+ }
+
bool ipv4_network_subnet::equal(const ipv4_network_subnet& other) const noexcept
{ return is_same_host(other) && m_mask == other.m_mask; }
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index c22eac5..339c029 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -176,7 +176,7 @@ namespace nodetool
const command_line::arg_descriptor<bool> arg_pad_transactions = {
"pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false
};
- const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of p2p connections allowed from the same IP address", 1};
+ const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of inbound p2p connections allowed from the same IPv4 address or shared across a public IPv6 /64 subnet", 1};
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
{
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index b84bceb..d6360dc 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -156,6 +156,22 @@ namespace nodetool
return is_forbidden_ipv4_mapped_ipv6_address(address);
}
+ inline bool is_same_p2p_connection_limit_host(const epee::net_utils::network_address& left, const epee::net_utils::network_address& right)
+ {
+ if (left.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id() &&
+ right.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ {
+ const boost::asio::ip::address_v6 left_ip = left.as<const epee::net_utils::ipv6_network_address>().ip();
+ const boost::asio::ip::address_v6 right_ip = right.as<const epee::net_utils::ipv6_network_address>().ip();
+ if (epee::net_utils::should_group_ipv6_by_prefix(left_ip) &&
+ epee::net_utils::should_group_ipv6_by_prefix(right_ip))
+ return epee::net_utils::get_ipv6_subnet_address(left_ip, 64) ==
+ epee::net_utils::get_ipv6_subnet_address(right_ip, 64);
+ }
+
+ return left.is_same_host(right);
+ }
+ //-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
node_server<t_payload_net_handler>::~node_server()
{
@@ -3077,7 +3093,7 @@ namespace nodetool
m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
{
- if (cntxt.m_is_income && cntxt.m_remote_address.is_same_host(address)) {
+ if (cntxt.m_is_income && is_same_p2p_connection_limit_host(cntxt.m_remote_address, address)) {
count++;
// the only call location happens BEFORE foreach_connection list is updated
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 142a7b5..8fb2b92 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -3131,7 +3131,7 @@ namespace cryptonote
const command_line::arg_descriptor<std::size_t> core_rpc_server::arg_rpc_max_connections_per_public_ip = {
"rpc-max-connections-per-public-ip"
- , "Max RPC connections per public IP permitted"
+ , "Max RPC connections permitted per public IPv4 address or shared across a public IPv6 /64 subnet"
, DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP
};
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 149d58e..e64df58 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -143,7 +143,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
const command_line::arg_descriptor<bool> arg_no_initial_sync = {"no-initial-sync", "Skips the initial sync before listening for connections", false};
- const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_public_ip = {"rpc-max-connections-per-public-ip", "Max RPC connections per public IP permitted", DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP};
+ const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_public_ip = {"rpc-max-connections-per-public-ip", "Max RPC connections permitted per public IPv4 address or shared across a public IPv6 /64 subnet", DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP};
const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_private_ip = {"rpc-max-connections-per-private-ip", "Max RPC connections per private and localhost IP permitted", DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP};
const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections = {"rpc-max-connections", "Max RPC connections permitted", DEFAULT_RPC_MAX_CONNECTIONS};
const command_line::arg_descriptor<std::size_t> arg_rpc_response_soft_limit = {"rpc-response-soft-limit", "Max response bytes that can be queued, enforced at next response attempt", DEFAULT_RPC_SOFT_LIMIT_SIZE};
diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp
index 5ecdd84..4341fbb 100644
--- a/tests/unit_tests/http.cpp
+++ b/tests/unit_tests/http.cpp
@@ -369,6 +369,83 @@ TEST(HTTP_Server_Auth, NotRequired)
EXPECT_FALSE(auth.get_response(http::http_request_info{}));
}
+TEST(HTTP, RpcConnectionLimitKeyIPv6By64)
+{
+ const epee::net_utils::network_address ipv6_a{
+ epee::net_utils::ipv6_network_address{
+ boost::asio::ip::make_address_v6("2001:db8:abcd:1234:1111:2222:3333:4444"), 18081
+ }
+ };
+ const epee::net_utils::network_address ipv6_b{
+ epee::net_utils::ipv6_network_address{
+ boost::asio::ip::make_address_v6("2001:db8:abcd:1234:ffff:eeee:dddd:cccc"), 18081
+ }
+ };
+ const epee::net_utils::network_address ipv6_c{
+ epee::net_utils::ipv6_network_address{
+ boost::asio::ip::make_address_v6("2001:db8:abcd:1235::1"), 18081
+ }
+ };
+
+ EXPECT_EQ("2001:db8:abcd:1234::/64", http::get_rpc_connection_limit_key(ipv6_a));
+ EXPECT_EQ(http::get_rpc_connection_limit_key(ipv6_a), http::get_rpc_connection_limit_key(ipv6_b));
+ EXPECT_NE(http::get_rpc_connection_limit_key(ipv6_a), http::get_rpc_connection_limit_key(ipv6_c));
+
+ const epee::net_utils::network_address loopback{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6::loopback(), 18081}
+ };
+ EXPECT_EQ(loopback.host_str(), http::get_rpc_connection_limit_key(loopback));
+
+ const epee::net_utils::network_address link_local{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fe80::1"), 18081}
+ };
+ EXPECT_EQ(link_local.host_str(), http::get_rpc_connection_limit_key(link_local));
+
+ const epee::net_utils::network_address unique_local_fc{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fc00::1"), 18081}
+ };
+ EXPECT_EQ(unique_local_fc.host_str(), http::get_rpc_connection_limit_key(unique_local_fc));
+
+ const epee::net_utils::network_address unique_local_fd{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fd00::1"), 18081}
+ };
+ EXPECT_EQ(unique_local_fd.host_str(), http::get_rpc_connection_limit_key(unique_local_fd));
+
+ const epee::net_utils::network_address unspecified{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6::any(), 18081}
+ };
+ EXPECT_EQ(unspecified.host_str(), http::get_rpc_connection_limit_key(unspecified));
+
+ const epee::net_utils::network_address multicast{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("ff00::1"), 18081}
+ };
+ EXPECT_EQ(multicast.host_str(), http::get_rpc_connection_limit_key(multicast));
+
+ const epee::net_utils::network_address site_local{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fec0::1"), 18081}
+ };
+ EXPECT_EQ(site_local.host_str(), http::get_rpc_connection_limit_key(site_local));
+
+ uint32_t ip = 0;
+ ASSERT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "203.0.113.1"));
+ const epee::net_utils::network_address ipv4{
+ epee::net_utils::ipv4_network_address{ip, 18081}
+ };
+ EXPECT_EQ(ipv4.host_str(), http::get_rpc_connection_limit_key(ipv4));
+
+ boost::asio::ip::address_v6::bytes_type bytes = {};
+ bytes[10] = 0xff;
+ bytes[11] = 0xff;
+ bytes[12] = 203;
+ bytes[13] = 0;
+ bytes[14] = 113;
+ bytes[15] = 1;
+ const epee::net_utils::network_address mapped{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6{bytes}, 18081}
+ };
+ EXPECT_EQ(mapped.host_str(), http::get_rpc_connection_limit_key(mapped));
+}
+
TEST(HTTP_Server_Auth, MissingAuth)
{
http::http_server_auth auth{{"foo", "bar"}, rng};
diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp
index b03826d..6ef280d 100644
--- a/tests/unit_tests/node_server.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -198,6 +198,98 @@ TEST(node_server, ipv4_mapped_ipv6_address)
EXPECT_FALSE(nodetool::should_skip_connect_address(ipv6, true));
}
+TEST(node_server, p2p_connection_limit_ipv6_by_64)
+{
+ const epee::net_utils::network_address ipv6_a{
+ epee::net_utils::ipv6_network_address{
+ boost::asio::ip::make_address_v6("2001:db8:abcd:1234:1111:2222:3333:4444"), 18080
+ }
+ };
+ const epee::net_utils::network_address ipv6_b{
+ epee::net_utils::ipv6_network_address{
+ boost::asio::ip::make_address_v6("2001:db8:abcd:1234:ffff:eeee:dddd:cccc"), 18080
+ }
+ };
+ const epee::net_utils::network_address ipv6_c{
+ epee::net_utils::ipv6_network_address{
+ boost::asio::ip::make_address_v6("2001:db8:abcd:1235::1"), 18080
+ }
+ };
+
+ EXPECT_TRUE(nodetool::is_same_p2p_connection_limit_host(ipv6_a, ipv6_b));
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(ipv6_a, ipv6_c));
+
+ const epee::net_utils::network_address loopback_a{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6::loopback(), 18080}
+ };
+ const epee::net_utils::network_address loopback_b{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("::2"), 18080}
+ };
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(loopback_a, loopback_b));
+
+ const epee::net_utils::network_address link_local_a{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fe80::1"), 18080}
+ };
+ const epee::net_utils::network_address link_local_b{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fe80::2"), 18080}
+ };
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(link_local_a, link_local_b));
+
+ const epee::net_utils::network_address unique_local_a{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fc00::1"), 18080}
+ };
+ const epee::net_utils::network_address unique_local_b{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fc00::2"), 18080}
+ };
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(unique_local_a, unique_local_b));
+
+ const epee::net_utils::network_address unique_local_c{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fd00::1"), 18080}
+ };
+ const epee::net_utils::network_address unique_local_d{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fd00::2"), 18080}
+ };
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(unique_local_c, unique_local_d));
+
+ const epee::net_utils::network_address multicast_a{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("ff00::1"), 18080}
+ };
+ const epee::net_utils::network_address multicast_b{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("ff00::2"), 18080}
+ };
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(multicast_a, multicast_b));
+
+ const epee::net_utils::network_address site_local_a{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fec0::1"), 18080}
+ };
+ const epee::net_utils::network_address site_local_b{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::make_address_v6("fec0::2"), 18080}
+ };
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(site_local_a, site_local_b));
+
+ const epee::net_utils::network_address ipv4_a{MAKE_IPV4_ADDRESS_PORT(203, 0, 113, 1, 18080)};
+ const epee::net_utils::network_address ipv4_b{MAKE_IPV4_ADDRESS_PORT(203, 0, 113, 2, 18080)};
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(ipv4_a, ipv4_b));
+
+ boost::asio::ip::address_v6::bytes_type bytes_a = {};
+ bytes_a[10] = 0xff;
+ bytes_a[11] = 0xff;
+ bytes_a[12] = 203;
+ bytes_a[13] = 0;
+ bytes_a[14] = 113;
+ bytes_a[15] = 1;
+ const epee::net_utils::network_address mapped_a{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6{bytes_a}, 18080}
+ };
+
+ boost::asio::ip::address_v6::bytes_type bytes_b = bytes_a;
+ bytes_b[15] = 2;
+ const epee::net_utils::network_address mapped_b{
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6{bytes_b}, 18080}
+ };
+ EXPECT_FALSE(nodetool::is_same_p2p_connection_limit_host(mapped_a, mapped_b));
+}
+
namespace
{
using path_t = boost::filesystem::path;
Why this scored 56/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.