Skip to content

Commit c46cc8d

Browse files
committed
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
2 parents 25d23e6 + 3ea54e5 commit c46cc8d

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
@@ -802,7 +802,7 @@ int AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) const
802802
return -1;
803803
}
804804

805-
std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
805+
std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
806806
{
807807
AssertLockHeld(cs);
808808

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

834834
// Filter for quality
835-
if (ai.IsTerrible(now)) continue;
835+
if (ai.IsTerrible(now) && filtered) continue;
836836

837837
addresses.push_back(ai);
838838
}
@@ -1216,11 +1216,11 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::optiona
12161216
return addrRet;
12171217
}
12181218

1219-
std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
1219+
std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
12201220
{
12211221
LOCK(cs);
12221222
Check();
1223-
auto addresses = GetAddr_(max_addresses, max_pct, network);
1223+
auto addresses = GetAddr_(max_addresses, max_pct, network, filtered);
12241224
Check();
12251225
return addresses;
12261226
}
@@ -1319,9 +1319,9 @@ std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, std::optional<Ne
13191319
return m_impl->Select(new_only, network);
13201320
}
13211321

1322-
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
1322+
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
13231323
{
1324-
return m_impl->GetAddr(max_addresses, max_pct, network);
1324+
return m_impl->GetAddr(max_addresses, max_pct, network, filtered);
13251325
}
13261326

13271327
std::vector<std::pair<AddrInfo, AddressPosition>> AddrMan::GetEntries(bool use_tried) const

src/addrman.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,11 @@ class AddrMan
164164
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
165165
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
166166
* @param[in] network Select only addresses of this network (nullopt = all).
167+
* @param[in] filtered Select only addresses that are considered good quality (false = all).
167168
*
168169
* @return A vector of randomly selected addresses from vRandom.
169170
*/
170-
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
171+
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const;
171172

172173
/**
173174
* Returns an information-location pair for all addresses in the selected addrman table.

src/addrman_impl.h

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

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

135135
std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries(bool from_tried) const
@@ -261,7 +261,7 @@ class AddrManImpl
261261
* */
262262
int GetEntry(bool use_tried, size_t bucket, size_t position) const EXCLUSIVE_LOCKS_REQUIRED(cs);
263263

264-
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
264+
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);
265265

266266
std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries_(bool from_tried) const EXCLUSIVE_LOCKS_REQUIRED(cs);
267267

src/net.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,6 +3278,12 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
32783278
// Dump network addresses
32793279
scheduler.scheduleEvery([this] { DumpAddresses(); }, DUMP_PEERS_INTERVAL);
32803280

3281+
// Run the ASMap Health check once and then schedule it to run every 24h.
3282+
if (m_netgroupman.UsingASMap()) {
3283+
ASMapHealthCheck();
3284+
scheduler.scheduleEvery([this] { ASMapHealthCheck(); }, ASMAP_HEALTH_CHECK_INTERVAL);
3285+
}
3286+
32813287
return true;
32823288
}
32833289

@@ -3383,9 +3389,9 @@ CConnman::~CConnman()
33833389
Stop();
33843390
}
33853391

3386-
std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
3392+
std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
33873393
{
3388-
std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct, network);
3394+
std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct, network, filtered);
33893395
if (m_banman) {
33903396
addresses.erase(std::remove_if(addresses.begin(), addresses.end(),
33913397
[this](const CAddress& addr){return m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr);}),
@@ -3840,6 +3846,19 @@ void CConnman::PerformReconnections()
38403846
}
38413847
}
38423848

3849+
void CConnman::ASMapHealthCheck()
3850+
{
3851+
const std::vector<CAddress> v4_addrs{GetAddresses(/*max_addresses=*/ 0, /*max_pct=*/ 0, Network::NET_IPV4, /*filtered=*/ false)};
3852+
const std::vector<CAddress> v6_addrs{GetAddresses(/*max_addresses=*/ 0, /*max_pct=*/ 0, Network::NET_IPV6, /*filtered=*/ false)};
3853+
std::vector<CNetAddr> clearnet_addrs;
3854+
clearnet_addrs.reserve(v4_addrs.size() + v6_addrs.size());
3855+
std::transform(v4_addrs.begin(), v4_addrs.end(), std::back_inserter(clearnet_addrs),
3856+
[](const CAddress& addr) { return static_cast<CNetAddr>(addr); });
3857+
std::transform(v6_addrs.begin(), v6_addrs.end(), std::back_inserter(clearnet_addrs),
3858+
[](const CAddress& addr) { return static_cast<CNetAddr>(addr); });
3859+
m_netgroupman.ASMapHealthCheck(clearnet_addrs);
3860+
}
3861+
38433862
// Dump binary message to file, with timestamp.
38443863
static void CaptureMessageToFile(const CAddress& addr,
38453864
const std::string& msg_type,

src/net.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ static const bool DEFAULT_BLOCKSONLY = false;
8888
static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60;
8989
/** Number of file descriptors required for message capture **/
9090
static const int NUM_FDS_MESSAGE_CAPTURE = 1;
91+
/** Interval for ASMap Health Check **/
92+
static constexpr std::chrono::hours ASMAP_HEALTH_CHECK_INTERVAL{24};
9193

9294
static constexpr bool DEFAULT_FORCEDNSSEED{false};
9395
static constexpr bool DEFAULT_DNSSEED{true};
@@ -1112,6 +1114,7 @@ class CConnman
11121114
void SetNetworkActive(bool active);
11131115
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant&& grant_outbound, const char* strDest, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
11141116
bool CheckIncomingNonce(uint64_t nonce);
1117+
void ASMapHealthCheck();
11151118

11161119
// alias for thread safety annotations only, not defined
11171120
RecursiveMutex& GetNodesMutex() const LOCK_RETURNED(m_nodes_mutex);
@@ -1146,8 +1149,9 @@ class CConnman
11461149
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
11471150
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
11481151
* @param[in] network Select only addresses of this network (nullopt = all).
1152+
* @param[in] filtered Select only addresses that are considered high quality (false = all).
11491153
*/
1150-
std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
1154+
std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const;
11511155
/**
11521156
* Cache is used to minimize topology leaks, so it should
11531157
* be used for all non-trusted calls, for example, p2p.

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
@@ -429,6 +429,24 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
429429
BOOST_CHECK_EQUAL(addrman->Size(), 2006U);
430430
}
431431

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

433451
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
434452
{

src/test/fuzz/addrman.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,8 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
286286
(void)const_addr_man.GetAddr(
287287
/*max_addresses=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
288288
/*max_pct=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
289-
network);
289+
network,
290+
/*filtered=*/fuzzed_data_provider.ConsumeBool());
290291
(void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network);
291292
std::optional<bool> in_new;
292293
if (fuzzed_data_provider.ConsumeBool()) {

src/test/fuzz/connman.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ FUZZ_TARGET(connman, .init = initialize_connman)
8888
(void)connman.GetAddresses(
8989
/*max_addresses=*/fuzzed_data_provider.ConsumeIntegral<size_t>(),
9090
/*max_pct=*/fuzzed_data_provider.ConsumeIntegral<size_t>(),
91-
/*network=*/std::nullopt);
91+
/*network=*/std::nullopt,
92+
/*filtered=*/fuzzed_data_provider.ConsumeBool());
9293
},
9394
[&] {
9495
(void)connman.GetAddresses(

0 commit comments

Comments
 (0)