aboutsummaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@protonmail.com>2020-09-21 18:46:52 +0200
committerWladimir J. van der Laan <laanwj@protonmail.com>2020-09-21 19:36:57 +0200
commitc0c409dcd330e8b837291b1d331894aeb0015ead (patch)
tree5610b0546fafad84bbbe4de6c09df0bed116afca /src/net.cpp
parentb99a1633b270e0e89479b2bb2ae19a8a8dc0fa05 (diff)
parent0d04784af151de249bbbcbad51e6e8ad9af8f5a3 (diff)
downloadbitcoin-c0c409dcd330e8b837291b1d331894aeb0015ead.tar.xz
Merge #19697: Improvements on ADDR caching
0d04784af151de249bbbcbad51e6e8ad9af8f5a3 Refactor the functional test (Gleb Naumenko) 83ad65f31b5c9441ae1618614082e584854a14e1 Address nits in ADDR caching (Gleb Naumenko) 81b00f87800f40cb14f2131ff27668bd2bb9e551 Add indexing ADDR cache by local socket addr (Gleb Naumenko) 42ec5585424ceb91bed07826dde15697c020661a Justify the choice of ADDR cache lifetime (Gleb Naumenko) Pull request description: This is a follow-up on #18991 which does 3 things: - improves privacy of a node listening to multiple addresses via adding cache index by local socket addr (suggested [here](https://github.com/bitcoin/bitcoin/pull/18991#issuecomment-668219345)) - documents on the choice of 24h cache lifetime - addresses nits from #18991 ACKs for top commit: jnewbery: utACK 0d04784af151de249bbbcbad51e6e8ad9af8f5a3 vasild: ACK 0d04784 jonatack: Code review ACK 0d04784 Tree-SHA512: bb65a34dd1ce2811186d3e4469bc33e8399cebaaa494ce13041c7cff23275870e4176a719f7a72f8d779c49f8b2344bf4fa1aeb3ea4e2626d5ae76514f00a750
Diffstat (limited to 'src/net.cpp')
-rw-r--r--src/net.cpp45
1 files changed, 39 insertions, 6 deletions
diff --git a/src/net.cpp b/src/net.cpp
index e35d05cec0..0fd95dfe3f 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -94,6 +94,7 @@ const std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
+static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
//
// Global state variables
//
@@ -2560,15 +2561,47 @@ std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pc
return addresses;
}
-std::vector<CAddress> CConnman::GetAddresses(Network requestor_network, size_t max_addresses, size_t max_pct)
+std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
{
+ SOCKET socket;
+ WITH_LOCK(requestor.cs_hSocket, socket = requestor.hSocket);
+ auto local_socket_bytes = GetBindAddress(socket).GetAddrBytes();
+ uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
+ .Write(requestor.addr.GetNetwork())
+ .Write(local_socket_bytes.data(), local_socket_bytes.size())
+ .Finalize();
const auto current_time = GetTime<std::chrono::microseconds>();
- if (m_addr_response_caches.find(requestor_network) == m_addr_response_caches.end() ||
- m_addr_response_caches[requestor_network].m_update_addr_response < current_time) {
- m_addr_response_caches[requestor_network].m_addrs_response_cache = GetAddresses(max_addresses, max_pct);
- m_addr_response_caches[requestor_network].m_update_addr_response = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
+ auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
+ CachedAddrResponse& cache_entry = r.first->second;
+ if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
+ cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct);
+ // Choosing a proper cache lifetime is a trade-off between the privacy leak minimization
+ // and the usefulness of ADDR responses to honest users.
+ //
+ // Longer cache lifetime makes it more difficult for an attacker to scrape
+ // enough AddrMan data to maliciously infer something useful.
+ // By the time an attacker scraped enough AddrMan records, most of
+ // the records should be old enough to not leak topology info by
+ // e.g. analyzing real-time changes in timestamps.
+ //
+ // It takes only several hundred requests to scrape everything from an AddrMan containing 100,000 nodes,
+ // so ~24 hours of cache lifetime indeed makes the data less inferable by the time
+ // most of it could be scraped (considering that timestamps are updated via
+ // ADDR self-announcements and when nodes communicate).
+ // We also should be robust to those attacks which may not require scraping *full* victim's AddrMan
+ // (because even several timestamps of the same handful of nodes may leak privacy).
+ //
+ // On the other hand, longer cache lifetime makes ADDR responses
+ // outdated and less useful for an honest requestor, e.g. if most nodes
+ // in the ADDR response are no longer active.
+ //
+ // However, the churn in the network is known to be rather low. Since we consider
+ // nodes to be "terrible" (see IsTerrible()) if the timestamps are older than 30 days,
+ // max. 24 hours of "penalty" due to cache shouldn't make any meaningful difference
+ // in terms of the freshness of the response.
+ cache_entry.m_cache_entry_expiration = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
}
- return m_addr_response_caches[requestor_network].m_addrs_response_cache;
+ return cache_entry.m_addrs_response_cache;
}
bool CConnman::AddNode(const std::string& strNode)