Loading res_cache.cpp +125 −173 Original line number Diff line number Diff line Loading @@ -872,10 +872,53 @@ static int entry_equals(const Entry* e1, const Entry* e2) { /* Maximum time for a thread to wait for an pending request */ constexpr int PENDING_REQUEST_TIMEOUT = 20; // lock protecting everything in the resolve_cache_info structs (next ptr, etc) // lock protecting everything in NetConfig. static std::mutex cache_mutex; static std::condition_variable cv; namespace { // Map format: ReturnCode:rate_denom // if the ReturnCode is not associated with any rate_denom, use default // Sampling rate varies by return code; events to log are chosen randomly, with a // probability proportional to the sampling rate. constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10"; std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() { using android::base::ParseInt; using android::base::ParseUint; using android::base::Split; using server_configurable_flags::GetServerConfigurableFlag; std::unordered_map<int, uint32_t> sampling_rate_map{}; std::vector<std::string> subsampling_vector = Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map", DEFAULT_SUBSAMPLING_MAP), " "); for (const auto& pair : subsampling_vector) { std::vector<std::string> rate_denom = Split(pair, ":"); int return_code; uint32_t denom; if (rate_denom.size() != 2) { LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair; continue; } if (rate_denom[0] == "default") { return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY; } else if (!ParseInt(rate_denom[0], &return_code)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } if (!ParseUint(rate_denom[1], &denom)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } sampling_rate_map[return_code] = denom; } return sampling_rate_map; } } // namespace // Note that Cache is not thread-safe per se, access to its members must be protected // by an external mutex. // Loading Loading @@ -933,22 +976,25 @@ struct Cache { } pending_requests{}; }; // TODO: allocate with new struct resolv_cache_info { unsigned netid; Cache* cache; // TODO: use unique_ptr or embed struct resolv_cache_info* next; int nscount; struct NetConfig { explicit NetConfig(unsigned netId) : netid(netId) { cache = std::make_unique<Cache>(); dns_event_subsampling_map = resolv_get_dns_event_subsampling_map(); } const unsigned netid; std::unique_ptr<Cache> cache; int nscount = 0; std::vector<std::string> nameservers; std::vector<IPSockAddr> nameserverSockAddrs; int revision_id; // # times the nameservers have been replaced res_params params; struct res_stats nsstats[MAXNS]; int revision_id = 0; // # times the nameservers have been replaced res_params params{}; res_stats nsstats[MAXNS]{}; std::vector<std::string> search_domains; int wait_for_pending_req_timeout_count; int wait_for_pending_req_timeout_count = 0; // Map format: ReturnCode:rate_denom std::unordered_map<int, uint32_t> dns_event_subsampling_map; std::unique_ptr<DnsStats> dnsStats; DnsStats dnsStats; }; /* gets cache associated with a network, or NULL if none exists */ Loading Loading @@ -1126,8 +1172,8 @@ static void _cache_remove_expired(Cache* cache) { } } // gets a resolv_cache_info associated with a network, or NULL if not found static resolv_cache_info* find_cache_info_locked(unsigned netid) REQUIRES(cache_mutex); // Get a NetConfig associated with a network, or nullptr if not found. static NetConfig* find_netconfig_locked(unsigned netid) REQUIRES(cache_mutex); ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int querylen, void* answer, int answersize, int* answerlen, uint32_t flags) { Loading Loading @@ -1189,7 +1235,7 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int que return RESOLV_CACHE_NOTFOUND; } if (ret == false) { resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info != NULL) { info->wait_for_pending_req_timeout_count++; } Loading Loading @@ -1375,169 +1421,75 @@ bool resolv_gethostbyaddr_from_cache(unsigned netid, char domain_name[], size_t return false; } // Head of the list of caches. static struct resolv_cache_info res_cache_list GUARDED_BY(cache_mutex); static std::unordered_map<unsigned, std::unique_ptr<NetConfig>> sNetConfigMap GUARDED_BY(cache_mutex); // insert resolv_cache_info into the list of resolv_cache_infos static void insert_cache_info_locked(resolv_cache_info* cache_info); // creates a resolv_cache_info static resolv_cache_info* create_cache_info(); // Clears nameservers set for |cache_info| and clears the stats static void free_nameservers_locked(resolv_cache_info* cache_info); // Clears nameservers set for |netconfig| and clears the stats static void free_nameservers_locked(NetConfig* netconfig); // Order-insensitive comparison for the two set of servers. static bool resolv_is_nameservers_equal(const std::vector<std::string>& oldServers, const std::vector<std::string>& newServers); // clears the stats samples contained withing the given cache_info static void res_cache_clear_stats_locked(resolv_cache_info* cache_info); // clears the stats samples contained withing the given netconfig. static void res_cache_clear_stats_locked(NetConfig* netconfig); // public API for netd to query if name server is set on specific netid bool resolv_has_nameservers(unsigned netid) { std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); return (info != nullptr) && (info->nscount > 0); } namespace { // Map format: ReturnCode:rate_denom // if the ReturnCode is not associated with any rate_denom, use default // Sampling rate varies by return code; events to log are chosen randomly, with a // probability proportional to the sampling rate. constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10"; std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() { using android::base::ParseInt; using android::base::ParseUint; using android::base::Split; using server_configurable_flags::GetServerConfigurableFlag; std::unordered_map<int, uint32_t> sampling_rate_map{}; std::vector<std::string> subsampling_vector = Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map", DEFAULT_SUBSAMPLING_MAP), " "); for (const auto& pair : subsampling_vector) { std::vector<std::string> rate_denom = Split(pair, ":"); int return_code; uint32_t denom; if (rate_denom.size() != 2) { LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair; continue; } if (rate_denom[0] == "default") { return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY; } else if (!ParseInt(rate_denom[0], &return_code)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } if (!ParseUint(rate_denom[1], &denom)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } sampling_rate_map[return_code] = denom; } return sampling_rate_map; } } // namespace int resolv_create_cache_for_net(unsigned netid) { std::lock_guard guard(cache_mutex); Cache* cache = find_named_cache_locked(netid); // Should not happen if (cache) { if (sNetConfigMap.find(netid) != sNetConfigMap.end()) { LOG(ERROR) << __func__ << ": Cache is already created, netId: " << netid; return -EEXIST; } resolv_cache_info* cache_info = create_cache_info(); if (!cache_info) return -ENOMEM; cache_info->netid = netid; cache_info->cache = new Cache; cache_info->dns_event_subsampling_map = resolv_get_dns_event_subsampling_map(); cache_info->dnsStats.reset(new DnsStats()); insert_cache_info_locked(cache_info); sNetConfigMap[netid] = std::make_unique<NetConfig>(netid); return 0; } void resolv_delete_cache_for_net(unsigned netid) { std::lock_guard guard(cache_mutex); struct resolv_cache_info* prev_cache_info = &res_cache_list; while (prev_cache_info->next) { struct resolv_cache_info* cache_info = prev_cache_info->next; if (cache_info->netid == netid) { prev_cache_info->next = cache_info->next; delete cache_info->cache; free_nameservers_locked(cache_info); // It won't be necessary after the memory of cache_info can be deallocated by the // C++ delete expression. cache_info->dnsStats.reset(); free(cache_info); break; } prev_cache_info = prev_cache_info->next; } sNetConfigMap.erase(netid); } int resolv_flush_cache_for_net(unsigned netid) { std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); if (cache_info == nullptr) { NetConfig* netconfig = find_netconfig_locked(netid); if (netconfig == nullptr) { return -ENONET; } cache_info->cache->flush(); netconfig->cache->flush(); // Also clear the NS statistics. res_cache_clear_stats_locked(cache_info); res_cache_clear_stats_locked(netconfig); return 0; } std::vector<unsigned> resolv_list_caches() { std::lock_guard guard(cache_mutex); struct resolv_cache_info* cache_info = res_cache_list.next; std::vector<unsigned> result; while (cache_info) { result.push_back(cache_info->netid); cache_info = cache_info->next; result.reserve(sNetConfigMap.size()); for (const auto& [netId, _] : sNetConfigMap) { result.push_back(netId); } return result; } static resolv_cache_info* create_cache_info() { return (struct resolv_cache_info*) calloc(sizeof(struct resolv_cache_info), 1); } // TODO: convert this to a simple and efficient C++ container. static void insert_cache_info_locked(struct resolv_cache_info* cache_info) { struct resolv_cache_info* last; for (last = &res_cache_list; last->next; last = last->next) {} last->next = cache_info; } static Cache* find_named_cache_locked(unsigned netid) { resolv_cache_info* info = find_cache_info_locked(netid); if (info != nullptr) return info->cache; NetConfig* info = find_netconfig_locked(netid); if (info != nullptr) return info->cache.get(); return nullptr; } static resolv_cache_info* find_cache_info_locked(unsigned netid) { struct resolv_cache_info* cache_info = res_cache_list.next; while (cache_info) { if (cache_info->netid == netid) { break; } cache_info = cache_info->next; static NetConfig* find_netconfig_locked(unsigned netid) { if (auto it = sNetConfigMap.find(netid); it != sNetConfigMap.end()) { return it->second.get(); } return cache_info; return nullptr; } static void resolv_set_experiment_params(res_params* params) { Loading Loading @@ -1613,41 +1565,41 @@ int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& serve } std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); NetConfig* netconfig = find_netconfig_locked(netid); if (cache_info == nullptr) return -ENONET; if (netconfig == nullptr) return -ENONET; uint8_t old_max_samples = cache_info->params.max_samples; cache_info->params = params; resolv_set_experiment_params(&cache_info->params); if (!resolv_is_nameservers_equal(cache_info->nameservers, nameservers)) { uint8_t old_max_samples = netconfig->params.max_samples; netconfig->params = params; resolv_set_experiment_params(&netconfig->params); if (!resolv_is_nameservers_equal(netconfig->nameservers, nameservers)) { // free current before adding new free_nameservers_locked(cache_info); cache_info->nameservers = std::move(nameservers); free_nameservers_locked(netconfig); netconfig->nameservers = std::move(nameservers); for (int i = 0; i < numservers; i++) { LOG(INFO) << __func__ << ": netid = " << netid << ", addr = " << cache_info->nameservers[i]; << ", addr = " << netconfig->nameservers[i]; } cache_info->nameserverSockAddrs = std::move(ipSockAddrs); cache_info->nscount = numservers; netconfig->nameserverSockAddrs = std::move(ipSockAddrs); netconfig->nscount = numservers; } else { if (cache_info->params.max_samples != old_max_samples) { if (netconfig->params.max_samples != old_max_samples) { // If the maximum number of samples changes, the overhead of keeping the most recent // samples around is not considered worth the effort, so they are cleared instead. // All other parameters do not affect shared state: Changing these parameters does // not invalidate the samples, as they only affect aggregation and the conditions // under which servers are considered usable. res_cache_clear_stats_locked(cache_info); res_cache_clear_stats_locked(netconfig); } } // Always update the search paths. Cache-flushing however is not necessary, // since the stored cache entries do contain the domain, not just the host name. cache_info->search_domains = filter_domains(domains); netconfig->search_domains = filter_domains(domains); // Setup stats for cleartext dns servers. if (!cache_info->dnsStats->setServers(cache_info->nameserverSockAddrs, PROTO_TCP) || !cache_info->dnsStats->setServers(cache_info->nameserverSockAddrs, PROTO_UDP)) { if (!netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_TCP) || !netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_UDP)) { LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats"; return -EINVAL; } Loading @@ -1668,11 +1620,11 @@ static bool resolv_is_nameservers_equal(const std::vector<std::string>& oldServe return olds == news; } static void free_nameservers_locked(resolv_cache_info* cache_info) { cache_info->nscount = 0; cache_info->nameservers.clear(); cache_info->nameserverSockAddrs.clear(); res_cache_clear_stats_locked(cache_info); static void free_nameservers_locked(NetConfig* netconfig) { netconfig->nscount = 0; netconfig->nameservers.clear(); netconfig->nameserverSockAddrs.clear(); res_cache_clear_stats_locked(netconfig); } void resolv_populate_res_for_net(ResState* statp) { Loading @@ -1682,7 +1634,7 @@ void resolv_populate_res_for_net(ResState* statp) { LOG(INFO) << __func__ << ": netid=" << statp->netid; std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(statp->netid); NetConfig* info = find_netconfig_locked(statp->netid); if (info == nullptr) return; // TODO: Convert nsaddrs[] to c++ container and remove the size-checking. Loading Loading @@ -1720,17 +1672,17 @@ static void res_cache_add_stats_sample_locked(res_stats* stats, const res_sample } } static void res_cache_clear_stats_locked(resolv_cache_info* cache_info) { static void res_cache_clear_stats_locked(NetConfig* netconfig) { for (int i = 0; i < MAXNS; ++i) { cache_info->nsstats[i].sample_count = 0; cache_info->nsstats[i].sample_next = 0; netconfig->nsstats[i].sample_count = 0; netconfig->nsstats[i].sample_next = 0; } // Increment the revision id to ensure that sample state is not written back if the // servers change; in theory it would suffice to do so only if the servers or // max_samples actually change, in practice the overhead of checking is higher than the // cost, and overflows are unlikely. ++cache_info->revision_id; ++netconfig->revision_id; } int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount, Loading @@ -1741,7 +1693,7 @@ int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount, int revision_id = -1; std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info) { if (info->nscount > MAXNS) { LOG(INFO) << __func__ << ": nscount " << info->nscount << " > MAXNS " << MAXNS; Loading Loading @@ -1779,10 +1731,10 @@ int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount, std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) { using android::base::StringPrintf; std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); if (cache_info == nullptr) return {}; NetConfig* netconfig = find_netconfig_locked(netid); if (netconfig == nullptr) return {}; std::vector<std::string> result; for (const auto& pair : cache_info->dns_event_subsampling_map) { for (const auto& pair : netconfig->dns_event_subsampling_map) { result.push_back(StringPrintf("%s:%d", (pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY) ? "default" Loading @@ -1798,9 +1750,9 @@ std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) { // Returns the subsampling rate if the event should be sampled, or 0 if it should be discarded. uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) { std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); if (cache_info == nullptr) return 0; // Don't log anything at all. const auto& subsampling_map = cache_info->dns_event_subsampling_map; NetConfig* netconfig = find_netconfig_locked(netid); if (netconfig == nullptr) return 0; // Don't log anything at all. const auto& subsampling_map = netconfig->dns_event_subsampling_map; auto search_returnCode = subsampling_map.find(return_code); uint32_t denom; if (search_returnCode != subsampling_map.end()) { Loading @@ -1814,7 +1766,7 @@ uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) { int resolv_cache_get_resolver_stats(unsigned netid, res_params* params, res_stats stats[MAXNS]) { std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info) { memcpy(stats, info->nsstats, sizeof(info->nsstats)); *params = info->params; Loading @@ -1829,7 +1781,7 @@ void resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, con if (max_samples <= 0 || sa == nullptr) return; std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info && info->revision_id == revision_id) { const int serverNum = std::min(MAXNS, static_cast<int>(info->nameserverSockAddrs.size())); Loading Loading @@ -1884,7 +1836,7 @@ int resolv_cache_get_expiration(unsigned netid, const std::vector<char>& query, int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::string>& servers) { std::lock_guard guard(cache_mutex); const auto info = find_cache_info_locked(netid); const auto info = find_netconfig_locked(netid); if (info == nullptr) return -ENONET; Loading @@ -1894,7 +1846,7 @@ int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::stri serverSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 853)); } if (!info->dnsStats->setServers(serverSockAddrs, android::net::PROTO_DOT)) { if (!info->dnsStats.setServers(serverSockAddrs, android::net::PROTO_DOT)) { LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats"; return -EINVAL; } Loading @@ -1907,15 +1859,15 @@ bool resolv_stats_add(unsigned netid, const android::netdutils::IPSockAddr& serv if (record == nullptr) return false; std::lock_guard guard(cache_mutex); if (const auto info = find_cache_info_locked(netid); info != nullptr) { return info->dnsStats->addStats(server, *record); if (const auto info = find_netconfig_locked(netid); info != nullptr) { return info->dnsStats.addStats(server, *record); } return false; } void resolv_stats_dump(DumpWriter& dw, unsigned netid) { std::lock_guard guard(cache_mutex); if (const auto info = find_cache_info_locked(netid); info != nullptr) { info->dnsStats->dump(dw); if (const auto info = find_netconfig_locked(netid); info != nullptr) { info->dnsStats.dump(dw); } } resolv_cache_unit_test.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -902,7 +902,7 @@ TEST_F(ResolvCacheTest, DnsEventSubsampling) { } } // TODO: Tests for struct resolv_cache_info, including: // TODO: Tests for NetConfig, including: // - res_params // -- resolv_cache_get_resolver_stats() // - res_stats Loading Loading
res_cache.cpp +125 −173 Original line number Diff line number Diff line Loading @@ -872,10 +872,53 @@ static int entry_equals(const Entry* e1, const Entry* e2) { /* Maximum time for a thread to wait for an pending request */ constexpr int PENDING_REQUEST_TIMEOUT = 20; // lock protecting everything in the resolve_cache_info structs (next ptr, etc) // lock protecting everything in NetConfig. static std::mutex cache_mutex; static std::condition_variable cv; namespace { // Map format: ReturnCode:rate_denom // if the ReturnCode is not associated with any rate_denom, use default // Sampling rate varies by return code; events to log are chosen randomly, with a // probability proportional to the sampling rate. constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10"; std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() { using android::base::ParseInt; using android::base::ParseUint; using android::base::Split; using server_configurable_flags::GetServerConfigurableFlag; std::unordered_map<int, uint32_t> sampling_rate_map{}; std::vector<std::string> subsampling_vector = Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map", DEFAULT_SUBSAMPLING_MAP), " "); for (const auto& pair : subsampling_vector) { std::vector<std::string> rate_denom = Split(pair, ":"); int return_code; uint32_t denom; if (rate_denom.size() != 2) { LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair; continue; } if (rate_denom[0] == "default") { return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY; } else if (!ParseInt(rate_denom[0], &return_code)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } if (!ParseUint(rate_denom[1], &denom)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } sampling_rate_map[return_code] = denom; } return sampling_rate_map; } } // namespace // Note that Cache is not thread-safe per se, access to its members must be protected // by an external mutex. // Loading Loading @@ -933,22 +976,25 @@ struct Cache { } pending_requests{}; }; // TODO: allocate with new struct resolv_cache_info { unsigned netid; Cache* cache; // TODO: use unique_ptr or embed struct resolv_cache_info* next; int nscount; struct NetConfig { explicit NetConfig(unsigned netId) : netid(netId) { cache = std::make_unique<Cache>(); dns_event_subsampling_map = resolv_get_dns_event_subsampling_map(); } const unsigned netid; std::unique_ptr<Cache> cache; int nscount = 0; std::vector<std::string> nameservers; std::vector<IPSockAddr> nameserverSockAddrs; int revision_id; // # times the nameservers have been replaced res_params params; struct res_stats nsstats[MAXNS]; int revision_id = 0; // # times the nameservers have been replaced res_params params{}; res_stats nsstats[MAXNS]{}; std::vector<std::string> search_domains; int wait_for_pending_req_timeout_count; int wait_for_pending_req_timeout_count = 0; // Map format: ReturnCode:rate_denom std::unordered_map<int, uint32_t> dns_event_subsampling_map; std::unique_ptr<DnsStats> dnsStats; DnsStats dnsStats; }; /* gets cache associated with a network, or NULL if none exists */ Loading Loading @@ -1126,8 +1172,8 @@ static void _cache_remove_expired(Cache* cache) { } } // gets a resolv_cache_info associated with a network, or NULL if not found static resolv_cache_info* find_cache_info_locked(unsigned netid) REQUIRES(cache_mutex); // Get a NetConfig associated with a network, or nullptr if not found. static NetConfig* find_netconfig_locked(unsigned netid) REQUIRES(cache_mutex); ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int querylen, void* answer, int answersize, int* answerlen, uint32_t flags) { Loading Loading @@ -1189,7 +1235,7 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int que return RESOLV_CACHE_NOTFOUND; } if (ret == false) { resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info != NULL) { info->wait_for_pending_req_timeout_count++; } Loading Loading @@ -1375,169 +1421,75 @@ bool resolv_gethostbyaddr_from_cache(unsigned netid, char domain_name[], size_t return false; } // Head of the list of caches. static struct resolv_cache_info res_cache_list GUARDED_BY(cache_mutex); static std::unordered_map<unsigned, std::unique_ptr<NetConfig>> sNetConfigMap GUARDED_BY(cache_mutex); // insert resolv_cache_info into the list of resolv_cache_infos static void insert_cache_info_locked(resolv_cache_info* cache_info); // creates a resolv_cache_info static resolv_cache_info* create_cache_info(); // Clears nameservers set for |cache_info| and clears the stats static void free_nameservers_locked(resolv_cache_info* cache_info); // Clears nameservers set for |netconfig| and clears the stats static void free_nameservers_locked(NetConfig* netconfig); // Order-insensitive comparison for the two set of servers. static bool resolv_is_nameservers_equal(const std::vector<std::string>& oldServers, const std::vector<std::string>& newServers); // clears the stats samples contained withing the given cache_info static void res_cache_clear_stats_locked(resolv_cache_info* cache_info); // clears the stats samples contained withing the given netconfig. static void res_cache_clear_stats_locked(NetConfig* netconfig); // public API for netd to query if name server is set on specific netid bool resolv_has_nameservers(unsigned netid) { std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); return (info != nullptr) && (info->nscount > 0); } namespace { // Map format: ReturnCode:rate_denom // if the ReturnCode is not associated with any rate_denom, use default // Sampling rate varies by return code; events to log are chosen randomly, with a // probability proportional to the sampling rate. constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10"; std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() { using android::base::ParseInt; using android::base::ParseUint; using android::base::Split; using server_configurable_flags::GetServerConfigurableFlag; std::unordered_map<int, uint32_t> sampling_rate_map{}; std::vector<std::string> subsampling_vector = Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map", DEFAULT_SUBSAMPLING_MAP), " "); for (const auto& pair : subsampling_vector) { std::vector<std::string> rate_denom = Split(pair, ":"); int return_code; uint32_t denom; if (rate_denom.size() != 2) { LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair; continue; } if (rate_denom[0] == "default") { return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY; } else if (!ParseInt(rate_denom[0], &return_code)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } if (!ParseUint(rate_denom[1], &denom)) { LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair; continue; } sampling_rate_map[return_code] = denom; } return sampling_rate_map; } } // namespace int resolv_create_cache_for_net(unsigned netid) { std::lock_guard guard(cache_mutex); Cache* cache = find_named_cache_locked(netid); // Should not happen if (cache) { if (sNetConfigMap.find(netid) != sNetConfigMap.end()) { LOG(ERROR) << __func__ << ": Cache is already created, netId: " << netid; return -EEXIST; } resolv_cache_info* cache_info = create_cache_info(); if (!cache_info) return -ENOMEM; cache_info->netid = netid; cache_info->cache = new Cache; cache_info->dns_event_subsampling_map = resolv_get_dns_event_subsampling_map(); cache_info->dnsStats.reset(new DnsStats()); insert_cache_info_locked(cache_info); sNetConfigMap[netid] = std::make_unique<NetConfig>(netid); return 0; } void resolv_delete_cache_for_net(unsigned netid) { std::lock_guard guard(cache_mutex); struct resolv_cache_info* prev_cache_info = &res_cache_list; while (prev_cache_info->next) { struct resolv_cache_info* cache_info = prev_cache_info->next; if (cache_info->netid == netid) { prev_cache_info->next = cache_info->next; delete cache_info->cache; free_nameservers_locked(cache_info); // It won't be necessary after the memory of cache_info can be deallocated by the // C++ delete expression. cache_info->dnsStats.reset(); free(cache_info); break; } prev_cache_info = prev_cache_info->next; } sNetConfigMap.erase(netid); } int resolv_flush_cache_for_net(unsigned netid) { std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); if (cache_info == nullptr) { NetConfig* netconfig = find_netconfig_locked(netid); if (netconfig == nullptr) { return -ENONET; } cache_info->cache->flush(); netconfig->cache->flush(); // Also clear the NS statistics. res_cache_clear_stats_locked(cache_info); res_cache_clear_stats_locked(netconfig); return 0; } std::vector<unsigned> resolv_list_caches() { std::lock_guard guard(cache_mutex); struct resolv_cache_info* cache_info = res_cache_list.next; std::vector<unsigned> result; while (cache_info) { result.push_back(cache_info->netid); cache_info = cache_info->next; result.reserve(sNetConfigMap.size()); for (const auto& [netId, _] : sNetConfigMap) { result.push_back(netId); } return result; } static resolv_cache_info* create_cache_info() { return (struct resolv_cache_info*) calloc(sizeof(struct resolv_cache_info), 1); } // TODO: convert this to a simple and efficient C++ container. static void insert_cache_info_locked(struct resolv_cache_info* cache_info) { struct resolv_cache_info* last; for (last = &res_cache_list; last->next; last = last->next) {} last->next = cache_info; } static Cache* find_named_cache_locked(unsigned netid) { resolv_cache_info* info = find_cache_info_locked(netid); if (info != nullptr) return info->cache; NetConfig* info = find_netconfig_locked(netid); if (info != nullptr) return info->cache.get(); return nullptr; } static resolv_cache_info* find_cache_info_locked(unsigned netid) { struct resolv_cache_info* cache_info = res_cache_list.next; while (cache_info) { if (cache_info->netid == netid) { break; } cache_info = cache_info->next; static NetConfig* find_netconfig_locked(unsigned netid) { if (auto it = sNetConfigMap.find(netid); it != sNetConfigMap.end()) { return it->second.get(); } return cache_info; return nullptr; } static void resolv_set_experiment_params(res_params* params) { Loading Loading @@ -1613,41 +1565,41 @@ int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& serve } std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); NetConfig* netconfig = find_netconfig_locked(netid); if (cache_info == nullptr) return -ENONET; if (netconfig == nullptr) return -ENONET; uint8_t old_max_samples = cache_info->params.max_samples; cache_info->params = params; resolv_set_experiment_params(&cache_info->params); if (!resolv_is_nameservers_equal(cache_info->nameservers, nameservers)) { uint8_t old_max_samples = netconfig->params.max_samples; netconfig->params = params; resolv_set_experiment_params(&netconfig->params); if (!resolv_is_nameservers_equal(netconfig->nameservers, nameservers)) { // free current before adding new free_nameservers_locked(cache_info); cache_info->nameservers = std::move(nameservers); free_nameservers_locked(netconfig); netconfig->nameservers = std::move(nameservers); for (int i = 0; i < numservers; i++) { LOG(INFO) << __func__ << ": netid = " << netid << ", addr = " << cache_info->nameservers[i]; << ", addr = " << netconfig->nameservers[i]; } cache_info->nameserverSockAddrs = std::move(ipSockAddrs); cache_info->nscount = numservers; netconfig->nameserverSockAddrs = std::move(ipSockAddrs); netconfig->nscount = numservers; } else { if (cache_info->params.max_samples != old_max_samples) { if (netconfig->params.max_samples != old_max_samples) { // If the maximum number of samples changes, the overhead of keeping the most recent // samples around is not considered worth the effort, so they are cleared instead. // All other parameters do not affect shared state: Changing these parameters does // not invalidate the samples, as they only affect aggregation and the conditions // under which servers are considered usable. res_cache_clear_stats_locked(cache_info); res_cache_clear_stats_locked(netconfig); } } // Always update the search paths. Cache-flushing however is not necessary, // since the stored cache entries do contain the domain, not just the host name. cache_info->search_domains = filter_domains(domains); netconfig->search_domains = filter_domains(domains); // Setup stats for cleartext dns servers. if (!cache_info->dnsStats->setServers(cache_info->nameserverSockAddrs, PROTO_TCP) || !cache_info->dnsStats->setServers(cache_info->nameserverSockAddrs, PROTO_UDP)) { if (!netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_TCP) || !netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_UDP)) { LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats"; return -EINVAL; } Loading @@ -1668,11 +1620,11 @@ static bool resolv_is_nameservers_equal(const std::vector<std::string>& oldServe return olds == news; } static void free_nameservers_locked(resolv_cache_info* cache_info) { cache_info->nscount = 0; cache_info->nameservers.clear(); cache_info->nameserverSockAddrs.clear(); res_cache_clear_stats_locked(cache_info); static void free_nameservers_locked(NetConfig* netconfig) { netconfig->nscount = 0; netconfig->nameservers.clear(); netconfig->nameserverSockAddrs.clear(); res_cache_clear_stats_locked(netconfig); } void resolv_populate_res_for_net(ResState* statp) { Loading @@ -1682,7 +1634,7 @@ void resolv_populate_res_for_net(ResState* statp) { LOG(INFO) << __func__ << ": netid=" << statp->netid; std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(statp->netid); NetConfig* info = find_netconfig_locked(statp->netid); if (info == nullptr) return; // TODO: Convert nsaddrs[] to c++ container and remove the size-checking. Loading Loading @@ -1720,17 +1672,17 @@ static void res_cache_add_stats_sample_locked(res_stats* stats, const res_sample } } static void res_cache_clear_stats_locked(resolv_cache_info* cache_info) { static void res_cache_clear_stats_locked(NetConfig* netconfig) { for (int i = 0; i < MAXNS; ++i) { cache_info->nsstats[i].sample_count = 0; cache_info->nsstats[i].sample_next = 0; netconfig->nsstats[i].sample_count = 0; netconfig->nsstats[i].sample_next = 0; } // Increment the revision id to ensure that sample state is not written back if the // servers change; in theory it would suffice to do so only if the servers or // max_samples actually change, in practice the overhead of checking is higher than the // cost, and overflows are unlikely. ++cache_info->revision_id; ++netconfig->revision_id; } int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount, Loading @@ -1741,7 +1693,7 @@ int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount, int revision_id = -1; std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info) { if (info->nscount > MAXNS) { LOG(INFO) << __func__ << ": nscount " << info->nscount << " > MAXNS " << MAXNS; Loading Loading @@ -1779,10 +1731,10 @@ int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount, std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) { using android::base::StringPrintf; std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); if (cache_info == nullptr) return {}; NetConfig* netconfig = find_netconfig_locked(netid); if (netconfig == nullptr) return {}; std::vector<std::string> result; for (const auto& pair : cache_info->dns_event_subsampling_map) { for (const auto& pair : netconfig->dns_event_subsampling_map) { result.push_back(StringPrintf("%s:%d", (pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY) ? "default" Loading @@ -1798,9 +1750,9 @@ std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) { // Returns the subsampling rate if the event should be sampled, or 0 if it should be discarded. uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) { std::lock_guard guard(cache_mutex); resolv_cache_info* cache_info = find_cache_info_locked(netid); if (cache_info == nullptr) return 0; // Don't log anything at all. const auto& subsampling_map = cache_info->dns_event_subsampling_map; NetConfig* netconfig = find_netconfig_locked(netid); if (netconfig == nullptr) return 0; // Don't log anything at all. const auto& subsampling_map = netconfig->dns_event_subsampling_map; auto search_returnCode = subsampling_map.find(return_code); uint32_t denom; if (search_returnCode != subsampling_map.end()) { Loading @@ -1814,7 +1766,7 @@ uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) { int resolv_cache_get_resolver_stats(unsigned netid, res_params* params, res_stats stats[MAXNS]) { std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info) { memcpy(stats, info->nsstats, sizeof(info->nsstats)); *params = info->params; Loading @@ -1829,7 +1781,7 @@ void resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, con if (max_samples <= 0 || sa == nullptr) return; std::lock_guard guard(cache_mutex); resolv_cache_info* info = find_cache_info_locked(netid); NetConfig* info = find_netconfig_locked(netid); if (info && info->revision_id == revision_id) { const int serverNum = std::min(MAXNS, static_cast<int>(info->nameserverSockAddrs.size())); Loading Loading @@ -1884,7 +1836,7 @@ int resolv_cache_get_expiration(unsigned netid, const std::vector<char>& query, int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::string>& servers) { std::lock_guard guard(cache_mutex); const auto info = find_cache_info_locked(netid); const auto info = find_netconfig_locked(netid); if (info == nullptr) return -ENONET; Loading @@ -1894,7 +1846,7 @@ int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::stri serverSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 853)); } if (!info->dnsStats->setServers(serverSockAddrs, android::net::PROTO_DOT)) { if (!info->dnsStats.setServers(serverSockAddrs, android::net::PROTO_DOT)) { LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats"; return -EINVAL; } Loading @@ -1907,15 +1859,15 @@ bool resolv_stats_add(unsigned netid, const android::netdutils::IPSockAddr& serv if (record == nullptr) return false; std::lock_guard guard(cache_mutex); if (const auto info = find_cache_info_locked(netid); info != nullptr) { return info->dnsStats->addStats(server, *record); if (const auto info = find_netconfig_locked(netid); info != nullptr) { return info->dnsStats.addStats(server, *record); } return false; } void resolv_stats_dump(DumpWriter& dw, unsigned netid) { std::lock_guard guard(cache_mutex); if (const auto info = find_cache_info_locked(netid); info != nullptr) { info->dnsStats->dump(dw); if (const auto info = find_netconfig_locked(netid); info != nullptr) { info->dnsStats.dump(dw); } }
resolv_cache_unit_test.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -902,7 +902,7 @@ TEST_F(ResolvCacheTest, DnsEventSubsampling) { } } // TODO: Tests for struct resolv_cache_info, including: // TODO: Tests for NetConfig, including: // - res_params // -- resolv_cache_get_resolver_stats() // - res_stats Loading