mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
merge bitcoin#19219: Replace automatic bans with discouragement filter
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
parent
f14bf83a9d
commit
a7ee2c2667
23
doc/release-notes-19219.md
Normal file
23
doc/release-notes-19219.md
Normal file
@ -0,0 +1,23 @@
|
||||
#### Changes regarding misbehaving peers
|
||||
|
||||
Peers that misbehave (e.g. send us invalid blocks) are now referred to as
|
||||
discouraged nodes in log output, as they're not (and weren't) strictly banned:
|
||||
incoming connections are still allowed from them, but they're preferred for
|
||||
eviction.
|
||||
|
||||
Furthermore, a few additional changes are introduced to how discouraged
|
||||
addresses are treated:
|
||||
|
||||
- Discouraging an address does not time out automatically after 24 hours
|
||||
(or the `-bantime` setting). Depending on traffic from other peers,
|
||||
discouragement may time out at an indeterminate time.
|
||||
|
||||
- Discouragement is not persisted over restarts.
|
||||
|
||||
- There is no method to list discouraged addresses. They are not returned by
|
||||
the `listbanned` RPC. That RPC also no longer reports the `ban_reason`
|
||||
field, as `"manually added"` is the only remaining option.
|
||||
|
||||
- Discouragement cannot be removed with the `setban remove` RPC command.
|
||||
If you need to remove a discouragement, you can remove all discouragements by
|
||||
stop-starting your node.
|
28
src/addrdb.h
28
src/addrdb.h
@ -17,13 +17,6 @@ class CSubNet;
|
||||
class CAddrMan;
|
||||
class CDataStream;
|
||||
|
||||
typedef enum BanReason
|
||||
{
|
||||
BanReasonUnknown = 0,
|
||||
BanReasonNodeMisbehaving = 1,
|
||||
BanReasonManuallyAdded = 2
|
||||
} BanReason;
|
||||
|
||||
class CBanEntry
|
||||
{
|
||||
public:
|
||||
@ -31,7 +24,6 @@ public:
|
||||
int nVersion;
|
||||
int64_t nCreateTime;
|
||||
int64_t nBanUntil;
|
||||
uint8_t banReason;
|
||||
|
||||
CBanEntry()
|
||||
{
|
||||
@ -44,31 +36,17 @@ public:
|
||||
nCreateTime = nCreateTimeIn;
|
||||
}
|
||||
|
||||
explicit CBanEntry(int64_t n_create_time_in, BanReason ban_reason_in) : CBanEntry(n_create_time_in)
|
||||
SERIALIZE_METHODS(CBanEntry, obj)
|
||||
{
|
||||
banReason = ban_reason_in;
|
||||
uint8_t ban_reason = 2; //! For backward compatibility
|
||||
READWRITE(obj.nVersion, obj.nCreateTime, obj.nBanUntil, ban_reason);
|
||||
}
|
||||
|
||||
SERIALIZE_METHODS(CBanEntry, obj) { READWRITE(obj.nVersion, obj.nCreateTime, obj.nBanUntil, obj.banReason); }
|
||||
|
||||
void SetNull()
|
||||
{
|
||||
nVersion = CBanEntry::CURRENT_VERSION;
|
||||
nCreateTime = 0;
|
||||
nBanUntil = 0;
|
||||
banReason = BanReasonUnknown;
|
||||
}
|
||||
|
||||
std::string banReasonToString() const
|
||||
{
|
||||
switch (banReason) {
|
||||
case BanReasonNodeMisbehaving:
|
||||
return "node misbehaving";
|
||||
case BanReasonManuallyAdded:
|
||||
return "manually added";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -68,28 +68,13 @@ void BanMan::ClearBanned()
|
||||
if (m_client_interface) m_client_interface->BannedListChanged();
|
||||
}
|
||||
|
||||
int BanMan::IsBannedLevel(CNetAddr net_addr)
|
||||
bool BanMan::IsDiscouraged(const CNetAddr& net_addr)
|
||||
{
|
||||
// Returns the most severe level of banning that applies to this address.
|
||||
// 0 - Not banned
|
||||
// 1 - Automatic misbehavior ban
|
||||
// 2 - Any other ban
|
||||
int level = 0;
|
||||
auto current_time = GetTime();
|
||||
LOCK(m_cs_banned);
|
||||
for (const auto& it : m_banned) {
|
||||
CSubNet sub_net = it.first;
|
||||
CBanEntry ban_entry = it.second;
|
||||
|
||||
if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) {
|
||||
if (ban_entry.banReason != BanReasonNodeMisbehaving) return 2;
|
||||
level = 1;
|
||||
}
|
||||
}
|
||||
return level;
|
||||
return m_discouraged.contains(net_addr.GetAddrBytes());
|
||||
}
|
||||
|
||||
bool BanMan::IsBanned(CNetAddr net_addr)
|
||||
bool BanMan::IsBanned(const CNetAddr& net_addr)
|
||||
{
|
||||
auto current_time = GetTime();
|
||||
LOCK(m_cs_banned);
|
||||
@ -104,7 +89,7 @@ bool BanMan::IsBanned(CNetAddr net_addr)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BanMan::IsBanned(CSubNet sub_net)
|
||||
bool BanMan::IsBanned(const CSubNet& sub_net)
|
||||
{
|
||||
auto current_time = GetTime();
|
||||
LOCK(m_cs_banned);
|
||||
@ -118,15 +103,21 @@ bool BanMan::IsBanned(CSubNet sub_net)
|
||||
return false;
|
||||
}
|
||||
|
||||
void BanMan::Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch)
|
||||
void BanMan::Ban(const CNetAddr& net_addr, int64_t ban_time_offset, bool since_unix_epoch)
|
||||
{
|
||||
CSubNet sub_net(net_addr);
|
||||
Ban(sub_net, ban_reason, ban_time_offset, since_unix_epoch);
|
||||
Ban(sub_net, ban_time_offset, since_unix_epoch);
|
||||
}
|
||||
|
||||
void BanMan::Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch)
|
||||
void BanMan::Discourage(const CNetAddr& net_addr)
|
||||
{
|
||||
CBanEntry ban_entry(GetTime(), ban_reason);
|
||||
LOCK(m_cs_banned);
|
||||
m_discouraged.insert(net_addr.GetAddrBytes());
|
||||
}
|
||||
|
||||
void BanMan::Ban(const CSubNet& sub_net, int64_t ban_time_offset, bool since_unix_epoch)
|
||||
{
|
||||
CBanEntry ban_entry(GetTime());
|
||||
|
||||
int64_t normalized_ban_time_offset = ban_time_offset;
|
||||
bool normalized_since_unix_epoch = since_unix_epoch;
|
||||
@ -146,8 +137,8 @@ void BanMan::Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ba
|
||||
}
|
||||
if (m_client_interface) m_client_interface->BannedListChanged();
|
||||
|
||||
//store banlist to disk immediately if user requested ban
|
||||
if (ban_reason == BanReasonManuallyAdded) DumpBanlist();
|
||||
//store banlist to disk immediately
|
||||
DumpBanlist();
|
||||
}
|
||||
|
||||
bool BanMan::Unban(const CNetAddr& net_addr)
|
||||
|
63
src/banman.h
63
src/banman.h
@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include <addrdb.h>
|
||||
#include <bloom.h>
|
||||
#include <fs.h>
|
||||
#include <net_types.h> // For banmap_t
|
||||
#include <sync.h>
|
||||
@ -20,32 +21,55 @@ class CClientUIInterface;
|
||||
class CNetAddr;
|
||||
class CSubNet;
|
||||
|
||||
// Denial-of-service detection/prevention
|
||||
// The idea is to detect peers that are behaving
|
||||
// badly and disconnect/ban them, but do it in a
|
||||
// one-coding-mistake-won't-shatter-the-entire-network
|
||||
// way.
|
||||
// IMPORTANT: There should be nothing I can give a
|
||||
// node that it will forward on that will make that
|
||||
// node's peers drop it. If there is, an attacker
|
||||
// can isolate a node and/or try to split the network.
|
||||
// Dropping a node for sending stuff that is invalid
|
||||
// now but might be valid in a later version is also
|
||||
// dangerous, because it can cause a network split
|
||||
// between nodes running old code and nodes running
|
||||
// new code.
|
||||
// Banman manages two related but distinct concepts:
|
||||
//
|
||||
// 1. Banning. This is configured manually by the user, through the setban RPC.
|
||||
// If an address or subnet is banned, we never accept incoming connections from
|
||||
// it and never create outgoing connections to it. We won't gossip its address
|
||||
// to other peers in addr messages. Banned addresses and subnets are stored to
|
||||
// banlist.dat on shutdown and reloaded on startup. Banning can be used to
|
||||
// prevent connections with spy nodes or other griefers.
|
||||
//
|
||||
// 2. Discouragement. If a peer misbehaves enough (see Misbehaving() in
|
||||
// net_processing.cpp), we'll mark that address as discouraged. We still allow
|
||||
// incoming connections from them, but they're preferred for eviction when
|
||||
// we receive new incoming connections. We never make outgoing connections to
|
||||
// them, and do not gossip their address to other peers. This is implemented as
|
||||
// a bloom filter. We can (probabilistically) test for membership, but can't
|
||||
// list all discouraged addresses or unmark them as discouraged. Discouragement
|
||||
// can prevent our limited connection slots being used up by incompatible
|
||||
// or broken peers.
|
||||
//
|
||||
// Neither banning nor discouragement are protections against denial-of-service
|
||||
// attacks, since if an attacker has a way to waste our resources and we
|
||||
// disconnect from them and ban that address, it's trivial for them to
|
||||
// reconnect from another IP address.
|
||||
//
|
||||
// Attempting to automatically disconnect or ban any class of peer carries the
|
||||
// risk of splitting the network. For example, if we banned/disconnected for a
|
||||
// transaction that fails a policy check and a future version changes the
|
||||
// policy check so the transaction is accepted, then that transaction could
|
||||
// cause the network to split between old nodes and new nodes.
|
||||
|
||||
class BanMan
|
||||
{
|
||||
public:
|
||||
~BanMan();
|
||||
BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time);
|
||||
void Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
|
||||
void Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
|
||||
void Ban(const CNetAddr& net_addr, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
|
||||
void Ban(const CSubNet& sub_net, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
|
||||
void Discourage(const CNetAddr& net_addr);
|
||||
void ClearBanned();
|
||||
int IsBannedLevel(CNetAddr net_addr);
|
||||
bool IsBanned(CNetAddr net_addr);
|
||||
bool IsBanned(CSubNet sub_net);
|
||||
|
||||
//! Return whether net_addr is banned
|
||||
bool IsBanned(const CNetAddr& net_addr);
|
||||
|
||||
//! Return whether sub_net is exactly banned
|
||||
bool IsBanned(const CSubNet& sub_net);
|
||||
|
||||
//! Return whether net_addr is discouraged.
|
||||
bool IsDiscouraged(const CNetAddr& net_addr);
|
||||
|
||||
bool Unban(const CNetAddr& net_addr);
|
||||
bool Unban(const CSubNet& sub_net);
|
||||
void GetBanned(banmap_t& banmap);
|
||||
@ -65,6 +89,7 @@ private:
|
||||
CClientUIInterface* m_client_interface = nullptr;
|
||||
CBanDB m_ban_db;
|
||||
const int64_t m_default_ban_time;
|
||||
CRollingBloomFilter m_discouraged GUARDED_BY(m_cs_banned) {50000, 0.000001};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -544,8 +544,8 @@ void SetupServerArgs(NodeContext& node)
|
||||
argsman.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-allowprivatenet", strprintf("Allow RFC1918 addresses to be relayed and connected to (default: %u)", DEFAULT_ALLOWPRIVATENET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting and discouraging misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
|
@ -257,10 +257,10 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) override
|
||||
bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) override
|
||||
{
|
||||
if (m_context->banman) {
|
||||
m_context->banman->Ban(net_addr, reason, ban_time_offset);
|
||||
m_context->banman->Ban(net_addr, ban_time_offset);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -195,7 +195,7 @@ public:
|
||||
virtual bool getBanned(banmap_t& banmap) = 0;
|
||||
|
||||
//! Ban node.
|
||||
virtual bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) = 0;
|
||||
virtual bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) = 0;
|
||||
|
||||
//! Unban node.
|
||||
virtual bool unban(const CSubNet& ip) = 0;
|
||||
|
23
src/net.cpp
23
src/net.cpp
@ -1095,17 +1095,24 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
|
||||
// on all platforms. Set it again here just to be sure.
|
||||
SetSocketNoDelay(hSocket);
|
||||
|
||||
int bannedlevel = m_banman ? m_banman->IsBannedLevel(addr) : 0;
|
||||
|
||||
// Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
|
||||
// if the only banning reason was an automatic misbehavior ban.
|
||||
if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
|
||||
// Don't accept connections from banned peers.
|
||||
bool banned = m_banman->IsBanned(addr);
|
||||
if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && banned)
|
||||
{
|
||||
LogPrint(BCLog::NET, "%s (banned)\n", strDropped);
|
||||
CloseSocket(hSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only accept connections from discouraged peers if our inbound slots aren't (almost) full.
|
||||
bool discouraged = m_banman->IsDiscouraged(addr);
|
||||
if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && nInbound + 1 >= nMaxInbound && discouraged)
|
||||
{
|
||||
LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
// Evict connections until we are below nMaxInbound. In case eviction protection resulted in nodes to not be evicted
|
||||
// before, they might get evicted in batches now (after the protection timeout).
|
||||
// We don't evict verified MN connections and also don't take them into account when checking limits. We can do this
|
||||
@ -1142,7 +1149,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
|
||||
pnode->m_permissionFlags = permissionFlags;
|
||||
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
|
||||
pnode->m_legacyWhitelisted = legacyWhitelisted;
|
||||
pnode->m_prefer_evict = bannedlevel > 0;
|
||||
pnode->m_prefer_evict = discouraged;
|
||||
m_msgproc->InitializeNode(pnode);
|
||||
|
||||
if (fLogIPs) {
|
||||
@ -2527,8 +2534,8 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
||||
return;
|
||||
}
|
||||
if (!pszDest) {
|
||||
// banned or exact match?
|
||||
if ((m_banman && m_banman->IsBanned(addrConnect)) || FindNode(addrConnect.ToStringIPPort()))
|
||||
// banned, discouraged or exact match?
|
||||
if ((m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect))) || FindNode(addrConnect.ToStringIPPort()))
|
||||
return;
|
||||
// local and not a connection to itself?
|
||||
bool fAllowLocal = Params().AllowMultiplePorts() && addrConnect.GetPort() != GetListenPort();
|
||||
|
@ -21,7 +21,7 @@ enum NetPermissionFlags
|
||||
// Always relay transactions from this peer, even if already in mempool or rejected from policy
|
||||
// Keep parameter interaction: forcerelay implies relay
|
||||
PF_FORCERELAY = (1U << 2) | PF_RELAY,
|
||||
// Can't be banned for misbehavior
|
||||
// Can't be banned/disconnected/discouraged for misbehavior
|
||||
PF_NOBAN = (1U << 4),
|
||||
// Can query the mempool
|
||||
PF_MEMPOOL = (1U << 5),
|
||||
|
@ -266,8 +266,8 @@ struct CNodeState {
|
||||
bool fCurrentlyConnected;
|
||||
//! Accumulated misbehaviour score for this peer.
|
||||
int nMisbehavior;
|
||||
//! Whether this peer should be disconnected and banned (unless whitelisted).
|
||||
bool fShouldBan;
|
||||
//! Whether this peer should be disconnected and marked as discouraged (unless whitelisted with noban).
|
||||
bool m_should_discourage;
|
||||
//! String name of this peer (debugging/logging purposes).
|
||||
const std::string name;
|
||||
//! List of asynchronously-determined block rejections to notify this peer about.
|
||||
@ -414,7 +414,7 @@ struct CNodeState {
|
||||
{
|
||||
fCurrentlyConnected = false;
|
||||
nMisbehavior = 0;
|
||||
fShouldBan = false;
|
||||
m_should_discourage = false;
|
||||
pindexBestKnownBlock = nullptr;
|
||||
hashLastUnknownBlock.SetNull();
|
||||
pindexLastCommonBlock = nullptr;
|
||||
@ -1121,7 +1121,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphansSize)
|
||||
void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
|
||||
|
||||
/**
|
||||
* Mark a misbehaving peer to be banned depending upon the value of `-banscore`.
|
||||
* Increment peer's misbehavior score. If the new value surpasses banscore (specified on startup or by default), mark node to be discouraged, meaning the peer might be disconnected & added to the discouragement filter.
|
||||
*/
|
||||
void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
{
|
||||
@ -1137,8 +1137,8 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
|
||||
std::string message_prefixed = message.empty() ? "" : (": " + message);
|
||||
if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
|
||||
{
|
||||
LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
|
||||
state->fShouldBan = true;
|
||||
LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
|
||||
state->m_should_discourage = true;
|
||||
statsClient.inc("misbehavior.banned", 1.0f);
|
||||
} else {
|
||||
LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
|
||||
@ -1151,7 +1151,7 @@ bool IsBanned(NodeId pnode)
|
||||
CNodeState *state = State(pnode);
|
||||
if (state == nullptr)
|
||||
return false;
|
||||
if (state->fShouldBan) {
|
||||
if (state->m_should_discourage) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -1169,7 +1169,7 @@ static bool TxRelayMayResultInDisconnect(const CValidationState& state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Potentially ban a node based on the contents of a CValidationState object
|
||||
* Potentially mark a node discouraged based on the contents of a CValidationState object
|
||||
*
|
||||
* @param[in] via_compact_block: this bool is passed in because net_processing should
|
||||
* punish peers differently depending on whether the data was provided in a compact
|
||||
@ -1201,7 +1201,7 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v
|
||||
break;
|
||||
}
|
||||
|
||||
// Ban outbound (but not inbound) peers if on an invalid chain.
|
||||
// Discourage outbound (but not inbound) peers if on an invalid chain.
|
||||
// Exempt HB compact block peers and manual connections.
|
||||
if (!via_compact_block && !node_state->m_is_inbound && !node_state->m_is_manual_connection) {
|
||||
Misbehaving(nodeid, 100, message);
|
||||
@ -1425,7 +1425,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle invalid block rejection and consequent peer banning, maintain which
|
||||
* Handle invalid block rejection and consequent peer discouragement, maintain which
|
||||
* peers announce compact blocks.
|
||||
*/
|
||||
void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
|
||||
@ -2892,7 +2892,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
|
||||
addr.nTime = nNow - 5 * 24 * 60 * 60;
|
||||
pfrom->AddAddressKnown(addr);
|
||||
if (banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
|
||||
if (banman->IsDiscouraged(addr)) continue; // Do not process banned/discouraged addresses beyond remembering we received them
|
||||
if (banman->IsBanned(addr)) continue;
|
||||
bool fReachable = IsReachable(addr);
|
||||
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
|
||||
{
|
||||
@ -3614,8 +3615,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
// relayed before full validation (see BIP 152), so we don't want to disconnect
|
||||
// the peer if the header turns out to be for an invalid block.
|
||||
// Note that if a peer tries to build on an invalid chain, that
|
||||
// will be detected and the peer will be banned.
|
||||
return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
|
||||
// will be detected and the peer will be disconnected/discouraged.
|
||||
return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*punish_duplicate_invalid=*/true);
|
||||
}
|
||||
|
||||
if (fBlockReconstructed) {
|
||||
@ -3700,7 +3701,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
// 3. the block is otherwise invalid (eg invalid coinbase,
|
||||
// block is too big, too many legacy sigops, etc).
|
||||
// So if CheckBlock failed, #3 is the only possibility.
|
||||
// Under BIP 152, we don't DoS-ban unless proof of work is
|
||||
// Under BIP 152, we don't discourage the peer unless proof of work is
|
||||
// invalid (we don't require all the stateless checks to have
|
||||
// been run). This is handled below, so just treat this as
|
||||
// though the block was successfully read, and rely on the
|
||||
@ -3833,7 +3834,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
std::vector<CAddress> vAddr = connman->GetAddresses();
|
||||
FastRandomContext insecure_rand;
|
||||
for (const CAddress &addr : vAddr) {
|
||||
if (!banman->IsBanned(addr)) {
|
||||
if (!banman->IsDiscouraged(addr) && !banman->IsBanned(addr)) {
|
||||
pfrom->PushAddress(addr, insecure_rand);
|
||||
}
|
||||
}
|
||||
@ -4126,7 +4127,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PeerLogicValidation::SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode* pnode, bool enable_bip61)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
CNodeState &state = *State(pnode->GetId());
|
||||
@ -4138,20 +4139,21 @@ bool PeerLogicValidation::SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_
|
||||
}
|
||||
state.rejects.clear();
|
||||
|
||||
if (state.fShouldBan) {
|
||||
state.fShouldBan = false;
|
||||
if (pnode->HasPermission(PF_NOBAN))
|
||||
if (state.m_should_discourage) {
|
||||
state.m_should_discourage = false;
|
||||
if (pnode->HasPermission(PF_NOBAN)) {
|
||||
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->GetLogString());
|
||||
else if (pnode->m_manual_connection)
|
||||
LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->GetLogString());
|
||||
else if (pnode->addr.IsLocal()) {
|
||||
// Disconnect but don't ban _this_ local node
|
||||
LogPrintf("Warning: disconnecting but not banning local peer %s!\n", pnode->GetLogString());
|
||||
} else if (pnode->m_manual_connection) {
|
||||
LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
|
||||
} else if (pnode->addr.IsLocal()) {
|
||||
// Disconnect but don't discourage this local node
|
||||
LogPrintf("Warning: disconnecting but not discouraging local peer %s!\n", pnode->GetLogString());
|
||||
pnode->fDisconnect = true;
|
||||
} else {
|
||||
// Disconnect and ban all nodes sharing the address
|
||||
// Disconnect and discourage all nodes sharing the address
|
||||
LogPrintf("Disconnecting and discouraging peer %s!\n", pnode->addr.ToString());
|
||||
if (m_banman) {
|
||||
m_banman->Ban(pnode->addr, BanReasonNodeMisbehaving);
|
||||
m_banman->Discourage(pnode->addr);
|
||||
}
|
||||
connman->DisconnectNode(pnode->addr);
|
||||
}
|
||||
@ -4276,7 +4278,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
SendRejectsAndCheckIfBanned(pfrom, m_enable_bip61);
|
||||
MaybeDiscourageAndDisconnect(pfrom, m_enable_bip61);
|
||||
|
||||
return fMoreWork;
|
||||
}
|
||||
@ -4480,7 +4482,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
if (!lockMain)
|
||||
return true;
|
||||
|
||||
if (SendRejectsAndCheckIfBanned(pto, m_enable_bip61)) return true;
|
||||
if (MaybeDiscourageAndDisconnect(pto, m_enable_bip61)) return true;
|
||||
|
||||
CNodeState &state = *State(pto->GetId());
|
||||
|
||||
// Address refresh broadcast
|
||||
|
@ -32,7 +32,7 @@ private:
|
||||
ChainstateManager& m_chainman;
|
||||
CTxMemPool& m_mempool;
|
||||
|
||||
bool SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
bool MaybeDiscourageAndDisconnect(CNode* pnode, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
public:
|
||||
PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler &scheduler, ChainstateManager& chainman, CTxMemPool& pool, bool enable_bip61);
|
||||
|
||||
|
@ -1360,7 +1360,7 @@ void RPCConsole::banSelectedNode(int bantime)
|
||||
// Find possible nodes, ban it and clear the selected node
|
||||
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
|
||||
if (stats) {
|
||||
m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
|
||||
m_node.ban(stats->nodeStats.addr, bantime);
|
||||
m_node.disconnect(stats->nodeStats.addr);
|
||||
}
|
||||
}
|
||||
|
@ -641,12 +641,12 @@ static UniValue setban(const JSONRPCRequest& request)
|
||||
absolute = true;
|
||||
|
||||
if (isSubnet) {
|
||||
node.banman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute);
|
||||
node.banman->Ban(subNet, banTime, absolute);
|
||||
if (node.connman) {
|
||||
node.connman->DisconnectNode(subNet);
|
||||
}
|
||||
} else {
|
||||
node.banman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
|
||||
node.banman->Ban(netAddr, banTime, absolute);
|
||||
if (node.connman) {
|
||||
node.connman->DisconnectNode(netAddr);
|
||||
}
|
||||
@ -655,7 +655,7 @@ static UniValue setban(const JSONRPCRequest& request)
|
||||
else if(strCommand == "remove")
|
||||
{
|
||||
if (!( isSubnet ? node.banman->Unban(subNet) : node.banman->Unban(netAddr) )) {
|
||||
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously banned.");
|
||||
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously manually banned.");
|
||||
}
|
||||
}
|
||||
return NullUniValue;
|
||||
@ -664,7 +664,7 @@ static UniValue setban(const JSONRPCRequest& request)
|
||||
static UniValue listbanned(const JSONRPCRequest& request)
|
||||
{
|
||||
RPCHelpMan{"listbanned",
|
||||
"\nList all banned IPs/Subnets.\n",
|
||||
"\nList all manually banned IPs/Subnets.\n",
|
||||
{},
|
||||
RPCResult{RPCResult::Type::ARR, "", "",
|
||||
{
|
||||
@ -673,7 +673,6 @@ static UniValue listbanned(const JSONRPCRequest& request)
|
||||
{RPCResult::Type::STR, "address", ""},
|
||||
{RPCResult::Type::NUM_TIME, "banned_until", ""},
|
||||
{RPCResult::Type::NUM_TIME, "ban_created", ""},
|
||||
{RPCResult::Type::STR, "ban_reason", ""},
|
||||
}},
|
||||
}},
|
||||
RPCExamples{
|
||||
@ -698,7 +697,6 @@ static UniValue listbanned(const JSONRPCRequest& request)
|
||||
rec.pushKV("address", entry.first.ToString());
|
||||
rec.pushKV("banned_until", banEntry.nBanUntil);
|
||||
rec.pushKV("ban_created", banEntry.nCreateTime);
|
||||
rec.pushKV("ban_reason", banEntry.banReasonToString());
|
||||
|
||||
bannedAddresses.push_back(rec);
|
||||
}
|
||||
|
@ -238,8 +238,8 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
|
||||
LOCK(dummyNode1.cs_sendProcessing);
|
||||
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
|
||||
}
|
||||
BOOST_CHECK(banman->IsBanned(addr1));
|
||||
BOOST_CHECK(!banman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
|
||||
BOOST_CHECK(banman->IsDiscouraged(addr1));
|
||||
BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
|
||||
|
||||
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
|
||||
CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true);
|
||||
@ -255,8 +255,8 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
|
||||
LOCK(dummyNode2.cs_sendProcessing);
|
||||
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
|
||||
}
|
||||
BOOST_CHECK(!banman->IsBanned(addr2)); // 2 not banned yet...
|
||||
BOOST_CHECK(banman->IsBanned(addr1)); // ... but 1 still should be
|
||||
BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not banned yet...
|
||||
BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
|
||||
{
|
||||
LOCK(cs_main);
|
||||
Misbehaving(dummyNode2.GetId(), 50);
|
||||
@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
|
||||
LOCK(dummyNode2.cs_sendProcessing);
|
||||
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
|
||||
}
|
||||
BOOST_CHECK(banman->IsBanned(addr2));
|
||||
BOOST_CHECK(banman->IsDiscouraged(addr2));
|
||||
|
||||
bool dummy;
|
||||
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
|
||||
@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
|
||||
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
|
||||
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
|
||||
}
|
||||
BOOST_CHECK(!banman->IsBanned(addr1));
|
||||
BOOST_CHECK(!banman->IsDiscouraged(addr1));
|
||||
{
|
||||
LOCK(cs_main);
|
||||
Misbehaving(dummyNode1.GetId(), 10);
|
||||
@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
|
||||
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
|
||||
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
|
||||
}
|
||||
BOOST_CHECK(!banman->IsBanned(addr1));
|
||||
BOOST_CHECK(!banman->IsDiscouraged(addr1));
|
||||
{
|
||||
LOCK(cs_main);
|
||||
Misbehaving(dummyNode1.GetId(), 1);
|
||||
@ -312,7 +312,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
|
||||
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
|
||||
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
|
||||
}
|
||||
BOOST_CHECK(banman->IsBanned(addr1));
|
||||
BOOST_CHECK(banman->IsDiscouraged(addr1));
|
||||
gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD));
|
||||
|
||||
bool dummy;
|
||||
@ -344,13 +344,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
|
||||
LOCK(dummyNode.cs_sendProcessing);
|
||||
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
|
||||
}
|
||||
BOOST_CHECK(banman->IsBanned(addr));
|
||||
|
||||
SetMockTime(nStartTime+60*60);
|
||||
BOOST_CHECK(banman->IsBanned(addr));
|
||||
|
||||
SetMockTime(nStartTime+60*60*24+1);
|
||||
BOOST_CHECK(!banman->IsBanned(addr));
|
||||
BOOST_CHECK(banman->IsDiscouraged(addr));
|
||||
|
||||
bool dummy;
|
||||
peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
|
||||
|
Loading…
Reference in New Issue
Block a user