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)
This commit is contained in:
commit
69dc5b51a0
29
src/init.cpp
29
src/init.cpp
@ -335,7 +335,6 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
|
||||
strUsage += HelpMessageOpt("-whitelist=<netmask>", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") +
|
||||
" " + _("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"));
|
||||
strUsage += HelpMessageOpt("-whiteconnections=<n>", strprintf(_("Reserve this many inbound connections for whitelisted peers (default: %d)"), 0));
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
strUsage += HelpMessageGroup(_("Wallet options:"));
|
||||
@ -754,25 +753,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
|
||||
int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
|
||||
nMaxConnections = std::max(nUserMaxConnections, 0);
|
||||
int nUserWhiteConnections = GetArg("-whiteconnections", 0);
|
||||
nWhiteConnections = std::max(nUserWhiteConnections, 0);
|
||||
|
||||
if ((mapArgs.count("-whitelist")) || (mapArgs.count("-whitebind"))) {
|
||||
if (!(mapArgs.count("-maxconnections"))) {
|
||||
// User is using whitelist feature,
|
||||
// but did not specify -maxconnections parameter.
|
||||
// Silently increase the default to compensate,
|
||||
// so that the whitelist connection reservation feature
|
||||
// does not inadvertently reduce the default
|
||||
// inbound connection capacity of the network.
|
||||
nMaxConnections += nWhiteConnections;
|
||||
}
|
||||
} else {
|
||||
// User not using whitelist feature.
|
||||
// Silently disable connection reservation,
|
||||
// for the same reason as above.
|
||||
nWhiteConnections = 0;
|
||||
}
|
||||
|
||||
// Trim requested connection counts, to fit into system limitations
|
||||
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)
|
||||
if (nMaxConnections < nUserMaxConnections)
|
||||
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
|
||||
|
||||
// Connection capacity is prioritized in this order:
|
||||
// outbound connections (hardcoded to 8),
|
||||
// then whitelisted connections,
|
||||
// then non-whitelisted connections get whatever's left (if any).
|
||||
if ((nWhiteConnections > 0) && (nWhiteConnections >= (nMaxConnections - 8)))
|
||||
InitWarning(strprintf(_("All non-whitelisted incoming connections will be dropped, because -whiteconnections is %d and -maxconnections is only %d."), nWhiteConnections, nMaxConnections));
|
||||
|
||||
// ********************************************************* Step 3: parameter-to-internal-flags
|
||||
|
||||
fDebug = !mapMultiArgs["-debug"].empty();
|
||||
@ -968,8 +941,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
LogPrintf("Using data directory %s\n", strDataDir);
|
||||
LogPrintf("Using config file %s\n", GetConfigFile().string());
|
||||
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
|
||||
if (nWhiteConnections > 0)
|
||||
LogPrintf("Reserving %i of these connections for whitelisted inbound peers\n", nWhiteConnections);
|
||||
std::ostringstream strErrors;
|
||||
|
||||
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
|
||||
|
@ -4522,6 +4522,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||
if (pingUsecTime > 0) {
|
||||
// Successful ping time measurement, replace previous
|
||||
pfrom->nPingUsecTime = pingUsecTime;
|
||||
pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
|
||||
} else {
|
||||
// This should never happen
|
||||
sProblem = "Timing mishap";
|
||||
|
276
src/net.cpp
276
src/net.cpp
@ -81,7 +81,6 @@ uint64_t nLocalHostNonce = 0;
|
||||
static std::vector<ListenSocket> vhListenSocket;
|
||||
CAddrMan addrman;
|
||||
int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
|
||||
int nWhiteConnections = 0;
|
||||
bool fAddressesInitialized = false;
|
||||
std::string strSubVersion;
|
||||
|
||||
@ -776,6 +775,222 @@ void SocketSendData(CNode *pnode)
|
||||
|
||||
static list<CNode*> vNodesDisconnected;
|
||||
|
||||
class CNodeRef {
|
||||
public:
|
||||
CNodeRef(CNode *pnode) : _pnode(pnode) {
|
||||
LOCK(cs_vNodes);
|
||||
_pnode->AddRef();
|
||||
}
|
||||
|
||||
~CNodeRef() {
|
||||
LOCK(cs_vNodes);
|
||||
_pnode->Release();
|
||||
}
|
||||
|
||||
CNode& operator *() const {return *_pnode;};
|
||||
CNode* operator ->() const {return _pnode;};
|
||||
|
||||
CNodeRef& operator =(const CNodeRef& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
LOCK(cs_vNodes);
|
||||
|
||||
_pnode->Release();
|
||||
_pnode = other._pnode;
|
||||
_pnode->AddRef();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CNodeRef(const CNodeRef& other):
|
||||
_pnode(other._pnode)
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
_pnode->AddRef();
|
||||
}
|
||||
private:
|
||||
CNode *_pnode;
|
||||
};
|
||||
|
||||
static bool ReverseCompareNodeMinPingTime(const CNodeRef &a, const CNodeRef &b)
|
||||
{
|
||||
return a->nMinPingUsecTime > b->nMinPingUsecTime;
|
||||
}
|
||||
|
||||
static bool ReverseCompareNodeTimeConnected(const CNodeRef &a, const CNodeRef &b)
|
||||
{
|
||||
return a->nTimeConnected > b->nTimeConnected;
|
||||
}
|
||||
|
||||
class CompareNetGroupKeyed
|
||||
{
|
||||
std::vector<unsigned char> vchSecretKey;
|
||||
public:
|
||||
CompareNetGroupKeyed()
|
||||
{
|
||||
vchSecretKey.resize(32, 0);
|
||||
GetRandBytes(vchSecretKey.data(), vchSecretKey.size());
|
||||
}
|
||||
|
||||
bool operator()(const CNodeRef &a, const CNodeRef &b)
|
||||
{
|
||||
std::vector<unsigned char> vchGroupA, vchGroupB;
|
||||
CSHA256 hashA, hashB;
|
||||
std::vector<unsigned char> vchA(32), vchB(32);
|
||||
|
||||
vchGroupA = a->addr.GetGroup();
|
||||
vchGroupB = b->addr.GetGroup();
|
||||
|
||||
hashA.Write(begin_ptr(vchGroupA), vchGroupA.size());
|
||||
hashB.Write(begin_ptr(vchGroupB), vchGroupB.size());
|
||||
|
||||
hashA.Write(begin_ptr(vchSecretKey), vchSecretKey.size());
|
||||
hashB.Write(begin_ptr(vchSecretKey), vchSecretKey.size());
|
||||
|
||||
hashA.Finalize(begin_ptr(vchA));
|
||||
hashB.Finalize(begin_ptr(vchB));
|
||||
|
||||
return vchA < vchB;
|
||||
}
|
||||
};
|
||||
|
||||
static bool AttemptToEvictConnection(bool fPreferNewConnection) {
|
||||
std::vector<CNodeRef> vEvictionCandidates;
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
|
||||
BOOST_FOREACH(CNode *node, vNodes) {
|
||||
if (node->fWhitelisted)
|
||||
continue;
|
||||
if (!node->fInbound)
|
||||
continue;
|
||||
if (node->fDisconnect)
|
||||
continue;
|
||||
if (node->addr.IsLocal())
|
||||
continue;
|
||||
vEvictionCandidates.push_back(CNodeRef(node));
|
||||
}
|
||||
}
|
||||
|
||||
if (vEvictionCandidates.empty()) return false;
|
||||
|
||||
// Protect connections with certain characteristics
|
||||
|
||||
// Deterministically select 4 peers to protect by netgroup.
|
||||
// An attacker cannot predict which netgroups will be protected.
|
||||
static CompareNetGroupKeyed comparerNetGroupKeyed;
|
||||
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), comparerNetGroupKeyed);
|
||||
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
|
||||
|
||||
if (vEvictionCandidates.empty()) return false;
|
||||
|
||||
// Protect the 8 nodes with the best ping times.
|
||||
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
|
||||
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime);
|
||||
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
|
||||
|
||||
if (vEvictionCandidates.empty()) return false;
|
||||
|
||||
// Protect the half of the remaining nodes which have been connected the longest.
|
||||
// This replicates the existing implicit behavior.
|
||||
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
|
||||
vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end());
|
||||
|
||||
if (vEvictionCandidates.empty()) return false;
|
||||
|
||||
// Identify the network group with the most connections
|
||||
std::vector<unsigned char> naMostConnections;
|
||||
unsigned int nMostConnections = 0;
|
||||
std::map<std::vector<unsigned char>, std::vector<CNodeRef> > mapAddrCounts;
|
||||
BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) {
|
||||
mapAddrCounts[node->addr.GetGroup()].push_back(node);
|
||||
|
||||
if (mapAddrCounts[node->addr.GetGroup()].size() > nMostConnections) {
|
||||
nMostConnections = mapAddrCounts[node->addr.GetGroup()].size();
|
||||
naMostConnections = node->addr.GetGroup();
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce to the network group with the most connections
|
||||
vEvictionCandidates = mapAddrCounts[naMostConnections];
|
||||
|
||||
// Do not disconnect peers if there is only 1 connection from their network group
|
||||
if (vEvictionCandidates.size() <= 1)
|
||||
// unless we prefer the new connection (for whitelisted peers)
|
||||
if (!fPreferNewConnection)
|
||||
return false;
|
||||
|
||||
// Disconnect the most recent connection from the network group with the most connections
|
||||
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
|
||||
vEvictionCandidates[0]->fDisconnect = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void AcceptConnection(const ListenSocket& hListenSocket) {
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
|
||||
CAddress addr;
|
||||
int nInbound = 0;
|
||||
int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS;
|
||||
|
||||
if (hSocket != INVALID_SOCKET)
|
||||
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
|
||||
LogPrintf("Warning: Unknown socket family\n");
|
||||
|
||||
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
if (pnode->fInbound)
|
||||
nInbound++;
|
||||
}
|
||||
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
{
|
||||
int nErr = WSAGetLastError();
|
||||
if (nErr != WSAEWOULDBLOCK)
|
||||
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsSelectableSocket(hSocket))
|
||||
{
|
||||
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CNode::IsBanned(addr) && !whitelisted)
|
||||
{
|
||||
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nInbound >= nMaxInbound)
|
||||
{
|
||||
if (!AttemptToEvictConnection(whitelisted)) {
|
||||
// No connection to evict, disconnect the new connection
|
||||
LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n");
|
||||
CloseSocket(hSocket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CNode* pnode = new CNode(hSocket, addr, "", true);
|
||||
pnode->AddRef();
|
||||
pnode->fWhitelisted = whitelisted;
|
||||
|
||||
LogPrint("net", "connection from %s accepted\n", addr.ToString());
|
||||
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
vNodes.push_back(pnode);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadSocketHandler()
|
||||
{
|
||||
unsigned int nPrevNodeCount = 0;
|
||||
@ -933,64 +1148,7 @@ void ThreadSocketHandler()
|
||||
{
|
||||
if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
|
||||
{
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
|
||||
CAddress addr;
|
||||
int nInbound = 0;
|
||||
int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS;
|
||||
|
||||
if (hSocket != INVALID_SOCKET)
|
||||
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
|
||||
LogPrintf("Warning: Unknown socket family\n");
|
||||
|
||||
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
if (pnode->fInbound)
|
||||
nInbound++;
|
||||
}
|
||||
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
{
|
||||
int nErr = WSAGetLastError();
|
||||
if (nErr != WSAEWOULDBLOCK)
|
||||
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
|
||||
}
|
||||
else if (!IsSelectableSocket(hSocket))
|
||||
{
|
||||
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
else if (nInbound >= nMaxInbound)
|
||||
{
|
||||
LogPrint("net", "connection from %s dropped (full)\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
else if (!whitelisted && (nInbound >= (nMaxInbound - nWhiteConnections)))
|
||||
{
|
||||
LogPrint("net", "connection from %s dropped (non-whitelisted)\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
else if (CNode::IsBanned(addr) && !whitelisted)
|
||||
{
|
||||
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
else
|
||||
{
|
||||
CNode* pnode = new CNode(hSocket, addr, "", true);
|
||||
pnode->AddRef();
|
||||
pnode->fWhitelisted = whitelisted;
|
||||
|
||||
LogPrint("net", "connection from %s accepted\n", addr.ToString());
|
||||
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
vNodes.push_back(pnode);
|
||||
}
|
||||
}
|
||||
AcceptConnection(hListenSocket);
|
||||
}
|
||||
}
|
||||
|
||||
|
13
src/net.h
13
src/net.h
@ -143,19 +143,8 @@ extern uint64_t nLocalServices;
|
||||
extern uint64_t nLocalHostNonce;
|
||||
extern CAddrMan addrman;
|
||||
|
||||
// The allocation of connections against the maximum allowed (nMaxConnections)
|
||||
// is prioritized as follows:
|
||||
// 1st: Outbound connections (MAX_OUTBOUND_CONNECTIONS)
|
||||
// 2nd: Inbound connections from whitelisted peers (nWhiteConnections)
|
||||
// 3rd: Inbound connections from non-whitelisted peers
|
||||
// Thus, the number of connection slots for the general public to use is:
|
||||
// nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + nWhiteConnections)
|
||||
// Any additional inbound connections beyond limits will be immediately closed
|
||||
|
||||
/** Maximum number of connections to simultaneously allow (aka connection slots) */
|
||||
extern int nMaxConnections;
|
||||
/** Number of connection slots to reserve for inbound from whitelisted peers */
|
||||
extern int nWhiteConnections;
|
||||
|
||||
extern std::vector<CNode*> vNodes;
|
||||
extern CCriticalSection cs_vNodes;
|
||||
@ -395,6 +384,8 @@ public:
|
||||
int64_t nPingUsecStart;
|
||||
// Last measured round-trip time.
|
||||
int64_t nPingUsecTime;
|
||||
// Best measured round-trip time.
|
||||
int64_t nMinPingUsecTime;
|
||||
// Whether a ping is requested.
|
||||
bool fPingQueued;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user