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

Commit 52cf009b authored by chenbruce's avatar chenbruce
Browse files

Subsampling DNS event for mDNS

Currently, a device generates some mDNS queries when the user uses
mDNS .local resolution.
Using the query info by subsampling events based on how interesting
they are. Because the number of mDNS query is much less than DNS
query, the mDNS subsampling rate is higer than DNS query.
- if return_code == 0,2,7 -> log 1 in 1 event
- if return_code == default -> log 1 in 1 event
Also allow to use experiment flag to update sub-sampling denom.

Example for dumpsys dnsresolver:
  NetId: 100
    DnsEvent subsampling map for MDNS: default:1

Bug: 197092658
Test: cd packages/modules/DnsResolver && atest
      m statsd_testdrive and run "statsd_testdrive 116"
Change-Id: I76073aa9a1cea43bda2675334592ed22e96a238e
parent 9b72daa3
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <sysutils/SocketClient.h>

#include "DnsResolver.h"
#include "Experiments.h"
#include "NetdPermissions.h"
#include "OperationLimiter.h"
#include "PrivateDnsConfiguration.h"
@@ -305,8 +306,8 @@ void initDnsEvent(NetworkDnsEventReported* event, const android_net_context& net

// Return 0 if the event should not be logged.
// Otherwise, return subsampling_denom
uint32_t getDnsEventSubsamplingRate(int netid, int returnCode) {
    uint32_t subsampling_denom = resolv_cache_get_subsampling_denom(netid, returnCode);
uint32_t getDnsEventSubsamplingRate(int netid, int returnCode, bool isMdns) {
    uint32_t subsampling_denom = resolv_cache_get_subsampling_denom(netid, returnCode, isMdns);
    if (subsampling_denom == 0) return 0;
    // Sample the event with a chance of 1 / denom.
    return (arc4random_uniform(subsampling_denom) == 0) ? subsampling_denom : 0;
@@ -333,7 +334,12 @@ void maybeLogQuery(int eventType, const android_net_context& netContext,
void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs,
                    int returnCode, NetworkDnsEventReported& event, const std::string& query_name,
                    const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) {
    if (uint32_t rate = getDnsEventSubsamplingRate(netContext.dns_netid, returnCode)) {
    uint32_t rate = (query_name.ends_with(".local") &&
                     android::net::Experiments::getInstance()->getFlag("mdns_resolution", 1))
                            ? getDnsEventSubsamplingRate(netContext.dns_netid, returnCode, true)
                            : getDnsEventSubsamplingRate(netContext.dns_netid, returnCode, false);

    if (rate) {
        const std::string& dnsQueryStats = event.dns_query_events().SerializeAsString();
        stats::BytesField dnsQueryBytesField{dnsQueryStats.c_str(), dnsQueryStats.size()};
        event.set_return_code(static_cast<ReturnCode>(returnCode));
+3 −1
Original line number Diff line number Diff line
@@ -319,7 +319,9 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) {
            dw.println("No DNS servers defined");
        } else {
            dw.println("DnsEvent subsampling map: " +
                       android::base::Join(resolv_cache_dump_subsampling_map(netId), ' '));
                       android::base::Join(resolv_cache_dump_subsampling_map(netId, false), ' '));
            dw.println("DnsEvent subsampling map for MDNS: " +
                       android::base::Join(resolv_cache_dump_subsampling_map(netId, true), ' '));
            dw.println(
                    "DNS servers: # IP (total, successes, errors, timeouts, internal errors, "
                    "RTT avg, last sample)");
+21 −13
Original line number Diff line number Diff line
@@ -909,17 +909,19 @@ namespace {
// 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:8 0:400 2:110 7:110";
constexpr const char DEFAULT_MDNS_SUBSAMPLING_MAP[] = "default:1";

std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() {
std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map(bool isMdns) {
    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),
                  " ");
    const char* flag = isMdns ? "mdns_event_subsample_map" : "dns_event_subsample_map";
    const char* defaultMap = isMdns ? DEFAULT_MDNS_SUBSAMPLING_MAP : DEFAULT_SUBSAMPLING_MAP;
    const std::vector<std::string> subsampling_vector =
            Split(GetServerConfigurableFlag("netd_native", flag, defaultMap), " ");

    for (const auto& pair : subsampling_vector) {
        std::vector<std::string> rate_denom = Split(pair, ":");
        int return_code;
@@ -1005,7 +1007,8 @@ struct Cache {
struct NetConfig {
    explicit NetConfig(unsigned netId) : netid(netId) {
        cache = std::make_unique<Cache>();
        dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
        dns_event_subsampling_map = resolv_get_dns_event_subsampling_map(false);
        mdns_event_subsampling_map = resolv_get_dns_event_subsampling_map(true);
    }
    int nameserverCount() { return nameserverSockAddrs.size(); }
    int setOptions(const ResolverOptionsParcel& resolverOptions) {
@@ -1036,6 +1039,7 @@ struct NetConfig {
    int wait_for_pending_req_timeout_count = 0;
    // Map format: ReturnCode:rate_denom
    std::unordered_map<int, uint32_t> dns_event_subsampling_map;
    std::unordered_map<int, uint32_t> mdns_event_subsampling_map;
    DnsStats dnsStats;

    // Customized hostname/address table will be stored in customizedTable.
@@ -1793,17 +1797,20 @@ int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount,
    return info->revision_id;
}

std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) {
std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid, bool is_mdns) {
    std::lock_guard guard(cache_mutex);
    NetConfig* netconfig = find_netconfig_locked(netid);
    if (netconfig == nullptr) return {};
    std::vector<std::string> result;
    for (const auto& pair : netconfig->dns_event_subsampling_map) {
    const auto& subsampling_map = (!is_mdns) ? netconfig->dns_event_subsampling_map
                                             : netconfig->mdns_event_subsampling_map;
    result.reserve(subsampling_map.size());
    for (const auto& [return_code, rate_denom] : subsampling_map) {
        result.push_back(fmt::format("{}:{}",
                                     (pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY)
                                     (return_code == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY)
                                             ? "default"
                                             : std::to_string(pair.first),
                                     pair.second));
                                             : std::to_string(return_code),
                                     rate_denom));
    }
    return result;
}
@@ -1812,11 +1819,12 @@ std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) {
// a sampling factor derived from the netid and the return code.
//
// 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) {
uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code, bool is_mdns) {
    std::lock_guard guard(cache_mutex);
    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;
    const auto& subsampling_map = (!is_mdns) ? netconfig->dns_event_subsampling_map
                                             : netconfig->mdns_event_subsampling_map;
    auto search_returnCode = subsampling_map.find(return_code);
    uint32_t denom;
    if (search_returnCode != subsampling_map.end()) {
+2 −2
Original line number Diff line number Diff line
@@ -50,8 +50,8 @@ void resolv_populate_res_for_net(ResState* statp);

std::vector<unsigned> resolv_list_caches();

std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid);
uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code);
std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid, bool is_mdns);
uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code, bool is_mdns);

typedef enum {
    RESOLV_CACHE_UNSUPPORTED, /* the cache can't handle that kind of queries */
+72 −22
Original line number Diff line number Diff line
@@ -910,11 +910,12 @@ namespace {
constexpr int EAI_OK = 0;
constexpr char DNS_EVENT_SUBSAMPLING_MAP_FLAG[] =
        "persist.device_config.netd_native.dns_event_subsample_map";
constexpr char MDNS_EVENT_SUBSAMPLING_MAP_FLAG[] =
        "persist.device_config.netd_native.mdns_event_subsample_map";

class ScopedCacheCreate {
  public:
    explicit ScopedCacheCreate(unsigned netid, const char* subsampling_map,
                               const char* property = DNS_EVENT_SUBSAMPLING_MAP_FLAG)
    explicit ScopedCacheCreate(unsigned netid, const char* subsampling_map, const char* property)
        : mStoredNetId(netid), mStoredProperty(property) {
        property_get(property, mStoredMap, "");
        property_set(property, subsampling_map);
@@ -936,45 +937,94 @@ class ScopedCacheCreate {
TEST_F(ResolvCacheTest, DnsEventSubsampling) {
    // Test defaults, default flag is "default:8 0:400 2:110 7:110" if no experiment flag is set
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "");
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_AGAIN), 110U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 110U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 400U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS),
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "", DNS_EVENT_SUBSAMPLING_MAP_FLAG);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_AGAIN, false), 110U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, false), 110U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, false), 400U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS, false),
                  8U);  // default
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID, false),
                    testing::UnorderedElementsAreArray({"default:8", "0:400", "2:110", "7:110"}));
    }
    // Now change the experiment flag to "0:42 default:666"
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:42 default:666");
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 42U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA),
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:42 default:666",
                                            DNS_EVENT_SUBSAMPLING_MAP_FLAG);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, false), 42U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, false),
                  666U);  // default
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID, false),
                    testing::UnorderedElementsAreArray({"default:666", "0:42"}));
    }
    // Now change the experiment flag to something illegal
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "asvaxx");
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "asvaxx", DNS_EVENT_SUBSAMPLING_MAP_FLAG);
        // 0(disable log) is the default value if experiment flag is invalid.
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 0U);
        EXPECT_TRUE(resolv_cache_dump_subsampling_map(TEST_NETID).empty());
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, false), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, false), 0U);
        EXPECT_TRUE(resolv_cache_dump_subsampling_map(TEST_NETID, false).empty());
    }
    // Test negative and zero denom
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:-42 default:-666 7:10 10:0");
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:-42 default:-666 7:10 10:0",
                                            DNS_EVENT_SUBSAMPLING_MAP_FLAG);
        // 0(disable log) is the default value if no valid denom is set
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 10U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_SOCKTYPE), 0U);
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, false), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS, false), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, false), 10U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_SOCKTYPE, false), 0U);
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID, false),
                    testing::UnorderedElementsAreArray({"7:10", "10:0"}));
    }
}

TEST_F(ResolvCacheTest, MdnsEventSubsampling) {
    // Test defaults, DEFAULT_MDNS_SUBSAMPLING_MAP is "default:1" if no experiment flag is set
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "", MDNS_EVENT_SUBSAMPLING_MAP_FLAG);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_AGAIN, true),
                  1U);  // default for all return_code
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, true), 1U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS, true), 1U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, true), 1U);
        // not equal to DEFAULT_SUBSAMPLING_MAP[] = "default:8 0:400 2:110 7:110";
        EXPECT_NE(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_AGAIN, true), 110U);
        EXPECT_NE(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, true), 110U);
        EXPECT_NE(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, true), 400U);
        EXPECT_NE(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS, true), 8U);
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID, true),
                    testing::UnorderedElementsAreArray({"default:1"}));
    }
    // Now change the experiment flag to "default:1 0:10"
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:10 default:1",
                                            MDNS_EVENT_SUBSAMPLING_MAP_FLAG);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, true), 10U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, true), 1U);  // default
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID, true),
                    testing::UnorderedElementsAreArray({"0:10", "default:1"}));
    }
    // Now change the experiment flag to something illegal
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "asvaxx", MDNS_EVENT_SUBSAMPLING_MAP_FLAG);
        // 0(disable log) is the default value if experiment flag is invalid.
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, true), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, true), 0U);
        EXPECT_TRUE(resolv_cache_dump_subsampling_map(TEST_NETID, true).empty());
    }
    // Test negative and zero denom
    {
        ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:-42 default:-666 7:10 10:0",
                                            MDNS_EVENT_SUBSAMPLING_MAP_FLAG);
        // 0(disable log) is the default value if no valid denom is set
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK, true), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS, true), 0U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA, true), 10U);
        EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_SOCKTYPE, true), 0U);
        EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID, true),
                    testing::UnorderedElementsAreArray({"7:10", "10:0"}));
    }
}
// TODO: Tests for NetConfig, including:
//     - res_stats
//         -- _resolv_cache_add_resolver_stats_sample()