Added feeler connections increasing good addrs in the tried table. (#1530)
Tests if addresses are online or offline by briefly connecting to them. These short lived connections are referred to as feeler connections. Feeler connections are designed to increase the number of fresh online addresses in tried by selecting and connecting to addresses in new. One feeler connection is attempted on average once every two minutes. This change was suggested as Countermeasure 4 in Eclipse Attacks on Bitcoin’s Peer-to-Peer Network, Ethan Heilman, Alison Kendler, Aviv Zohar, Sharon Goldberg. ePrint Archive Report 2015/263. March 2015.
This commit is contained in:
parent
27b6f36338
commit
d934ffb2ff
@ -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)
|
||||
{
|
||||
|
59
src/net.cpp
59
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; i<MAX_OUTBOUND_CONNECTIONS; i++)
|
||||
for (int i=0; i<(MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++)
|
||||
semOutbound->post();
|
||||
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user