Merge #14605: Return of the Banman

18185b57c3 scripted-diff: batch-recase BanMan variables (Carl Dong)
c2e04d37f3 banman: Add, use CBanEntry ctor that takes ban reason (Carl Dong)
1ffa4ce27d banman: reformulate nBanUtil calculation (Carl Dong)
daae598feb banman: add thread annotations and mark members const where possible (Cory Fields)
84fc3fbd03 scripted-diff: batch-rename BanMan members (Cory Fields)
af3503d903 net: move BanMan to its own files (Cory Fields)
d0469b2e93 banman: pass in default ban time as a parameter (Cory Fields)
2e56702ece banman: pass the banfile path in (Cory Fields)
4c0d961eb0 banman: create and split out banman (Cory Fields)
83c1ea2e5e net: split up addresses/ban dumps in preparation for moving them (Cory Fields)
136bd7926c tests: remove member connman/peerLogic in TestingSetup (Cory Fields)
7cc2b9f678 net: Break disconnecting out of Ban() (Cory Fields)

Pull request description:

  **Old English à la Beowulf**
  ```
  Banman wæs bréme    --blaéd wíde sprang--
  Connmanes eafera    Coreum in.
  aéglaéca            léodum forstandan
  Swá bealdode        bearn Connmanes
  guma gúðum cúð      gódum daédum·
  dréah æfter dóme·   nealles druncne slóg
  ```

  **Modern English Translation**
  ```
  Banman was famed              --his renown spread wide--
  Conman's hier,                in Core-land.
  against the evil creature     defend the people
  Thus he was bold,             the son of Connman
  man famed in war,             for good deeds;
  he led his life for glory,    never, having drunk, slew
  ```

  --

  With @theuni's blessing, here is Banman, rebased. Original PR: https://github.com/bitcoin/bitcoin/pull/11457

  --

  Followup PRs:
  1. Give `CNode` a `Disconnect` method ([source](https://github.com/bitcoin/bitcoin/pull/14605#discussion_r248065847))
  2. Add a comment to `std::atomic_bool fDisconnect` in `net.h` that setting this to true will cause the node to be disconnected the next time `DisconnectNodes()` runs ([source](https://github.com/bitcoin/bitcoin/pull/14605#discussion_r248384309))

Tree-SHA512: 9c207edbf577415c22c9811113e393322d936a843d4ff265186728152a67c057779ac4d4f27b895de9729f7a53e870f828b9ebc8bcdab757520c2aebe1e9be35
This commit is contained in:
Wladimir J. van der Laan 2019-01-21 18:45:59 +01:00
commit 5baa9092c4
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
18 changed files with 455 additions and 331 deletions

View file

@ -96,6 +96,7 @@ BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
attributes.h \
banman.h \
base58.h \
bech32.h \
bloom.h \
@ -225,6 +226,7 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
banman.cpp \
bloom.cpp \
blockencodings.cpp \
blockfilter.cpp \

View file

@ -105,19 +105,18 @@ bool DeserializeFileDB(const fs::path& path, Data& data)
}
CBanDB::CBanDB()
CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path))
{
pathBanlist = GetDataDir() / "banlist.dat";
}
bool CBanDB::Write(const banmap_t& banSet)
{
return SerializeFileDB("banlist", pathBanlist, banSet);
return SerializeFileDB("banlist", m_ban_list_path, banSet);
}
bool CBanDB::Read(banmap_t& banSet)
{
return DeserializeFileDB(pathBanlist, banSet);
return DeserializeFileDB(m_ban_list_path, banSet);
}
CAddrDB::CAddrDB()

View file

@ -43,6 +43,11 @@ public:
nCreateTime = nCreateTimeIn;
}
explicit CBanEntry(int64_t n_create_time_in, BanReason ban_reason_in) : CBanEntry(n_create_time_in)
{
banReason = ban_reason_in;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
@ -92,9 +97,9 @@ public:
class CBanDB
{
private:
fs::path pathBanlist;
const fs::path m_ban_list_path;
public:
CBanDB();
explicit CBanDB(fs::path ban_list_path);
bool Write(const banmap_t& banSet);
bool Read(banmap_t& banSet);
};

197
src/banman.cpp Normal file
View file

@ -0,0 +1,197 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <banman.h>
#include <netaddress.h>
#include <ui_interface.h>
#include <util/system.h>
#include <util/time.h>
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
{
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist..."));
int64_t n_start = GetTimeMillis();
m_is_dirty = false;
banmap_t banmap;
if (m_ban_db.Read(banmap)) {
SetBanned(banmap); // thread save setter
SetBannedSetDirty(false); // no need to write down, just read data
SweepBanned(); // sweep out unused entries
LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - n_start);
} else {
LogPrintf("Invalid or missing banlist.dat; recreating\n");
SetBannedSetDirty(true); // force write
DumpBanlist();
}
}
BanMan::~BanMan()
{
DumpBanlist();
}
void BanMan::DumpBanlist()
{
SweepBanned(); // clean unused entries (if bantime has expired)
if (!BannedSetIsDirty()) return;
int64_t n_start = GetTimeMillis();
banmap_t banmap;
GetBanned(banmap);
if (m_ban_db.Write(banmap)) {
SetBannedSetDirty(false);
}
LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - n_start);
}
void BanMan::ClearBanned()
{
{
LOCK(m_cs_banned);
m_banned.clear();
m_is_dirty = true;
}
DumpBanlist(); //store banlist to disk
if (m_client_interface) m_client_interface->BannedListChanged();
}
bool BanMan::IsBanned(CNetAddr net_addr)
{
LOCK(m_cs_banned);
for (const auto& it : m_banned) {
CSubNet sub_net = it.first;
CBanEntry ban_entry = it.second;
if (sub_net.Match(net_addr) && GetTime() < ban_entry.nBanUntil) {
return true;
}
}
return false;
}
bool BanMan::IsBanned(CSubNet sub_net)
{
LOCK(m_cs_banned);
banmap_t::iterator i = m_banned.find(sub_net);
if (i != m_banned.end()) {
CBanEntry ban_entry = (*i).second;
if (GetTime() < ban_entry.nBanUntil) {
return true;
}
}
return false;
}
void BanMan::Ban(const CNetAddr& net_addr, const BanReason& ban_reason, 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);
}
void BanMan::Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch)
{
CBanEntry ban_entry(GetTime(), ban_reason);
int64_t normalized_ban_time_offset = ban_time_offset;
bool normalized_since_unix_epoch = since_unix_epoch;
if (ban_time_offset <= 0) {
normalized_ban_time_offset = m_default_ban_time;
normalized_since_unix_epoch = false;
}
ban_entry.nBanUntil = (normalized_since_unix_epoch ? 0 : GetTime()) + normalized_ban_time_offset;
{
LOCK(m_cs_banned);
if (m_banned[sub_net].nBanUntil < ban_entry.nBanUntil) {
m_banned[sub_net] = ban_entry;
m_is_dirty = true;
} else
return;
}
if (m_client_interface) m_client_interface->BannedListChanged();
//store banlist to disk immediately if user requested ban
if (ban_reason == BanReasonManuallyAdded) DumpBanlist();
}
bool BanMan::Unban(const CNetAddr& net_addr)
{
CSubNet sub_net(net_addr);
return Unban(sub_net);
}
bool BanMan::Unban(const CSubNet& sub_net)
{
{
LOCK(m_cs_banned);
if (m_banned.erase(sub_net) == 0) return false;
m_is_dirty = true;
}
if (m_client_interface) m_client_interface->BannedListChanged();
DumpBanlist(); //store banlist to disk immediately
return true;
}
void BanMan::GetBanned(banmap_t& banmap)
{
LOCK(m_cs_banned);
// Sweep the banlist so expired bans are not returned
SweepBanned();
banmap = m_banned; //create a thread safe copy
}
void BanMan::SetBanned(const banmap_t& banmap)
{
LOCK(m_cs_banned);
m_banned = banmap;
m_is_dirty = true;
}
void BanMan::SweepBanned()
{
int64_t now = GetTime();
bool notify_ui = false;
{
LOCK(m_cs_banned);
banmap_t::iterator it = m_banned.begin();
while (it != m_banned.end()) {
CSubNet sub_net = (*it).first;
CBanEntry ban_entry = (*it).second;
if (now > ban_entry.nBanUntil) {
m_banned.erase(it++);
m_is_dirty = true;
notify_ui = true;
LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, sub_net.ToString());
} else
++it;
}
}
// update UI
if (notify_ui && m_client_interface) {
m_client_interface->BannedListChanged();
}
}
bool BanMan::BannedSetIsDirty()
{
LOCK(m_cs_banned);
return m_is_dirty;
}
void BanMan::SetBannedSetDirty(bool dirty)
{
LOCK(m_cs_banned); //reuse m_banned lock for the m_is_dirty flag
m_is_dirty = dirty;
}

69
src/banman.h Normal file
View file

@ -0,0 +1,69 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_BANMAN_H
#define BITCOIN_BANMAN_H
#include <cstdint>
#include <memory>
#include <addrdb.h>
#include <fs.h>
#include <sync.h>
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
static constexpr unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
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.
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 ClearBanned();
bool IsBanned(CNetAddr net_addr);
bool IsBanned(CSubNet sub_net);
bool Unban(const CNetAddr& net_addr);
bool Unban(const CSubNet& sub_net);
void GetBanned(banmap_t& banmap);
void DumpBanlist();
private:
void SetBanned(const banmap_t& banmap);
bool BannedSetIsDirty();
//!set the "dirty" flag for the banlist
void SetBannedSetDirty(bool dirty = true);
//!clean unused entries (if bantime has expired)
void SweepBanned();
CCriticalSection m_cs_banned;
banmap_t m_banned GUARDED_BY(m_cs_banned);
bool m_is_dirty GUARDED_BY(m_cs_banned);
CClientUIInterface* m_client_interface = nullptr;
CBanDB m_ban_db;
const int64_t m_default_ban_time;
};
extern std::unique_ptr<BanMan> g_banman;
#endif

View file

@ -11,6 +11,7 @@
#include <addrman.h>
#include <amount.h>
#include <banman.h>
#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
@ -73,8 +74,12 @@ static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
// Dump addresses to banlist.dat every 15 minutes (900s)
static constexpr int DUMP_BANS_INTERVAL = 60 * 15;
std::unique_ptr<CConnman> g_connman;
std::unique_ptr<PeerLogicValidation> peerLogic;
std::unique_ptr<BanMan> g_banman;
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for
@ -199,6 +204,7 @@ void Shutdown(InitInterfaces& interfaces)
// destruct and reset all to nullptr.
peerLogic.reset();
g_connman.reset();
g_banman.reset();
g_txindex.reset();
if (g_is_mempool_loaded && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
@ -1290,11 +1296,12 @@ bool AppInitMain(InitInterfaces& interfaces)
// is not yet setup and may end up being set up twice if we
// need to reindex later.
assert(!g_banman);
g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!g_connman);
g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
CConnman& connman = *g_connman;
peerLogic.reset(new PeerLogicValidation(&connman, scheduler, gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61)));
peerLogic.reset(new PeerLogicValidation(g_connman.get(), g_banman.get(), scheduler, gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61)));
RegisterValidationInterface(peerLogic.get());
// sanitize comments per BIP-0014, format user agent and check total size
@ -1704,6 +1711,7 @@ bool AppInitMain(InitInterfaces& interfaces)
connOptions.nMaxFeeler = 1;
connOptions.nBestHeight = chain_active_height;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = g_banman.get();
connOptions.m_msgproc = peerLogic.get();
connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
@ -1749,7 +1757,7 @@ bool AppInitMain(InitInterfaces& interfaces)
connOptions.m_specified_outgoing = connect;
}
}
if (!connman.Start(scheduler, connOptions)) {
if (!g_connman->Start(scheduler, connOptions)) {
return false;
}
@ -1762,5 +1770,9 @@ bool AppInitMain(InitInterfaces& interfaces)
client->start(scheduler);
}
scheduler.scheduleEvery([]{
g_banman->DumpBanlist();
}, DUMP_BANS_INTERVAL * 1000);
return true;
}

View file

@ -6,6 +6,7 @@
#include <addrdb.h>
#include <amount.h>
#include <banman.h>
#include <chain.h>
#include <chainparams.h>
#include <init.h>
@ -122,28 +123,35 @@ public:
}
bool getBanned(banmap_t& banmap) override
{
if (g_connman) {
g_connman->GetBanned(banmap);
if (g_banman) {
g_banman->GetBanned(banmap);
return true;
}
return false;
}
bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) override
{
if (g_connman) {
g_connman->Ban(net_addr, reason, ban_time_offset);
if (g_banman) {
g_banman->Ban(net_addr, reason, ban_time_offset);
return true;
}
return false;
}
bool unban(const CSubNet& ip) override
{
if (g_connman) {
g_connman->Unban(ip);
if (g_banman) {
g_banman->Unban(ip);
return true;
}
return false;
}
bool disconnect(const CNetAddr& net_addr) override
{
if (g_connman) {
return g_connman->DisconnectNode(net_addr);
}
return false;
}
bool disconnect(NodeId id) override
{
if (g_connman) {

View file

@ -18,6 +18,7 @@
#include <tuple>
#include <vector>
class BanMan;
class CCoinControl;
class CFeeRate;
class CNodeStats;
@ -113,7 +114,10 @@ public:
//! Unban node.
virtual bool unban(const CSubNet& ip) = 0;
//! Disconnect node.
//! Disconnect node by address.
virtual bool disconnect(const CNetAddr& net_addr) = 0;
//! Disconnect node by id.
virtual bool disconnect(NodeId id) = 0;
//! Get total bytes recv.

View file

@ -9,6 +9,7 @@
#include <net.h>
#include <banman.h>
#include <chainparams.h>
#include <clientversion.h>
#include <consensus/consensus.h>
@ -41,8 +42,8 @@
#include <math.h>
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
#define DUMP_ADDRESSES_INTERVAL 900
// Dump addresses to peers.dat every 15 minutes (900s)
static constexpr int DUMP_PEERS_INTERVAL = 15 * 60;
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
@ -457,26 +458,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
return pnode;
}
void CConnman::DumpBanlist()
{
SweepBanned(); // clean unused entries (if bantime has expired)
if (!BannedSetIsDirty())
return;
int64_t nStart = GetTimeMillis();
CBanDB bandb;
banmap_t banmap;
GetBanned(banmap);
if (bandb.Write(banmap)) {
SetBannedSetDirty(false);
}
LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
}
void CNode::CloseSocketDisconnect()
{
fDisconnect = true;
@ -488,157 +469,6 @@ void CNode::CloseSocketDisconnect()
}
}
void CConnman::ClearBanned()
{
{
LOCK(cs_setBanned);
setBanned.clear();
setBannedIsDirty = true;
}
DumpBanlist(); //store banlist to disk
if(clientInterface)
clientInterface->BannedListChanged();
}
bool CConnman::IsBanned(CNetAddr ip)
{
LOCK(cs_setBanned);
for (const auto& it : setBanned) {
CSubNet subNet = it.first;
CBanEntry banEntry = it.second;
if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) {
return true;
}
}
return false;
}
bool CConnman::IsBanned(CSubNet subnet)
{
LOCK(cs_setBanned);
banmap_t::iterator i = setBanned.find(subnet);
if (i != setBanned.end())
{
CBanEntry banEntry = (*i).second;
if (GetTime() < banEntry.nBanUntil) {
return true;
}
}
return false;
}
void CConnman::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
CSubNet subNet(addr);
Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch);
}
void CConnman::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
CBanEntry banEntry(GetTime());
banEntry.banReason = banReason;
if (bantimeoffset <= 0)
{
bantimeoffset = gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME);
sinceUnixEpoch = false;
}
banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
{
LOCK(cs_setBanned);
if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) {
setBanned[subNet] = banEntry;
setBannedIsDirty = true;
}
else
return;
}
if(clientInterface)
clientInterface->BannedListChanged();
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (subNet.Match(static_cast<CNetAddr>(pnode->addr)))
pnode->fDisconnect = true;
}
}
if(banReason == BanReasonManuallyAdded)
DumpBanlist(); //store banlist to disk immediately if user requested ban
}
bool CConnman::Unban(const CNetAddr &addr) {
CSubNet subNet(addr);
return Unban(subNet);
}
bool CConnman::Unban(const CSubNet &subNet) {
{
LOCK(cs_setBanned);
if (!setBanned.erase(subNet))
return false;
setBannedIsDirty = true;
}
if(clientInterface)
clientInterface->BannedListChanged();
DumpBanlist(); //store banlist to disk immediately
return true;
}
void CConnman::GetBanned(banmap_t &banMap)
{
LOCK(cs_setBanned);
// Sweep the banlist so expired bans are not returned
SweepBanned();
banMap = setBanned; //create a thread safe copy
}
void CConnman::SetBanned(const banmap_t &banMap)
{
LOCK(cs_setBanned);
setBanned = banMap;
setBannedIsDirty = true;
}
void CConnman::SweepBanned()
{
int64_t now = GetTime();
bool notifyUI = false;
{
LOCK(cs_setBanned);
banmap_t::iterator it = setBanned.begin();
while(it != setBanned.end())
{
CSubNet subNet = (*it).first;
CBanEntry banEntry = (*it).second;
if(now > banEntry.nBanUntil)
{
setBanned.erase(it++);
setBannedIsDirty = true;
notifyUI = true;
LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString());
}
else
++it;
}
}
// update UI
if(notifyUI && clientInterface) {
clientInterface->BannedListChanged();
}
}
bool CConnman::BannedSetIsDirty()
{
LOCK(cs_setBanned);
return setBannedIsDirty;
}
void CConnman::SetBannedSetDirty(bool dirty)
{
LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag
setBannedIsDirty = dirty;
}
bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
for (const CSubNet& subnet : vWhitelistedRange) {
if (subnet.Match(addr))
@ -1107,7 +937,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
// on all platforms. Set it again here just to be sure.
SetSocketNoDelay(hSocket);
if (IsBanned(addr) && !whitelisted)
if (m_banman && m_banman->IsBanned(addr) && !whitelisted)
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
@ -1775,12 +1605,6 @@ void CConnman::DumpAddresses()
addrman.size(), GetTimeMillis() - nStart);
}
void CConnman::DumpData()
{
DumpAddresses();
DumpBanlist();
}
void CConnman::ProcessOneShot()
{
std::string strDest;
@ -2085,7 +1909,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
}
if (!pszDest) {
if (IsLocal(addrConnect) ||
FindNode(static_cast<CNetAddr>(addrConnect)) || IsBanned(addrConnect) ||
FindNode(static_cast<CNetAddr>(addrConnect)) || (m_banman && m_banman->IsBanned(addrConnect)) ||
FindNode(addrConnect.ToStringIPPort()))
return;
} else if (FindNode(std::string(pszDest)))
@ -2386,24 +2210,6 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
DumpAddresses();
}
}
if (clientInterface)
clientInterface->InitMessage(_("Loading banlist..."));
// Load addresses from banlist.dat
nStart = GetTimeMillis();
CBanDB bandb;
banmap_t banmap;
if (bandb.Read(banmap)) {
SetBanned(banmap); // thread save setter
SetBannedSetDirty(false); // no need to write down, just read data
SweepBanned(); // sweep out unused entries
LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
} else {
LogPrintf("Invalid or missing banlist.dat; recreating\n");
SetBannedSetDirty(true); // force write
DumpBanlist();
}
uiInterface.InitMessage(_("Starting network threads..."));
@ -2457,7 +2263,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this)));
// Dump network addresses
scheduler.scheduleEvery(std::bind(&CConnman::DumpData, this), DUMP_ADDRESSES_INTERVAL * 1000);
scheduler.scheduleEvery(std::bind(&CConnman::DumpAddresses, this), DUMP_PEERS_INTERVAL * 1000);
return true;
}
@ -2516,7 +2322,7 @@ void CConnman::Stop()
if (fAddressesInitialized)
{
DumpData();
DumpAddresses();
fAddressesInitialized = false;
}
@ -2643,6 +2449,25 @@ bool CConnman::DisconnectNode(const std::string& strNode)
}
return false;
}
bool CConnman::DisconnectNode(const CSubNet& subnet)
{
bool disconnected = false;
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (subnet.Match(pnode->addr)) {
pnode->fDisconnect = true;
disconnected = true;
}
}
return disconnected;
}
bool CConnman::DisconnectNode(const CNetAddr& addr)
{
return DisconnectNode(CSubNet(addr));
}
bool CConnman::DisconnectNode(NodeId id)
{
LOCK(cs_vNodes);

View file

@ -37,6 +37,7 @@
class CScheduler;
class CNode;
class BanMan;
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
static const int PING_INTERVAL = 2 * 60;
@ -85,9 +86,6 @@ static const bool DEFAULT_FORCEDNSSEED = false;
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
typedef int64_t NodeId;
struct AddedNodeInfo
@ -114,6 +112,7 @@ struct CSerializedNetMsg
std::string command;
};
class NetEventsInterface;
class CConnman
{
@ -136,6 +135,7 @@ public:
int nBestHeight = 0;
CClientUIInterface* uiInterface = nullptr;
NetEventsInterface* m_msgproc = nullptr;
BanMan* m_banman = nullptr;
unsigned int nSendBufferMaxSize = 0;
unsigned int nReceiveFloodSize = 0;
uint64_t nMaxOutboundTimeframe = 0;
@ -158,6 +158,7 @@ public:
nMaxFeeler = connOptions.nMaxFeeler;
nBestHeight = connOptions.nBestHeight;
clientInterface = connOptions.uiInterface;
m_banman = connOptions.m_banman;
m_msgproc = connOptions.m_msgproc;
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
nReceiveFloodSize = connOptions.nReceiveFloodSize;
@ -238,30 +239,6 @@ public:
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
std::vector<CAddress> GetAddresses();
// 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.
void Ban(const CNetAddr& netAddr, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
void Ban(const CSubNet& subNet, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
void ClearBanned(); // needed for unit testing
bool IsBanned(CNetAddr ip);
bool IsBanned(CSubNet subnet);
bool Unban(const CNetAddr &ip);
bool Unban(const CSubNet &ip);
void GetBanned(banmap_t &banmap);
void SetBanned(const banmap_t &banmap);
// This allows temporarily exceeding nMaxOutbound, with the goal of finding
// a peer that is better than all our current peers.
void SetTryNewOutboundPeer(bool flag);
@ -282,6 +259,8 @@ public:
size_t GetNodeCount(NumConnections num);
void GetNodeStats(std::vector<CNodeStats>& vstats);
bool DisconnectNode(const std::string& node);
bool DisconnectNode(const CSubNet& subnet);
bool DisconnectNode(const CNetAddr& addr);
bool DisconnectNode(NodeId id);
ServiceFlags GetLocalServices() const;
@ -368,15 +347,7 @@ private:
NodeId GetNewNodeId();
size_t SocketSendData(CNode *pnode) const;
//!check is the banlist has unwritten changes
bool BannedSetIsDirty();
//!set the "dirty" flag for the banlist
void SetBannedSetDirty(bool dirty=true);
//!clean unused entries (if bantime has expired)
void SweepBanned();
void DumpAddresses();
void DumpData();
void DumpBanlist();
// Network stats
void RecordBytesRecv(uint64_t bytes);
@ -409,9 +380,6 @@ private:
std::vector<ListenSocket> vhListenSocket;
std::atomic<bool> fNetworkActive{true};
banmap_t setBanned GUARDED_BY(cs_setBanned);
CCriticalSection cs_setBanned;
bool setBannedIsDirty GUARDED_BY(cs_setBanned){false};
bool fAddressesInitialized{false};
CAddrMan addrman;
std::deque<std::string> vOneShots GUARDED_BY(cs_vOneShots);
@ -437,6 +405,7 @@ private:
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
NetEventsInterface* m_msgproc;
BanMan* m_banman;
/** SipHasher seeds for deterministic randomness */
const uint64_t nSeed0, nSeed1;
@ -466,6 +435,7 @@ private:
friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
extern std::unique_ptr<BanMan> g_banman;
void Discover();
void StartMapPort();
void InterruptMapPort();

View file

@ -6,6 +6,7 @@
#include <net_processing.h>
#include <addrman.h>
#include <banman.h>
#include <arith_uint256.h>
#include <blockencodings.h>
#include <chainparams.h>
@ -841,9 +842,8 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler, bool enable_bip61)
: connman(connmanIn), m_stale_tip_check_time(0), m_enable_bip61(enable_bip61) {
PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler &scheduler, bool enable_bip61)
: connman(connmanIn), m_banman(banman), m_stale_tip_check_time(0), m_enable_bip61(enable_bip61) {
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@ -2943,7 +2943,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool PeerLogicValidation::SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pnode->GetId());
@ -2961,14 +2961,16 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool en
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString());
else if (pnode->m_manual_connection)
LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
else {
else if (pnode->addr.IsLocal()) {
// Disconnect but don't ban _this_ local node
LogPrintf("Warning: disconnecting but not banning local peer %s!\n", pnode->addr.ToString());
pnode->fDisconnect = true;
if (pnode->addr.IsLocal())
LogPrintf("Warning: not banning local peer %s!\n", pnode->addr.ToString());
else
{
connman->Ban(pnode->addr, BanReasonNodeMisbehaving);
} else {
// Disconnect and ban all nodes sharing the address
if (m_banman) {
m_banman->Ban(pnode->addr, BanReasonNodeMisbehaving);
}
connman->DisconnectNode(pnode->addr);
}
return true;
}
@ -3092,7 +3094,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
LOCK(cs_main);
SendRejectsAndCheckIfBanned(pfrom, connman, m_enable_bip61);
SendRejectsAndCheckIfBanned(pfrom, m_enable_bip61);
return fMoreWork;
}
@ -3293,8 +3295,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!lockMain)
return true;
if (SendRejectsAndCheckIfBanned(pto, connman, m_enable_bip61))
return true;
if (SendRejectsAndCheckIfBanned(pto, m_enable_bip61)) return true;
CNodeState &state = *State(pto->GetId());
// Address refresh broadcast

View file

@ -23,9 +23,11 @@ static constexpr bool DEFAULT_ENABLE_BIP61{false};
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
CConnman* const connman;
BanMan* const m_banman;
bool SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
public:
explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler, bool enable_bip61);
PeerLogicValidation(CConnman* connman, BanMan* banman, CScheduler &scheduler, bool enable_bip61);
/**
* Overridden from CValidationInterface.

View file

@ -1213,13 +1213,13 @@ void RPCConsole::banSelectedNode(int bantime)
// Get currently selected peer address
int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id);
if(detailNodeRow < 0)
return;
if (detailNodeRow < 0) return;
// Find possible nodes, ban it and clear the selected node
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if(stats) {
if (stats) {
m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
m_node.disconnect(stats->nodeStats.addr);
}
}
clearSelectedNode();

View file

@ -4,6 +4,7 @@
#include <rpc/server.h>
#include <banman.h>
#include <chainparams.h>
#include <clientversion.h>
#include <core_io.h>
@ -531,8 +532,9 @@ static UniValue setban(const JSONRPCRequest& request)
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (!g_banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
CSubNet subNet;
CNetAddr netAddr;
@ -554,8 +556,9 @@ static UniValue setban(const JSONRPCRequest& request)
if (strCommand == "add")
{
if (isSubnet ? g_connman->IsBanned(subNet) : g_connman->IsBanned(netAddr))
if (isSubnet ? g_banman->IsBanned(subNet) : g_banman->IsBanned(netAddr)) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
}
int64_t banTime = 0; //use standard bantime if not specified
if (!request.params[2].isNull())
@ -565,13 +568,24 @@ static UniValue setban(const JSONRPCRequest& request)
if (request.params[3].isTrue())
absolute = true;
isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
if (isSubnet) {
g_banman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute);
if (g_connman) {
g_connman->DisconnectNode(subNet);
}
} else {
g_banman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
if (g_connman) {
g_connman->DisconnectNode(netAddr);
}
}
}
else if(strCommand == "remove")
{
if (!( isSubnet ? g_connman->Unban(subNet) : g_connman->Unban(netAddr) ))
if (!( isSubnet ? g_banman->Unban(subNet) : g_banman->Unban(netAddr) )) {
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously banned.");
}
}
return NullUniValue;
}
@ -587,11 +601,12 @@ static UniValue listbanned(const JSONRPCRequest& request)
+ HelpExampleRpc("listbanned", "")
);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if(!g_banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
banmap_t banMap;
g_connman->GetBanned(banMap);
g_banman->GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR);
for (const auto& entry : banMap)
@ -620,10 +635,11 @@ static UniValue clearbanned(const JSONRPCRequest& request)
+ HelpExampleCli("clearbanned", "")
+ HelpExampleRpc("clearbanned", "")
);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (!g_banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
g_connman->ClearBanned();
g_banman->ClearBanned();
return NullUniValue;
}

View file

@ -4,6 +4,7 @@
// Unit tests for denial-of-service detection/prevention code
#include <banman.h>
#include <chainparams.h>
#include <keystore.h>
#include <net.h>
@ -20,6 +21,23 @@
#include <boost/test/unit_test.hpp>
struct CConnmanTest : public CConnman {
using CConnman::CConnman;
void AddNode(CNode& node)
{
LOCK(cs_vNodes);
vNodes.push_back(&node);
}
void ClearNodes()
{
LOCK(cs_vNodes);
for (CNode* node : vNodes) {
delete node;
}
vNodes.clear();
}
};
// Tests these internal-to-net_processing.cpp methods:
extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
extern void EraseOrphansFor(NodeId peer);
@ -57,6 +75,8 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler, false);
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@ -109,7 +129,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic, CConnmanTest* connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
@ -120,11 +140,14 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidat
node.nVersion = 1;
node.fSuccessfullyConnected = true;
CConnmanTest::AddNode(node);
connman->AddNode(node);
}
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337);
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler, false);
const Consensus::Params& consensusParams = Params().GetConsensus();
constexpr int nMaxOutbound = 8;
CConnman::Options options;
@ -137,7 +160,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// Mock some outbound peers
for (int i=0; i<nMaxOutbound; ++i) {
AddRandomOutboundPeer(vNodes, *peerLogic);
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
}
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
@ -162,7 +185,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// If we add one more peer, something should get marked for eviction
// on the next check (since we're mocking the time to be in the future, the
// required time connected check should be satisfied).
AddRandomOutboundPeer(vNodes, *peerLogic);
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
for (int i=0; i<nMaxOutbound; ++i) {
@ -189,13 +212,16 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
peerLogic->FinalizeNode(node->GetId(), dummy);
}
CConnmanTest::ClearNodes();
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(DoS_banning)
{
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler, false);
connman->ClearBanned();
banman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true);
dummyNode1.SetSendVersion(PROTOCOL_VERSION);
@ -210,8 +236,8 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(connman->IsBanned(addr1));
BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
BOOST_CHECK(banman->IsBanned(addr1));
BOOST_CHECK(!banman->IsBanned(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);
@ -227,8 +253,8 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
LOCK2(cs_main, dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet...
BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be
BOOST_CHECK(!banman->IsBanned(addr2)); // 2 not banned yet...
BOOST_CHECK(banman->IsBanned(addr1)); // ... but 1 still should be
{
LOCK(cs_main);
Misbehaving(dummyNode2.GetId(), 50);
@ -237,7 +263,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
LOCK2(cs_main, dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
BOOST_CHECK(connman->IsBanned(addr2));
BOOST_CHECK(banman->IsBanned(addr2));
bool dummy;
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
@ -246,8 +272,11 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
BOOST_AUTO_TEST_CASE(DoS_banscore)
{
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler, false);
connman->ClearBanned();
banman->ClearBanned();
gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true);
@ -263,7 +292,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(!connman->IsBanned(addr1));
BOOST_CHECK(!banman->IsBanned(addr1));
{
LOCK(cs_main);
Misbehaving(dummyNode1.GetId(), 10);
@ -272,7 +301,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(!connman->IsBanned(addr1));
BOOST_CHECK(!banman->IsBanned(addr1));
{
LOCK(cs_main);
Misbehaving(dummyNode1.GetId(), 1);
@ -281,7 +310,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(connman->IsBanned(addr1));
BOOST_CHECK(banman->IsBanned(addr1));
gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD));
bool dummy;
@ -290,8 +319,11 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler, false);
connman->ClearBanned();
banman->ClearBanned();
int64_t nStartTime = GetTime();
SetMockTime(nStartTime); // Overrides future calls to GetTime()
@ -310,13 +342,13 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
LOCK2(cs_main, dummyNode.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
}
BOOST_CHECK(connman->IsBanned(addr));
BOOST_CHECK(banman->IsBanned(addr));
SetMockTime(nStartTime+60*60);
BOOST_CHECK(connman->IsBanned(addr));
BOOST_CHECK(banman->IsBanned(addr));
SetMockTime(nStartTime+60*60*24+1);
BOOST_CHECK(!connman->IsBanned(addr));
BOOST_CHECK(!banman->IsBanned(addr));
bool dummy;
peerLogic->FinalizeNode(dummyNode.GetId(), dummy);

View file

@ -4,6 +4,7 @@
#include <test/test_bitcoin.h>
#include <banman.h>
#include <chainparams.h>
#include <consensus/consensus.h>
#include <consensus/params.h>
@ -24,21 +25,6 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
FastRandomContext g_insecure_rand_ctx;
void CConnmanTest::AddNode(CNode& node)
{
LOCK(g_connman->cs_vNodes);
g_connman->vNodes.push_back(&node);
}
void CConnmanTest::ClearNodes()
{
LOCK(g_connman->cs_vNodes);
for (const CNode* node : g_connman->vNodes) {
delete node;
}
g_connman->vNodes.clear();
}
std::ostream& operator<<(std::ostream& os, const uint256& num)
{
os << num.ToString();
@ -108,9 +94,9 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
nScriptCheckThreads = 3;
for (int i=0; i < nScriptCheckThreads-1; i++)
threadGroup.create_thread(&ThreadScriptCheck);
g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
connman = g_connman.get();
peerLogic.reset(new PeerLogicValidation(connman, scheduler, /*enable_bip61=*/true));
}
TestingSetup::~TestingSetup()
@ -120,7 +106,7 @@ TestingSetup::~TestingSetup()
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
g_connman.reset();
peerLogic.reset();
g_banman.reset();
UnloadBlockIndex();
pcoinsTip.reset();
pcoinsdbview.reset();

View file

@ -68,17 +68,11 @@ private:
*/
class CConnman;
class CNode;
struct CConnmanTest {
static void AddNode(CNode& node);
static void ClearNodes();
};
class PeerLogicValidation;
struct TestingSetup : public BasicTestingSetup {
boost::thread_group threadGroup;
CConnman* connman;
CScheduler scheduler;
std::unique_ptr<PeerLogicValidation> peerLogic;
explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~TestingSetup();

View file

@ -4,6 +4,7 @@
#define BOOST_TEST_MODULE Bitcoin Test Suite
#include <banman.h>
#include <net.h>
#include <memory>
@ -11,6 +12,7 @@
#include <boost/test/unit_test.hpp>
std::unique_ptr<CConnman> g_connman;
std::unique_ptr<BanMan> g_banman;
[[noreturn]] void Shutdown(void* parg)
{