Skip to content

Commit 69dc5b5

Browse files
committed
Merge pull request #6374
027de94 Use network group instead of CNetAddr in final pass to select node to disconnect (Patrick Strateman) 000c18a Fix comment (Patrick Strateman) fed3094 Acquire cs_vNodes before changing refrence counts (Patrick Strateman) 69ee1aa CNodeRef copy constructor and assignment operator (Patrick Strateman) dc81dd0 Return false early if vEvictionCandidates is empty (Patrick Strateman) 17f3533 Better support for nodes with non-standard nMaxConnections (Patrick Strateman) 1317cd1 RAII wrapper for CNode* (Patrick Strateman) df23937 Add comments to AttemptToEvictConnection (Patrick Strateman) a8f6e45 Remove redundant whiteconnections option (Patrick Strateman) b105ba3 Prefer to disconnect peers in favor of whitelisted peers (Patrick Strateman) 2c70153 AttemptToEvictConnection (Patrick Strateman) 4bac601 Record nMinPingUsecTime (Patrick Strateman) ae037b7 Refactor: Move failure conditions to the top of AcceptConnection (Patrick Strateman) 1ef4817 Refactor: Bail early in AcceptConnection (Patrick Strateman) 541a1dd Refactor: AcceptConnection (Patrick Strateman)
2 parents 5e1ec3b + 027de94 commit 69dc5b5

File tree

4 files changed

+220
-99
lines changed

4 files changed

+220
-99
lines changed

src/init.cpp

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,6 @@ std::string HelpMessage(HelpMessageMode mode)
335335
strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
336336
strUsage += HelpMessageOpt("-whitelist=<netmask>", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") +
337337
" " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway"));
338-
strUsage += HelpMessageOpt("-whiteconnections=<n>", strprintf(_("Reserve this many inbound connections for whitelisted peers (default: %d)"), 0));
339338

340339
#ifdef ENABLE_WALLET
341340
strUsage += HelpMessageGroup(_("Wallet options:"));
@@ -754,25 +753,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
754753
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
755754
int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
756755
nMaxConnections = std::max(nUserMaxConnections, 0);
757-
int nUserWhiteConnections = GetArg("-whiteconnections", 0);
758-
nWhiteConnections = std::max(nUserWhiteConnections, 0);
759-
760-
if ((mapArgs.count("-whitelist")) || (mapArgs.count("-whitebind"))) {
761-
if (!(mapArgs.count("-maxconnections"))) {
762-
// User is using whitelist feature,
763-
// but did not specify -maxconnections parameter.
764-
// Silently increase the default to compensate,
765-
// so that the whitelist connection reservation feature
766-
// does not inadvertently reduce the default
767-
// inbound connection capacity of the network.
768-
nMaxConnections += nWhiteConnections;
769-
}
770-
} else {
771-
// User not using whitelist feature.
772-
// Silently disable connection reservation,
773-
// for the same reason as above.
774-
nWhiteConnections = 0;
775-
}
776756

777757
// Trim requested connection counts, to fit into system limitations
778758
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
@@ -784,13 +764,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
784764
if (nMaxConnections < nUserMaxConnections)
785765
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
786766

787-
// Connection capacity is prioritized in this order:
788-
// outbound connections (hardcoded to 8),
789-
// then whitelisted connections,
790-
// then non-whitelisted connections get whatever's left (if any).
791-
if ((nWhiteConnections > 0) && (nWhiteConnections >= (nMaxConnections - 8)))
792-
InitWarning(strprintf(_("All non-whitelisted incoming connections will be dropped, because -whiteconnections is %d and -maxconnections is only %d."), nWhiteConnections, nMaxConnections));
793-
794767
// ********************************************************* Step 3: parameter-to-internal-flags
795768

796769
fDebug = !mapMultiArgs["-debug"].empty();
@@ -968,8 +941,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
968941
LogPrintf("Using data directory %s\n", strDataDir);
969942
LogPrintf("Using config file %s\n", GetConfigFile().string());
970943
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
971-
if (nWhiteConnections > 0)
972-
LogPrintf("Reserving %i of these connections for whitelisted inbound peers\n", nWhiteConnections);
973944
std::ostringstream strErrors;
974945

975946
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);

src/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4522,6 +4522,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
45224522
if (pingUsecTime > 0) {
45234523
// Successful ping time measurement, replace previous
45244524
pfrom->nPingUsecTime = pingUsecTime;
4525+
pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
45254526
} else {
45264527
// This should never happen
45274528
sProblem = "Timing mishap";

src/net.cpp

Lines changed: 217 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ uint64_t nLocalHostNonce = 0;
8181
static std::vector<ListenSocket> vhListenSocket;
8282
CAddrMan addrman;
8383
int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
84-
int nWhiteConnections = 0;
8584
bool fAddressesInitialized = false;
8685
std::string strSubVersion;
8786

@@ -776,6 +775,222 @@ void SocketSendData(CNode *pnode)
776775

777776
static list<CNode*> vNodesDisconnected;
778777

778+
class CNodeRef {
779+
public:
780+
CNodeRef(CNode *pnode) : _pnode(pnode) {
781+
LOCK(cs_vNodes);
782+
_pnode->AddRef();
783+
}
784+
785+
~CNodeRef() {
786+
LOCK(cs_vNodes);
787+
_pnode->Release();
788+
}
789+
790+
CNode& operator *() const {return *_pnode;};
791+
CNode* operator ->() const {return _pnode;};
792+
793+
CNodeRef& operator =(const CNodeRef& other)
794+
{
795+
if (this != &other) {
796+
LOCK(cs_vNodes);
797+
798+
_pnode->Release();
799+
_pnode = other._pnode;
800+
_pnode->AddRef();
801+
}
802+
return *this;
803+
}
804+
805+
CNodeRef(const CNodeRef& other):
806+
_pnode(other._pnode)
807+
{
808+
LOCK(cs_vNodes);
809+
_pnode->AddRef();
810+
}
811+
private:
812+
CNode *_pnode;
813+
};
814+
815+
static bool ReverseCompareNodeMinPingTime(const CNodeRef &a, const CNodeRef &b)
816+
{
817+
return a->nMinPingUsecTime > b->nMinPingUsecTime;
818+
}
819+
820+
static bool ReverseCompareNodeTimeConnected(const CNodeRef &a, const CNodeRef &b)
821+
{
822+
return a->nTimeConnected > b->nTimeConnected;
823+
}
824+
825+
class CompareNetGroupKeyed
826+
{
827+
std::vector<unsigned char> vchSecretKey;
828+
public:
829+
CompareNetGroupKeyed()
830+
{
831+
vchSecretKey.resize(32, 0);
832+
GetRandBytes(vchSecretKey.data(), vchSecretKey.size());
833+
}
834+
835+
bool operator()(const CNodeRef &a, const CNodeRef &b)
836+
{
837+
std::vector<unsigned char> vchGroupA, vchGroupB;
838+
CSHA256 hashA, hashB;
839+
std::vector<unsigned char> vchA(32), vchB(32);
840+
841+
vchGroupA = a->addr.GetGroup();
842+
vchGroupB = b->addr.GetGroup();
843+
844+
hashA.Write(begin_ptr(vchGroupA), vchGroupA.size());
845+
hashB.Write(begin_ptr(vchGroupB), vchGroupB.size());
846+
847+
hashA.Write(begin_ptr(vchSecretKey), vchSecretKey.size());
848+
hashB.Write(begin_ptr(vchSecretKey), vchSecretKey.size());
849+
850+
hashA.Finalize(begin_ptr(vchA));
851+
hashB.Finalize(begin_ptr(vchB));
852+
853+
return vchA < vchB;
854+
}
855+
};
856+
857+
static bool AttemptToEvictConnection(bool fPreferNewConnection) {
858+
std::vector<CNodeRef> vEvictionCandidates;
859+
{
860+
LOCK(cs_vNodes);
861+
862+
BOOST_FOREACH(CNode *node, vNodes) {
863+
if (node->fWhitelisted)
864+
continue;
865+
if (!node->fInbound)
866+
continue;
867+
if (node->fDisconnect)
868+
continue;
869+
if (node->addr.IsLocal())
870+
continue;
871+
vEvictionCandidates.push_back(CNodeRef(node));
872+
}
873+
}
874+
875+
if (vEvictionCandidates.empty()) return false;
876+
877+
// Protect connections with certain characteristics
878+
879+
// Deterministically select 4 peers to protect by netgroup.
880+
// An attacker cannot predict which netgroups will be protected.
881+
static CompareNetGroupKeyed comparerNetGroupKeyed;
882+
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), comparerNetGroupKeyed);
883+
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
884+
885+
if (vEvictionCandidates.empty()) return false;
886+
887+
// Protect the 8 nodes with the best ping times.
888+
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
889+
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime);
890+
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
891+
892+
if (vEvictionCandidates.empty()) return false;
893+
894+
// Protect the half of the remaining nodes which have been connected the longest.
895+
// This replicates the existing implicit behavior.
896+
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
897+
vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end());
898+
899+
if (vEvictionCandidates.empty()) return false;
900+
901+
// Identify the network group with the most connections
902+
std::vector<unsigned char> naMostConnections;
903+
unsigned int nMostConnections = 0;
904+
std::map<std::vector<unsigned char>, std::vector<CNodeRef> > mapAddrCounts;
905+
BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) {
906+
mapAddrCounts[node->addr.GetGroup()].push_back(node);
907+
908+
if (mapAddrCounts[node->addr.GetGroup()].size() > nMostConnections) {
909+
nMostConnections = mapAddrCounts[node->addr.GetGroup()].size();
910+
naMostConnections = node->addr.GetGroup();
911+
}
912+
}
913+
914+
// Reduce to the network group with the most connections
915+
vEvictionCandidates = mapAddrCounts[naMostConnections];
916+
917+
// Do not disconnect peers if there is only 1 connection from their network group
918+
if (vEvictionCandidates.size() <= 1)
919+
// unless we prefer the new connection (for whitelisted peers)
920+
if (!fPreferNewConnection)
921+
return false;
922+
923+
// Disconnect the most recent connection from the network group with the most connections
924+
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
925+
vEvictionCandidates[0]->fDisconnect = true;
926+
927+
return true;
928+
}
929+
930+
static void AcceptConnection(const ListenSocket& hListenSocket) {
931+
struct sockaddr_storage sockaddr;
932+
socklen_t len = sizeof(sockaddr);
933+
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
934+
CAddress addr;
935+
int nInbound = 0;
936+
int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS;
937+
938+
if (hSocket != INVALID_SOCKET)
939+
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
940+
LogPrintf("Warning: Unknown socket family\n");
941+
942+
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
943+
{
944+
LOCK(cs_vNodes);
945+
BOOST_FOREACH(CNode* pnode, vNodes)
946+
if (pnode->fInbound)
947+
nInbound++;
948+
}
949+
950+
if (hSocket == INVALID_SOCKET)
951+
{
952+
int nErr = WSAGetLastError();
953+
if (nErr != WSAEWOULDBLOCK)
954+
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
955+
return;
956+
}
957+
958+
if (!IsSelectableSocket(hSocket))
959+
{
960+
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
961+
CloseSocket(hSocket);
962+
return;
963+
}
964+
965+
if (CNode::IsBanned(addr) && !whitelisted)
966+
{
967+
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
968+
CloseSocket(hSocket);
969+
return;
970+
}
971+
972+
if (nInbound >= nMaxInbound)
973+
{
974+
if (!AttemptToEvictConnection(whitelisted)) {
975+
// No connection to evict, disconnect the new connection
976+
LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n");
977+
CloseSocket(hSocket);
978+
return;
979+
}
980+
}
981+
982+
CNode* pnode = new CNode(hSocket, addr, "", true);
983+
pnode->AddRef();
984+
pnode->fWhitelisted = whitelisted;
985+
986+
LogPrint("net", "connection from %s accepted\n", addr.ToString());
987+
988+
{
989+
LOCK(cs_vNodes);
990+
vNodes.push_back(pnode);
991+
}
992+
}
993+
779994
void ThreadSocketHandler()
780995
{
781996
unsigned int nPrevNodeCount = 0;
@@ -933,64 +1148,7 @@ void ThreadSocketHandler()
9331148
{
9341149
if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
9351150
{
936-
struct sockaddr_storage sockaddr;
937-
socklen_t len = sizeof(sockaddr);
938-
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
939-
CAddress addr;
940-
int nInbound = 0;
941-
int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS;
942-
943-
if (hSocket != INVALID_SOCKET)
944-
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
945-
LogPrintf("Warning: Unknown socket family\n");
946-
947-
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
948-
{
949-
LOCK(cs_vNodes);
950-
BOOST_FOREACH(CNode* pnode, vNodes)
951-
if (pnode->fInbound)
952-
nInbound++;
953-
}
954-
955-
if (hSocket == INVALID_SOCKET)
956-
{
957-
int nErr = WSAGetLastError();
958-
if (nErr != WSAEWOULDBLOCK)
959-
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
960-
}
961-
else if (!IsSelectableSocket(hSocket))
962-
{
963-
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
964-
CloseSocket(hSocket);
965-
}
966-
else if (nInbound >= nMaxInbound)
967-
{
968-
LogPrint("net", "connection from %s dropped (full)\n", addr.ToString());
969-
CloseSocket(hSocket);
970-
}
971-
else if (!whitelisted && (nInbound >= (nMaxInbound - nWhiteConnections)))
972-
{
973-
LogPrint("net", "connection from %s dropped (non-whitelisted)\n", addr.ToString());
974-
CloseSocket(hSocket);
975-
}
976-
else if (CNode::IsBanned(addr) && !whitelisted)
977-
{
978-
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
979-
CloseSocket(hSocket);
980-
}
981-
else
982-
{
983-
CNode* pnode = new CNode(hSocket, addr, "", true);
984-
pnode->AddRef();
985-
pnode->fWhitelisted = whitelisted;
986-
987-
LogPrint("net", "connection from %s accepted\n", addr.ToString());
988-
989-
{
990-
LOCK(cs_vNodes);
991-
vNodes.push_back(pnode);
992-
}
993-
}
1151+
AcceptConnection(hListenSocket);
9941152
}
9951153
}
9961154

src/net.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -143,19 +143,8 @@ extern uint64_t nLocalServices;
143143
extern uint64_t nLocalHostNonce;
144144
extern CAddrMan addrman;
145145

146-
// The allocation of connections against the maximum allowed (nMaxConnections)
147-
// is prioritized as follows:
148-
// 1st: Outbound connections (MAX_OUTBOUND_CONNECTIONS)
149-
// 2nd: Inbound connections from whitelisted peers (nWhiteConnections)
150-
// 3rd: Inbound connections from non-whitelisted peers
151-
// Thus, the number of connection slots for the general public to use is:
152-
// nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + nWhiteConnections)
153-
// Any additional inbound connections beyond limits will be immediately closed
154-
155146
/** Maximum number of connections to simultaneously allow (aka connection slots) */
156147
extern int nMaxConnections;
157-
/** Number of connection slots to reserve for inbound from whitelisted peers */
158-
extern int nWhiteConnections;
159148

160149
extern std::vector<CNode*> vNodes;
161150
extern CCriticalSection cs_vNodes;
@@ -395,6 +384,8 @@ class CNode
395384
int64_t nPingUsecStart;
396385
// Last measured round-trip time.
397386
int64_t nPingUsecTime;
387+
// Best measured round-trip time.
388+
int64_t nMinPingUsecTime;
398389
// Whether a ping is requested.
399390
bool fPingQueued;
400391

0 commit comments

Comments
 (0)