Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0765c8d7 authored by Mike Yu's avatar Mike Yu Committed by Gerrit Code Review
Browse files

Merge changes Iae392108,Ib00da89c,Ie8175701,Ia4bc47a0

* changes:
  Embedding DnsStats instead of using a smart pointer
  Rename resolv_cache_info to NetConfig
  Remove the head node of resolv_cache_info
  Allocate resolv_cache_info with operator new
parents 47c3eb58 15ad2f9b
Loading
Loading
Loading
Loading
+125 −173
Original line number Diff line number Diff line
@@ -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.
//
@@ -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 */
@@ -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) {
@@ -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++;
                }
@@ -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) {
@@ -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;
    }
@@ -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) {
@@ -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.
@@ -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,
@@ -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;
@@ -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"
@@ -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()) {
@@ -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;
@@ -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()));
@@ -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;

@@ -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;
    }
@@ -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);
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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