Skip to content

Commit 67be6d7

Browse files
committed
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e Add functional tests for flexible whitebind/list (nicolas.dorier) d541fa3 Replace the use of fWhitelisted by permission checks (nicolas.dorier) ecd5cf7 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier) e5b26de Make whitebind/whitelist permissions more flexible (nicolas.dorier) Pull request description: # Motivation In 0.19, bloom filter will be disabled by default. I tried to make [a PR](bitcoin/bitcoin#16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`. Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum. It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes. When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](bitcoin/bitcoin#16176 (comment)) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute. Doing so will also make follow up idea very easy to implement in a backward compatible way. # Implementation details The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`. The following permissions exists: * ForceRelay * Relay * NoBan * BloomFilter * Mempool Example: * `[email protected]/32`. * `-whitebind=bloomfilter,relay,[email protected]:10020`. If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible) When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`. To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node. `-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`. # Follow up idea Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way: * Changing `connect` at rpc and config file level to understand the permissions flags. * Changing the permissions of a peer at RPC level. ACKs for top commit: laanwj: re-ACK c5b404e Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2 parents a7aa809 + c5b404e commit 67be6d7

File tree

13 files changed

+450
-66
lines changed

13 files changed

+450
-66
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ BITCOIN_CORE_H = \
150150
merkleblock.h \
151151
miner.h \
152152
net.h \
153+
net_permissions.h \
153154
net_processing.h \
154155
netaddress.h \
155156
netbase.h \
@@ -454,6 +455,7 @@ libbitcoin_common_a_SOURCES = \
454455
merkleblock.cpp \
455456
netaddress.cpp \
456457
netbase.cpp \
458+
net_permissions.cpp \
457459
outputtype.cpp \
458460
policy/feerate.cpp \
459461
policy/policy.cpp \

src/init.cpp

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <key.h>
2828
#include <miner.h>
2929
#include <net.h>
30+
#include <net_permissions.h>
3031
#include <net_processing.h>
3132
#include <netbase.h>
3233
#include <policy/feerate.h>
@@ -1775,21 +1776,16 @@ bool AppInitMain(InitInterfaces& interfaces)
17751776
connOptions.vBinds.push_back(addrBind);
17761777
}
17771778
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
1778-
CService addrBind;
1779-
if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
1780-
return InitError(ResolveErrMsg("whitebind", strBind));
1781-
}
1782-
if (addrBind.GetPort() == 0) {
1783-
return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind));
1784-
}
1785-
connOptions.vWhiteBinds.push_back(addrBind);
1779+
NetWhitebindPermissions whitebind;
1780+
std::string error;
1781+
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
1782+
connOptions.vWhiteBinds.push_back(whitebind);
17861783
}
17871784

17881785
for (const auto& net : gArgs.GetArgs("-whitelist")) {
1789-
CSubNet subnet;
1790-
LookupSubNet(net.c_str(), subnet);
1791-
if (!subnet.IsValid())
1792-
return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net));
1786+
NetWhitelistPermissions subnet;
1787+
std::string error;
1788+
if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
17931789
connOptions.vWhitelistedRange.push_back(subnet);
17941790
}
17951791

src/net.cpp

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <crypto/common.h>
1717
#include <crypto/sha256.h>
1818
#include <netbase.h>
19+
#include <net_permissions.h>
1920
#include <primitives/transaction.h>
2021
#include <scheduler.h>
2122
#include <ui_interface.h>
@@ -67,7 +68,6 @@ enum BindFlags {
6768
BF_NONE = 0,
6869
BF_EXPLICIT = (1U << 0),
6970
BF_REPORT_ERROR = (1U << 1),
70-
BF_WHITELIST = (1U << 2),
7171
};
7272

7373
// The set of sockets cannot be modified while waiting
@@ -459,12 +459,10 @@ void CNode::CloseSocketDisconnect()
459459
}
460460
}
461461

462-
bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
463-
for (const CSubNet& subnet : vWhitelistedRange) {
464-
if (subnet.Match(addr))
465-
return true;
462+
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const {
463+
for (const auto& subnet : vWhitelistedRange) {
464+
if (subnet.m_subnet.Match(addr)) NetPermissions::AddFlag(flags, subnet.m_flags);
466465
}
467-
return false;
468466
}
469467

470468
std::string CNode::GetAddrName() const {
@@ -528,7 +526,8 @@ void CNode::copyStats(CNodeStats &stats)
528526
X(mapRecvBytesPerMsgCmd);
529527
X(nRecvBytes);
530528
}
531-
X(fWhitelisted);
529+
X(m_legacyWhitelisted);
530+
X(m_permissionFlags);
532531
{
533532
LOCK(cs_feeFilter);
534533
X(minFeeFilter);
@@ -813,7 +812,7 @@ bool CConnman::AttemptToEvictConnection()
813812
LOCK(cs_vNodes);
814813

815814
for (const CNode* node : vNodes) {
816-
if (node->fWhitelisted)
815+
if (node->HasPermission(PF_NOBAN))
817816
continue;
818817
if (!node->fInbound)
819818
continue;
@@ -904,7 +903,20 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
904903
}
905904
}
906905

907-
bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
906+
NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
907+
hListenSocket.AddSocketPermissionFlags(permissionFlags);
908+
AddWhitelistPermissionFlags(permissionFlags, addr);
909+
const bool noban = NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN);
910+
bool legacyWhitelisted = false;
911+
if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
912+
NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
913+
if (gArgs.GetBoolArg("-whitelistforcerelay", false)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY);
914+
if (gArgs.GetBoolArg("-whitelistrelay", false)) NetPermissions::AddFlag(permissionFlags, PF_RELAY);
915+
NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL);
916+
NetPermissions::AddFlag(permissionFlags, PF_NOBAN);
917+
legacyWhitelisted = true;
918+
}
919+
908920
{
909921
LOCK(cs_vNodes);
910922
for (const CNode* pnode : vNodes) {
@@ -941,7 +953,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
941953

942954
// Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
943955
// if the only banning reason was an automatic misbehavior ban.
944-
if (!whitelisted && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
956+
if (!noban && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
945957
{
946958
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
947959
CloseSocket(hSocket);
@@ -962,9 +974,15 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
962974
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
963975
CAddress addr_bind = GetBindAddress(hSocket);
964976

965-
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
977+
ServiceFlags nodeServices = nLocalServices;
978+
if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
979+
nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
980+
}
981+
CNode* pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
966982
pnode->AddRef();
967-
pnode->fWhitelisted = whitelisted;
983+
pnode->m_permissionFlags = permissionFlags;
984+
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
985+
pnode->m_legacyWhitelisted = legacyWhitelisted;
968986
pnode->m_prefer_evict = bannedlevel > 0;
969987
m_msgproc->InitializeNode(pnode);
970988

@@ -1983,7 +2001,7 @@ void CConnman::ThreadMessageHandler()
19832001

19842002

19852003

1986-
bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
2004+
bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, NetPermissionFlags permissions)
19872005
{
19882006
strError = "";
19892007
int nOne = 1;
@@ -2044,9 +2062,9 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
20442062
return false;
20452063
}
20462064

2047-
vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
2065+
vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
20482066

2049-
if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
2067+
if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0)
20502068
AddLocal(addrBind, LOCAL_BIND);
20512069

20522070
return true;
@@ -2130,11 +2148,11 @@ NodeId CConnman::GetNewNodeId()
21302148
}
21312149

21322150

2133-
bool CConnman::Bind(const CService &addr, unsigned int flags) {
2151+
bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
21342152
if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
21352153
return false;
21362154
std::string strError;
2137-
if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
2155+
if (!BindListenPort(addr, strError, permissions)) {
21382156
if ((flags & BF_REPORT_ERROR) && clientInterface) {
21392157
clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR);
21402158
}
@@ -2143,20 +2161,21 @@ bool CConnman::Bind(const CService &addr, unsigned int flags) {
21432161
return true;
21442162
}
21452163

2146-
bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds) {
2164+
bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds)
2165+
{
21472166
bool fBound = false;
21482167
for (const auto& addrBind : binds) {
2149-
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
2168+
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE);
21502169
}
21512170
for (const auto& addrBind : whiteBinds) {
2152-
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
2171+
fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
21532172
}
21542173
if (binds.empty() && whiteBinds.empty()) {
21552174
struct in_addr inaddr_any;
21562175
inaddr_any.s_addr = INADDR_ANY;
21572176
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
2158-
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE);
2159-
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
2177+
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
2178+
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
21602179
}
21612180
return fBound;
21622181
}

src/net.h

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <hash.h>
1616
#include <limitedmap.h>
1717
#include <netaddress.h>
18+
#include <net_permissions.h>
1819
#include <policy/feerate.h>
1920
#include <protocol.h>
2021
#include <random.h>
@@ -138,8 +139,9 @@ class CConnman
138139
uint64_t nMaxOutboundLimit = 0;
139140
int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT;
140141
std::vector<std::string> vSeedNodes;
141-
std::vector<CSubNet> vWhitelistedRange;
142-
std::vector<CService> vBinds, vWhiteBinds;
142+
std::vector<NetWhitelistPermissions> vWhitelistedRange;
143+
std::vector<NetWhitebindPermissions> vWhiteBinds;
144+
std::vector<CService> vBinds;
143145
bool m_use_addrman_outgoing = true;
144146
std::vector<std::string> m_specified_outgoing;
145147
std::vector<std::string> m_added_nodes;
@@ -314,15 +316,17 @@ class CConnman
314316

315317
private:
316318
struct ListenSocket {
319+
public:
317320
SOCKET socket;
318-
bool whitelisted;
319-
320-
ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
321+
inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); }
322+
ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {}
323+
private:
324+
NetPermissionFlags m_permissions;
321325
};
322326

323-
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
324-
bool Bind(const CService &addr, unsigned int flags);
325-
bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds);
327+
bool BindListenPort(const CService& bindAddr, std::string& strError, NetPermissionFlags permissions);
328+
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
329+
bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds);
326330
void ThreadOpenAddedConnections();
327331
void AddOneShot(const std::string& strDest);
328332
void ProcessOneShot();
@@ -347,7 +351,7 @@ class CConnman
347351

348352
bool AttemptToEvictConnection();
349353
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection);
350-
bool IsWhitelistedRange(const CNetAddr &addr);
354+
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
351355

352356
void DeleteNode(CNode* pnode);
353357

@@ -380,7 +384,7 @@ class CConnman
380384

381385
// Whitelisted ranges. Any node connecting from these is automatically
382386
// whitelisted (as well as those connecting to whitelisted binds).
383-
std::vector<CSubNet> vWhitelistedRange;
387+
std::vector<NetWhitelistPermissions> vWhitelistedRange;
384388

385389
unsigned int nSendBufferMaxSize{0};
386390
unsigned int nReceiveFloodSize{0};
@@ -448,7 +452,6 @@ void StartMapPort();
448452
void InterruptMapPort();
449453
void StopMapPort();
450454
unsigned short GetListenPort();
451-
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
452455

453456
struct CombinerAll
454457
{
@@ -555,7 +558,8 @@ class CNodeStats
555558
mapMsgCmdSize mapSendBytesPerMsgCmd;
556559
uint64_t nRecvBytes;
557560
mapMsgCmdSize mapRecvBytesPerMsgCmd;
558-
bool fWhitelisted;
561+
NetPermissionFlags m_permissionFlags;
562+
bool m_legacyWhitelisted;
559563
double dPingTime;
560564
double dPingWait;
561565
double dMinPing;
@@ -657,7 +661,11 @@ class CNode
657661
*/
658662
std::string cleanSubVer GUARDED_BY(cs_SubVer){};
659663
bool m_prefer_evict{false}; // This peer is preferred for eviction.
660-
bool fWhitelisted{false}; // This peer can bypass DoS banning.
664+
bool HasPermission(NetPermissionFlags permission) const {
665+
return NetPermissions::HasFlag(m_permissionFlags, permission);
666+
}
667+
// This boolean is unusued in actual processing, only present for backward compatibility at RPC/QT level
668+
bool m_legacyWhitelisted{false};
661669
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
662670
bool fOneShot{false};
663671
bool m_manual_connection{false};
@@ -753,6 +761,7 @@ class CNode
753761
const ServiceFlags nLocalServices;
754762
const int nMyStartingHeight;
755763
int nSendVersion{0};
764+
NetPermissionFlags m_permissionFlags{ PF_NONE };
756765
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
757766

758767
mutable CCriticalSection cs_addrName;

0 commit comments

Comments
 (0)