From af3503d903b1a608cd212e2d74b274103199078c Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 5 Oct 2017 16:40:43 -0400 Subject: net: move BanMan to its own files --- src/Makefile.am | 2 + src/banman.cpp | 195 +++++++++++++++++++++++++++++++++++++ src/banman.h | 69 +++++++++++++ src/init.cpp | 1 + src/interfaces/node.cpp | 1 + src/interfaces/node.h | 1 + src/net.cpp | 190 +----------------------------------- src/net.h | 49 +--------- src/net_processing.cpp | 1 + src/rpc/net.cpp | 1 + src/test/denialofservice_tests.cpp | 1 + src/test/test_bitcoin.cpp | 1 + src/test/test_bitcoin_main.cpp | 1 + 13 files changed, 276 insertions(+), 237 deletions(-) create mode 100644 src/banman.cpp create mode 100644 src/banman.h diff --git a/src/Makefile.am b/src/Makefile.am index 09daaebd23..4b07f06c95 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/banman.cpp b/src/banman.cpp new file mode 100644 index 0000000000..59cfe26318 --- /dev/null +++ b/src/banman.cpp @@ -0,0 +1,195 @@ +// 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 + +#include +#include +#include +#include + + +BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time) + : clientInterface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time) +{ + if (clientInterface) clientInterface->InitMessage(_("Loading banlist...")); + + int64_t nStart = GetTimeMillis(); + setBannedIsDirty = 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() - nStart); + } 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 nStart = 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() - nStart); +} + +void BanMan::ClearBanned() +{ + { + LOCK(cs_setBanned); + setBanned.clear(); + setBannedIsDirty = true; + } + DumpBanlist(); //store banlist to disk + if (clientInterface) clientInterface->BannedListChanged(); +} + +bool BanMan::IsBanned(CNetAddr netAddr) +{ + LOCK(cs_setBanned); + for (const auto& it : setBanned) { + CSubNet subNet = it.first; + CBanEntry banEntry = it.second; + + if (subNet.Match(netAddr) && GetTime() < banEntry.nBanUntil) { + return true; + } + } + return false; +} + +bool BanMan::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 BanMan::Ban(const CNetAddr& netAddr, const BanReason& banReason, int64_t bantimeoffset, bool sinceUnixEpoch) +{ + CSubNet subNet(netAddr); + Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); +} + +void BanMan::Ban(const CSubNet& subNet, const BanReason& banReason, int64_t bantimeoffset, bool sinceUnixEpoch) +{ + CBanEntry banEntry(GetTime()); + banEntry.banReason = banReason; + if (bantimeoffset <= 0) { + bantimeoffset = m_default_ban_time; + 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(); + + //store banlist to disk immediately if user requested ban + if (banReason == BanReasonManuallyAdded) DumpBanlist(); +} + +bool BanMan::Unban(const CNetAddr& netAddr) +{ + CSubNet subNet(netAddr); + return Unban(subNet); +} + +bool BanMan::Unban(const CSubNet& subNet) +{ + { + LOCK(cs_setBanned); + if (setBanned.erase(subNet) == 0) return false; + setBannedIsDirty = true; + } + if (clientInterface) clientInterface->BannedListChanged(); + DumpBanlist(); //store banlist to disk immediately + return true; +} + +void BanMan::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 BanMan::SetBanned(const banmap_t& banMap) +{ + LOCK(cs_setBanned); + setBanned = banMap; + setBannedIsDirty = true; +} + +void BanMan::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 BanMan::BannedSetIsDirty() +{ + LOCK(cs_setBanned); + return setBannedIsDirty; +} + +void BanMan::SetBannedSetDirty(bool dirty) +{ + LOCK(cs_setBanned); //reuse setBanned lock for the setBannedIsDirty flag + setBannedIsDirty = dirty; +} diff --git a/src/banman.h b/src/banman.h new file mode 100644 index 0000000000..898ae85197 --- /dev/null +++ b/src/banman.h @@ -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 +#include + +#include +#include +#include + +// 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& netAddr, const BanReason& banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + void Ban(const CSubNet& subNet, const BanReason& banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + void ClearBanned(); // needed for unit testing + bool IsBanned(CNetAddr netAddr); + bool IsBanned(CSubNet subNet); + bool Unban(const CNetAddr& netAddr); + bool Unban(const CSubNet& subNet); + 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(); + + banmap_t setBanned; + CCriticalSection cs_setBanned; + bool setBannedIsDirty; + CClientUIInterface* clientInterface = nullptr; + CBanDB m_ban_db; + int64_t m_default_ban_time; +}; + +extern std::unique_ptr g_banman; +#endif diff --git a/src/init.cpp b/src/init.cpp index 0f59dfbef5..77d0505610 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index c535b29315..c574f960e6 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 6aa8ce0797..54c2d78338 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -18,6 +18,7 @@ #include #include +class BanMan; class CCoinControl; class CFeeRate; class CNodeStats; diff --git a/src/net.cpp b/src/net.cpp index f6491bc8db..0490ccd6db 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -457,25 +458,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo return pnode; } -void BanMan::DumpBanlist() -{ - SweepBanned(); // clean unused entries (if bantime has expired) - - if (!BannedSetIsDirty()) - return; - - int64_t nStart = 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() - nStart); -} - void CNode::CloseSocketDisconnect() { fDisconnect = true; @@ -487,150 +469,6 @@ void CNode::CloseSocketDisconnect() } } -void BanMan::ClearBanned() -{ - { - LOCK(cs_setBanned); - setBanned.clear(); - setBannedIsDirty = true; - } - DumpBanlist(); //store banlist to disk - if(clientInterface) - clientInterface->BannedListChanged(); -} - -bool BanMan::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 BanMan::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 BanMan::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { - CSubNet subNet(addr); - Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); -} - -void BanMan::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { - CBanEntry banEntry(GetTime()); - banEntry.banReason = banReason; - if (bantimeoffset <= 0) - { - bantimeoffset = m_default_ban_time; - 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(); - if(banReason == BanReasonManuallyAdded) - DumpBanlist(); //store banlist to disk immediately if user requested ban -} - -bool BanMan::Unban(const CNetAddr &addr) { - CSubNet subNet(addr); - return Unban(subNet); -} - -bool BanMan::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 BanMan::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 BanMan::SetBanned(const banmap_t &banMap) -{ - LOCK(cs_setBanned); - setBanned = banMap; - setBannedIsDirty = true; -} - -void BanMan::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 BanMan::BannedSetIsDirty() -{ - LOCK(cs_setBanned); - return setBannedIsDirty; -} - -void BanMan::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)) @@ -2430,32 +2268,6 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) return true; } -BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time) : clientInterface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time) -{ - if (clientInterface) clientInterface->InitMessage(_("Loading banlist...")); - - int64_t nStart = GetTimeMillis(); - setBannedIsDirty = 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() - nStart); - } else { - LogPrintf("Invalid or missing banlist.dat; recreating\n"); - SetBannedSetDirty(true); // force write - DumpBanlist(); - } -} - -BanMan::~BanMan() -{ - DumpBanlist(); -} - class CNetCleanup { public: diff --git a/src/net.h b/src/net.h index a6a536d68a..3606b4d7ba 100644 --- a/src/net.h +++ b/src/net.h @@ -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 constexpr unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban - typedef int64_t NodeId; struct AddedNodeInfo @@ -115,51 +113,6 @@ struct CSerializedNetMsg }; -class BanMan -{ -public: - // 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(); - BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time); - 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 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(); - - banmap_t setBanned; - CCriticalSection cs_setBanned; - bool setBannedIsDirty; - CClientUIInterface* clientInterface = nullptr; - CBanDB m_ban_db; - int64_t m_default_ban_time; -}; - class NetEventsInterface; class CConnman { diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 6dd6368f74..62b7d4e966 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 19561c4fc7..7994d3b125 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 29bb34bddc..e5d62a3ab2 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -4,6 +4,7 @@ // Unit tests for denial-of-service detection/prevention code +#include #include #include #include diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 75bd8db67e..ad7fa01710 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include diff --git a/src/test/test_bitcoin_main.cpp b/src/test/test_bitcoin_main.cpp index caa35c0efc..46b63b93b4 100644 --- a/src/test/test_bitcoin_main.cpp +++ b/src/test/test_bitcoin_main.cpp @@ -4,6 +4,7 @@ #define BOOST_TEST_MODULE Bitcoin Test Suite +#include #include #include -- cgit v1.2.3