Skip to content

Commit dbb1f64

Browse files
committed
Added feeler connections increasing good addrs in the tried table.
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.
1 parent 8250de1 commit dbb1f64

File tree

4 files changed

+83
-9
lines changed

4 files changed

+83
-9
lines changed

src/main.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4903,6 +4903,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
49034903

49044904
if (strCommand == NetMsgType::VERSION)
49054905
{
4906+
// Feeler connections exist only to verify if address is online.
4907+
if (pfrom->fFeeler) {
4908+
assert(pfrom->fInbound == false);
4909+
pfrom->fDisconnect = true;
4910+
}
4911+
49064912
// Each connection can only send one version message
49074913
if (pfrom->nVersion != 0)
49084914
{

src/net.cpp

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
4444
#define DUMP_ADDRESSES_INTERVAL 900
4545

46+
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
47+
#define FEELER_SLEEP_WINDOW 1
48+
4649
#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)
4750
#define MSG_NOSIGNAL 0
4851
#endif
@@ -61,6 +64,7 @@
6164

6265
namespace {
6366
const int MAX_OUTBOUND_CONNECTIONS = 8;
67+
const int MAX_FEELER_CONNECTIONS = 1;
6468

6569
struct ListenSocket {
6670
SOCKET socket;
@@ -1017,7 +1021,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
10171021
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
10181022
CAddress addr;
10191023
int nInbound = 0;
1020-
int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS;
1024+
int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS);
1025+
assert(nMaxInbound > 0);
10211026

10221027
if (hSocket != INVALID_SOCKET)
10231028
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@@ -1613,6 +1618,9 @@ void ThreadOpenConnections()
16131618

16141619
// Initiate network connections
16151620
int64_t nStart = GetTime();
1621+
1622+
// Minimum time before next feeler connection (in microseconds).
1623+
int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
16161624
while (true)
16171625
{
16181626
ProcessOneShot();
@@ -1652,13 +1660,36 @@ void ThreadOpenConnections()
16521660
}
16531661
}
16541662
}
1663+
assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS));
16551664

1656-
int64_t nANow = GetAdjustedTime();
1665+
// Feeler Connections
1666+
//
1667+
// Design goals:
1668+
// * Increase the number of connectable addresses in the tried table.
1669+
//
1670+
// Method:
1671+
// * Choose a random address from new and attempt to connect to it if we can connect
1672+
// successfully it is added to tried.
1673+
// * Start attempting feeler connections only after node finishes making outbound
1674+
// connections.
1675+
// * Only make a feeler connection once every few minutes.
1676+
//
1677+
bool fFeeler = false;
1678+
if (nOutbound >= MAX_OUTBOUND_CONNECTIONS) {
1679+
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
1680+
if (nTime > nNextFeeler) {
1681+
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
1682+
fFeeler = true;
1683+
} else {
1684+
continue;
1685+
}
1686+
}
16571687

1688+
int64_t nANow = GetAdjustedTime();
16581689
int nTries = 0;
16591690
while (true)
16601691
{
1661-
CAddrInfo addr = addrman.Select();
1692+
CAddrInfo addr = addrman.Select(fFeeler);
16621693

16631694
// if we selected an invalid address, restart
16641695
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
@@ -1694,8 +1725,17 @@ void ThreadOpenConnections()
16941725
break;
16951726
}
16961727

1697-
if (addrConnect.IsValid())
1698-
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant);
1728+
if (addrConnect.IsValid()) {
1729+
1730+
if (fFeeler) {
1731+
// Add small amount of random noise before connection to avoid synchronization.
1732+
int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);
1733+
MilliSleep(randsleep);
1734+
LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString());
1735+
}
1736+
1737+
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler);
1738+
}
16991739
}
17001740
}
17011741

@@ -1777,7 +1817,7 @@ void ThreadOpenAddedConnections()
17771817
}
17781818

17791819
// if successful, this moves the passed grant to the constructed node
1780-
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot)
1820+
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
17811821
{
17821822
//
17831823
// Initiate outbound network connection
@@ -1801,6 +1841,8 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem
18011841
pnode->fNetworkNode = true;
18021842
if (fOneShot)
18031843
pnode->fOneShot = true;
1844+
if (fFeeler)
1845+
pnode->fFeeler = true;
18041846

18051847
return true;
18061848
}
@@ -2062,7 +2104,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
20622104

20632105
if (semOutbound == NULL) {
20642106
// initialize semaphore
2065-
int nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections);
2107+
int nMaxOutbound = std::min((MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS), nMaxConnections);
20662108
semOutbound = new CSemaphore(nMaxOutbound);
20672109
}
20682110

@@ -2107,7 +2149,7 @@ bool StopNode()
21072149
LogPrintf("StopNode()\n");
21082150
MapPort(false);
21092151
if (semOutbound)
2110-
for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
2152+
for (int i=0; i<(MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++)
21112153
semOutbound->post();
21122154

21132155
if (fAddressesInitialized)
@@ -2448,6 +2490,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
24482490
fWhitelisted = false;
24492491
fOneShot = false;
24502492
fClient = false; // set by version message
2493+
fFeeler = false;
24512494
fInbound = fInboundIn;
24522495
fNetworkNode = false;
24532496
fSuccessfullyConnected = false;

src/net.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ namespace boost {
4141
static const int PING_INTERVAL = 2 * 60;
4242
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
4343
static const int TIMEOUT_INTERVAL = 20 * 60;
44+
/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/
45+
static const int FEELER_INTERVAL = 120;
4446
/** The maximum number of entries in an 'inv' protocol message */
4547
static const unsigned int MAX_INV_SZ = 50000;
4648
/** The maximum number of new addresses to accumulate before announcing. */
@@ -89,7 +91,7 @@ CNode* FindNode(const CSubNet& subNet);
8991
CNode* FindNode(const std::string& addrName);
9092
CNode* FindNode(const CService& ip);
9193
CNode* FindNode(const NodeId id); //TODO: Remove this
92-
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
94+
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
9395
void MapPort(bool fUseUPnP);
9496
unsigned short GetListenPort();
9597
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
@@ -350,6 +352,7 @@ class CNode
350352
// the network or wire types and the cleaned string used when displayed or logged.
351353
std::string strSubVer, cleanSubVer;
352354
bool fWhitelisted; // This peer can bypass DoS banning.
355+
bool fFeeler; // If true this node is being used as a short lived feeler.
353356
bool fOneShot;
354357
bool fClient;
355358
bool fInbound;

src/test/net_tests.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,26 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
150150
BOOST_CHECK(addrman2.size() == 0);
151151
}
152152

153+
BOOST_AUTO_TEST_CASE(cnode_simple_test)
154+
{
155+
SOCKET hSocket = INVALID_SOCKET;
156+
157+
in_addr ipv4Addr;
158+
ipv4Addr.s_addr = 0xa0b0c001;
159+
160+
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
161+
std::string pszDest = "";
162+
bool fInboundIn = false;
163+
164+
// Test that fFeeler is false by default.
165+
CNode* pnode1 = new CNode(hSocket, addr, pszDest, fInboundIn);
166+
BOOST_CHECK(pnode1->fInbound == false);
167+
BOOST_CHECK(pnode1->fFeeler == false);
168+
169+
fInboundIn = true;
170+
CNode* pnode2 = new CNode(hSocket, addr, pszDest, fInboundIn);
171+
BOOST_CHECK(pnode2->fInbound == true);
172+
BOOST_CHECK(pnode2->fFeeler == false);
173+
}
174+
153175
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)