Loading Experiments.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ class Experiments { "dot_validation_latency_offset_ms", "dot_xport_unusable_threshold", "keep_listening_udp", "max_cache_entries", "max_queries_global", "mdns_resolution", "parallel_lookup_release", Loading res_cache.cpp +35 −7 Original line number Diff line number Diff line Loading @@ -153,7 +153,8 @@ using std::span; * * Upping by another 5x for the centralized nature * ***************************************** */ const int CONFIG_MAX_ENTRIES = 64 * 2 * 5; const int MAX_ENTRIES_DEFAULT = 64 * 2 * 5; const int MAX_ENTRIES_UPPER_BOUND = 100 * 1000; constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1; static time_t _time_now(void) { Loading Loading @@ -947,14 +948,14 @@ std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map(bool isMd // // TODO: move all cache manipulation code here and make data members private. struct Cache { Cache() { entries.resize(CONFIG_MAX_ENTRIES); Cache() : max_cache_entries(get_max_cache_entries_from_flag()) { entries.resize(max_cache_entries); mru_list.mru_prev = mru_list.mru_next = &mru_list; } ~Cache() { flush(); } void flush() { for (int nn = 0; nn < CONFIG_MAX_ENTRIES; nn++) { for (int nn = 0; nn < max_cache_entries; nn++) { Entry** pnode = (Entry**)&entries[nn]; while (*pnode) { Loading Loading @@ -985,6 +986,8 @@ struct Cache { cv.notify_all(); } int get_max_cache_entries() { return max_cache_entries; } int num_entries = 0; // TODO: convert to std::list Loading @@ -997,6 +1000,21 @@ struct Cache { unsigned int hash; struct pending_req_info* next; } pending_requests{}; private: int get_max_cache_entries_from_flag() { int entries = android::net::Experiments::getInstance()->getFlag("max_cache_entries", MAX_ENTRIES_DEFAULT); // Check both lower and upper bounds to prevent irrational values mistakenly pushed by // server. if (entries < MAX_ENTRIES_DEFAULT || entries > MAX_ENTRIES_UPPER_BOUND) { LOG(ERROR) << "Misconfiguration on max_cache_entries " << entries; entries = MAX_ENTRIES_DEFAULT; } return entries; } const int max_cache_entries; }; struct NetConfig { Loading Loading @@ -1139,7 +1157,7 @@ static void cache_dump_mru_locked(Cache* cache) { * table. */ static Entry** _cache_lookup_p(Cache* cache, Entry* key) { int index = key->hash % CONFIG_MAX_ENTRIES; int index = key->hash % cache->get_max_cache_entries(); Entry** pnode = (Entry**) &cache->entries[index]; while (*pnode != NULL) { Loading Loading @@ -1355,9 +1373,9 @@ int resolv_cache_add(unsigned netid, span<const uint8_t> query, span<const uint8 return -EEXIST; } if (cache->num_entries >= CONFIG_MAX_ENTRIES) { if (cache->num_entries >= cache->get_max_cache_entries()) { _cache_remove_expired(cache); if (cache->num_entries >= CONFIG_MAX_ENTRIES) { if (cache->num_entries >= cache->get_max_cache_entries()) { _cache_remove_oldest(cache); } // TODO: It looks useless, remove below code after having test to prove it. Loading Loading @@ -2074,3 +2092,13 @@ void resolv_netconfig_dump(DumpWriter& dw, unsigned netid) { dw.println("TransportType: %s", transport_type_to_str(info->transportTypes)); } } int resolv_get_max_cache_entries(unsigned netid) { std::lock_guard guard(cache_mutex); NetConfig* info = find_netconfig_locked(netid); if (!info) { LOG(WARNING) << __func__ << ": NetConfig for netid " << netid << " not found"; return -1; } return info->cache->get_max_cache_entries(); } No newline at end of file resolv_cache.h +4 −0 Original line number Diff line number Diff line Loading @@ -139,3 +139,7 @@ android::net::NetworkType convert_network_type(const std::vector<int32_t>& trans // Dump net configuration log for a given network. void resolv_netconfig_dump(android::netdutils::DumpWriter& dw, unsigned netid); // Get the maximum cache size of a network. // Return positive value on success, -1 on failure. int resolv_get_max_cache_entries(unsigned netid); tests/resolv_cache_unit_test.cpp +34 −11 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <gtest/gtest.h> #include <netdutils/NetNativeTestBase.h> #include "Experiments.h" #include "resolv_cache.h" #include "resolv_private.h" #include "stats.h" Loading @@ -41,12 +42,15 @@ using namespace std::chrono_literals; using android::netdutils::IPSockAddr; const std::string kMaxCacheEntriesFlag("persist.device_config.netd_native.max_cache_entries"); constexpr int TEST_NETID_2 = 31; constexpr int DNS_PORT = 53; // Constant values sync'd from res_cache.cpp constexpr int DNS_HEADER_SIZE = 12; constexpr int MAX_ENTRIES = 64 * 2 * 5; constexpr int MAX_ENTRIES_DEFAULT = 64 * 2 * 5; constexpr int MAX_ENTRIES_UPPER_BOUND = 100 * 1000; namespace { Loading Loading @@ -558,9 +562,10 @@ TEST_F(ResolvCacheTest, PendingRequest_CacheDestroyed) { TEST_F(ResolvCacheTest, MaxEntries) { EXPECT_EQ(0, cacheCreate(TEST_NETID)); std::vector<CacheEntry> ces; const int max_cache_entries = resolv_get_max_cache_entries(TEST_NETID); for (int i = 0; i < 2 * MAX_ENTRIES; i++) { std::string qname = fmt::format("cache.{:04d}", i); for (int i = 0; i < 2 * max_cache_entries; i++) { std::string qname = fmt::format("cache.{:06d}", i); SCOPED_TRACE(qname); CacheEntry ce = makeCacheEntry(QUERY, qname.data(), ns_c_in, ns_t_a, "1.2.3.4"); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce)); Loading @@ -568,12 +573,12 @@ TEST_F(ResolvCacheTest, MaxEntries) { ces.emplace_back(ce); } for (int i = 0; i < 2 * MAX_ENTRIES; i++) { std::string qname = fmt::format("cache.{:04d}", i); for (int i = 0; i < 2 * max_cache_entries; i++) { std::string qname = fmt::format("cache.{:06d}", i); SCOPED_TRACE(qname); if (i < MAX_ENTRIES) { if (i < max_cache_entries) { // Because the cache is LRU, the oldest queries should have been purged, // and the most recent MAX_ENTRIES ones should still be present. // and the most recent max_cache_entries ones should still be present. EXPECT_TRUE(cacheLookup(RESOLV_CACHE_NOTFOUND, TEST_NETID, ces[i])); } else { EXPECT_TRUE(cacheLookup(RESOLV_CACHE_FOUND, TEST_NETID, ces[i])); Loading @@ -584,17 +589,18 @@ TEST_F(ResolvCacheTest, MaxEntries) { TEST_F(ResolvCacheTest, CacheFull) { EXPECT_EQ(0, cacheCreate(TEST_NETID)); CacheEntry ce1 = makeCacheEntry(QUERY, "cache.0000", ns_c_in, ns_t_a, "1.2.3.4", 100s); CacheEntry ce1 = makeCacheEntry(QUERY, "cache.000000", ns_c_in, ns_t_a, "1.2.3.4", 100s); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce1)); EXPECT_TRUE(cacheLookup(RESOLV_CACHE_FOUND, TEST_NETID, ce1)); CacheEntry ce2 = makeCacheEntry(QUERY, "cache.0001", ns_c_in, ns_t_a, "1.2.3.4", 1s); CacheEntry ce2 = makeCacheEntry(QUERY, "cache.000001", ns_c_in, ns_t_a, "1.2.3.4", 1s); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce2)); EXPECT_TRUE(cacheLookup(RESOLV_CACHE_FOUND, TEST_NETID, ce2)); // Stuff the resolver cache. for (int i = 2; i < MAX_ENTRIES; i++) { std::string qname = fmt::format("cache.{:04d}", i); const int max_cache_entries = resolv_get_max_cache_entries(TEST_NETID); for (int i = 2; i < max_cache_entries; i++) { std::string qname = fmt::format("cache.{:06d}", i); SCOPED_TRACE(qname); CacheEntry ce = makeCacheEntry(QUERY, qname.data(), ns_c_in, ns_t_a, "1.2.3.4", 50s); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce)); Loading @@ -617,6 +623,23 @@ TEST_F(ResolvCacheTest, CacheFull) { EXPECT_TRUE(cacheLookup(RESOLV_CACHE_NOTFOUND, TEST_NETID, ce1)); } class ResolvCacheParameterizedTest : public ResolvCacheTest, public testing::WithParamInterface<int> {}; INSTANTIATE_TEST_SUITE_P(MaxCacheEntries, ResolvCacheParameterizedTest, testing::Values(0, MAX_ENTRIES_UPPER_BOUND + 1), [](const testing::TestParamInfo<int>& info) { return std::to_string(info.param); }); TEST_P(ResolvCacheParameterizedTest, IrrationalCacheSize) { // Assign an out-of-bounds value. ScopedSystemProperties sp1(kMaxCacheEntriesFlag, std::to_string(GetParam())); android::net::Experiments::getInstance()->update(); EXPECT_EQ(0, cacheCreate(TEST_NETID)); EXPECT_EQ(MAX_ENTRIES_DEFAULT, resolv_get_max_cache_entries(TEST_NETID)); } TEST_F(ResolvCacheTest, ResolverSetup) { const SetupParams setup = { .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"}, Loading Loading
Experiments.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ class Experiments { "dot_validation_latency_offset_ms", "dot_xport_unusable_threshold", "keep_listening_udp", "max_cache_entries", "max_queries_global", "mdns_resolution", "parallel_lookup_release", Loading
res_cache.cpp +35 −7 Original line number Diff line number Diff line Loading @@ -153,7 +153,8 @@ using std::span; * * Upping by another 5x for the centralized nature * ***************************************** */ const int CONFIG_MAX_ENTRIES = 64 * 2 * 5; const int MAX_ENTRIES_DEFAULT = 64 * 2 * 5; const int MAX_ENTRIES_UPPER_BOUND = 100 * 1000; constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1; static time_t _time_now(void) { Loading Loading @@ -947,14 +948,14 @@ std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map(bool isMd // // TODO: move all cache manipulation code here and make data members private. struct Cache { Cache() { entries.resize(CONFIG_MAX_ENTRIES); Cache() : max_cache_entries(get_max_cache_entries_from_flag()) { entries.resize(max_cache_entries); mru_list.mru_prev = mru_list.mru_next = &mru_list; } ~Cache() { flush(); } void flush() { for (int nn = 0; nn < CONFIG_MAX_ENTRIES; nn++) { for (int nn = 0; nn < max_cache_entries; nn++) { Entry** pnode = (Entry**)&entries[nn]; while (*pnode) { Loading Loading @@ -985,6 +986,8 @@ struct Cache { cv.notify_all(); } int get_max_cache_entries() { return max_cache_entries; } int num_entries = 0; // TODO: convert to std::list Loading @@ -997,6 +1000,21 @@ struct Cache { unsigned int hash; struct pending_req_info* next; } pending_requests{}; private: int get_max_cache_entries_from_flag() { int entries = android::net::Experiments::getInstance()->getFlag("max_cache_entries", MAX_ENTRIES_DEFAULT); // Check both lower and upper bounds to prevent irrational values mistakenly pushed by // server. if (entries < MAX_ENTRIES_DEFAULT || entries > MAX_ENTRIES_UPPER_BOUND) { LOG(ERROR) << "Misconfiguration on max_cache_entries " << entries; entries = MAX_ENTRIES_DEFAULT; } return entries; } const int max_cache_entries; }; struct NetConfig { Loading Loading @@ -1139,7 +1157,7 @@ static void cache_dump_mru_locked(Cache* cache) { * table. */ static Entry** _cache_lookup_p(Cache* cache, Entry* key) { int index = key->hash % CONFIG_MAX_ENTRIES; int index = key->hash % cache->get_max_cache_entries(); Entry** pnode = (Entry**) &cache->entries[index]; while (*pnode != NULL) { Loading Loading @@ -1355,9 +1373,9 @@ int resolv_cache_add(unsigned netid, span<const uint8_t> query, span<const uint8 return -EEXIST; } if (cache->num_entries >= CONFIG_MAX_ENTRIES) { if (cache->num_entries >= cache->get_max_cache_entries()) { _cache_remove_expired(cache); if (cache->num_entries >= CONFIG_MAX_ENTRIES) { if (cache->num_entries >= cache->get_max_cache_entries()) { _cache_remove_oldest(cache); } // TODO: It looks useless, remove below code after having test to prove it. Loading Loading @@ -2074,3 +2092,13 @@ void resolv_netconfig_dump(DumpWriter& dw, unsigned netid) { dw.println("TransportType: %s", transport_type_to_str(info->transportTypes)); } } int resolv_get_max_cache_entries(unsigned netid) { std::lock_guard guard(cache_mutex); NetConfig* info = find_netconfig_locked(netid); if (!info) { LOG(WARNING) << __func__ << ": NetConfig for netid " << netid << " not found"; return -1; } return info->cache->get_max_cache_entries(); } No newline at end of file
resolv_cache.h +4 −0 Original line number Diff line number Diff line Loading @@ -139,3 +139,7 @@ android::net::NetworkType convert_network_type(const std::vector<int32_t>& trans // Dump net configuration log for a given network. void resolv_netconfig_dump(android::netdutils::DumpWriter& dw, unsigned netid); // Get the maximum cache size of a network. // Return positive value on success, -1 on failure. int resolv_get_max_cache_entries(unsigned netid);
tests/resolv_cache_unit_test.cpp +34 −11 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <gtest/gtest.h> #include <netdutils/NetNativeTestBase.h> #include "Experiments.h" #include "resolv_cache.h" #include "resolv_private.h" #include "stats.h" Loading @@ -41,12 +42,15 @@ using namespace std::chrono_literals; using android::netdutils::IPSockAddr; const std::string kMaxCacheEntriesFlag("persist.device_config.netd_native.max_cache_entries"); constexpr int TEST_NETID_2 = 31; constexpr int DNS_PORT = 53; // Constant values sync'd from res_cache.cpp constexpr int DNS_HEADER_SIZE = 12; constexpr int MAX_ENTRIES = 64 * 2 * 5; constexpr int MAX_ENTRIES_DEFAULT = 64 * 2 * 5; constexpr int MAX_ENTRIES_UPPER_BOUND = 100 * 1000; namespace { Loading Loading @@ -558,9 +562,10 @@ TEST_F(ResolvCacheTest, PendingRequest_CacheDestroyed) { TEST_F(ResolvCacheTest, MaxEntries) { EXPECT_EQ(0, cacheCreate(TEST_NETID)); std::vector<CacheEntry> ces; const int max_cache_entries = resolv_get_max_cache_entries(TEST_NETID); for (int i = 0; i < 2 * MAX_ENTRIES; i++) { std::string qname = fmt::format("cache.{:04d}", i); for (int i = 0; i < 2 * max_cache_entries; i++) { std::string qname = fmt::format("cache.{:06d}", i); SCOPED_TRACE(qname); CacheEntry ce = makeCacheEntry(QUERY, qname.data(), ns_c_in, ns_t_a, "1.2.3.4"); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce)); Loading @@ -568,12 +573,12 @@ TEST_F(ResolvCacheTest, MaxEntries) { ces.emplace_back(ce); } for (int i = 0; i < 2 * MAX_ENTRIES; i++) { std::string qname = fmt::format("cache.{:04d}", i); for (int i = 0; i < 2 * max_cache_entries; i++) { std::string qname = fmt::format("cache.{:06d}", i); SCOPED_TRACE(qname); if (i < MAX_ENTRIES) { if (i < max_cache_entries) { // Because the cache is LRU, the oldest queries should have been purged, // and the most recent MAX_ENTRIES ones should still be present. // and the most recent max_cache_entries ones should still be present. EXPECT_TRUE(cacheLookup(RESOLV_CACHE_NOTFOUND, TEST_NETID, ces[i])); } else { EXPECT_TRUE(cacheLookup(RESOLV_CACHE_FOUND, TEST_NETID, ces[i])); Loading @@ -584,17 +589,18 @@ TEST_F(ResolvCacheTest, MaxEntries) { TEST_F(ResolvCacheTest, CacheFull) { EXPECT_EQ(0, cacheCreate(TEST_NETID)); CacheEntry ce1 = makeCacheEntry(QUERY, "cache.0000", ns_c_in, ns_t_a, "1.2.3.4", 100s); CacheEntry ce1 = makeCacheEntry(QUERY, "cache.000000", ns_c_in, ns_t_a, "1.2.3.4", 100s); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce1)); EXPECT_TRUE(cacheLookup(RESOLV_CACHE_FOUND, TEST_NETID, ce1)); CacheEntry ce2 = makeCacheEntry(QUERY, "cache.0001", ns_c_in, ns_t_a, "1.2.3.4", 1s); CacheEntry ce2 = makeCacheEntry(QUERY, "cache.000001", ns_c_in, ns_t_a, "1.2.3.4", 1s); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce2)); EXPECT_TRUE(cacheLookup(RESOLV_CACHE_FOUND, TEST_NETID, ce2)); // Stuff the resolver cache. for (int i = 2; i < MAX_ENTRIES; i++) { std::string qname = fmt::format("cache.{:04d}", i); const int max_cache_entries = resolv_get_max_cache_entries(TEST_NETID); for (int i = 2; i < max_cache_entries; i++) { std::string qname = fmt::format("cache.{:06d}", i); SCOPED_TRACE(qname); CacheEntry ce = makeCacheEntry(QUERY, qname.data(), ns_c_in, ns_t_a, "1.2.3.4", 50s); EXPECT_EQ(0, cacheAdd(TEST_NETID, ce)); Loading @@ -617,6 +623,23 @@ TEST_F(ResolvCacheTest, CacheFull) { EXPECT_TRUE(cacheLookup(RESOLV_CACHE_NOTFOUND, TEST_NETID, ce1)); } class ResolvCacheParameterizedTest : public ResolvCacheTest, public testing::WithParamInterface<int> {}; INSTANTIATE_TEST_SUITE_P(MaxCacheEntries, ResolvCacheParameterizedTest, testing::Values(0, MAX_ENTRIES_UPPER_BOUND + 1), [](const testing::TestParamInfo<int>& info) { return std::to_string(info.param); }); TEST_P(ResolvCacheParameterizedTest, IrrationalCacheSize) { // Assign an out-of-bounds value. ScopedSystemProperties sp1(kMaxCacheEntriesFlag, std::to_string(GetParam())); android::net::Experiments::getInstance()->update(); EXPECT_EQ(0, cacheCreate(TEST_NETID)); EXPECT_EQ(MAX_ENTRIES_DEFAULT, resolv_get_max_cache_entries(TEST_NETID)); } TEST_F(ResolvCacheTest, ResolverSetup) { const SetupParams setup = { .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"}, Loading