feat: Add verify_endpoints to help local peer network development (#61)

This commit is contained in:
Jingchen
2026-04-16 18:45:27 +01:00
committed by Bart
parent bc168453dd
commit f511eeb27b
11 changed files with 121 additions and 26 deletions

View File

@@ -527,6 +527,17 @@
#
# The current default (which is subject to change) is 300 seconds.
#
# verify_endpoints = <0 | 1>
#
# If set to 0, the server will skip validation of endpoint
# addresses received in TMEndpoints peer protocol messages,
# allowing addresses that are not publicly routable or have a
# port of 0. The default is 1 (verification enabled).
#
# WARNING: Disabling this option is a security risk and should
# only be used for local testing and debugging. Do not disable
# on mainnet.
#
#
# [transaction_queue] EXPERIMENTAL
#

View File

@@ -37,7 +37,45 @@ is_private(AddressV4 const& addr)
bool
is_public(AddressV4 const& addr)
{
return !is_private(addr) && !addr.is_multicast();
if (is_private(addr))
return false;
if (addr.is_multicast())
return false;
auto const ip = addr.to_uint();
// 0.0.0.0/8 "This network"
if ((ip & 0xff000000) == 0x00000000)
return false;
// 100.64.0.0/10 Shared Address Space (CGNAT) - RFC 6598
if ((ip & 0xffc00000) == 0x64400000)
return false;
// 169.254.0.0/16 Link-local
if ((ip & 0xffff0000) == 0xa9fe0000)
return false;
// 192.0.0.0/24 IETF Protocol Assignments - RFC 6890
if ((ip & 0xffffff00) == 0xc0000000)
return false;
// 192.0.2.0/24 TEST-NET-1 (documentation) - RFC 5737
if ((ip & 0xffffff00) == 0xc0000200)
return false;
// 192.88.99.0/24 6to4 Relay Anycast (deprecated) - RFC 7526
if ((ip & 0xffffff00) == 0xc0586300)
return false;
// 198.18.0.0/15 Benchmarking - RFC 2544
if ((ip & 0xfffe0000) == 0xc6120000)
return false;
// 198.51.100.0/24 TEST-NET-2 (documentation) - RFC 5737
if ((ip & 0xffffff00) == 0xc6336400)
return false;
// 203.0.113.0/24 TEST-NET-3 (documentation) - RFC 5737
if ((ip & 0xffffff00) == 0xcb007100)
return false;
// 240.0.0.0/4 Reserved for future use - RFC 1112
if ((ip & 0xf0000000) == 0xf0000000)
return false;
return true;
}
char

View File

@@ -26,16 +26,52 @@ namespace IP {
bool
is_private(AddressV6 const& addr)
{
return (
(addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ?
(addr.is_v4_mapped() && is_private(addr.to_v4())));
// fc00::/7 - Unique Local Address (ULA), covers fc00:: and fd00::
if ((addr.to_bytes()[0] & 0xfe) == 0xfc)
return true;
if (addr.is_v4_mapped())
return is_private(addr.to_v4());
return false;
}
bool
is_public(AddressV6 const& addr)
{
// TODO is this correct?
return !is_private(addr) && !addr.is_multicast();
if (addr.is_loopback())
return false;
if (addr.is_v4_mapped())
return is_public(
boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, addr));
if (is_private(addr))
return false;
if (addr.is_multicast())
return false;
auto const b = addr.to_bytes();
// fe80::/10 - Link-local
if (b[0] == 0xfe && (b[1] & 0xc0) == 0x80)
return false;
// 100::/64 - Discard prefix (RFC 6666)
if (b[0] == 0x01 && b[1] == 0x00 && b[2] == 0 && b[3] == 0 && b[4] == 0 &&
b[5] == 0 && b[6] == 0 && b[7] == 0)
return false;
// 2001:db8::/32 - Documentation (RFC 3849)
if (b[0] == 0x20 && b[1] == 0x01 && b[2] == 0x0d && b[3] == 0xb8)
return false;
// 2001::/32 - IETF Protocol Assignments / Teredo (RFC 4380)
if (b[0] == 0x20 && b[1] == 0x01 && b[2] == 0x00 && b[3] == 0x00)
return false;
// 2001:20::/28 - ORCHIDv2 (RFC 7343)
// 28-bit prefix: 0x2001002 => b[0]=0x20, b[1]=0x01, b[2]=0x00,
// top nibble of b[3]=0x2
if (b[0] == 0x20 && b[1] == 0x01 && b[2] == 0x00 && (b[3] & 0xf0) == 0x20)
return false;
// 2002::/16 - 6to4 (RFC 3056, deprecated by RFC 7526)
if (b[0] == 0x20 && b[1] == 0x02)
return false;
return true;
}
} // namespace IP

View File

@@ -440,7 +440,7 @@ public:
c.PEERS_OUT_MAX == 0) ||
(c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut));
Config config = Config::makeConfig(c, port, false, 0);
Config config = Config::makeConfig(c, port, false, 0, true);
Counts counts;
counts.onConfig(config);

View File

@@ -1394,7 +1394,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
// if (!config_.standalone())
overlay_ = make_Overlay(
*this,
setup_Overlay(*config_),
setup_Overlay(*config_, m_journal),
*serverHandler_,
*m_resourceManager,
*m_resolver,

View File

@@ -71,6 +71,7 @@ public:
std::uint32_t crawlOptions = 0;
std::optional<std::uint32_t> networkID;
bool vlEnabled = true;
bool verifyEndpoints = false;
};
using PeerSequence = std::vector<std::shared_ptr<Peer>>;

View File

@@ -483,7 +483,8 @@ OverlayImpl::start()
app_.config(),
serverHandler_.setup().overlay.port(),
app_.getValidationPublicKey().has_value(),
setup_.ipLimit);
setup_.ipLimit,
setup_.verifyEndpoints);
m_peerFinder->setConfig(config);
m_peerFinder->start();
@@ -1493,7 +1494,7 @@ OverlayImpl::deleteIdlePeers()
//------------------------------------------------------------------------------
Overlay::Setup
setup_Overlay(BasicConfig const& config)
setup_Overlay(BasicConfig const& config, beast::Journal j)
{
Overlay::Setup setup;
@@ -1514,6 +1515,14 @@ setup_Overlay(BasicConfig const& config)
if (ec || beast::IP::is_private(setup.public_ip))
Throw<std::runtime_error>("Configured public IP is invalid");
}
set(setup.verifyEndpoints, true, "verify_endpoints", section);
if (!setup.verifyEndpoints)
{
JLOG(j.warn()) << "Endpoint verification is disabled. This is a "
"security risk and should only be used for "
"testing.";
}
}
{

View File

@@ -30,7 +30,7 @@
namespace ripple {
Overlay::Setup
setup_Overlay(BasicConfig const& config);
setup_Overlay(BasicConfig const& config, beast::Journal j);
/** Creates the implementation of Overlay. */
std::unique_ptr<Overlay>

View File

@@ -79,6 +79,9 @@ struct Config
/** Limit how many incoming connections we allow per IP */
int ipLimit;
/** `true` if we want to verify endpoints in TMEndpoints messages */
bool verifyEndpoints = false;
//--------------------------------------------------------------------------
/** Create a configuration with default values. */
@@ -101,6 +104,8 @@ struct Config
* @param port server's listening port
* @param validationPublicKey true if validation public key is not empty
* @param ipLimit limit of incoming connections per IP
* @param verifyEndpoints `true` if we want to verify endpoints in
* TMEndpoints messages
* @return PeerFinder::Config
*/
static Config
@@ -108,10 +113,11 @@ struct Config
ripple::Config const& config,
std::uint16_t port,
bool validationPublicKey,
int ipLimit);
int ipLimit,
bool verifyEndpoints);
friend bool
operator==(Config const& lhs, Config const& rhs);
operator==(Config const& lhs, Config const& rhs) = default;
};
//------------------------------------------------------------------------------

View File

@@ -754,7 +754,7 @@ public:
}
// Discard invalid addresses
if (!is_valid_address(ep.address))
if (!is_valid_address(ep.address) && config_.verifyEndpoints)
{
JLOG(m_journal.debug()) << beast::leftw(18) << "Endpoints drop "
<< ep.address << " as invalid";
@@ -1152,6 +1152,8 @@ public:
{
if (is_unspecified(address))
return false;
if (is_loopback(address))
return false;
if (!is_public(address))
return false;
if (address.port() == 0)

View File

@@ -34,17 +34,6 @@ Config::Config()
{
}
bool
operator==(Config const& lhs, Config const& rhs)
{
return lhs.autoConnect == rhs.autoConnect &&
lhs.peerPrivate == rhs.peerPrivate &&
lhs.wantIncoming == rhs.wantIncoming && lhs.inPeers == rhs.inPeers &&
lhs.maxPeers == rhs.maxPeers && lhs.outPeers == rhs.outPeers &&
lhs.features == lhs.features && lhs.ipLimit == rhs.ipLimit &&
lhs.listeningPort == rhs.listeningPort;
}
std::size_t
Config::calcOutPeers() const
{
@@ -83,6 +72,7 @@ Config::onWrite(beast::PropertyStream::Map& map)
map["port"] = listeningPort;
map["features"] = features;
map["ip_limit"] = ipLimit;
map["verify_endpoints"] = verifyEndpoints;
}
Config
@@ -90,7 +80,8 @@ Config::makeConfig(
ripple::Config const& cfg,
std::uint16_t port,
bool validationPublicKey,
int ipLimit)
int ipLimit,
bool verifyEndpoints)
{
PeerFinder::Config config;
@@ -140,6 +131,7 @@ Config::makeConfig(
config.listeningPort = port;
config.features = "";
config.ipLimit = ipLimit;
config.verifyEndpoints = verifyEndpoints;
// Enforce business rules
config.applyTuning();