Browse Source

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.
0.13
Ethan Heilman 9 years ago committed by Pieter Wuille
parent
commit
2611ad79a5
  1. 6
      src/main.cpp
  2. 59
      src/net.cpp
  3. 5
      src/net.h
  4. 22
      src/test/net_tests.cpp

6
src/main.cpp

@ -4917,6 +4917,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (strCommand == NetMsgType::VERSION) 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 // Each connection can only send one version message
if (pfrom->nVersion != 0) if (pfrom->nVersion != 0)
{ {

59
src/net.cpp

@ -42,6 +42,9 @@
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) // Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
#define DUMP_ADDRESSES_INTERVAL 900 #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) #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0 #define MSG_NOSIGNAL 0
#endif #endif
@ -60,6 +63,7 @@
namespace { namespace {
const int MAX_OUTBOUND_CONNECTIONS = 8; const int MAX_OUTBOUND_CONNECTIONS = 8;
const int MAX_FEELER_CONNECTIONS = 1;
struct ListenSocket { struct ListenSocket {
SOCKET socket; SOCKET socket;
@ -1016,7 +1020,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr; CAddress addr;
int nInbound = 0; 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 (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@ -1609,6 +1614,9 @@ void ThreadOpenConnections()
// Initiate network connections // Initiate network connections
int64_t nStart = GetTime(); int64_t nStart = GetTime();
// Minimum time before next feeler connection (in microseconds).
int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
while (true) while (true)
{ {
ProcessOneShot(); ProcessOneShot();
@ -1646,13 +1654,36 @@ void ThreadOpenConnections()
} }
} }
} }
assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS));
int64_t nANow = GetAdjustedTime(); // 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; int nTries = 0;
while (true) while (true)
{ {
CAddrInfo addr = addrman.Select(); CAddrInfo addr = addrman.Select(fFeeler);
// if we selected an invalid address, restart // if we selected an invalid address, restart
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
@ -1688,8 +1719,17 @@ void ThreadOpenConnections()
break; break;
} }
if (addrConnect.IsValid()) if (addrConnect.IsValid()) {
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant);
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, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler);
}
} }
} }
@ -1771,7 +1811,7 @@ void ThreadOpenAddedConnections()
} }
// if successful, this moves the passed grant to the constructed node // if successful, this moves the passed grant to the constructed node
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
{ {
// //
// Initiate outbound network connection // Initiate outbound network connection
@ -1795,6 +1835,8 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem
pnode->fNetworkNode = true; pnode->fNetworkNode = true;
if (fOneShot) if (fOneShot)
pnode->fOneShot = true; pnode->fOneShot = true;
if (fFeeler)
pnode->fFeeler = true;
return true; return true;
} }
@ -2054,7 +2096,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
if (semOutbound == NULL) { if (semOutbound == NULL) {
// initialize semaphore // 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); semOutbound = new CSemaphore(nMaxOutbound);
} }
@ -2096,7 +2138,7 @@ bool StopNode()
LogPrintf("StopNode()\n"); LogPrintf("StopNode()\n");
MapPort(false); MapPort(false);
if (semOutbound) 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(); semOutbound->post();
if (fAddressesInitialized) if (fAddressesInitialized)
@ -2437,6 +2479,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
fWhitelisted = false; fWhitelisted = false;
fOneShot = false; fOneShot = false;
fClient = false; // set by version message fClient = false; // set by version message
fFeeler = false;
fInbound = fInboundIn; fInbound = fInboundIn;
fNetworkNode = false; fNetworkNode = false;
fSuccessfullyConnected = false; fSuccessfullyConnected = false;

5
src/net.h

@ -41,6 +41,8 @@ namespace boost {
static const int PING_INTERVAL = 2 * 60; static const int PING_INTERVAL = 2 * 60;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */ /** Time after which to disconnect, after waiting for a ping response (or inactivity). */
static const int TIMEOUT_INTERVAL = 20 * 60; static const int TIMEOUT_INTERVAL = 20 * 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 */ /** The maximum number of entries in an 'inv' protocol message */
static const unsigned int MAX_INV_SZ = 50000; static const unsigned int MAX_INV_SZ = 50000;
/** The maximum number of new addresses to accumulate before announcing. */ /** The maximum number of new addresses to accumulate before announcing. */
@ -89,7 +91,7 @@ CNode* FindNode(const CSubNet& subNet);
CNode* FindNode(const std::string& addrName); CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& ip); CNode* FindNode(const CService& ip);
CNode* FindNode(const NodeId id); //TODO: Remove this CNode* FindNode(const NodeId id); //TODO: Remove this
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
void MapPort(bool fUseUPnP); void MapPort(bool fUseUPnP);
unsigned short GetListenPort(); unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
@ -350,6 +352,7 @@ public:
// the network or wire types and the cleaned string used when displayed or logged. // the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer; std::string strSubVer, cleanSubVer;
bool fWhitelisted; // This peer can bypass DoS banning. 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 fOneShot;
bool fClient; bool fClient;
bool fInbound; bool fInbound;

22
src/test/net_tests.cpp

@ -142,4 +142,26 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
BOOST_CHECK(addrman2.size() == 0); 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() BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save