Rate limit incoming addr p2p messages
This commit is contained in:
34
src/main.cpp
34
src/main.cpp
@@ -6997,6 +6997,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
{
|
{
|
||||||
pfrom->PushMessage(NetMsgType::GETADDR);
|
pfrom->PushMessage(NetMsgType::GETADDR);
|
||||||
pfrom->fGetAddr = true;
|
pfrom->fGetAddr = true;
|
||||||
|
// pfrom->m_getaddr_sent = true;
|
||||||
|
// When requesting a getaddr, accept an additional MAX_ADDR_TO_SEND addresses in response
|
||||||
|
// (bypassing the MAX_ADDR_PROCESSING_TOKEN_BUCKET limit).
|
||||||
|
pfrom->m_addr_token_bucket += MAX_ADDR_TO_SEND;
|
||||||
}
|
}
|
||||||
addrman.Good(pfrom->addr);
|
addrman.Good(pfrom->addr);
|
||||||
} else {
|
} else {
|
||||||
@@ -7100,15 +7104,36 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
vector<CAddress> vAddrOk;
|
vector<CAddress> vAddrOk;
|
||||||
int64_t nNow = GetTime();
|
int64_t nNow = GetTime();
|
||||||
int64_t nSince = nNow - 10 * 60;
|
int64_t nSince = nNow - 10 * 60;
|
||||||
|
|
||||||
|
// Update/increment addr rate limiting bucket.
|
||||||
|
const int64_t current_time = GetTimeMicros();
|
||||||
|
if (pfrom->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) {
|
||||||
|
// Don't increment bucket if it's already full
|
||||||
|
const auto time_diff = std::max(current_time - pfrom->m_addr_token_timestamp, (int64_t) 0);
|
||||||
|
const double increment = (time_diff / 1000000) * MAX_ADDR_RATE_PER_SECOND;
|
||||||
|
pfrom->m_addr_token_bucket = std::min<double>(pfrom->m_addr_token_bucket + increment, MAX_ADDR_PROCESSING_TOKEN_BUCKET);
|
||||||
|
}
|
||||||
|
pfrom->m_addr_token_timestamp = current_time;
|
||||||
|
|
||||||
|
uint64_t num_proc = 0;
|
||||||
|
uint64_t num_rate_limit = 0;
|
||||||
|
|
||||||
BOOST_FOREACH(CAddress& addr, vAddr)
|
BOOST_FOREACH(CAddress& addr, vAddr)
|
||||||
{
|
{
|
||||||
boost::this_thread::interruption_point();
|
boost::this_thread::interruption_point();
|
||||||
|
// Apply rate limiting if the address is not allowlisted
|
||||||
|
if (!pfrom->IsAllowlistedRange(addr)) {
|
||||||
|
if (pfrom->m_addr_token_bucket < 1.0) break;
|
||||||
|
pfrom->m_addr_token_bucket -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
if(p2pdebug)
|
if(p2pdebug)
|
||||||
fprintf(stderr,"%s: %s.nTime=%d\n", __func__, addr.ToString().c_str(), addr.nTime);
|
fprintf(stderr,"%s: %s.nTime=%d\n", __func__, addr.ToString().c_str(), addr.nTime);
|
||||||
|
|
||||||
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
|
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
|
||||||
addr.nTime = nNow - 5 * 24 * 60 * 60;
|
addr.nTime = nNow - 5 * 24 * 60 * 60;
|
||||||
pfrom->AddAddressKnown(addr);
|
pfrom->AddAddressKnown(addr);
|
||||||
|
++num_proc;
|
||||||
bool fReachable = IsReachable(addr);
|
bool fReachable = IsReachable(addr);
|
||||||
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
|
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
|
||||||
{
|
{
|
||||||
@@ -7147,6 +7172,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
fprintf(stderr,"%s: %s with nTime=%d is not reachable\n",__func__,addr.ToString().c_str(), addr.nTime);
|
fprintf(stderr,"%s: %s with nTime=%d is not reachable\n",__func__,addr.ToString().c_str(), addr.nTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pfrom->m_addr_processed += num_proc;
|
||||||
|
pfrom->m_addr_rate_limited += num_rate_limit;
|
||||||
|
LogPrintf("ProcessMessage: Received addr: %u addresses (%u processed, %u rate-limited) from peer=%d%s\n",
|
||||||
|
vAddr.size(),
|
||||||
|
num_proc,
|
||||||
|
num_rate_limit,
|
||||||
|
pfrom->GetId(),
|
||||||
|
fLogIPs ? ", peeraddr=" + pfrom->addr.ToString() : "");
|
||||||
|
|
||||||
addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60);
|
addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60);
|
||||||
if (vAddr.size() < 1000)
|
if (vAddr.size() < 1000)
|
||||||
pfrom->fGetAddr = false;
|
pfrom->fGetAddr = false;
|
||||||
|
|||||||
@@ -809,6 +809,9 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
|
|||||||
stats.dPingTime = (((double)nPingUsecTime) / 1e6);
|
stats.dPingTime = (((double)nPingUsecTime) / 1e6);
|
||||||
stats.dMinPing = (((double)nMinPingUsecTime) / 1e6);
|
stats.dMinPing = (((double)nMinPingUsecTime) / 1e6);
|
||||||
stats.dPingWait = (((double)nPingUsecWait) / 1e6);
|
stats.dPingWait = (((double)nPingUsecWait) / 1e6);
|
||||||
|
stats.m_addr_processed = m_addr_processed.load();
|
||||||
|
stats.m_addr_rate_limited = m_addr_rate_limited.load();
|
||||||
|
|
||||||
|
|
||||||
// Leave string empty if addrLocal invalid (not filled in yet)
|
// Leave string empty if addrLocal invalid (not filled in yet)
|
||||||
stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : "";
|
stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : "";
|
||||||
|
|||||||
22
src/net.h
22
src/net.h
@@ -72,6 +72,13 @@ static const int TIMEOUT_INTERVAL = 20 * 60;
|
|||||||
static const unsigned int MAX_INV_SZ = 50000;
|
static const unsigned int MAX_INV_SZ = 50000;
|
||||||
/** The maximum number of new addresses to accumulate before announcing. */
|
/** The maximum number of new addresses to accumulate before announcing. */
|
||||||
static const unsigned int MAX_ADDR_TO_SEND = 1000;
|
static const unsigned int MAX_ADDR_TO_SEND = 1000;
|
||||||
|
/** The maximum rate of address records we're willing to process on average. Can be bypassed using
|
||||||
|
* the NetPermissionFlags::Addr permission. */
|
||||||
|
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};
|
||||||
/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */
|
/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */
|
||||||
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = (_MAX_BLOCK_SIZE + 24); // 24 is msgheader size
|
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = (_MAX_BLOCK_SIZE + 24); // 24 is msgheader size
|
||||||
/** Maximum length of strSubVer in `version` message */
|
/** Maximum length of strSubVer in `version` message */
|
||||||
@@ -295,6 +302,9 @@ public:
|
|||||||
// CAddress addrBind; // https://github.com/bitcoin/bitcoin/commit/a7e3c2814c8e49197889a4679461be42254e5c51
|
// CAddress addrBind; // https://github.com/bitcoin/bitcoin/commit/a7e3c2814c8e49197889a4679461be42254e5c51
|
||||||
uint32_t m_mapped_as;
|
uint32_t m_mapped_as;
|
||||||
|
|
||||||
|
uint64_t m_addr_processed{0};
|
||||||
|
uint64_t m_addr_rate_limited{0};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the peer has signaled support for receiving ADDRv2 (BIP155)
|
* Whether the peer has signaled support for receiving ADDRv2 (BIP155)
|
||||||
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
|
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
|
||||||
@@ -302,9 +312,6 @@ public:
|
|||||||
bool m_wants_addrv2;
|
bool m_wants_addrv2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CNetMessage {
|
class CNetMessage {
|
||||||
public:
|
public:
|
||||||
bool in_data; // parsing header (false) or data (true)
|
bool in_data; // parsing header (false) or data (true)
|
||||||
@@ -433,6 +440,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool m_wants_addrv2{false};
|
bool m_wants_addrv2{false};
|
||||||
|
|
||||||
|
/** Number of addr messages that can be processed from this peer. Start at 1 to permit self-announcement. */
|
||||||
|
double m_addr_token_bucket{1.0};
|
||||||
|
/** When m_addr_token_bucket was last updated */
|
||||||
|
int64_t m_addr_token_timestamp{GetTimeMicros()};
|
||||||
|
/** Total number of addresses that were dropped due to rate limiting. */
|
||||||
|
std::atomic<uint64_t> m_addr_rate_limited{0};
|
||||||
|
/** Total number of addresses that were processed (excludes rate limited ones). */
|
||||||
|
std::atomic<uint64_t> m_addr_processed{0};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// Denial-of-service detection/prevention
|
// Denial-of-service detection/prevention
|
||||||
|
|||||||
@@ -204,6 +204,8 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
|||||||
obj.push_back(Pair("inflight", heights));
|
obj.push_back(Pair("inflight", heights));
|
||||||
}
|
}
|
||||||
obj.push_back(Pair("allowlisted", stats.fAllowlisted));
|
obj.push_back(Pair("allowlisted", stats.fAllowlisted));
|
||||||
|
obj.pushKV("addr_processed", stats.m_addr_processed);
|
||||||
|
obj.pushKV("addr_rate_limited", stats.m_addr_rate_limited);
|
||||||
|
|
||||||
ret.push_back(obj);
|
ret.push_back(obj);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user