diff --git a/src/main.cpp b/src/main.cpp index ca3f718ab2..eec4c6eddd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5284,6 +5284,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (strCommand == NetMsgType::VERSION) { + // Feeler connections exist only to verify if address is online. + if (pfrom->fFeeler) { + assert(pfrom->fInbound == false); + pfrom->fDisconnect = true; + } + // Each connection can only send one version message if (pfrom->nVersion != 0) { diff --git a/src/net.cpp b/src/net.cpp index 9991d9f18b..b2e0b34451 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -48,6 +48,9 @@ // Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 +// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization. +#define FEELER_SLEEP_WINDOW 1 + #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif @@ -67,6 +70,7 @@ namespace { const int MAX_OUTBOUND_CONNECTIONS = 8; const int MAX_OUTBOUND_MASTERNODE_CONNECTIONS = 20; + const int MAX_FEELER_CONNECTIONS = 1; struct ListenSocket { SOCKET socket; @@ -1053,7 +1057,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); CAddress addr; int nInbound = 0; - int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS; + int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); + assert(nMaxInbound > 0); if (hSocket != INVALID_SOCKET) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) @@ -1627,6 +1632,9 @@ void ThreadOpenConnections() // Initiate network connections int64_t nStart = GetTime(); + + // Minimum time before next feeler connection (in microseconds). + int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL); while (true) { ProcessOneShot(); @@ -1664,13 +1672,36 @@ void ThreadOpenConnections() } } } + assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS)); + + // Feeler Connections + // + // Design goals: + // * Increase the number of connectable addresses in the tried table. + // + // Method: + // * Choose a random address from new and attempt to connect to it if we can connect + // successfully it is added to tried. + // * Start attempting feeler connections only after node finishes making outbound + // connections. + // * Only make a feeler connection once every few minutes. + // + bool fFeeler = false; + if (nOutbound >= MAX_OUTBOUND_CONNECTIONS) { + int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds). + if (nTime > nNextFeeler) { + nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); + fFeeler = true; + } else { + continue; + } + } int64_t nANow = GetAdjustedTime(); - int nTries = 0; while (true) { - CAddrInfo addr = addrman.Select(); + CAddrInfo addr = addrman.Select(fFeeler); // if we selected an invalid address, restart if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) @@ -1702,8 +1733,17 @@ void ThreadOpenConnections() break; } - if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect, &grant); + if (addrConnect.IsValid()) { + + if (fFeeler) { + // Add small amount of random noise before connection to avoid synchronization. + int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000); + MilliSleep(randsleep); + LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString()); + } + + OpenNetworkConnection(addrConnect, &grant, NULL, false, fFeeler); + } } } @@ -1829,7 +1869,7 @@ void ThreadMnbRequestConnections() } // if successful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler) { // // Initiate outbound network connection @@ -1852,6 +1892,8 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu grantOutbound->MoveTo(pnode->grantOutbound); if (fOneShot) pnode->fOneShot = true; + if (fFeeler) + pnode->fFeeler = true; return true; } @@ -2101,7 +2143,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) if (semOutbound == NULL) { // initialize semaphore - int nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); + int nMaxOutbound = std::min((MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS), nMaxConnections); semOutbound = new CSemaphore(nMaxOutbound); } @@ -2151,7 +2193,7 @@ bool StopNode() LogPrintf("StopNode()\n"); MapPort(false); if (semOutbound) - for (int i=0; ipost(); if (semMasternodeOutbound) @@ -2554,6 +2596,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa fWhitelisted = false; fOneShot = false; fClient = false; // set by version message + fFeeler = false; fInbound = fInboundIn; fNetworkNode = fNetworkNodeIn; fSuccessfullyConnected = false; diff --git a/src/net.h b/src/net.h index b02c610856..8f1da56319 100644 --- a/src/net.h +++ b/src/net.h @@ -43,6 +43,8 @@ static const int PING_INTERVAL = 2 * 60; static const int TIMEOUT_INTERVAL = 20 * 60; /** Minimum time between warnings printed to log. */ static const int WARNING_INTERVAL = 10 * 60; +/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/ +static const int FEELER_INTERVAL = 120; /** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; /** The maximum number of new addresses to accumulate before announcing. */ @@ -91,7 +93,7 @@ CNode* FindNode(const CService& ip); // fConnectToMasternode should be 'true' only if you want this node to allow to connect to itself // and/or you want it to be disconnected on CMasternodeMan::ProcessMasternodeConnections() CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL, bool fConnectToMasternode = false); -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); @@ -357,6 +359,7 @@ public: // the network or wire types and the cleaned string used when displayed or logged. std::string strSubVer, cleanSubVer; bool fWhitelisted; // This peer can bypass DoS banning. + bool fFeeler; // If true this node is being used as a short lived feeler. bool fOneShot; bool fClient; bool fInbound; diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 58812d37de..e32568dd8f 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -142,4 +142,26 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) BOOST_CHECK(addrman2.size() == 0); } +BOOST_AUTO_TEST_CASE(cnode_simple_test) +{ + SOCKET hSocket = INVALID_SOCKET; + + in_addr ipv4Addr; + ipv4Addr.s_addr = 0xa0b0c001; + + CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); + std::string pszDest = ""; + bool fInboundIn = false; + + // Test that fFeeler is false by default. + CNode* pnode1 = new CNode(hSocket, addr, pszDest, fInboundIn); + BOOST_CHECK(pnode1->fInbound == false); + BOOST_CHECK(pnode1->fFeeler == false); + + fInboundIn = true; + CNode* pnode2 = new CNode(hSocket, addr, pszDest, fInboundIn); + BOOST_CHECK(pnode2->fInbound == true); + BOOST_CHECK(pnode2->fFeeler == false); +} + BOOST_AUTO_TEST_SUITE_END()