mirror of
https://github.com/dogecoin/dogecoin.git
synced 2026-01-31 10:30:52 +00:00
net: Rate limit the processing of incoming addr messages
While limitations on the influence of attackers on addrman already
exist (affected buckets are restricted to a subset based on incoming
IP / network group), there is no reason to permit them to let them
feed us addresses at more than a multiple of the normal network
rate.
This commit introduces a "token bucket" rate limiter for the
processing of addresses in incoming ADDR messages. Every connection
gets an associated token bucket. Processing an address in an ADDR
message from non-whitelisted peers consumes a token from the bucket.
If the bucket is empty, the address is ignored (it is not processed,
stored or forwarded). The token counter increases at a rate of 0.1
tokens per second, and will accrue up to a maximum of 1000 tokens
(the maximum we accept in a single ADDR message).
When a GETADDR is sent to a peer, it immediately gets 1000 additional
tokens, as we actively desire many addresses from such peers (this
may temporarily cause the token count to exceed 1000). This is
similar to allowing bursts.
The rate limit of 0.1 addr/s was copied from Bitcoin Core.
Backported from: 0d64b8f7, 5648138f, f424d601 and d930c7f5
Original authors: Pieter Wuille <pieter@wuille.net>, and
Jon Atack <jon@atack.com>
This commit is contained in:
parent
de26bbfbe1
commit
84a82b2603
@ -659,6 +659,8 @@ void CNode::copyStats(CNodeStats &stats)
|
||||
}
|
||||
X(fWhitelisted);
|
||||
X(minFeeFilter);
|
||||
X(nProcessedAddrs);
|
||||
X(nRatelimitedAddrs);
|
||||
|
||||
// It is common for nodes with good ping times to suddenly become lagged,
|
||||
// due to a new block arriving or other large transfer.
|
||||
@ -2735,6 +2737,10 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
|
||||
fGetAddr = false;
|
||||
nNextLocalAddrSend = 0;
|
||||
nNextAddrSend = 0;
|
||||
nAddrTokenBucket = 1; // initialize to 1 to allow self-announcement
|
||||
nAddrTokenTimestamp = GetTimeMicros();
|
||||
nProcessedAddrs = 0;
|
||||
nRatelimitedAddrs = 0;
|
||||
nNextInvSend = 0;
|
||||
fRelayTxes = false;
|
||||
fSentAddr = false;
|
||||
|
||||
10
src/net.h
10
src/net.h
@ -520,6 +520,8 @@ public:
|
||||
std::string addrLocal;
|
||||
CAddress addr;
|
||||
CAmount minFeeFilter;
|
||||
uint64_t nProcessedAddrs;
|
||||
uint64_t nRatelimitedAddrs;
|
||||
};
|
||||
|
||||
|
||||
@ -648,6 +650,14 @@ public:
|
||||
int64_t nNextAddrSend;
|
||||
int64_t nNextLocalAddrSend;
|
||||
|
||||
/** Number of addresses that can be processed from this peer. */
|
||||
double nAddrTokenBucket;
|
||||
/** When nAddrTokenBucket was last updated, in microseconds */
|
||||
int64_t nAddrTokenTimestamp;
|
||||
|
||||
std::atomic<uint64_t> nProcessedAddrs;
|
||||
std::atomic<uint64_t> nRatelimitedAddrs;
|
||||
|
||||
// inventory based relay
|
||||
CRollingBloomFilter filterInventoryKnown;
|
||||
// Set of transaction ids we still have to announce.
|
||||
|
||||
@ -1371,6 +1371,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
{
|
||||
connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
|
||||
pfrom->fGetAddr = true;
|
||||
|
||||
// When requesting a getaddr, accept an additional MAX_ADDR_TO_SEND addresses in response
|
||||
// (bypassing the MAX_ADDR_PROCESSING_TOKEN_BUCKET limit).
|
||||
pfrom->nAddrTokenBucket += MAX_ADDR_TO_SEND;
|
||||
}
|
||||
connman.MarkAddressGood(pfrom->addr);
|
||||
}
|
||||
@ -1468,11 +1472,40 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
std::vector<CAddress> vAddrOk;
|
||||
int64_t nNow = GetAdjustedTime();
|
||||
int64_t nSince = nNow - 10 * 60;
|
||||
|
||||
// track rate limiting within this message
|
||||
uint64_t nProcessedAddrs = 0;
|
||||
uint64_t nRatelimitedAddrs = 0;
|
||||
|
||||
// Update/increment addr rate limiting bucket.
|
||||
const uint64_t nCurrentTime = GetMockableTimeMicros();
|
||||
if (pfrom->nAddrTokenBucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) {
|
||||
const uint64_t nTimeElapsed = std::max(nCurrentTime - pfrom->nAddrTokenTimestamp, uint64_t(0));
|
||||
const double nIncrement = nTimeElapsed * MAX_ADDR_RATE_PER_SECOND / 1e6;
|
||||
pfrom->nAddrTokenBucket = std::min<double>(pfrom->nAddrTokenBucket + nIncrement, MAX_ADDR_PROCESSING_TOKEN_BUCKET);
|
||||
}
|
||||
pfrom->nAddrTokenTimestamp = nCurrentTime;
|
||||
|
||||
// Randomize entries before processing, to prevent an attacker to
|
||||
// determine which entries will make it through the rate limit
|
||||
random_shuffle(vAddr.begin(), vAddr.end(), GetRandInt);
|
||||
|
||||
BOOST_FOREACH(CAddress& addr, vAddr)
|
||||
{
|
||||
if (interruptMsgProc)
|
||||
return true;
|
||||
|
||||
// apply rate limiting
|
||||
if (!pfrom->fWhitelisted) {
|
||||
if (pfrom->nAddrTokenBucket < 1.0) {
|
||||
nRatelimitedAddrs++;
|
||||
continue;
|
||||
}
|
||||
pfrom->nAddrTokenBucket -= 1.0;
|
||||
}
|
||||
|
||||
nProcessedAddrs++;
|
||||
|
||||
if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
|
||||
continue;
|
||||
|
||||
@ -1489,6 +1522,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
if (fReachable)
|
||||
vAddrOk.push_back(addr);
|
||||
}
|
||||
|
||||
pfrom->nProcessedAddrs += nProcessedAddrs;
|
||||
pfrom->nRatelimitedAddrs += nRatelimitedAddrs;
|
||||
|
||||
LogPrint("net", "Received addr: %u addresses (%u processed, %u rate-limited) peer=%d\n",
|
||||
vAddr.size(), nProcessedAddrs, nRatelimitedAddrs, pfrom->GetId());
|
||||
|
||||
connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
|
||||
if (vAddr.size() < 1000)
|
||||
pfrom->fGetAddr = false;
|
||||
|
||||
@ -26,6 +26,16 @@ static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/head
|
||||
* is set to 1 second.
|
||||
*/
|
||||
static constexpr int64_t MIN_BLOCK_DOWNLOAD_MULTIPLIER = 10; // 10 seconds
|
||||
|
||||
/** The maximum rate of address records we're willing to process on average.
|
||||
* Is bypassed for whitelisted connections. */
|
||||
static constexpr double MAX_ADDR_RATE_PER_SECOND{0.1};
|
||||
|
||||
/** The soft limit of the address processing token bucket (the regular MAX_ADDR_RATE_PER_SECOND
|
||||
* based increments won't go above this, but the MAX_ADDR_TO_SEND increment following GETADDR
|
||||
* is exempt from this limit. */
|
||||
static constexpr size_t MAX_ADDR_PROCESSING_TOKEN_BUCKET{MAX_ADDR_TO_SEND};
|
||||
|
||||
/** Register with a network node to receive its signals */
|
||||
void RegisterNodeSignals(CNodeSignals& nodeSignals);
|
||||
/** Unregister a network node */
|
||||
|
||||
@ -135,7 +135,9 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
|
||||
" n, (numeric) The heights of blocks we're currently asking from this peer\n"
|
||||
" ...\n"
|
||||
" ],\n"
|
||||
" \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\n"
|
||||
" \"addr_processed\": n, (numeric) The total number of addresses processed, excluding those dropped due to rate limiting\n"
|
||||
" \"addr_rate_limited\": n, (numeric) The total number of addresses dropped due to rate limiting\n"
|
||||
" \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\n"
|
||||
" \"bytessent_per_msg\": {\n"
|
||||
" \"addr\": n, (numeric) The total bytes sent aggregated by message type\n"
|
||||
" ...\n"
|
||||
@ -201,6 +203,8 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
|
||||
}
|
||||
obj.pushKV("inflight", heights);
|
||||
}
|
||||
obj.pushKV("addr_processed", stats.nProcessedAddrs);
|
||||
obj.pushKV("addr_rate_limited", stats.nRatelimitedAddrs);
|
||||
obj.pushKV("whitelisted", stats.fWhitelisted);
|
||||
|
||||
UniValue sendPerMsgCmd(UniValue::VOBJ);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user