Skip to content

Commit 50bd12c

Browse files
committed
Break addnode out from the outbound connection limits.
Previously addnodes were in competition with outbound connections for access to the eight outbound slots. One result of this is that frequently a node with several addnode configured peers would end up connected to none of them, because while the addnode loop was in its two minute sleep the automatic connection logic would fill any free slots with random peers. This is particularly unwelcome to users trying to maintain links to specific nodes for fast block relay or purposes. Another result is that a group of nine or more nodes which are have addnode configured towards each other can become partitioned from the public network. This commit introduces a new limit of eight connections just for addnode peers which is not subject to any of the other connection limitations (including maxconnections). The choice of eight is sufficient so that under no condition would a user find themselves connected to fewer addnoded peers than previously. It is also low enough that users who are confused about the significance of more connections and have gotten too copy-and-paste happy will not consume more than twice the slot usage of a typical user. Any additional load on the network resulting from this will likely be offset by a reduction in users applying even more wasteful workaround for the prior behavior. The retry delays are reduced to avoid nodes sitting around without their added peers up, but are still sufficient to prevent overly aggressive repeated connections. The reduced delays also make the system much more responsive to the addnode RPC. Ban-disconnects are also exempted for peers added via addnode since the outbound addnode logic ignores bans. Previously it would ban an addnode then immediately reconnect to it. A minor change was also made to CSemaphoreGrant so that it is possible to re-acquire via an object whos grant was moved.
1 parent ce43630 commit 50bd12c

File tree

6 files changed

+48
-13
lines changed

6 files changed

+48
-13
lines changed

src/init.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -871,11 +871,11 @@ bool AppInitParameterInteraction()
871871
nMaxConnections = std::max(nUserMaxConnections, 0);
872872

873873
// Trim requested connection counts, to fit into system limitations
874-
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
875-
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
874+
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS)), 0);
875+
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS);
876876
if (nFD < MIN_CORE_FILEDESCRIPTORS)
877877
return InitError(_("Not enough file descriptors available."));
878-
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections);
878+
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
879879

880880
if (nMaxConnections < nUserMaxConnections)
881881
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
@@ -1109,7 +1109,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
11091109
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
11101110
LogPrintf("Using data directory %s\n", GetDataDir().string());
11111111
LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
1112-
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
1112+
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
11131113

11141114
InitSignatureCache();
11151115

@@ -1565,6 +1565,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
15651565
connOptions.nRelevantServices = nRelevantServices;
15661566
connOptions.nMaxConnections = nMaxConnections;
15671567
connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
1568+
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
15681569
connOptions.nMaxFeeler = 1;
15691570
connOptions.nBestHeight = chainActive.Height();
15701571
connOptions.uiInterface = &uiInterface;

src/net.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ void CNode::copyStats(CNodeStats &stats)
621621
X(nVersion);
622622
X(cleanSubVer);
623623
X(fInbound);
624+
X(fAddnode);
624625
X(nStartingHeight);
625626
X(nSendBytes);
626627
X(mapSendBytesPerMsgCmd);
@@ -1631,7 +1632,7 @@ void CConnman::ThreadOpenConnections()
16311632
{
16321633
LOCK(cs_vNodes);
16331634
BOOST_FOREACH(CNode* pnode, vNodes) {
1634-
if (!pnode->fInbound) {
1635+
if (!pnode->fInbound && !pnode->fAddnode) {
16351636
setConnected.insert(pnode->addr.GetGroup());
16361637
nOutbound++;
16371638
}
@@ -1776,27 +1777,35 @@ void CConnman::ThreadOpenAddedConnections()
17761777
vAddedNodes = mapMultiArgs.at("-addnode");
17771778
}
17781779

1779-
for (unsigned int i = 0; true; i++)
1780+
while (true)
17801781
{
1782+
CSemaphoreGrant grant(*semAddnode);
17811783
std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo();
1784+
bool tried = false;
17821785
for (const AddedNodeInfo& info : vInfo) {
17831786
if (!info.fConnected) {
1784-
CSemaphoreGrant grant(*semOutbound);
1787+
if (!grant.TryAcquire()) {
1788+
// If we've used up our semaphore and need a new one, lets not wait here since while we are waiting
1789+
// the addednodeinfo state might change.
1790+
break;
1791+
}
17851792
// If strAddedNode is an IP/port, decode it immediately, so
17861793
// OpenNetworkConnection can detect existing connections to that IP/port.
1794+
tried = true;
17871795
CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort()));
1788-
OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false);
1796+
OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true);
17891797
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
17901798
return;
17911799
}
17921800
}
1793-
if (!interruptNet.sleep_for(std::chrono::minutes(2)))
1801+
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
1802+
if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2)));
17941803
return;
17951804
}
17961805
}
17971806

17981807
// if successful, this moves the passed grant to the constructed node
1799-
bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
1808+
bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode)
18001809
{
18011810
//
18021811
// Initiate outbound network connection
@@ -1825,6 +1834,8 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
18251834
pnode->fOneShot = true;
18261835
if (fFeeler)
18271836
pnode->fFeeler = true;
1837+
if (fAddnode)
1838+
pnode->fAddnode = true;
18281839

18291840
return true;
18301841
}
@@ -2076,8 +2087,10 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe
20762087
nSendBufferMaxSize = 0;
20772088
nReceiveFloodSize = 0;
20782089
semOutbound = NULL;
2090+
semAddnode = NULL;
20792091
nMaxConnections = 0;
20802092
nMaxOutbound = 0;
2093+
nMaxAddnode = 0;
20812094
nBestHeight = 0;
20822095
clientInterface = NULL;
20832096
flagInterruptMsgProc = false;
@@ -2099,6 +2112,7 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c
20992112
nLocalServices = connOptions.nLocalServices;
21002113
nMaxConnections = connOptions.nMaxConnections;
21012114
nMaxOutbound = std::min((connOptions.nMaxOutbound), nMaxConnections);
2115+
nMaxAddnode = connOptions.nMaxAddnode;
21022116
nMaxFeeler = connOptions.nMaxFeeler;
21032117

21042118
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
@@ -2151,6 +2165,10 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c
21512165
// initialize semaphore
21522166
semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
21532167
}
2168+
if (semAddnode == NULL) {
2169+
// initialize semaphore
2170+
semAddnode = new CSemaphore(nMaxAddnode);
2171+
}
21542172

21552173
//
21562174
// Start threads
@@ -2227,6 +2245,10 @@ void CConnman::Stop()
22272245
if (threadSocketHandler.joinable())
22282246
threadSocketHandler.join();
22292247

2248+
if (semAddnode)
2249+
for (int i=0; i<nMaxAddnode; i++)
2250+
semOutbound->post();
2251+
22302252
if (fAddressesInitialized)
22312253
{
22322254
DumpData();
@@ -2254,6 +2276,8 @@ void CConnman::Stop()
22542276
vhListenSocket.clear();
22552277
delete semOutbound;
22562278
semOutbound = NULL;
2279+
delete semAddnode;
2280+
semAddnode = NULL;
22572281
}
22582282

22592283
void CConnman::DeleteNode(CNode* pnode)
@@ -2554,6 +2578,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
25542578
strSubVer = "";
25552579
fWhitelisted = false;
25562580
fOneShot = false;
2581+
fAddnode = false;
25572582
fClient = false; // set by version message
25582583
fFeeler = false;
25592584
fSuccessfullyConnected = false;

src/net.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ static const unsigned int MAX_ADDR_TO_SEND = 1000;
5858
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
5959
/** Maximum length of strSubVer in `version` message */
6060
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
61-
/** Maximum number of outgoing nodes */
61+
/** Maximum number of automatic outgoing nodes */
6262
static const int MAX_OUTBOUND_CONNECTIONS = 8;
63+
/** Maximum number of addnode outgoing nodes */
64+
static const int MAX_ADDNODE_CONNECTIONS = 8;
6365
/** -listen default */
6466
static const bool DEFAULT_LISTEN = true;
6567
/** -upnp default */
@@ -135,6 +137,7 @@ class CConnman
135137
ServiceFlags nRelevantServices = NODE_NONE;
136138
int nMaxConnections = 0;
137139
int nMaxOutbound = 0;
140+
int nMaxAddnode = 0;
138141
int nMaxFeeler = 0;
139142
int nBestHeight = 0;
140143
CClientUIInterface* uiInterface = nullptr;
@@ -151,7 +154,7 @@ class CConnman
151154
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
152155
bool GetNetworkActive() const { return fNetworkActive; };
153156
void SetNetworkActive(bool active);
154-
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
157+
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false);
155158
bool CheckIncomingNonce(uint64_t nonce);
156159

157160
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -414,8 +417,10 @@ class CConnman
414417
ServiceFlags nRelevantServices;
415418

416419
CSemaphore *semOutbound;
420+
CSemaphore *semAddnode;
417421
int nMaxConnections;
418422
int nMaxOutbound;
423+
int nMaxAddnode;
419424
int nMaxFeeler;
420425
std::atomic<int> nBestHeight;
421426
CClientUIInterface* clientInterface;
@@ -529,6 +534,7 @@ class CNodeStats
529534
int nVersion;
530535
std::string cleanSubVer;
531536
bool fInbound;
537+
bool fAddnode;
532538
int nStartingHeight;
533539
uint64_t nSendBytes;
534540
mapMsgCmdSize mapSendBytesPerMsgCmd;
@@ -626,6 +632,7 @@ class CNode
626632
bool fWhitelisted; // This peer can bypass DoS banning.
627633
bool fFeeler; // If true this node is being used as a short lived feeler.
628634
bool fOneShot;
635+
bool fAddnode;
629636
bool fClient;
630637
const bool fInbound;
631638
bool fSuccessfullyConnected;

src/net_processing.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2644,6 +2644,8 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg
26442644
state.fShouldBan = false;
26452645
if (pto->fWhitelisted)
26462646
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
2647+
else if (pto->fAddnode)
2648+
LogPrintf("Warning: not punishing addnoded peer %s!\n", pto->addr.ToString());
26472649
else {
26482650
pto->fDisconnect = true;
26492651
if (pto->addr.IsLocal())

src/rpc/net.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
152152
// their ver message.
153153
obj.push_back(Pair("subver", stats.cleanSubVer));
154154
obj.push_back(Pair("inbound", stats.fInbound));
155+
obj.push_back(Pair("addnode", stats.fAddnode));
155156
obj.push_back(Pair("startingheight", stats.nStartingHeight));
156157
if (fStateStats) {
157158
obj.push_back(Pair("banscore", statestats.nMisbehavior));

src/sync.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,6 @@ class CSemaphoreGrant
264264
grant.Release();
265265
grant.sem = sem;
266266
grant.fHaveGrant = fHaveGrant;
267-
sem = NULL;
268267
fHaveGrant = false;
269268
}
270269

0 commit comments

Comments
 (0)