Skip to content

Commit 2d790e8

Browse files
committed
Merge #14929: net: Allow connections from misbehavior banned peers
0297be6 Allow connections from misbehavior banned peers. (Gregory Maxwell) Pull request description: This allows incoming connections from peers which are only banned due to an automatic misbehavior ban if doing so won't fill inbound. These peers are preferred for eviction when inbound fills, but may still be kept if they fall into the protected classes. This eviction preference lasts the entire life of the connection even if the ban expires. If they misbehave again they'll still get disconnected. The main purpose of banning on misbehavior is to prevent our connections from being wasted on unhelpful peers such as ones running incompatible consensus rules. For inbound peers this can be better accomplished with eviction preferences. A secondary purpose was to reduce resource waste from repeated abuse but virtually any attacker can get a nearly unlimited supply of addresses, so disconnection is about the best we can do. This can reduce the potential from negative impact due to incorrect misbehaviour bans. Tree-SHA512: 03bc8ec8bae365cc437daf70000c8f2edc512e37db821bc4e0fafa6cf56cc185e9ab40453aa02445f48d6a2e3e7268767ca2017655aca5383108416f1e2cf20f
2 parents 7275365 + 0297be6 commit 2d790e8

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

src/banman.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,36 @@ void BanMan::ClearBanned()
6767
if (m_client_interface) m_client_interface->BannedListChanged();
6868
}
6969

70+
int BanMan::IsBannedLevel(CNetAddr net_addr)
71+
{
72+
// Returns the most severe level of banning that applies to this address.
73+
// 0 - Not banned
74+
// 1 - Automatic misbehavior ban
75+
// 2 - Any other ban
76+
int level = 0;
77+
auto current_time = GetTime();
78+
LOCK(m_cs_banned);
79+
for (const auto& it : m_banned) {
80+
CSubNet sub_net = it.first;
81+
CBanEntry ban_entry = it.second;
82+
83+
if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) {
84+
if (ban_entry.banReason != BanReasonNodeMisbehaving) return 2;
85+
level = 1;
86+
}
87+
}
88+
return level;
89+
}
90+
7091
bool BanMan::IsBanned(CNetAddr net_addr)
7192
{
93+
auto current_time = GetTime();
7294
LOCK(m_cs_banned);
7395
for (const auto& it : m_banned) {
7496
CSubNet sub_net = it.first;
7597
CBanEntry ban_entry = it.second;
7698

77-
if (sub_net.Match(net_addr) && GetTime() < ban_entry.nBanUntil) {
99+
if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) {
78100
return true;
79101
}
80102
}
@@ -83,11 +105,12 @@ bool BanMan::IsBanned(CNetAddr net_addr)
83105

84106
bool BanMan::IsBanned(CSubNet sub_net)
85107
{
108+
auto current_time = GetTime();
86109
LOCK(m_cs_banned);
87110
banmap_t::iterator i = m_banned.find(sub_net);
88111
if (i != m_banned.end()) {
89112
CBanEntry ban_entry = (*i).second;
90-
if (GetTime() < ban_entry.nBanUntil) {
113+
if (current_time < ban_entry.nBanUntil) {
91114
return true;
92115
}
93116
}

src/banman.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class BanMan
4242
void Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
4343
void Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
4444
void ClearBanned();
45+
int IsBannedLevel(CNetAddr net_addr);
4546
bool IsBanned(CNetAddr net_addr);
4647
bool IsBanned(CSubNet sub_net);
4748
bool Unban(const CNetAddr& net_addr);

src/net.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,7 @@ struct NodeEvictionCandidate
764764
bool fBloomFilter;
765765
CAddress addr;
766766
uint64_t nKeyedNetGroup;
767+
bool prefer_evict;
767768
};
768769

769770
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -832,7 +833,8 @@ bool CConnman::AttemptToEvictConnection()
832833
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
833834
node->nLastBlockTime, node->nLastTXTime,
834835
HasAllDesirableServiceFlags(node->nServices),
835-
node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup};
836+
node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup,
837+
node->m_prefer_evict};
836838
vEvictionCandidates.push_back(candidate);
837839
}
838840
}
@@ -857,6 +859,14 @@ bool CConnman::AttemptToEvictConnection()
857859

858860
if (vEvictionCandidates.empty()) return false;
859861

862+
// If any remaining peers are preferred for eviction consider only them.
863+
// This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks)
864+
// then we probably don't want to evict it no matter what.
865+
if (std::any_of(vEvictionCandidates.begin(),vEvictionCandidates.end(),[](NodeEvictionCandidate const &n){return n.prefer_evict;})) {
866+
vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.begin(),vEvictionCandidates.end(),
867+
[](NodeEvictionCandidate const &n){return !n.prefer_evict;}),vEvictionCandidates.end());
868+
}
869+
860870
// Identify the network group with the most connections and youngest member.
861871
// (vEvictionCandidates is already sorted by reverse connect time)
862872
uint64_t naMostConnections;
@@ -937,7 +947,11 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
937947
// on all platforms. Set it again here just to be sure.
938948
SetSocketNoDelay(hSocket);
939949

940-
if (m_banman && m_banman->IsBanned(addr) && !whitelisted)
950+
int bannedlevel = m_banman ? m_banman->IsBannedLevel(addr) : 0;
951+
952+
// Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
953+
// if the only banning reason was an automatic misbehavior ban.
954+
if (!whitelisted && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
941955
{
942956
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
943957
CloseSocket(hSocket);
@@ -961,6 +975,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
961975
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
962976
pnode->AddRef();
963977
pnode->fWhitelisted = whitelisted;
978+
pnode->m_prefer_evict = bannedlevel > 0;
964979
m_msgproc->InitializeNode(pnode);
965980

966981
LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString());

src/net.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ class CNode
651651
// the network or wire types and the cleaned string used when displayed or logged.
652652
std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer);
653653
CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
654+
bool m_prefer_evict{false}; // This peer is preferred for eviction.
654655
bool fWhitelisted{false}; // This peer can bypass DoS banning.
655656
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
656657
bool fOneShot{false};

0 commit comments

Comments
 (0)