mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-26 14:05:51 +00:00
Allow multiple incoming connections from the same IP:
Multiple servers behind NAT might share a single public IP, making it difficult for them to connect to the Ripple network since multiple incoming connections from the same non-private IP are currently not allowed. RippleD now automatically allows between 2 and 5 incoming connections, from the same public IP based on the total number of peers that it is configured to accept. Administrators can manually change the limit by adding an "ip_limit" key value pair in the [overlay] stanza of the configuration file and specifying a positive non-zero number. For example: [overlay] ip_limit=3 The previous "one connection per IP" strategy can be emulated by setting "ip_limit" to 1. The implementation imposes both soft and hard upper limits and will adjust the value so that a single IP cannot consume all inbound slots.
This commit is contained in:
@@ -373,6 +373,15 @@
|
||||
# Peers will use this information to reject attempt to proxy
|
||||
# connections to or from this server.
|
||||
#
|
||||
# ip_limit = <number>
|
||||
#
|
||||
# The maximum number of incoming peer connections allowed by a single
|
||||
# IP that isn't classified as "private" in RFC1918. The implementation
|
||||
# imposes some hard and soft upper limits on this value to prevent a
|
||||
# single host from consuming all inbound slots. If the value is not
|
||||
# present the server will autoconfigure an appropriate limit.
|
||||
#
|
||||
#
|
||||
#
|
||||
# [transaction_queue] EXPERIMENTAL
|
||||
#
|
||||
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
|
||||
// Peer networking parameters
|
||||
bool PEER_PRIVATE = false; // True to ask peers not to relay current IP.
|
||||
unsigned int PEERS_MAX = 0;
|
||||
int PEERS_MAX = 0;
|
||||
|
||||
int WEBSOCKET_PING_FREQ = 5 * 60;
|
||||
|
||||
|
||||
@@ -371,11 +371,12 @@ void Config::loadFromString (std::string const& fileContents)
|
||||
(void) getSingleSection (secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE, j_);
|
||||
|
||||
std::string strTemp;
|
||||
|
||||
if (getSingleSection (secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
|
||||
PEER_PRIVATE = beast::lexicalCastThrow <bool> (strTemp);
|
||||
|
||||
if (getSingleSection (secConfig, SECTION_PEERS_MAX, strTemp, j_))
|
||||
PEERS_MAX = beast::lexicalCastThrow <int> (strTemp);
|
||||
PEERS_MAX = std::max (0, beast::lexicalCastThrow <int> (strTemp));
|
||||
|
||||
if (getSingleSection (secConfig, SECTION_NODE_SIZE, strTemp, j_))
|
||||
{
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
std::shared_ptr<boost::asio::ssl::context> context;
|
||||
bool expire = false;
|
||||
beast::IP::Address public_ip;
|
||||
int ipLimit = 0;
|
||||
};
|
||||
|
||||
using PeerSequence = std::vector <Peer::ptr>;
|
||||
|
||||
@@ -506,6 +506,7 @@ OverlayImpl::onPrepare()
|
||||
!app_.config().PEER_PRIVATE;
|
||||
config.listeningPort = port;
|
||||
config.features = "";
|
||||
config.ipLimit = setup_.ipLimit;
|
||||
|
||||
// Enforce business rules
|
||||
config.applyTuning();
|
||||
@@ -1056,6 +1057,10 @@ setup_Overlay (BasicConfig const& config)
|
||||
setup.context = make_SSLContext();
|
||||
setup.expire = get<bool>(section, "expire", false);
|
||||
|
||||
set (setup.ipLimit, "ip_limit", section);
|
||||
if (setup.ipLimit < 0)
|
||||
throw std::runtime_error ("Configured IP limit is invalid");
|
||||
|
||||
std::string ip;
|
||||
set (ip, "public_ip", section);
|
||||
if (! ip.empty ())
|
||||
|
||||
@@ -73,6 +73,9 @@ struct Config
|
||||
/** The set of features we advertise. */
|
||||
std::string features;
|
||||
|
||||
/** Limit how many incoming connections we allow per IP */
|
||||
int ipLimit;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Create a configuration with default values. */
|
||||
|
||||
@@ -263,13 +263,13 @@ public:
|
||||
// Check for duplicate connection
|
||||
if (is_public (remote_endpoint))
|
||||
{
|
||||
auto const iter = connectedAddresses_.find (
|
||||
auto const count = connectedAddresses_.count (
|
||||
remote_endpoint.address());
|
||||
if (iter != connectedAddresses_.end())
|
||||
if (count > config_.ipLimit)
|
||||
{
|
||||
if (m_journal.debug) m_journal.debug << beast::leftw (18) <<
|
||||
"Logic dropping inbound " << remote_endpoint <<
|
||||
" as duplicate";
|
||||
" because of ip limits.";
|
||||
return SlotImp::ptr();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ Config::Config()
|
||||
, wantIncoming (true)
|
||||
, autoConnect (true)
|
||||
, listeningPort (0)
|
||||
, ipLimit (0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -45,6 +46,25 @@ void Config::applyTuning ()
|
||||
if (maxPeers < Tuning::minOutCount)
|
||||
maxPeers = Tuning::minOutCount;
|
||||
outPeers = calcOutPeers ();
|
||||
|
||||
auto const inPeers = maxPeers - outPeers;
|
||||
|
||||
if (ipLimit == 0)
|
||||
{
|
||||
// Unless a limit is explicitly set, we allow between
|
||||
// 2 and 5 connections from non RFC-1918 "private"
|
||||
// IP addresses.
|
||||
ipLimit = 2;
|
||||
|
||||
if (inPeers > Tuning::defaultMaxPeers)
|
||||
ipLimit += std::min(5,
|
||||
static_cast<int>(inPeers / Tuning::defaultMaxPeers));
|
||||
}
|
||||
|
||||
// We don't allow a single IP to consume all incoming slots,
|
||||
// unless we only have one incoming slot available.
|
||||
ipLimit = std::max(1,
|
||||
std::min(ipLimit, static_cast<int>(inPeers / 2)));
|
||||
}
|
||||
|
||||
void Config::onWrite (beast::PropertyStream::Map &map)
|
||||
@@ -55,6 +75,7 @@ void Config::onWrite (beast::PropertyStream::Map &map)
|
||||
map ["auto_connect"] = autoConnect;
|
||||
map ["port"] = listeningPort;
|
||||
map ["features"] = features;
|
||||
map ["ip_limit"] = ipLimit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user