Skip to content

Commit 8eeff95

Browse files
achow101vijaydasmp
authored andcommitted
Merge bitcoin#27581: net: Continuous ASMap health check
3ea54e5 net: Add continuous ASMap health check logging (Fabian Jahr) 28d7e55 test: Add tests for unfiltered GetAddr usage (Fabian Jahr) b8843d3 fuzz: Let fuzzers use filter options in GetAddr/GetAddresses (Fabian Jahr) e16f420 net: Optionally include terrible addresses in GetAddr results (Fabian Jahr) Pull request description: There are certain statistics we can collect by running all our known clearnet addresses against the ASMap file. This could show issues with a maliciously manipulated file or with an old file that has decayed with time. This is just a proof of concept for now. My idea currently is to run the analysis once per day and print the results to logs if an ASMap file is used. ACKs for top commit: achow101: ACK 3ea54e5 mzumsande: ACK 3ea54e5 brunoerg: crACK 3ea54e5 Tree-SHA512: 777acbfac43cc43ce4a0a3612434e4ddbc65f59ae8ffc9e24f21de09011bccb297f0599cbaa82bcf40ef68e5af582c4e98556379db7ceff7d9f97574a1cf8e09
1 parent 98e57c7 commit 8eeff95

File tree

11 files changed

+98
-14
lines changed

11 files changed

+98
-14
lines changed

src/addrman.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ int AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) const
808808
return -1;
809809
}
810810

811-
std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
811+
std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
812812
{
813813
AssertLockHeld(cs);
814814

@@ -838,7 +838,7 @@ std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct
838838
if (network != std::nullopt && ai.GetNetClass() != network) continue;
839839

840840
// Filter for quality
841-
if (ai.IsTerrible(now)) continue;
841+
if (ai.IsTerrible(now) && filtered) continue;
842842

843843
addresses.push_back(ai);
844844
}
@@ -1209,11 +1209,11 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::optiona
12091209
return addrRet;
12101210
}
12111211

1212-
std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
1212+
std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
12131213
{
12141214
LOCK(cs);
12151215
Check();
1216-
auto addresses = GetAddr_(max_addresses, max_pct, network);
1216+
auto addresses = GetAddr_(max_addresses, max_pct, network, filtered);
12171217
Check();
12181218
return addresses;
12191219
}
@@ -1315,9 +1315,9 @@ std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, std::optional<Ne
13151315
return m_impl->Select(new_only, network);
13161316
}
13171317

1318-
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
1318+
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
13191319
{
1320-
return m_impl->GetAddr(max_addresses, max_pct, network);
1320+
return m_impl->GetAddr(max_addresses, max_pct, network, filtered);
13211321
}
13221322

13231323
void AddrMan::Connected(const CService& addr, NodeSeconds time)

src/addrman.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,11 @@ class AddrMan
177177
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
178178
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
179179
* @param[in] network Select only addresses of this network (nullopt = all).
180+
* @param[in] filtered Select only addresses that are considered good quality (false = all).
180181
*
181182
* @return A vector of randomly selected addresses from vRandom.
182183
*/
183-
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
184+
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const;
184185

185186
/** We have successfully connected to this peer. Calling this function
186187
* updates the CAddress's nTime, which is used in our IsTerrible()

src/addrman_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class AddrManImpl
130130
std::pair<CAddress, NodeSeconds> Select(bool new_only, std::optional<Network> network) const
131131
EXCLUSIVE_LOCKS_REQUIRED(!cs);
132132

133-
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
133+
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const
134134
EXCLUSIVE_LOCKS_REQUIRED(!cs);
135135

136136
void Connected(const CService& addr, NodeSeconds time)
@@ -262,7 +262,7 @@ class AddrManImpl
262262
* */
263263
int GetEntry(bool use_tried, size_t bucket, size_t position) const EXCLUSIVE_LOCKS_REQUIRED(cs);
264264

265-
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
265+
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const EXCLUSIVE_LOCKS_REQUIRED(cs);
266266

267267
void Connected_(const CService& addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs);
268268

src/net.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,6 +4089,12 @@ bool CConnman::Start(CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_met
40894089
// Dump network addresses
40904090
scheduler.scheduleEvery([this] { DumpAddresses(); }, DUMP_PEERS_INTERVAL);
40914091

4092+
// Run the ASMap Health check once and then schedule it to run every 24h.
4093+
if (m_netgroupman.UsingASMap()) {
4094+
ASMapHealthCheck();
4095+
scheduler.scheduleEvery([this] { ASMapHealthCheck(); }, ASMAP_HEALTH_CHECK_INTERVAL);
4096+
}
4097+
40924098
return true;
40934099
}
40944100

@@ -4224,9 +4230,9 @@ CConnman::~CConnman()
42244230
Stop();
42254231
}
42264232

4227-
std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
4233+
std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
42284234
{
4229-
std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct, network);
4235+
std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct, network, filtered);
42304236
if (m_banman) {
42314237
addresses.erase(std::remove_if(addresses.begin(), addresses.end(),
42324238
[this](const CAddress& addr){return m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr);}),
@@ -4951,6 +4957,19 @@ void CConnman::PerformReconnections()
49514957
}
49524958
}
49534959

4960+
void CConnman::ASMapHealthCheck()
4961+
{
4962+
const std::vector<CAddress> v4_addrs{GetAddresses(/*max_addresses=*/ 0, /*max_pct=*/ 0, Network::NET_IPV4, /*filtered=*/ false)};
4963+
const std::vector<CAddress> v6_addrs{GetAddresses(/*max_addresses=*/ 0, /*max_pct=*/ 0, Network::NET_IPV6, /*filtered=*/ false)};
4964+
std::vector<CNetAddr> clearnet_addrs;
4965+
clearnet_addrs.reserve(v4_addrs.size() + v6_addrs.size());
4966+
std::transform(v4_addrs.begin(), v4_addrs.end(), std::back_inserter(clearnet_addrs),
4967+
[](const CAddress& addr) { return static_cast<CNetAddr>(addr); });
4968+
std::transform(v6_addrs.begin(), v6_addrs.end(), std::back_inserter(clearnet_addrs),
4969+
[](const CAddress& addr) { return static_cast<CNetAddr>(addr); });
4970+
m_netgroupman.ASMapHealthCheck(clearnet_addrs);
4971+
}
4972+
49544973
// Dump binary message to file, with timestamp.
49554974
static void CaptureMessageToFile(const CAddress& addr,
49564975
const std::string& msg_type,

src/net.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ static const bool DEFAULT_BLOCKSONLY = false;
111111
static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60;
112112
/** Number of file descriptors required for message capture **/
113113
static const int NUM_FDS_MESSAGE_CAPTURE = 1;
114+
/** Interval for ASMap Health Check **/
115+
static constexpr std::chrono::hours ASMAP_HEALTH_CHECK_INTERVAL{24};
114116

115117
static constexpr bool DEFAULT_FORCEDNSSEED{false};
116118
static constexpr bool DEFAULT_DNSSEED{true};
@@ -1295,6 +1297,7 @@ friend class CNode;
12951297
void OpenMasternodeConnection(const CAddress& addrConnect, bool use_v2transport, MasternodeProbeConn probe = MasternodeProbeConn::IsConnection)
12961298
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_unused_i2p_sessions_mutex, !mutexMsgProc, !cs_mapSocketToNode);
12971299
bool CheckIncomingNonce(uint64_t nonce) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
1300+
void ASMapHealthCheck();
12981301

12991302
// alias for thread safety annotations only, not defined
13001303
SharedMutex& GetNodesMutex() const LOCK_RETURNED(m_nodes_mutex);
@@ -1405,8 +1408,9 @@ friend class CNode;
14051408
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
14061409
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
14071410
* @param[in] network Select only addresses of this network (nullopt = all).
1411+
* @param[in] filtered Select only addresses that are considered high quality (false = all).
14081412
*/
1409-
std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
1413+
std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const;
14101414

14111415
/**
14121416
* Cache is used to minimize topology leaks, so it should

src/netgroup.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <netgroup.h>
66

77
#include <hash.h>
8+
#include <logging.h>
89
#include <util/asmap.h>
910

1011
uint256 NetGroupManager::GetAsmapChecksum() const
@@ -109,3 +110,23 @@ uint32_t NetGroupManager::GetMappedAS(const CNetAddr& address) const
109110
uint32_t mapped_as = Interpret(m_asmap, ip_bits);
110111
return mapped_as;
111112
}
113+
114+
void NetGroupManager::ASMapHealthCheck(const std::vector<CNetAddr>& clearnet_addrs) const {
115+
std::set<uint32_t> clearnet_asns{};
116+
int unmapped_count{0};
117+
118+
for (const auto& addr : clearnet_addrs) {
119+
uint32_t asn = GetMappedAS(addr);
120+
if (asn == 0) {
121+
++unmapped_count;
122+
continue;
123+
}
124+
clearnet_asns.insert(asn);
125+
}
126+
127+
LogPrintf("ASMap Health Check: %i clearnet peers are mapped to %i ASNs with %i peers being unmapped\n", clearnet_addrs.size(), clearnet_asns.size(), unmapped_count);
128+
}
129+
130+
bool NetGroupManager::UsingASMap() const {
131+
return m_asmap.size() > 0;
132+
}

src/netgroup.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ class NetGroupManager {
4141
*/
4242
uint32_t GetMappedAS(const CNetAddr& address) const;
4343

44+
/**
45+
* Analyze and log current health of ASMap based buckets.
46+
*/
47+
void ASMapHealthCheck(const std::vector<CNetAddr>& clearnet_addrs) const;
48+
49+
/**
50+
* Indicates whether ASMap is being used for clearnet bucketing.
51+
*/
52+
bool UsingASMap() const;
53+
4454
private:
4555
/** Compressed IP->ASN mapping, loaded from a file when a node starts.
4656
*

src/test/addrman_tests.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,24 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
428428
BOOST_CHECK_EQUAL(addrman->Size(), 2006U);
429429
}
430430

431+
BOOST_AUTO_TEST_CASE(getaddr_unfiltered)
432+
{
433+
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
434+
435+
// Set time on this addr so isTerrible = false
436+
CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
437+
addr1.nTime = Now<NodeSeconds>();
438+
// Not setting time so this addr should be isTerrible = true
439+
CAddress addr2 = CAddress(ResolveService("250.251.2.2", 9999), NODE_NONE);
440+
441+
CNetAddr source = ResolveIP("250.1.2.1");
442+
BOOST_CHECK(addrman->Add({addr1, addr2}, source));
443+
444+
// Filtered GetAddr should only return addr1
445+
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 1U);
446+
// Unfiltered GetAddr should return addr1 and addr2
447+
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 2U);
448+
}
431449

432450
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
433451
{

src/test/fuzz/addrman.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
283283
(void)const_addr_man.GetAddr(
284284
/* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
285285
/* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
286-
/* network */ std::nullopt);
286+
/* network */ std::nullopt,
287+
/* filtered =*/ fuzzed_data_provider.ConsumeBool());
287288
(void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool());
288289
(void)const_addr_man.Size();
289290
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);

src/test/fuzz/connman.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ FUZZ_TARGET(connman, .init = initialize_connman)
9494
(void)connman.GetAddresses(
9595
/*max_addresses=*/fuzzed_data_provider.ConsumeIntegral<size_t>(),
9696
/*max_pct=*/fuzzed_data_provider.ConsumeIntegral<size_t>(),
97-
/*network=*/std::nullopt);
97+
/*network=*/std::nullopt,
98+
/*filtered=*/fuzzed_data_provider.ConsumeBool());
9899
},
99100
[&] {
100101
(void)connman.GetAddresses(

0 commit comments

Comments
 (0)