Skip to content

Commit 5baa909

Browse files
committed
Merge #14605: Return of the Banman
18185b5 scripted-diff: batch-recase BanMan variables (Carl Dong) c2e04d3 banman: Add, use CBanEntry ctor that takes ban reason (Carl Dong) 1ffa4ce banman: reformulate nBanUtil calculation (Carl Dong) daae598 banman: add thread annotations and mark members const where possible (Cory Fields) 84fc3fb scripted-diff: batch-rename BanMan members (Cory Fields) af3503d net: move BanMan to its own files (Cory Fields) d0469b2 banman: pass in default ban time as a parameter (Cory Fields) 2e56702 banman: pass the banfile path in (Cory Fields) 4c0d961 banman: create and split out banman (Cory Fields) 83c1ea2 net: split up addresses/ban dumps in preparation for moving them (Cory Fields) 136bd79 tests: remove member connman/peerLogic in TestingSetup (Cory Fields) 7cc2b9f 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: bitcoin/bitcoin#11457 -- Followup PRs: 1. Give `CNode` a `Disconnect` method ([source](bitcoin/bitcoin#14605 (comment))) 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](bitcoin/bitcoin#14605 (comment))) Tree-SHA512: 9c207edbf577415c22c9811113e393322d936a843d4ff265186728152a67c057779ac4d4f27b895de9729f7a53e870f828b9ebc8bcdab757520c2aebe1e9be35
2 parents 0f1576a + 18185b5 commit 5baa909

18 files changed

+456
-332
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ BITCOIN_CORE_H = \
9696
addrdb.h \
9797
addrman.h \
9898
attributes.h \
99+
banman.h \
99100
base58.h \
100101
bech32.h \
101102
bloom.h \
@@ -225,6 +226,7 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
225226
libbitcoin_server_a_SOURCES = \
226227
addrdb.cpp \
227228
addrman.cpp \
229+
banman.cpp \
228230
bloom.cpp \
229231
blockencodings.cpp \
230232
blockfilter.cpp \

src/addrdb.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,18 @@ bool DeserializeFileDB(const fs::path& path, Data& data)
105105

106106
}
107107

108-
CBanDB::CBanDB()
108+
CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path))
109109
{
110-
pathBanlist = GetDataDir() / "banlist.dat";
111110
}
112111

113112
bool CBanDB::Write(const banmap_t& banSet)
114113
{
115-
return SerializeFileDB("banlist", pathBanlist, banSet);
114+
return SerializeFileDB("banlist", m_ban_list_path, banSet);
116115
}
117116

118117
bool CBanDB::Read(banmap_t& banSet)
119118
{
120-
return DeserializeFileDB(pathBanlist, banSet);
119+
return DeserializeFileDB(m_ban_list_path, banSet);
121120
}
122121

123122
CAddrDB::CAddrDB()

src/addrdb.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ class CBanEntry
4343
nCreateTime = nCreateTimeIn;
4444
}
4545

46+
explicit CBanEntry(int64_t n_create_time_in, BanReason ban_reason_in) : CBanEntry(n_create_time_in)
47+
{
48+
banReason = ban_reason_in;
49+
}
50+
4651
ADD_SERIALIZE_METHODS;
4752

4853
template <typename Stream, typename Operation>
@@ -92,9 +97,9 @@ class CAddrDB
9297
class CBanDB
9398
{
9499
private:
95-
fs::path pathBanlist;
100+
const fs::path m_ban_list_path;
96101
public:
97-
CBanDB();
102+
explicit CBanDB(fs::path ban_list_path);
98103
bool Write(const banmap_t& banSet);
99104
bool Read(banmap_t& banSet);
100105
};

src/banman.cpp

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2017 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#include <banman.h>
7+
8+
#include <netaddress.h>
9+
#include <ui_interface.h>
10+
#include <util/system.h>
11+
#include <util/time.h>
12+
13+
14+
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
15+
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
16+
{
17+
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist..."));
18+
19+
int64_t n_start = GetTimeMillis();
20+
m_is_dirty = false;
21+
banmap_t banmap;
22+
if (m_ban_db.Read(banmap)) {
23+
SetBanned(banmap); // thread save setter
24+
SetBannedSetDirty(false); // no need to write down, just read data
25+
SweepBanned(); // sweep out unused entries
26+
27+
LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n",
28+
banmap.size(), GetTimeMillis() - n_start);
29+
} else {
30+
LogPrintf("Invalid or missing banlist.dat; recreating\n");
31+
SetBannedSetDirty(true); // force write
32+
DumpBanlist();
33+
}
34+
}
35+
36+
BanMan::~BanMan()
37+
{
38+
DumpBanlist();
39+
}
40+
41+
void BanMan::DumpBanlist()
42+
{
43+
SweepBanned(); // clean unused entries (if bantime has expired)
44+
45+
if (!BannedSetIsDirty()) return;
46+
47+
int64_t n_start = GetTimeMillis();
48+
49+
banmap_t banmap;
50+
GetBanned(banmap);
51+
if (m_ban_db.Write(banmap)) {
52+
SetBannedSetDirty(false);
53+
}
54+
55+
LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
56+
banmap.size(), GetTimeMillis() - n_start);
57+
}
58+
59+
void BanMan::ClearBanned()
60+
{
61+
{
62+
LOCK(m_cs_banned);
63+
m_banned.clear();
64+
m_is_dirty = true;
65+
}
66+
DumpBanlist(); //store banlist to disk
67+
if (m_client_interface) m_client_interface->BannedListChanged();
68+
}
69+
70+
bool BanMan::IsBanned(CNetAddr net_addr)
71+
{
72+
LOCK(m_cs_banned);
73+
for (const auto& it : m_banned) {
74+
CSubNet sub_net = it.first;
75+
CBanEntry ban_entry = it.second;
76+
77+
if (sub_net.Match(net_addr) && GetTime() < ban_entry.nBanUntil) {
78+
return true;
79+
}
80+
}
81+
return false;
82+
}
83+
84+
bool BanMan::IsBanned(CSubNet sub_net)
85+
{
86+
LOCK(m_cs_banned);
87+
banmap_t::iterator i = m_banned.find(sub_net);
88+
if (i != m_banned.end()) {
89+
CBanEntry ban_entry = (*i).second;
90+
if (GetTime() < ban_entry.nBanUntil) {
91+
return true;
92+
}
93+
}
94+
return false;
95+
}
96+
97+
void BanMan::Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch)
98+
{
99+
CSubNet sub_net(net_addr);
100+
Ban(sub_net, ban_reason, ban_time_offset, since_unix_epoch);
101+
}
102+
103+
void BanMan::Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch)
104+
{
105+
CBanEntry ban_entry(GetTime(), ban_reason);
106+
107+
int64_t normalized_ban_time_offset = ban_time_offset;
108+
bool normalized_since_unix_epoch = since_unix_epoch;
109+
if (ban_time_offset <= 0) {
110+
normalized_ban_time_offset = m_default_ban_time;
111+
normalized_since_unix_epoch = false;
112+
}
113+
ban_entry.nBanUntil = (normalized_since_unix_epoch ? 0 : GetTime()) + normalized_ban_time_offset;
114+
115+
{
116+
LOCK(m_cs_banned);
117+
if (m_banned[sub_net].nBanUntil < ban_entry.nBanUntil) {
118+
m_banned[sub_net] = ban_entry;
119+
m_is_dirty = true;
120+
} else
121+
return;
122+
}
123+
if (m_client_interface) m_client_interface->BannedListChanged();
124+
125+
//store banlist to disk immediately if user requested ban
126+
if (ban_reason == BanReasonManuallyAdded) DumpBanlist();
127+
}
128+
129+
bool BanMan::Unban(const CNetAddr& net_addr)
130+
{
131+
CSubNet sub_net(net_addr);
132+
return Unban(sub_net);
133+
}
134+
135+
bool BanMan::Unban(const CSubNet& sub_net)
136+
{
137+
{
138+
LOCK(m_cs_banned);
139+
if (m_banned.erase(sub_net) == 0) return false;
140+
m_is_dirty = true;
141+
}
142+
if (m_client_interface) m_client_interface->BannedListChanged();
143+
DumpBanlist(); //store banlist to disk immediately
144+
return true;
145+
}
146+
147+
void BanMan::GetBanned(banmap_t& banmap)
148+
{
149+
LOCK(m_cs_banned);
150+
// Sweep the banlist so expired bans are not returned
151+
SweepBanned();
152+
banmap = m_banned; //create a thread safe copy
153+
}
154+
155+
void BanMan::SetBanned(const banmap_t& banmap)
156+
{
157+
LOCK(m_cs_banned);
158+
m_banned = banmap;
159+
m_is_dirty = true;
160+
}
161+
162+
void BanMan::SweepBanned()
163+
{
164+
int64_t now = GetTime();
165+
bool notify_ui = false;
166+
{
167+
LOCK(m_cs_banned);
168+
banmap_t::iterator it = m_banned.begin();
169+
while (it != m_banned.end()) {
170+
CSubNet sub_net = (*it).first;
171+
CBanEntry ban_entry = (*it).second;
172+
if (now > ban_entry.nBanUntil) {
173+
m_banned.erase(it++);
174+
m_is_dirty = true;
175+
notify_ui = true;
176+
LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, sub_net.ToString());
177+
} else
178+
++it;
179+
}
180+
}
181+
// update UI
182+
if (notify_ui && m_client_interface) {
183+
m_client_interface->BannedListChanged();
184+
}
185+
}
186+
187+
bool BanMan::BannedSetIsDirty()
188+
{
189+
LOCK(m_cs_banned);
190+
return m_is_dirty;
191+
}
192+
193+
void BanMan::SetBannedSetDirty(bool dirty)
194+
{
195+
LOCK(m_cs_banned); //reuse m_banned lock for the m_is_dirty flag
196+
m_is_dirty = dirty;
197+
}

src/banman.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2017 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
#ifndef BITCOIN_BANMAN_H
6+
#define BITCOIN_BANMAN_H
7+
8+
#include <cstdint>
9+
#include <memory>
10+
11+
#include <addrdb.h>
12+
#include <fs.h>
13+
#include <sync.h>
14+
15+
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
16+
static constexpr unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
17+
18+
class CClientUIInterface;
19+
class CNetAddr;
20+
class CSubNet;
21+
22+
// Denial-of-service detection/prevention
23+
// The idea is to detect peers that are behaving
24+
// badly and disconnect/ban them, but do it in a
25+
// one-coding-mistake-won't-shatter-the-entire-network
26+
// way.
27+
// IMPORTANT: There should be nothing I can give a
28+
// node that it will forward on that will make that
29+
// node's peers drop it. If there is, an attacker
30+
// can isolate a node and/or try to split the network.
31+
// Dropping a node for sending stuff that is invalid
32+
// now but might be valid in a later version is also
33+
// dangerous, because it can cause a network split
34+
// between nodes running old code and nodes running
35+
// new code.
36+
37+
class BanMan
38+
{
39+
public:
40+
~BanMan();
41+
BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time);
42+
void Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
43+
void Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
44+
void ClearBanned();
45+
bool IsBanned(CNetAddr net_addr);
46+
bool IsBanned(CSubNet sub_net);
47+
bool Unban(const CNetAddr& net_addr);
48+
bool Unban(const CSubNet& sub_net);
49+
void GetBanned(banmap_t& banmap);
50+
void DumpBanlist();
51+
52+
private:
53+
void SetBanned(const banmap_t& banmap);
54+
bool BannedSetIsDirty();
55+
//!set the "dirty" flag for the banlist
56+
void SetBannedSetDirty(bool dirty = true);
57+
//!clean unused entries (if bantime has expired)
58+
void SweepBanned();
59+
60+
CCriticalSection m_cs_banned;
61+
banmap_t m_banned GUARDED_BY(m_cs_banned);
62+
bool m_is_dirty GUARDED_BY(m_cs_banned);
63+
CClientUIInterface* m_client_interface = nullptr;
64+
CBanDB m_ban_db;
65+
const int64_t m_default_ban_time;
66+
};
67+
68+
extern std::unique_ptr<BanMan> g_banman;
69+
#endif

0 commit comments

Comments
 (0)