Improve protocol-level handshaking protocol:

This commit restructures the HTTP based protocol negotiation that `rippled`
executes and introduces support for negotiation of compression for peer
links which, if implemented, should result in significant bandwidth savings
for some server roles.

This commit also introduces the new `[network_id]` configuration option
that administrators can use to specify which network the server is part of
and intends to join. This makes it possible for servers from different
networks to drop the link early.

The changeset also improves the log messages generated when negotiation
of a peer link upgrade fails. In the past, no useful information would
be logged, making it more difficult for admins to troubleshoot errors.

This commit also fixes RIPD-237 and RIPD-451
This commit is contained in:
Nik Bougalis
2019-06-22 17:02:13 -07:00
parent 3ea525430e
commit f6916bfd42
41 changed files with 1496 additions and 1581 deletions

View File

@@ -29,6 +29,7 @@
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/app/tx/apply.h>
#include <ripple/basics/base64.h>
#include <ripple/basics/random.h>
#include <ripple/basics/safe_cast.h>
#include <ripple/basics/UptimeClock.h>
@@ -53,10 +54,10 @@ using namespace std::chrono_literals;
namespace ripple {
PeerImp::PeerImp (Application& app, id_t id, endpoint_type remote_endpoint,
PeerFinder::Slot::ptr const& slot, http_request_type&& request,
protocol::TMHello const& hello, PublicKey const& publicKey,
Resource::Consumer consumer,
PeerImp::PeerImp (Application& app, id_t id,
std::shared_ptr<PeerFinder::Slot> const& slot, http_request_type&& request,
PublicKey const& publicKey,
ProtocolVersion protocol, Resource::Consumer consumer,
std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
OverlayImpl& overlay)
: Child (overlay)
@@ -71,16 +72,15 @@ PeerImp::PeerImp (Application& app, id_t id, endpoint_type remote_endpoint,
, stream_ (ssl_bundle_->stream)
, strand_ (socket_.get_executor())
, timer_ (beast::create_waitable_timer<waitable_timer>(socket_))
, remote_address_ (
beast::IPAddressConversion::from_asio(remote_endpoint))
, remote_address_ (slot->remote_endpoint())
, overlay_ (overlay)
, m_inbound (true)
, protocol_ (protocol)
, state_ (State::active)
, sanity_ (Sanity::unknown)
, insaneTime_ (clock_type::now())
, publicKey_(publicKey)
, creationTime_ (clock_type::now())
, hello_(hello)
, usage_(consumer)
, fee_ (Resource::feeLightPeer)
, slot_ (slot)
@@ -116,8 +116,51 @@ void
PeerImp::run()
{
if(! strand_.running_in_this_thread())
return post(strand_, std::bind (
&PeerImp::run, shared_from_this()));
return post(strand_, std::bind(&PeerImp::run, shared_from_this()));
// We need to decipher
auto parseLedgerHash = [](std::string const& value) -> boost::optional<uint256>
{
uint256 ret;
if (ret.SetHexExact(value))
return { ret };
auto const s = base64_decode(value);
if (s.size() != uint256::size())
return boost::none;
return uint256{ s };
};
boost::optional<uint256> closed;
boost::optional<uint256> previous;
if (auto const iter = headers_.find("Closed-Ledger"); iter != headers_.end())
{
closed = parseLedgerHash(iter->value().to_string());
if (!closed)
fail("Malformed handshake data (1)");
}
if (auto const iter = headers_.find("Previous-Ledger"); iter != headers_.end())
{
previous = parseLedgerHash(iter->value().to_string());
if (!previous)
fail("Malformed handshake data (2)");
}
if (previous && !closed)
fail("Malformed handshake data (3)");
{
std::lock_guard<std::mutex> sl(recentLock_);
if (closed)
closedLedgerHash_ = *closed;
if (previous)
previousLedgerHash_ = *previous;
}
if (m_inbound)
{
doAccept();
@@ -127,27 +170,6 @@ PeerImp::run()
assert (state_ == State::active);
// XXX Set timer: connection is in grace period to be useful.
// XXX Set timer: connection idle (idle may vary depending on connection type.)
if (hello_.has_ledgerclosed() &&
stringIsUint256Sized (hello_.ledgerclosed()))
{
// Operations on closedLedgerHash_ and previousLedgerHash_ must be
// guarded by recentLock_.
std::lock_guard sl(recentLock_);
closedLedgerHash_ = hello_.ledgerclosed();
if (hello_.has_ledgerprevious() &&
stringIsUint256Sized (hello_.ledgerprevious()))
{
previousLedgerHash_ = hello_.ledgerprevious();
addLedger (previousLedgerHash_, sl);
}
else
{
previousLedgerHash_.zero();
}
}
doProtocolStart();
}
@@ -186,7 +208,7 @@ PeerImp::stop()
//------------------------------------------------------------------------------
void
PeerImp::send (Message::pointer const& m)
PeerImp::send (std::shared_ptr<Message> const& m)
{
if (! strand_.running_in_this_thread())
return post(strand_, std::bind(&PeerImp::send, shared_from_this(), m));
@@ -266,10 +288,9 @@ PeerImp::cluster() const
std::string
PeerImp::getVersion() const
{
if (hello_.has_fullversion ())
return hello_.fullversion ();
return std::string ();
if (m_inbound)
return headers_["User-Agent"].to_string();
return headers_["Server"].to_string();
}
Json::Value
@@ -295,17 +316,14 @@ PeerImp::json()
ret[jss::load] = usage_.balance ();
if (hello_.has_fullversion ())
ret[jss::version] = hello_.fullversion ();
if (hello_.has_protoversion ())
{
auto protocol = BuildInfo::make_protocol (hello_.protoversion ());
if (protocol != BuildInfo::getCurrentProtocol())
ret[jss::protocol] = to_string (protocol);
auto const version = getVersion();
if (!version.empty())
ret[jss::version] = version;
}
ret[jss::protocol] = to_string (protocol_);
{
std::lock_guard sl (recentLock_);
if (latency_)
@@ -444,12 +462,6 @@ PeerImp::cycleStatus ()
closedLedgerHash_.zero ();
}
bool
PeerImp::supportsVersion (int version)
{
return hello_.has_protoversion () && (hello_.protoversion () >= version);
}
bool
PeerImp::hasRange (std::uint32_t uMin, std::uint32_t uMax)
{
@@ -682,12 +694,11 @@ PeerImp::onShutdown(error_code ec)
void PeerImp::doAccept()
{
assert(read_buffer_.size() == 0);
// assert(request_.upgrade);
JLOG(journal_.debug()) << "doAccept: " << remote_address_;
auto sharedValue = makeSharedValue(
ssl_bundle_->stream.native_handle(), journal_);
auto const sharedValue = makeSharedValue(*ssl_bundle_, journal_);
// This shouldn't fail since we already computed
// the shared value successfully in OverlayImpl
if(! sharedValue)
@@ -697,14 +708,13 @@ void PeerImp::doAccept()
boost::beast::ostream(write_buffer_) << makeResponse(
! overlay_.peerFinder().config().peerPrivate,
request_, remote_address_, *sharedValue);
request_, remote_address_.address(), *sharedValue);
JLOG(journal_.info()) << "Protocol: " <<
to_string(protocol_);
JLOG(journal_.info()) << "Public Key: " <<
toBase58 (TokenType::NodePublic, publicKey_);
auto const protocol = BuildInfo::make_protocol(hello_.protoversion());
JLOG(journal_.info()) << "Protocol: " << to_string(protocol);
JLOG(journal_.info()) <<
"Public Key: " << toBase58 (
TokenType::NodePublic,
publicKey_);
if (auto member = app_.cluster().member(publicKey_))
{
{
@@ -718,26 +728,6 @@ void PeerImp::doAccept()
// XXX Set timer: connection is in grace period to be useful.
// XXX Set timer: connection idle (idle may vary depending on connection type.)
if (hello_.has_ledgerclosed() &&
stringIsUint256Sized (hello_.ledgerclosed()))
{
// Operations on closedLedgerHash_ and previousLedgerHash_ must be
// guarded by recentLock_.
std::lock_guard sl(recentLock_);
closedLedgerHash_ = hello_.ledgerclosed();
if (hello_.has_ledgerprevious() &&
stringIsUint256Sized (hello_.ledgerprevious()))
{
previousLedgerHash_ = hello_.ledgerprevious();
addLedger (previousLedgerHash_, sl);
}
else
{
previousLedgerHash_.zero();
}
}
onWriteResponse(error_code(), 0);
}
@@ -745,20 +735,21 @@ void PeerImp::doAccept()
http_response_type
PeerImp::makeResponse (bool crawl,
http_request_type const& req,
beast::IP::Endpoint remote,
beast::IP::Address remote_ip,
uint256 const& sharedValue)
{
http_response_type resp;
resp.result(boost::beast::http::status::switching_protocols);
resp.version(req.version());
resp.insert("Connection", "Upgrade");
resp.insert("Upgrade", "RTXP/1.2");
resp.insert("Upgrade", to_string(protocol_));
resp.insert("Connect-As", "Peer");
resp.insert("Server", BuildInfo::getFullVersionString());
resp.insert("Crawl", crawl ? "public" : "private");
protocol::TMHello hello = buildHello(sharedValue,
overlay_.setup().public_ip, remote, app_);
appendHello(resp, hello);
buildHandshake(resp, sharedValue, overlay_.setup().networkID,
overlay_.setup().public_ip, remote_ip, app_);
return resp;
}
@@ -940,15 +931,13 @@ PeerImp::onWriteMessage (error_code ec, std::size_t bytes_transferred)
//
//------------------------------------------------------------------------------
PeerImp::error_code
void
PeerImp::onMessageUnknown (std::uint16_t type)
{
error_code ec;
// TODO
return ec;
}
PeerImp::error_code
void
PeerImp::onMessageBegin (std::uint16_t type,
std::shared_ptr <::google::protobuf::Message> const& m,
std::size_t size)
@@ -958,7 +947,6 @@ PeerImp::onMessageBegin (std::uint16_t type,
fee_ = Resource::feeLightPeer;
overlay_.reportTraffic (TrafficCount::categorize (*m, type, true),
true, static_cast<int>(size));
return error_code{};
}
void
@@ -969,12 +957,6 @@ PeerImp::onMessageEnd (std::uint16_t,
charge (fee_);
}
void
PeerImp::onMessage (std::shared_ptr <protocol::TMHello> const& m)
{
fail("Deprecated TMHello");
}
void
PeerImp::onMessage (std::shared_ptr<protocol::TMManifests> const& m)
{
@@ -1366,20 +1348,6 @@ PeerImp::onMessage(std::shared_ptr <protocol::TMPeerShardInfo> const& m)
overlay_.lastLink(id_);
}
void
PeerImp::onMessage (std::shared_ptr <protocol::TMGetPeers> const& m)
{
// This message is obsolete due to PeerFinder and
// we no longer provide a response to it.
}
void
PeerImp::onMessage (std::shared_ptr <protocol::TMPeers> const& m)
{
// This message is obsolete due to PeerFinder and
// we no longer process it.
}
void
PeerImp::onMessage (std::shared_ptr <protocol::TMEndpoints> const& m)
{
@@ -2701,7 +2669,7 @@ PeerImp::getLedger (std::shared_ptr<protocol::TMGetLedger> const& m)
}
}
Message::pointer oPacket = std::make_shared<Message> (
auto oPacket = std::make_shared<Message> (
reply, protocol::mtLEDGER_DATA);
send (oPacket);
return;
@@ -2806,7 +2774,7 @@ PeerImp::getLedger (std::shared_ptr<protocol::TMGetLedger> const& m)
"Got request for " << packet.nodeids().size() << " nodes at depth " <<
depth << ", return " << reply.nodes().size() << " nodes";
Message::pointer oPacket = std::make_shared<Message> (
auto oPacket = std::make_shared<Message> (
reply, protocol::mtLEDGER_DATA);
send (oPacket);
}