Feeler connections ported from BTC core, eclipse attack mitigation

This commit is contained in:
Duke Leto
2021-02-28 23:28:49 -05:00
parent cadab16cdb
commit ea2b68c1d3
4 changed files with 126 additions and 43 deletions

View File

@@ -62,6 +62,9 @@ using namespace hush;
#endif
#endif
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
#define USE_TLS "encrypted as fuck"
#if defined(USE_TLS) && !defined(TLS1_3_VERSION)
@@ -75,6 +78,7 @@ using namespace std;
namespace {
//TODO: Make these CLI args
const int MAX_OUTBOUND_CONNECTIONS = 64;
const int MAX_FEELER_CONNECTIONS = 1;
const int MAX_INBOUND_FROMIP = 3;
struct ListenSocket {
@@ -1023,8 +1027,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
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;
int nInbound = 0;
int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS);
if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@@ -1412,6 +1416,11 @@ void static ProcessOneShot()
}
}
int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
{
return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
}
void ThreadOpenConnections()
{
// Connect to specific addresses
@@ -1436,8 +1445,11 @@ void ThreadOpenConnections()
// Initiate network connections
int64_t nStart = GetTime();
while (true)
{
// Minimum time before next feeler connection (in microseconds).
int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
while (true) {
ProcessOneShot();
MilliSleep(500);
@@ -1450,19 +1462,17 @@ void ThreadOpenConnections()
if (GetTime() - nStart > 60) {
static bool done = false;
if (!done) {
// skip DNS seeds for staked chains.
LogPrintf("Adding fixed seed nodes.\n");
addrman.Add(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1"));
done = true;
}
}
// Choose an address to connect to based on most recently seen
CAddress addrConnect;
// Only connect out to one peer per network group (/16 for IPv4).
// Use -asmap for ASN bucketing
// Only connect out to one peer per network group. Originally /16 for IPv4, now ASNs via
// -asmap for ASN bucketing, which is enabled by default
// Do this here so we don't have to critsect vNodes inside mapAddresses critsect.
int nOutbound = 0;
set<vector<unsigned char> > setConnected;
@@ -1475,13 +1485,38 @@ void ThreadOpenConnections()
}
}
}
assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS));
// "Feeler Connections" as per https://eprint.iacr.org/2015/263.pdf
// "Eclipse Attacks on Bitcoins Peer-to-Peer Network" by
// Ethan Heilman, Alison Kendler, Aviv Zohar, Sharon Goldberg.
//
// 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.
// * Make feeler connections randomly with 120s average interval via PoissonNextSend.
// Originally from https://github.com/bitcoin/bitcoin/pull/8282
// Modified for API changes by Duke Leto
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 = GetTime();
int nTries = 0;
while (true)
{
CAddrInfo addr = addrman.Select();
int nTries = 0;
while (true) {
CAddrInfo addr = addrman.Select(fFeeler);
// if we selected an invalid address, restart
if (!addr.IsValid() || setConnected.count(addr.GetGroup(addrman.m_asmap)) || IsLocal(addr))
@@ -1501,6 +1536,13 @@ void ThreadOpenConnections()
if (nANow - addr.nLastTry < 600 && nTries < 30)
continue;
/* TODO: port this code
// only consider nodes missing relevant services after 40 failed attempts
if ((addr.nServices & nRelevantServices) != nRelevantServices && nTries < 40)
continue;
*/
//TODO: why is this a good thing?
// do not allow non-default ports, unless after 50 invalid addresses selected already
if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50)
continue;
@@ -1509,8 +1551,18 @@ 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().c_str());
printf("%s: Making feeler connection to %s\n", __func__, addrConnect.ToString().c_str());
}
//int failures = setConnected.size() >= std::min(nMaxConnections - 1, 2);
OpenNetworkConnection(addrConnect,/*failures,*/ &grant, NULL, false, fFeeler);
}
}
}
@@ -1591,7 +1643,7 @@ void ThreadOpenAddedConnections()
}
// 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, /* bool fCountFailure, */ CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
{
// Initiate outbound network connection
boost::this_thread::interruption_point();
@@ -1604,6 +1656,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
return false;
CNode* pnode = ConnectNode(addrConnect, pszDest);
//CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure);
boost::this_thread::interruption_point();
if (!pnode)
@@ -1611,8 +1664,11 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
if (grantOutbound)
grantOutbound->MoveTo(pnode->grantOutbound);
pnode->fNetworkNode = true;
if (fOneShot)
pnode->fOneShot = true;
if (fFeeler)
pnode->fFeeler = true;
return true;
}
@@ -1858,7 +1914,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
if (semOutbound == NULL) {
// initialize semaphore
int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections);
int nMaxOutbound = min((MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS), nMaxConnections);
semOutbound = new CSemaphore(nMaxOutbound);
}
@@ -1907,7 +1963,7 @@ bool StopNode()
{
LogPrintf("StopNode()\n");
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 (HUSH_NSPV_FULLNODE && fAddressesInitialized)
@@ -2166,12 +2222,13 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
nTimeOffset = 0;
addr = addrIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0;
strSubVer = "";
nVersion = 0;
strSubVer = "";
fAllowlisted = false;
fOneShot = false;
fClient = false; // set by version message
fInbound = fInboundIn;
fOneShot = false;
fClient = false; // set by version message
fFeeler = false;
fInbound = fInboundIn;
fNetworkNode = false;
fSuccessfullyConnected = false;
fDisconnect = false;