From 0c67364e6c8694289eadeb561366cf97018309ca Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Wed, 21 Oct 2015 19:31:21 -0700 Subject: [PATCH] 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. --- doc/rippled-example.cfg | 9 ++++++++ src/ripple/core/Config.h | 2 +- src/ripple/core/impl/Config.cpp | 3 ++- src/ripple/overlay/Overlay.h | 1 + src/ripple/overlay/impl/OverlayImpl.cpp | 5 +++++ src/ripple/peerfinder/PeerfinderManager.h | 3 +++ src/ripple/peerfinder/impl/Logic.h | 6 +++--- .../peerfinder/impl/PeerfinderConfig.cpp | 21 +++++++++++++++++++ 8 files changed, 45 insertions(+), 5 deletions(-) diff --git a/doc/rippled-example.cfg b/doc/rippled-example.cfg index 41bfa51cfc..db0280a8d9 100644 --- a/doc/rippled-example.cfg +++ b/doc/rippled-example.cfg @@ -373,6 +373,15 @@ # Peers will use this information to reject attempt to proxy # connections to or from this server. # +# ip_limit = +# +# 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 # diff --git a/src/ripple/core/Config.h b/src/ripple/core/Config.h index f71868e944..64392fed36 100644 --- a/src/ripple/core/Config.h +++ b/src/ripple/core/Config.h @@ -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; diff --git a/src/ripple/core/impl/Config.cpp b/src/ripple/core/impl/Config.cpp index 8735344f8b..cb98cacc96 100644 --- a/src/ripple/core/impl/Config.cpp +++ b/src/ripple/core/impl/Config.cpp @@ -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 (strTemp); if (getSingleSection (secConfig, SECTION_PEERS_MAX, strTemp, j_)) - PEERS_MAX = beast::lexicalCastThrow (strTemp); + PEERS_MAX = std::max (0, beast::lexicalCastThrow (strTemp)); if (getSingleSection (secConfig, SECTION_NODE_SIZE, strTemp, j_)) { diff --git a/src/ripple/overlay/Overlay.h b/src/ripple/overlay/Overlay.h index bfb7564b5f..673079999c 100644 --- a/src/ripple/overlay/Overlay.h +++ b/src/ripple/overlay/Overlay.h @@ -70,6 +70,7 @@ public: std::shared_ptr context; bool expire = false; beast::IP::Address public_ip; + int ipLimit = 0; }; using PeerSequence = std::vector ; diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 803d7339f1..d0aac43fda 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -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(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 ()) diff --git a/src/ripple/peerfinder/PeerfinderManager.h b/src/ripple/peerfinder/PeerfinderManager.h index df72df8f6a..261cdcd5c0 100644 --- a/src/ripple/peerfinder/PeerfinderManager.h +++ b/src/ripple/peerfinder/PeerfinderManager.h @@ -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. */ diff --git a/src/ripple/peerfinder/impl/Logic.h b/src/ripple/peerfinder/impl/Logic.h index 80a2255ee4..5524b00be4 100644 --- a/src/ripple/peerfinder/impl/Logic.h +++ b/src/ripple/peerfinder/impl/Logic.h @@ -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(); } } diff --git a/src/ripple/peerfinder/impl/PeerfinderConfig.cpp b/src/ripple/peerfinder/impl/PeerfinderConfig.cpp index 7831cd04cb..d05aafb00c 100644 --- a/src/ripple/peerfinder/impl/PeerfinderConfig.cpp +++ b/src/ripple/peerfinder/impl/PeerfinderConfig.cpp @@ -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(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(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; } }