mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-13 07:28:59 +01:00
Merge #17428: p2p: Try to preserve outbound block-relay-only connections during restart
a490d074b3doc: Add anchors.dat to files.md (Hennadii Stepanov)0a85e5a7bcp2p: Try to connect to anchors once (Hennadii Stepanov)5543c7ab28p2p: Fix off-by-one error in fetching address loop (Hennadii Stepanov)4170b46544p2p: Integrate DumpAnchors() and ReadAnchors() into CConnman (Hennadii Stepanov)bad16aff49p2p: Add CConnman::GetCurrentBlockRelayOnlyConns() (Hennadii Stepanov)c29272a157p2p: Add ReadAnchors() (Hennadii Stepanov)567008d2a0p2p: Add DumpAnchors() (Hennadii Stepanov) Pull request description: This is an implementation of #17326: - all (currently 2) outbound block-relay-only connections (#15759) are dumped to `anchors.dat` file - on restart a node tries to connect to the addresses from `anchors.dat` This PR prevents a type of eclipse attack when an attacker exploits a victim node restart to force it to connect to new, probably adversarial, peers. ACKs for top commit: jnewbery: code review ACKa490d074b3laanwj: Code review ACKa490d074b3Tree-SHA512: 0f5098a3882f2814be1aa21de308cd09e6654f4e7054b79f3cfeaf26bc02b814ca271497ed00018d199ee596a8cb9b126acee8b666a29e225b08eb2a49b02ddd
This commit is contained in:
71
src/net.cpp
71
src/net.cpp
@@ -47,6 +47,12 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/** Maximum number of block-relay-only anchor connections */
|
||||
static constexpr size_t MAX_BLOCK_RELAY_ONLY_ANCHORS = 2;
|
||||
static_assert (MAX_BLOCK_RELAY_ONLY_ANCHORS <= static_cast<size_t>(MAX_BLOCK_RELAY_ONLY_CONNECTIONS), "MAX_BLOCK_RELAY_ONLY_ANCHORS must not exceed MAX_BLOCK_RELAY_ONLY_CONNECTIONS.");
|
||||
/** Anchor IP address database file name */
|
||||
const char* const ANCHORS_DATABASE_FILENAME = "anchors.dat";
|
||||
|
||||
// How often to dump addresses to peers.dat
|
||||
static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15};
|
||||
|
||||
@@ -1933,10 +1939,12 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
|
||||
ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
|
||||
int64_t nTime = GetTimeMicros();
|
||||
bool anchor = false;
|
||||
bool fFeeler = false;
|
||||
|
||||
// Determine what type of connection to open. Opening
|
||||
// OUTBOUND_FULL_RELAY connections gets the highest priority until we
|
||||
// BLOCK_RELAY connections to addresses from anchors.dat gets the highest
|
||||
// priority. Then we open OUTBOUND_FULL_RELAY priority until we
|
||||
// meet our full-relay capacity. Then we open BLOCK_RELAY connection
|
||||
// until we hit our block-relay-only peer limit.
|
||||
// GetTryNewOutboundPeer() gets set when a stale tip is detected, so we
|
||||
@@ -1944,7 +1952,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
// these conditions are met, check the nNextFeeler timer to decide if
|
||||
// we should open a FEELER.
|
||||
|
||||
if (nOutboundFullRelay < m_max_outbound_full_relay) {
|
||||
if (!m_anchors.empty() && (nOutboundBlockRelay < m_max_outbound_block_relay)) {
|
||||
conn_type = ConnectionType::BLOCK_RELAY;
|
||||
anchor = true;
|
||||
} else if (nOutboundFullRelay < m_max_outbound_full_relay) {
|
||||
// OUTBOUND_FULL_RELAY
|
||||
} else if (nOutboundBlockRelay < m_max_outbound_block_relay) {
|
||||
conn_type = ConnectionType::BLOCK_RELAY;
|
||||
@@ -1965,6 +1976,24 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
int nTries = 0;
|
||||
while (!interruptNet)
|
||||
{
|
||||
if (anchor && !m_anchors.empty()) {
|
||||
const CAddress addr = m_anchors.back();
|
||||
m_anchors.pop_back();
|
||||
if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) ||
|
||||
!HasAllDesirableServiceFlags(addr.nServices) ||
|
||||
setConnected.count(addr.GetGroup(addrman.m_asmap))) continue;
|
||||
addrConnect = addr;
|
||||
LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
|
||||
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
|
||||
// already-connected network ranges, ...) before trying new addrman addresses.
|
||||
nTries++;
|
||||
if (nTries > 100)
|
||||
break;
|
||||
|
||||
CAddrInfo addr = addrman.SelectTriedCollision();
|
||||
|
||||
// SelectTriedCollision returns an invalid address if it is empty.
|
||||
@@ -1982,13 +2011,6 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
|
||||
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
|
||||
// already-connected network ranges, ...) before trying new addrman addresses.
|
||||
nTries++;
|
||||
if (nTries > 100)
|
||||
break;
|
||||
|
||||
if (!IsReachable(addr))
|
||||
continue;
|
||||
|
||||
@@ -2028,6 +2050,19 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
|
||||
{
|
||||
std::vector<CAddress> ret;
|
||||
LOCK(cs_vNodes);
|
||||
for (const CNode* pnode : vNodes) {
|
||||
if (pnode->IsBlockOnlyConn()) {
|
||||
ret.push_back(pnode->addr);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
|
||||
{
|
||||
std::vector<AddedNodeInfo> ret;
|
||||
@@ -2427,6 +2462,15 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||
}
|
||||
}
|
||||
|
||||
if (m_use_addrman_outgoing) {
|
||||
// Load addresses from anchors.dat
|
||||
m_anchors = ReadAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME);
|
||||
if (m_anchors.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
|
||||
m_anchors.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
|
||||
}
|
||||
LogPrintf("%i block-relay-only anchors will be tried for connections.\n", m_anchors.size());
|
||||
}
|
||||
|
||||
uiInterface.InitMessage(_("Starting network threads...").translated);
|
||||
|
||||
fAddressesInitialized = true;
|
||||
@@ -2542,6 +2586,15 @@ void CConnman::StopNodes()
|
||||
if (fAddressesInitialized) {
|
||||
DumpAddresses();
|
||||
fAddressesInitialized = false;
|
||||
|
||||
if (m_use_addrman_outgoing) {
|
||||
// Anchor connections are only dumped during clean shutdown.
|
||||
std::vector<CAddress> anchors_to_dump = GetCurrentBlockRelayOnlyConns();
|
||||
if (anchors_to_dump.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
|
||||
anchors_to_dump.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
|
||||
}
|
||||
DumpAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME, anchors_to_dump);
|
||||
}
|
||||
}
|
||||
|
||||
// Close sockets
|
||||
|
||||
Reference in New Issue
Block a user