Merge pull request #6374
027de94Use network group instead of CNetAddr in final pass to select node to disconnect (Patrick Strateman)000c18aFix comment (Patrick Strateman)fed3094Acquire cs_vNodes before changing refrence counts (Patrick Strateman)69ee1aaCNodeRef copy constructor and assignment operator (Patrick Strateman)dc81dd0Return false early if vEvictionCandidates is empty (Patrick Strateman)17f3533Better support for nodes with non-standard nMaxConnections (Patrick Strateman)1317cd1RAII wrapper for CNode* (Patrick Strateman)df23937Add comments to AttemptToEvictConnection (Patrick Strateman)a8f6e45Remove redundant whiteconnections option (Patrick Strateman)b105ba3Prefer to disconnect peers in favor of whitelisted peers (Patrick Strateman)2c70153AttemptToEvictConnection (Patrick Strateman)4bac601Record nMinPingUsecTime (Patrick Strateman)ae037b7Refactor: Move failure conditions to the top of AcceptConnection (Patrick Strateman)1ef4817Refactor: Bail early in AcceptConnection (Patrick Strateman)541a1ddRefactor: AcceptConnection (Patrick Strateman)
This commit is contained in:
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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user