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:
Nik Bougalis
2015-10-21 19:31:21 -07:00
parent f00c09d9fc
commit 0c67364e6c
8 changed files with 45 additions and 5 deletions

View File

@@ -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
#

View File

@@ -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;

View File

@@ -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_))
{

View File

@@ -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>;

View File

@@ -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 ())

View File

@@ -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. */

View File

@@ -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();
}
}

View File

@@ -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;
}
}