diff options
| author | android-build-team Robot <android-build-team-robot@google.com> | 2019-07-27 23:06:32 +0000 |
|---|---|---|
| committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-07-27 23:06:32 +0000 |
| commit | c8400101938fddb8789bc8de87c713b382f569e4 (patch) | |
| tree | b39647c8c88f09a0294f0f8173b91fa14a790bdb | |
| parent | 5b1c32b39ed9157ea6eefadc141d866d9201ffce (diff) | |
| parent | 72e88a79af497b294787c6ba7e232c3bd4f3c137 (diff) | |
Snap for 5761090 from 72e88a79af497b294787c6ba7e232c3bd4f3c137 to qt-c2f2-releaseq10.0-r9rebase
Change-Id: Ibea92fe8f60ff6049c82d9ada661b9b01129e6bd
| -rw-r--r-- | resolv/Android.bp | 5 | ||||
| -rw-r--r-- | resolv/Dns64Configuration.cpp | 7 | ||||
| -rw-r--r-- | resolv/DnsProxyListener.cpp | 116 | ||||
| -rw-r--r-- | resolv/DnsProxyListener.h | 8 | ||||
| -rw-r--r-- | resolv/DnsTlsDispatcher.cpp | 34 | ||||
| -rw-r--r-- | resolv/DnsTlsDispatcher.h | 7 | ||||
| -rw-r--r-- | resolv/ResolverController.cpp | 5 | ||||
| -rw-r--r-- | resolv/getaddrinfo.cpp | 26 | ||||
| -rw-r--r-- | resolv/getaddrinfo.h | 4 | ||||
| -rw-r--r-- | resolv/gethnamaddr.cpp | 54 | ||||
| -rw-r--r-- | resolv/gethnamaddr.h | 6 | ||||
| -rw-r--r-- | resolv/libnetd_resolv_test.cpp | 48 | ||||
| -rw-r--r-- | resolv/res_cache.cpp | 88 | ||||
| -rw-r--r-- | resolv/res_cache_test.cpp | 95 | ||||
| -rw-r--r-- | resolv/res_init.cpp | 4 | ||||
| -rw-r--r-- | resolv/res_send.cpp | 87 | ||||
| -rw-r--r-- | resolv/res_send.h | 4 | ||||
| -rw-r--r-- | resolv/resolv_cache.h | 6 | ||||
| -rw-r--r-- | resolv/resolv_private.h | 17 | ||||
| -rw-r--r-- | resolv/stats.proto | 40 |
20 files changed, 528 insertions, 133 deletions
diff --git a/resolv/Android.bp b/resolv/Android.bp index 30d5f5af..6819b7ac 100644 --- a/resolv/Android.bp +++ b/resolv/Android.bp @@ -205,17 +205,22 @@ cc_test { srcs: [ "dns_tls_test.cpp", "libnetd_resolv_test.cpp", + "res_cache_test.cpp", ], shared_libs: [ "libbase", "libcrypto", + "libcutils", "liblog", "libssl", ], static_libs: [ + "libgmock", "libnetd_resolv", "libnetd_test_dnsresponder", "libnetdutils", + "libprotobuf-cpp-lite", "server_configurable_flags", + "stats_proto", ], } diff --git a/resolv/Dns64Configuration.cpp b/resolv/Dns64Configuration.cpp index 6cd9c2e3..a1dfdca0 100644 --- a/resolv/Dns64Configuration.cpp +++ b/resolv/Dns64Configuration.cpp @@ -33,9 +33,11 @@ #include "netdutils/BackoffSequence.h" #include "netdutils/DumpWriter.h" #include "netid_client.h" +#include "stats.pb.h" namespace android { +using android::net::NetworkDnsEventReported; using netdutils::DumpWriter; using netdutils::IPAddress; using netdutils::IPPrefix; @@ -152,8 +154,9 @@ bool Dns64Configuration::doRfc7050PrefixDiscovery(const android_net_context& net // ourselves, which means we also bypass all the special netcontext flag // handling and the resolver event logging. struct addrinfo* res = nullptr; - const int status = - android_getaddrinfofornetcontext(kIPv4OnlyHost, nullptr, &hints, &netcontext, &res); + NetworkDnsEventReported event; + const int status = android_getaddrinfofornetcontext(kIPv4OnlyHost, nullptr, &hints, &netcontext, + &res, &event); ScopedAddrinfo result(res); if (status != 0) { ALOGW("(%u, %u) plat_prefix/dns(%s) status = %d/%s", cfg->netId, cfg->discoveryId, diff --git a/resolv/DnsProxyListener.cpp b/resolv/DnsProxyListener.cpp index 63aa3310..8925bc4d 100644 --- a/resolv/DnsProxyListener.cpp +++ b/resolv/DnsProxyListener.cpp @@ -32,7 +32,6 @@ #define LOG_TAG "DnsProxyListener" #include <algorithm> -#include <list> #include <vector> #include <android-base/stringprintf.h> @@ -57,6 +56,7 @@ #include "gethnamaddr.h" #include "netd_resolv/stats.h" // RCODE_TIMEOUT #include "res_send.h" +#include "resolv_cache.h" #include "resolv_private.h" #include "stats.pb.h" @@ -299,17 +299,34 @@ bool parseQuery(const uint8_t* msg, size_t msgLen, uint16_t* query_id, int* rr_t return true; } +void initDnsEvent(NetworkDnsEventReported* event) { + // The value 0 has the special meaning of unset/unknown in Westworld atoms. + event->set_hints_ai_flags(-1); + event->set_res_nsend_flags(-1); +} + +// 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); + if (subsampling_denom == 0) return 0; + // Sample the event with a chance of 1 / denom. + return (arc4random_uniform(subsampling_denom) == 0) ? subsampling_denom : 0; +} + void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs, - int returnCode, const NetworkDnsEventReported& dnsEvent, - const std::string& query_name, const std::vector<std::string>& ip_addrs = {}, - int total_ip_addr_count = 0) { - std::string dnsQueryStats = dnsEvent.dns_query_events().SerializeAsString(); - char const* dnsQueryStatsBytes = dnsQueryStats.c_str(); - stats::BytesField dnsQueryBytesField{dnsQueryStatsBytes, dnsQueryStats.size()}; - android::net::stats::stats_write(android::net::stats::NETWORK_DNS_EVENT_REPORTED, eventType, - returnCode, latencyUs, dnsEvent.hints_ai_flags(), - dnsEvent.res_nsend_flags(), dnsEvent.network_type(), - dnsEvent.private_dns_modes(), dnsQueryBytesField); + 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)) { + 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)); + android::net::stats::stats_write(android::net::stats::NETWORK_DNS_EVENT_REPORTED, + event.event_type(), event.return_code(), + event.latency_micros(), event.hints_ai_flags(), + event.res_nsend_flags(), event.network_type(), + event.private_dns_modes(), dnsQueryBytesField, rate); + } const auto& listeners = ResolverEventReporter::getInstance().getListeners(); if (listeners.size() == 0) { @@ -590,7 +607,8 @@ static bool sendaddrinfo(SocketClient* c, addrinfo* ai) { return true; } -void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res) { +void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res, + NetworkDnsEventReported* event) { if (mHost == nullptr) return; const bool ipv6WantedButNoData = (mHints && mHints->ai_family == AF_INET6 && *rv == EAI_NODATA); @@ -613,7 +631,8 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf mHints->ai_family = AF_INET; // Don't need to do freeaddrinfo(res) before starting new DNS lookup because previous // DNS lookup is failed with error EAI_NODATA. - *rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, res); + *rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, res, + event); queryLimiter.finish(uid); if (*rv) { *rv = EAI_NODATA; // return original error code @@ -648,9 +667,11 @@ void DnsProxyListener::GetAddrInfoHandler::run() { maybeFixupNetContext(&mNetContext); const uid_t uid = mClient->getUid(); int32_t rv = 0; - NetworkDnsEventReported dnsEvent; + NetworkDnsEventReported event; + initDnsEvent(&event); if (queryLimiter.start(uid)) { - rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result); + rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result, + &event); queryLimiter.finish(uid); } else { // Note that this error code is currently not passed down to the client. @@ -660,8 +681,11 @@ void DnsProxyListener::GetAddrInfoHandler::run() { << ", max concurrent queries reached"; } - doDns64Synthesis(&rv, &result); - const int latencyUs = int(s.timeTakenUs()); + doDns64Synthesis(&rv, &result, &event); + const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs()); + event.set_latency_micros(latencyUs); + event.set_event_type(EVENT_GETADDRINFO); + event.set_hints_ai_flags((mHints ? mHints->ai_flags : 0)); if (rv) { // getaddrinfo failed @@ -680,8 +704,8 @@ void DnsProxyListener::GetAddrInfoHandler::run() { } std::vector<std::string> ip_addrs; const int total_ip_addr_count = extractGetAddrInfoAnswers(result, &ip_addrs); - reportDnsEvent(INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyUs, rv, dnsEvent, - mHost, ip_addrs, total_ip_addr_count); + reportDnsEvent(INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyUs, rv, event, mHost, + ip_addrs, total_ip_addr_count); freeaddrinfo(result); mClient->decRef(); } @@ -854,10 +878,11 @@ void DnsProxyListener::ResNSendHandler::run() { // Send DNS query std::vector<uint8_t> ansBuf(MAXPACKET, 0); int arcode, nsendAns = -1; - NetworkDnsEventReported dnsEvent; + NetworkDnsEventReported event; + initDnsEvent(&event); if (queryLimiter.start(uid)) { nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET, - &arcode, static_cast<ResNsendFlags>(mFlags)); + &arcode, static_cast<ResNsendFlags>(mFlags), &event); queryLimiter.finish(uid); } else { LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid @@ -865,14 +890,17 @@ void DnsProxyListener::ResNSendHandler::run() { nsendAns = -EBUSY; } - const int latencyUs = int(s.timeTakenUs()); + const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs()); + event.set_latency_micros(latencyUs); + event.set_event_type(EVENT_RES_NSEND); + event.set_res_nsend_flags(static_cast<ResNsendFlags>(mFlags)); // Fail, send -errno if (nsendAns < 0) { sendBE32(mClient, nsendAns); if (rr_type == ns_t_a || rr_type == ns_t_aaaa) { reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs, - resNSendToAiError(nsendAns, arcode), dnsEvent, rr_name); + resNSendToAiError(nsendAns, arcode), event, rr_name); } return; } @@ -895,7 +923,7 @@ void DnsProxyListener::ResNSendHandler::run() { const int total_ip_addr_count = extractResNsendAnswers((uint8_t*) ansBuf.data(), nsendAns, rr_type, &ip_addrs); reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs, - resNSendToAiError(nsendAns, arcode), dnsEvent, rr_name, ip_addrs, + resNSendToAiError(nsendAns, arcode), event, rr_name, ip_addrs, total_ip_addr_count); } } @@ -994,7 +1022,8 @@ DnsProxyListener::GetHostByNameHandler::~GetHostByNameHandler() { free(mName); } -void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, struct hostent** hpp) { +void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, struct hostent** hpp, + NetworkDnsEventReported* event) { // Don't have to consider family AF_UNSPEC case because gethostbyname{, 2} only supports // family AF_INET or AF_INET6. const bool ipv6WantedButNoData = (mAf == AF_INET6 && *rv == EAI_NODATA); @@ -1011,7 +1040,7 @@ void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, struc // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis const uid_t uid = mClient->getUid(); if (queryLimiter.start(uid)) { - *rv = android_gethostbynamefornetcontext(mName, AF_INET, &mNetContext, hpp); + *rv = android_gethostbynamefornetcontext(mName, AF_INET, &mNetContext, hpp, event); queryLimiter.finish(uid); if (*rv) { *rv = EAI_NODATA; // return original error code @@ -1035,9 +1064,10 @@ void DnsProxyListener::GetHostByNameHandler::run() { const uid_t uid = mClient->getUid(); hostent* hp = nullptr; int32_t rv = 0; - NetworkDnsEventReported dnsEvent; + NetworkDnsEventReported event; + initDnsEvent(&event); if (queryLimiter.start(uid)) { - rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp); + rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp, &event); queryLimiter.finish(uid); } else { rv = EAI_MEMORY; @@ -1045,8 +1075,11 @@ void DnsProxyListener::GetHostByNameHandler::run() { << ", max concurrent queries reached"; } - doDns64Synthesis(&rv, &hp); - const int latencyUs = lround(s.timeTakenUs()); + doDns64Synthesis(&rv, &hp, &event); + const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs()); + event.set_latency_micros(latencyUs); + event.set_event_type(EVENT_GETHOSTBYNAME); + LOG(DEBUG) << "GetHostByNameHandler::run: errno: " << (hp ? "success" : strerror(errno)); bool success = true; @@ -1064,7 +1097,7 @@ void DnsProxyListener::GetHostByNameHandler::run() { std::vector<std::string> ip_addrs; const int total_ip_addr_count = extractGetHostByNameAnswers(hp, &ip_addrs); - reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext, latencyUs, rv, dnsEvent, + reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext, latencyUs, rv, event, mName, ip_addrs, total_ip_addr_count); mClient->decRef(); } @@ -1134,7 +1167,8 @@ DnsProxyListener::GetHostByAddrHandler::~GetHostByAddrHandler() { free(mAddress); } -void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent** hpp) { +void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent** hpp, + NetworkDnsEventReported* event) { if (*hpp != nullptr || mAddressFamily != AF_INET6 || !mAddress) { return; } @@ -1162,7 +1196,8 @@ void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent if (queryLimiter.start(uid)) { // Remove NAT64 prefix and do reverse DNS query struct in_addr v4addr = {.s_addr = v6addr.s6_addr32[3]}; - android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET, &mNetContext, hpp); + android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET, &mNetContext, hpp, + event); queryLimiter.finish(uid); if (*hpp) { // Replace IPv4 address with original queried IPv6 address in place. The space has @@ -1184,10 +1219,11 @@ void DnsProxyListener::GetHostByAddrHandler::run() { const uid_t uid = mClient->getUid(); hostent* hp = nullptr; int32_t rv = 0; - NetworkDnsEventReported dnsEvent; + NetworkDnsEventReported event; + initDnsEvent(&event); if (queryLimiter.start(uid)) { - rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily, - &mNetContext, &hp); + rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily, &mNetContext, + &hp, &event); queryLimiter.finish(uid); } else { rv = EAI_MEMORY; @@ -1195,8 +1231,10 @@ void DnsProxyListener::GetHostByAddrHandler::run() { << ", max concurrent queries reached"; } - doDns64ReverseLookup(&hp); - const int latencyUs = int(s.timeTakenUs()); + doDns64ReverseLookup(&hp, &event); + const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs()); + event.set_latency_micros(latencyUs); + event.set_event_type(EVENT_GETHOSTBYADDR); LOG(DEBUG) << "GetHostByAddrHandler::run: result: " << (hp ? "success" : gai_strerror(rv)); @@ -1212,7 +1250,7 @@ void DnsProxyListener::GetHostByAddrHandler::run() { LOG(WARNING) << "GetHostByAddrHandler::run: Error writing DNS result to client"; } - reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv, dnsEvent, + reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv, event, (hp && hp->h_name) ? hp->h_name : "null", {}, 0); mClient->decRef(); } diff --git a/resolv/DnsProxyListener.h b/resolv/DnsProxyListener.h index 423e77f2..9b71bc54 100644 --- a/resolv/DnsProxyListener.h +++ b/resolv/DnsProxyListener.h @@ -28,6 +28,8 @@ struct hostent; namespace android { namespace net { +class NetworkDnsEventReported; + class DnsProxyListener : public FrameworkListener { public: DnsProxyListener(); @@ -54,7 +56,7 @@ class DnsProxyListener : public FrameworkListener { void run(); private: - void doDns64Synthesis(int32_t* rv, addrinfo** res); + void doDns64Synthesis(int32_t* rv, addrinfo** res, NetworkDnsEventReported* event); SocketClient* mClient; // ref counted char* mHost; // owned. TODO: convert to std::string. @@ -80,7 +82,7 @@ class DnsProxyListener : public FrameworkListener { void run(); private: - void doDns64Synthesis(int32_t* rv, hostent** hpp); + void doDns64Synthesis(int32_t* rv, hostent** hpp, NetworkDnsEventReported* event); SocketClient* mClient; // ref counted char* mName; // owned. TODO: convert to std::string. @@ -105,7 +107,7 @@ class DnsProxyListener : public FrameworkListener { void run(); private: - void doDns64ReverseLookup(hostent** hpp); + void doDns64ReverseLookup(hostent** hpp, NetworkDnsEventReported* event); SocketClient* mClient; // ref counted void* mAddress; // address to lookup; owned diff --git a/resolv/DnsTlsDispatcher.cpp b/resolv/DnsTlsDispatcher.cpp index d9896ad7..baafa53d 100644 --- a/resolv/DnsTlsDispatcher.cpp +++ b/resolv/DnsTlsDispatcher.cpp @@ -18,13 +18,17 @@ //#define LOG_NDEBUG 0 #include "DnsTlsDispatcher.h" +#include <netdutils/Stopwatch.h> #include "DnsTlsSocketFactory.h" +#include "resolv_private.h" +#include "stats.pb.h" #include "log/log.h" namespace android { namespace net { +using android::netdutils::Stopwatch; using netdutils::Slice; // static @@ -82,29 +86,45 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedServerList( return out; } -DnsTlsTransport::Response DnsTlsDispatcher::query( - const std::list<DnsTlsServer> &tlsServers, unsigned mark, - const Slice query, const Slice ans, int *resplen) { - const std::list<DnsTlsServer> orderedServers(getOrderedServerList(tlsServers, mark)); +DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& tlsServers, + res_state statp, const Slice query, + const Slice ans, int* resplen) { + const std::list<DnsTlsServer> orderedServers(getOrderedServerList(tlsServers, statp->_mark)); if (orderedServers.empty()) ALOGW("Empty DnsTlsServer list"); DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error; + int serverCount = 0; for (const auto& server : orderedServers) { - code = this->query(server, mark, query, ans, resplen); + DnsQueryEvent* dnsQueryEvent = + statp->event->mutable_dns_query_events()->add_dns_query_event(); + dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR); + Stopwatch query_stopwatch; + code = this->query(server, statp->_mark, query, ans, resplen); + + dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(query_stopwatch.timeTakenUs())); + dnsQueryEvent->set_dns_server_index(serverCount++); + dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(server.ss.ss_family)); + dnsQueryEvent->set_protocol(PROTO_DOT); + dnsQueryEvent->set_type(getQueryType(query.base(), query.size())); + switch (code) { // These response codes are valid responses and not expected to // change if another server is queried. case DnsTlsTransport::Response::success: + dnsQueryEvent->set_rcode( + static_cast<NsRcode>(reinterpret_cast<HEADER*>(ans.base())->rcode)); + [[fallthrough]]; case DnsTlsTransport::Response::limit_error: return code; - break; // These response codes might differ when trying other servers, so // keep iterating to see if we can get a different (better) result. case DnsTlsTransport::Response::network_error: + // Sync from res_tls_send in res_send.cpp + dnsQueryEvent->set_rcode(NS_R_TIMEOUT); + [[fallthrough]]; case DnsTlsTransport::Response::internal_error: continue; - break; // No "default" statement. } } diff --git a/resolv/DnsTlsDispatcher.h b/resolv/DnsTlsDispatcher.h index 7a480899..b8e99680 100644 --- a/resolv/DnsTlsDispatcher.h +++ b/resolv/DnsTlsDispatcher.h @@ -28,6 +28,7 @@ #include "DnsTlsServer.h" #include "DnsTlsTransport.h" #include "IDnsTlsSocketFactory.h" +#include "resolv_private.h" namespace android { namespace net { @@ -48,9 +49,9 @@ class DnsTlsDispatcher { // the count of bytes written in |resplen|. Returns a success or error code. // The order in which servers from |tlsServers| are queried may not be the // order passed in by the caller. - DnsTlsTransport::Response query(const std::list<DnsTlsServer>& tlsServers, unsigned mark, - const netdutils::Slice query, const netdutils::Slice ans, - int* _Nonnull resplen); + DnsTlsTransport::Response query(const std::list<DnsTlsServer>& tlsServers, + res_state _Nonnull statp, const netdutils::Slice query, + const netdutils::Slice ans, int* _Nonnull resplen); // Given a |query|, sends it to the server on the network indicated by |mark|, // and writes the response into |ans|, and indicates diff --git a/resolv/ResolverController.cpp b/resolv/ResolverController.cpp index e16ca69a..ac242596 100644 --- a/resolv/ResolverController.cpp +++ b/resolv/ResolverController.cpp @@ -317,6 +317,8 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) { if (servers.empty()) { dw.println("No DNS servers defined"); } else { + dw.println("DnsEvent subsampling map: " + + android::base::Join(resolv_cache_dump_subsampling_map(netId), ' ')); dw.println( "DNS servers: # IP (total, successes, errors, timeouts, internal errors, " "RTT avg, last sample)"); @@ -357,8 +359,7 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) { mDns64Configuration.dump(dw, netId); ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}}; gPrivateDnsConfiguration.getStatus(netId, &privateDnsStatus); - dw.println("Private DNS mode: %s", - getPrivateDnsModeString(static_cast<PrivateDnsMode>(privateDnsStatus.mode))); + dw.println("Private DNS mode: %s", getPrivateDnsModeString(privateDnsStatus.mode)); if (!privateDnsStatus.numServers) { dw.println("No Private DNS servers configured"); } else { diff --git a/resolv/getaddrinfo.cpp b/resolv/getaddrinfo.cpp index 1d5038e2..bb9ef071 100644 --- a/resolv/getaddrinfo.cpp +++ b/resolv/getaddrinfo.cpp @@ -63,6 +63,8 @@ #define ANY 0 +using android::net::NetworkDnsEventReported; + const char in_addrany[] = {0, 0, 0, 0}; const char in_loopback[] = {127, 0, 0, 1}; const char in6_addrany[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -125,7 +127,7 @@ struct res_target { static int str2number(const char*); static int explore_fqdn(const struct addrinfo*, const char*, const char*, struct addrinfo**, - const struct android_net_context*); + const struct android_net_context*, NetworkDnsEventReported* event); static int explore_null(const struct addrinfo*, const char*, struct addrinfo**); static int explore_numeric(const struct addrinfo*, const char*, const char*, struct addrinfo**, const char*); @@ -141,7 +143,8 @@ static int ip6_str2scopeid(const char*, struct sockaddr_in6*, u_int32_t*); static struct addrinfo* getanswer(const querybuf*, int, const char*, int, const struct addrinfo*, int* herrno); static int dns_getaddrinfo(const char* name, const addrinfo* pai, - const android_net_context* netcontext, addrinfo** rv); + const android_net_context* netcontext, addrinfo** rv, + NetworkDnsEventReported* event); static void _sethtent(FILE**); static void _endhtent(FILE**); static struct addrinfo* _gethtent(FILE**, const char*, const struct addrinfo*); @@ -265,13 +268,15 @@ int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hin .dns_mark = MARK_UNSET, .uid = NET_CONTEXT_INVALID_UID, }; - return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result); + NetworkDnsEventReported event; + return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result, + &event); } int android_getaddrinfofornetcontext(const char* hostname, const char* servname, const struct addrinfo* hints, const struct android_net_context* netcontext, - struct addrinfo** res) { + struct addrinfo** res, NetworkDnsEventReported* event) { struct addrinfo sentinel = {}; struct addrinfo* cur = &sentinel; int error = 0; @@ -281,6 +286,7 @@ int android_getaddrinfofornetcontext(const char* hostname, const char* servname, // hints is allowed to be nullptr assert(res != nullptr); assert(netcontext != nullptr); + assert(event != nullptr); struct addrinfo ai = { .ai_flags = 0, @@ -413,7 +419,7 @@ int android_getaddrinfofornetcontext(const char* hostname, const char* servname, LOG(DEBUG) << __func__ << ": explore_fqdn(): ai_family=" << tmp.ai_family << " ai_socktype=" << tmp.ai_socktype << " ai_protocol=" << tmp.ai_protocol; - error = explore_fqdn(&tmp, hostname, servname, &cur->ai_next, netcontext); + error = explore_fqdn(&tmp, hostname, servname, &cur->ai_next, netcontext, event); while (cur->ai_next) cur = cur->ai_next; } @@ -436,7 +442,8 @@ int android_getaddrinfofornetcontext(const char* hostname, const char* servname, // FQDN hostname, DNS lookup static int explore_fqdn(const struct addrinfo* pai, const char* hostname, const char* servname, - struct addrinfo** res, const struct android_net_context* netcontext) { + struct addrinfo** res, const struct android_net_context* netcontext, + NetworkDnsEventReported* event) { struct addrinfo* result; int error = 0; @@ -451,7 +458,7 @@ static int explore_fqdn(const struct addrinfo* pai, const char* hostname, const if (get_portmatch(pai, servname) != 0) return 0; if (!files_getaddrinfo(hostname, pai, &result)) { - error = dns_getaddrinfo(hostname, pai, netcontext, &result); + error = dns_getaddrinfo(hostname, pai, netcontext, &result, event); } if (!error) { struct addrinfo* cur; @@ -1379,7 +1386,8 @@ error: } static int dns_getaddrinfo(const char* name, const addrinfo* pai, - const android_net_context* netcontext, addrinfo** rv) { + const android_net_context* netcontext, addrinfo** rv, + NetworkDnsEventReported* event) { res_target q = {}; res_target q2 = {}; @@ -1441,7 +1449,7 @@ static int dns_getaddrinfo(const char* name, const addrinfo* pai, * fully populate the thread private data here, but if we get down there * and have a cache hit that would be wasted, so we do the rest there on miss */ - res_setnetcontext(res, netcontext); + res_setnetcontext(res, netcontext, event); int he; if (res_searchN(name, &q, res, &he) < 0) { diff --git a/resolv/getaddrinfo.h b/resolv/getaddrinfo.h index cf54cabf..e8ba5dca 100644 --- a/resolv/getaddrinfo.h +++ b/resolv/getaddrinfo.h @@ -17,9 +17,11 @@ #pragma once #include "netd_resolv/resolv.h" // struct android_net_context +#include "stats.pb.h" struct addrinfo; // This is the DNS proxy entry point for getaddrinfo(). int android_getaddrinfofornetcontext(const char*, const char*, const addrinfo*, - const android_net_context*, addrinfo**); + const android_net_context*, addrinfo**, + android::net::NetworkDnsEventReported*); diff --git a/resolv/gethnamaddr.cpp b/resolv/gethnamaddr.cpp index 1cf06943..a40a7b78 100644 --- a/resolv/gethnamaddr.cpp +++ b/resolv/gethnamaddr.cpp @@ -78,6 +78,9 @@ #include "netd_resolv/resolv.h" #include "resolv_cache.h" #include "resolv_private.h" +#include "stats.pb.h" + +using android::net::NetworkDnsEventReported; // NetBSD uses _DIAGASSERT to null-check arguments and the like, // but it's clear from the number of mistakes in their assertions @@ -116,19 +119,22 @@ static void pad_v4v6_hostent(struct hostent* hp, char** bpp, char* ep); static void addrsort(char**, int, res_state); static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af, - const android_net_context* netcontext, getnamaddr* info); + const android_net_context* netcontext, getnamaddr* info, + NetworkDnsEventReported* event); static int dns_gethtbyname(const char* name, int af, getnamaddr* info); static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf, - size_t hbuflen, const android_net_context* netcontext); + size_t hbuflen, const android_net_context* netcontext, + NetworkDnsEventReported* event); static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp, char* buf, size_t buflen); static int android_gethostbyaddrfornetcontext_proxy_internal(const void*, socklen_t, int, struct hostent*, char*, size_t, - const struct android_net_context*); + const struct android_net_context*, + NetworkDnsEventReported* event); static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af, const struct android_net_context* netcontext, - hostent** hp); + hostent** hp, NetworkDnsEventReported* event); #define BOUNDED_INCR(x) \ do { \ @@ -494,14 +500,16 @@ fake: // very similar in proxy-ness to android_getaddrinfo_proxy static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf, - size_t hbuflen, const android_net_context* netcontext) { - res_setnetcontext(res, netcontext); + size_t hbuflen, const android_net_context* netcontext, + NetworkDnsEventReported* event) { + res_setnetcontext(res, netcontext, event); return gethostbyname_internal_real(name, af, res, hp, hbuf, hbuflen); } static int android_gethostbyaddrfornetcontext_real(const void* addr, socklen_t len, int af, struct hostent* hp, char* buf, size_t buflen, - const struct android_net_context* netcontext) { + const struct android_net_context* netcontext, + NetworkDnsEventReported* event) { const u_char* uaddr = (const u_char*) addr; socklen_t size; struct getnamaddr info; @@ -543,7 +551,7 @@ static int android_gethostbyaddrfornetcontext_real(const void* addr, socklen_t l info.buf = buf; info.buflen = buflen; if (_hf_gethtbyaddr(uaddr, len, af, &info)) { - int error = dns_gethtbyaddr(uaddr, len, af, netcontext, &info); + int error = dns_gethtbyaddr(uaddr, len, af, netcontext, &info, event); if (error != 0) return error; } return 0; @@ -551,8 +559,9 @@ static int android_gethostbyaddrfornetcontext_real(const void* addr, socklen_t l static int android_gethostbyaddrfornetcontext_proxy_internal( const void* addr, socklen_t len, int af, struct hostent* hp, char* hbuf, size_t hbuflen, - const struct android_net_context* netcontext) { - return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext); + const struct android_net_context* netcontext, NetworkDnsEventReported* event) { + return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext, + event); } // TODO: Consider leaving function without returning error code as _gethtent() does because @@ -795,7 +804,8 @@ static int dns_gethtbyname(const char* name, int addr_type, getnamaddr* info) { } static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af, - const android_net_context* netcontext, getnamaddr* info) { + const android_net_context* netcontext, getnamaddr* info, + NetworkDnsEventReported* event) { char qbuf[MAXDNAME + 1], *qp, *ep; int n; int advance; @@ -842,7 +852,7 @@ static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af, res_state res = res_get_state(); if (!res) return EAI_MEMORY; - res_setnetcontext(res, netcontext); + res_setnetcontext(res, netcontext, event); int he; n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he); if (n < 0) { @@ -886,13 +896,16 @@ nospc: */ int android_gethostbynamefornetcontext(const char* name, int af, - const struct android_net_context* netcontext, hostent** hp) { - int error; + const struct android_net_context* netcontext, hostent** hp, + NetworkDnsEventReported* event) { + assert(event != nullptr); + res_state res = res_get_state(); if (res == NULL) return EAI_MEMORY; res_static* rs = res_get_static(); // For thread-safety. + int error; error = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), - netcontext); + netcontext, event); if (error == 0) { *hp = &rs->host; } @@ -900,16 +913,19 @@ int android_gethostbynamefornetcontext(const char* name, int af, } int android_gethostbyaddrfornetcontext(const void* addr, socklen_t len, int af, - const struct android_net_context* netcontext, hostent** hp) { - return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp); + const struct android_net_context* netcontext, hostent** hp, + NetworkDnsEventReported* event) { + return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp, event); } static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af, const struct android_net_context* netcontext, - hostent** hp) { + hostent** hp, NetworkDnsEventReported* event) { + assert(event != nullptr); + struct res_static* rs = res_get_static(); // For thread-safety. int error = android_gethostbyaddrfornetcontext_proxy_internal( - addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), netcontext); + addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), netcontext, event); if (error == 0) *hp = &rs->host; return error; } diff --git a/resolv/gethnamaddr.h b/resolv/gethnamaddr.h index 27cc1c2c..bfdb14ee 100644 --- a/resolv/gethnamaddr.h +++ b/resolv/gethnamaddr.h @@ -18,10 +18,12 @@ #include <netdb.h> // struct hostent #include "netd_resolv/resolv.h" // struct android_net_context +#include "stats.pb.h" // This is the entry point for the gethostbyname() family of legacy calls. -int android_gethostbynamefornetcontext(const char*, int, const android_net_context*, hostent**); +int android_gethostbynamefornetcontext(const char*, int, const android_net_context*, hostent**, + android::net::NetworkDnsEventReported*); // This is the entry point for the gethostbyaddr() family of legacy calls. int android_gethostbyaddrfornetcontext(const void*, socklen_t, int, const android_net_context*, - hostent**); + hostent**, android::net::NetworkDnsEventReported*); diff --git a/resolv/libnetd_resolv_test.cpp b/resolv/libnetd_resolv_test.cpp index 7fc5669a..6e4b9eec 100644 --- a/resolv/libnetd_resolv_test.cpp +++ b/resolv/libnetd_resolv_test.cpp @@ -26,6 +26,7 @@ #include "getaddrinfo.h" #include "gethnamaddr.h" #include "resolv_cache.h" +#include "stats.pb.h" // TODO: make this dynamic and stop depending on implementation details. constexpr unsigned int TEST_NETID = 30; @@ -39,6 +40,8 @@ using android::base::StringPrintf; namespace android { namespace net { +using android::net::NetworkDnsEventReported; + // Minimize class ResolverTest to be class TestBase because class TestBase doesn't need all member // functions of class ResolverTest and class DnsResponderClient. class TestBase : public ::testing::Test { @@ -112,8 +115,9 @@ TEST_F(GetAddrInfoForNetContextTest, InvalidParameters) { // Invalid hostname and servname. // Both hostname and servname are null pointers. Expect error number EAI_NONAME. struct addrinfo* result = nullptr; + NetworkDnsEventReported event; int rv = android_getaddrinfofornetcontext(nullptr /*hostname*/, nullptr /*servname*/, - nullptr /*hints*/, &mNetcontext, &result); + nullptr /*hints*/, &mNetcontext, &result, &event); EXPECT_EQ(EAI_NONAME, rv); if (result) { freeaddrinfo(result); @@ -167,8 +171,9 @@ TEST_F(GetAddrInfoForNetContextTest, InvalidParameters) { .ai_next = config.ai_next, }; + NetworkDnsEventReported event; rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints, - &mNetcontext, &result); + &mNetcontext, &result, &event); EXPECT_EQ(config.expected_errorno, rv); if (result) { @@ -190,8 +195,9 @@ TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_Family) { .ai_family = family, // unsupported family }; + NetworkDnsEventReported event; int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints, - &mNetcontext, &result); + &mNetcontext, &result, &event); EXPECT_EQ(EAI_FAMILY, rv); if (result) freeaddrinfo(result); @@ -237,8 +243,9 @@ TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_MeaningfulSocktypeAndProt .ai_socktype = socktype, }; + NetworkDnsEventReported event; int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints, - &mNetcontext, &result); + &mNetcontext, &result, &event); EXPECT_EQ(EAI_BADHINTS, rv); if (result) freeaddrinfo(result); @@ -320,8 +327,9 @@ TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_PortNameAndNumber) { }; struct addrinfo* result = nullptr; + NetworkDnsEventReported event; int rv = android_getaddrinfofornetcontext("localhost", config.servname, &hints, - &mNetcontext, &result); + &mNetcontext, &result, &event); EXPECT_EQ(config.expected_errorno, rv); if (result) freeaddrinfo(result); @@ -344,7 +352,9 @@ TEST_F(GetAddrInfoForNetContextTest, AlphabeticalHostname_NoData) { // Want AAAA answer but DNS server has A answer only. struct addrinfo* result = nullptr; const addrinfo hints = {.ai_family = AF_INET6}; - int rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &mNetcontext, &result); + NetworkDnsEventReported event; + int rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &mNetcontext, &result, + &event); EXPECT_LE(1U, GetNumQueries(dns, v4_host_name)); EXPECT_TRUE(result == nullptr); EXPECT_EQ(EAI_NODATA, rv); @@ -382,8 +392,9 @@ TEST_F(GetAddrInfoForNetContextTest, AlphabeticalHostname) { struct addrinfo* result = nullptr; const struct addrinfo hints = {.ai_family = config.ai_family}; - int rv = - android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &mNetcontext, &result); + NetworkDnsEventReported event; + int rv = android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &mNetcontext, &result, + &event); EXPECT_EQ(0, rv); EXPECT_TRUE(result != nullptr); EXPECT_EQ(1U, GetNumQueries(dns, host_name)); @@ -429,8 +440,9 @@ TEST_F(GetAddrInfoForNetContextTest, ServerResponseError) { struct addrinfo* result = nullptr; const struct addrinfo hints = {.ai_family = AF_UNSPEC}; - int rv = - android_getaddrinfofornetcontext(host_name, nullptr, &hints, &mNetcontext, &result); + NetworkDnsEventReported event; + int rv = android_getaddrinfofornetcontext(host_name, nullptr, &hints, &mNetcontext, &result, + &event); EXPECT_EQ(config.expected_errorno, rv); if (result) freeaddrinfo(result); @@ -453,7 +465,9 @@ TEST_F(GetAddrInfoForNetContextTest, ServerTimeout) { struct addrinfo* result = nullptr; const struct addrinfo hints = {.ai_family = AF_UNSPEC}; - int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &mNetcontext, &result); + NetworkDnsEventReported event; + int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &mNetcontext, &result, + &event); EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv); if (result) freeaddrinfo(result); @@ -488,8 +502,9 @@ TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) { dns.clearQueries(); struct hostent* hp = nullptr; + NetworkDnsEventReported event; int rv = android_gethostbynamefornetcontext("jiababuei", config.ai_family, &mNetcontext, - &hp); + &hp, &event); EXPECT_EQ(0, rv); EXPECT_TRUE(hp != nullptr); EXPECT_EQ(1U, GetNumQueries(dns, host_name)); @@ -512,7 +527,8 @@ TEST_F(GetHostByNameForNetContextTest, NoData) { // Want AAAA answer but DNS server has A answer only. struct hostent* hp = nullptr; - int rv = android_gethostbynamefornetcontext("v4only", AF_INET6, &mNetcontext, &hp); + NetworkDnsEventReported event; + int rv = android_gethostbynamefornetcontext("v4only", AF_INET6, &mNetcontext, &hp, &event); EXPECT_LE(1U, GetNumQueries(dns, v4_host_name)); EXPECT_TRUE(hp == nullptr); EXPECT_EQ(EAI_NODATA, rv); @@ -555,7 +571,8 @@ TEST_F(GetHostByNameForNetContextTest, ServerResponseError) { mDefaultSearchDomains, &mDefaultParams_Binder)); struct hostent* hp = nullptr; - int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp); + NetworkDnsEventReported event; + int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp, &event); EXPECT_TRUE(hp == nullptr); EXPECT_EQ(config.expected_errorno, rv); } @@ -576,7 +593,8 @@ TEST_F(GetHostByNameForNetContextTest, ServerTimeout) { mDefaultSearchDomains, &mDefaultParams_Binder)); struct hostent* hp = nullptr; - int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp); + NetworkDnsEventReported event; + int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp, &event); EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv); } diff --git a/resolv/res_cache.cpp b/resolv/res_cache.cpp index f0ff5641..d6f0cd80 100644 --- a/resolv/res_cache.cpp +++ b/resolv/res_cache.cpp @@ -36,7 +36,11 @@ #include <stdlib.h> #include <string.h> #include <time.h> + #include <mutex> +#include <string> +#include <unordered_map> +#include <vector> #include <arpa/inet.h> #include <arpa/nameser.h> @@ -47,6 +51,8 @@ #include <android-base/logging.h> #include <android-base/parseint.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <android-base/thread_annotations.h> #include <android/multinetwork.h> // ResNsendFlags @@ -1146,6 +1152,8 @@ struct resolv_cache_info { char defdname[MAXDNSRCHPATH]; int dnsrch_offset[MAXDNSRCH + 1]; // offsets into defdname int wait_for_pending_req_timeout_count; + // Map format: ReturnCode:rate_denom + std::unordered_map<int, uint32_t> dns_event_subsampling_map; }; // A helper class for the Clang Thread Safety Analysis to deal with @@ -1604,6 +1612,49 @@ bool resolv_has_nameservers(unsigned 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 + static int resolv_create_cache_for_net_locked(unsigned netid) { resolv_cache* cache = find_named_cache_locked(netid); // Should not happen @@ -1621,6 +1672,7 @@ static int resolv_create_cache_for_net_locked(unsigned netid) { } cache_info->cache = cache; cache_info->netid = netid; + cache_info->dns_event_subsampling_map = resolv_get_dns_event_subsampling_map(); insert_cache_info_locked(cache_info); return 0; @@ -2000,6 +2052,42 @@ int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount, return revision_id; } +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 {}; + std::vector<std::string> result; + for (const auto& pair : cache_info->dns_event_subsampling_map) { + result.push_back(StringPrintf("%s:%d", + (pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY) + ? "default" + : std::to_string(pair.first).c_str(), + pair.second)); + } + return result; +} + +// Decides whether an event should be sampled using a random number generator and +// 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) { + 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; + auto search_returnCode = subsampling_map.find(return_code); + uint32_t denom; + if (search_returnCode != subsampling_map.end()) { + denom = search_returnCode->second; + } else { + auto search_default = subsampling_map.find(DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY); + denom = (search_default == subsampling_map.end()) ? 0 : search_default->second; + } + return denom; +} + 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); diff --git a/resolv/res_cache_test.cpp b/resolv/res_cache_test.cpp new file mode 100644 index 00000000..0b09d88c --- /dev/null +++ b/resolv/res_cache_test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <netdb.h> + +#include <cutils/properties.h> +#include <gmock/gmock-matchers.h> +#include <gtest/gtest.h> + +#include "netd_resolv/stats.h" +#include "resolv_cache.h" + +constexpr int TEST_NETID = 30; + +namespace { + +constexpr int EAI_OK = 0; +constexpr char DNS_EVENT_SUBSAMPLING_MAP_FLAG[] = + "persist.device_config.netd_native.dns_event_subsample_map"; + +class ScopedCacheCreate { + public: + explicit ScopedCacheCreate(unsigned netid, const char* subsampling_map, + const char* property = DNS_EVENT_SUBSAMPLING_MAP_FLAG) + : mStoredNetId(netid), mStoredProperty(property) { + property_get(property, mStoredMap, ""); + property_set(property, subsampling_map); + EXPECT_EQ(0, resolv_create_cache_for_net(netid)); + } + ~ScopedCacheCreate() { + resolv_delete_cache_for_net(mStoredNetId); + property_set(mStoredProperty, mStoredMap); + } + + private: + unsigned mStoredNetId; + const char* mStoredProperty; + char mStoredMap[PROPERTY_VALUE_MAX]{}; +}; + +} // namespace + +TEST(ResolvCacheTest, DnsEventSubsampling) { + // Test defaults, default flag is "default:1 0:100 7:10" if no experiment flag is set + { + ScopedCacheCreate scopedCacheCreate(TEST_NETID, ""); + EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 10U); + EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 100U); + EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS), + 1U); // default + EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID), + testing::UnorderedElementsAreArray({"default:1", "0:100", "7:10"})); + } + // 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), + 666U); // default + EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID), + testing::UnorderedElementsAreArray({"default:666", "0:42"})); + } + // Now change the experiment flag to something illegal + { + ScopedCacheCreate scopedCacheCreate(TEST_NETID, "asvaxx"); + // 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()); + } + // Test negative and zero denom + { + ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:-42 default:-666 7:10 10:0"); + // 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), + testing::UnorderedElementsAreArray({"7:10", "10:0"})); + } +} diff --git a/resolv/res_init.cpp b/resolv/res_init.cpp index ceb30232..bdbe162e 100644 --- a/resolv/res_init.cpp +++ b/resolv/res_init.cpp @@ -383,7 +383,8 @@ int res_getservers(res_state statp, sockaddr_union* set, int cnt) { return (statp->nscount); } -void res_setnetcontext(res_state statp, const struct android_net_context* netcontext) { +void res_setnetcontext(res_state statp, const struct android_net_context* netcontext, + android::net::NetworkDnsEventReported* _Nonnull event) { if (statp != NULL) { statp->netid = netcontext->dns_netid; statp->_mark = netcontext->dns_mark; @@ -393,5 +394,6 @@ void res_setnetcontext(res_state statp, const struct android_net_context* netcon if (netcontext->flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) { statp->use_local_nameserver = true; } + statp->event = event; } } diff --git a/resolv/res_send.cpp b/resolv/res_send.cpp index d89ad7ee..1ccfc741 100644 --- a/resolv/res_send.cpp +++ b/resolv/res_send.cpp @@ -101,6 +101,7 @@ #include <android/multinetwork.h> // ResNsendFlags #include <netdutils/Slice.h> +#include <netdutils/Stopwatch.h> #include "DnsTlsDispatcher.h" #include "DnsTlsTransport.h" #include "PrivateDnsConfiguration.h" @@ -110,10 +111,13 @@ #include "res_state_ext.h" #include "resolv_cache.h" #include "resolv_private.h" +#include "stats.pb.h" // TODO: use the namespace something like android::netd_resolv for libnetd_resolv using namespace android::net; +using android::net::NetworkDnsEventReported; using android::netdutils::Slice; +using android::netdutils::Stopwatch; static DnsTlsDispatcher sDnsTlsDispatcher; @@ -133,11 +137,29 @@ static int retrying_poll(const int sock, short events, const struct timespec* fi static int res_tls_send(res_state, const Slice query, const Slice answer, int* rcode, bool* fallback); -/* BIONIC-BEGIN: implement source port randomization */ +NsType getQueryType(const uint8_t* msg, size_t msgLen) { + ns_msg handle; + ns_rr rr; + if (ns_initparse((const uint8_t*)msg, msgLen, &handle) < 0 || + ns_parserr(&handle, ns_s_qd, 0, &rr) < 0) { + return NS_T_INVALID; + } + return static_cast<NsType>(ns_rr_type(rr)); +} + +IpVersion ipFamilyToIPVersion(const int ipFamily) { + switch (ipFamily) { + case AF_INET: + return IV_IPV4; + case AF_INET6: + return IV_IPV6; + default: + return IV_UNKNOWN; + } +} // BEGIN: Code copied from ISC eventlib // TODO: move away from this code - #define BILLION 1000000000 static struct timespec evConsTime(time_t sec, long nsec) { @@ -201,6 +223,7 @@ static struct iovec evConsIovec(void* buf, size_t cnt) { // END: Code copied from ISC eventlib +/* BIONIC-BEGIN: implement source port randomization */ static int random_bind(int s, int family) { sockaddr_union u; int j; @@ -373,6 +396,10 @@ int res_queriesmatch(const u_char* buf1, const u_char* eom1, const u_char* buf2, return (1); } +static DnsQueryEvent* addDnsQueryEvent(NetworkDnsEventReported* event) { + return event->mutable_dns_query_events()->add_dns_query_event(); +} + int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int anssiz, int* rcode, uint32_t flags) { int gotsomewhere, terrno, v_circuit, resplen, n; @@ -391,11 +418,15 @@ int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int a terrno = ETIMEDOUT; int anslen = 0; + Stopwatch cache_stopwatch; cache_status = _resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags); - + const int32_t cacheLatencyUs = saturate_cast<int32_t>(cache_stopwatch.timeTakenUs()); if (cache_status == RESOLV_CACHE_FOUND) { HEADER* hp = (HEADER*)(void*)ans; *rcode = hp->rcode; + DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event); + dnsQueryEvent->set_latency_micros(cacheLatencyUs); + dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status)); return anslen; } else if (cache_status != RESOLV_CACHE_UNSUPPORTED) { // had a cache miss for a known network, so populate the thread private @@ -522,12 +553,11 @@ int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int a for (int ns = 0; ns < statp->nscount; ns++) { if (!usable_servers[ns]) continue; - struct sockaddr* nsap; int nsaplen; time_t now = 0; int delay = 0; *rcode = RCODE_INTERNAL_ERROR; - nsap = get_nsaddr(statp, (size_t) ns); + const sockaddr* nsap = get_nsaddr(statp, ns); nsaplen = get_salen(nsap); same_ns: @@ -551,13 +581,16 @@ int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int a } } - [[maybe_unused]] static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; - [[maybe_unused]] char abuf[NI_MAXHOST]; + static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; + char abuf[NI_MAXHOST]; + DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event); + dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status)); if (getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), NULL, 0, niflags) == 0) LOG(DEBUG) << __func__ << ": Querying server (# " << ns + 1 << ") address = " << abuf; + Stopwatch query_stopwatch; if (v_circuit) { /* Use VC; at most one attempt per server. */ bool shouldRecordStats = (attempt == 0); @@ -566,6 +599,15 @@ int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int a n = send_vc(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &now, rcode, &delay); + dnsQueryEvent->set_latency_micros( + saturate_cast<int32_t>(query_stopwatch.timeTakenUs())); + dnsQueryEvent->set_dns_server_index(ns); + dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family)); + dnsQueryEvent->set_retry_times(attempt); + dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode)); + dnsQueryEvent->set_protocol(PROTO_TCP); + dnsQueryEvent->set_type(getQueryType(buf, buflen)); + /* * Only record stats the first time we try a query. This ensures that * queries that deterministically fail (e.g., a name that always returns @@ -594,6 +636,15 @@ int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int a n = send_dg(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &v_circuit, &gotsomewhere, &now, rcode, &delay); + dnsQueryEvent->set_latency_micros( + saturate_cast<int32_t>(query_stopwatch.timeTakenUs())); + dnsQueryEvent->set_dns_server_index(ns); + dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family)); + dnsQueryEvent->set_retry_times(attempt); + dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode)); + dnsQueryEvent->set_protocol(PROTO_UDP); + dnsQueryEvent->set_type(getQueryType(buf, buflen)); + /* Only record stats the first time we try a query. See above. */ if (attempt == 0) { res_sample sample; @@ -1211,13 +1262,25 @@ static int sock_eq(struct sockaddr* a, struct sockaddr* b) { } } +PrivateDnsModes convertEnumType(PrivateDnsMode privateDnsmode) { + switch (privateDnsmode) { + case PrivateDnsMode::OFF: + return PrivateDnsModes::PDM_OFF; + case PrivateDnsMode::OPPORTUNISTIC: + return PrivateDnsModes::PDM_OPPORTUNISTIC; + case PrivateDnsMode::STRICT: + return PrivateDnsModes::PDM_STRICT; + } + return PrivateDnsModes::PDM_UNKNOWN; +} + static int res_tls_send(res_state statp, const Slice query, const Slice answer, int* rcode, bool* fallback) { int resplen = 0; const unsigned netId = statp->netid; - const unsigned mark = statp->_mark; PrivateDnsStatus privateDnsStatus = gPrivateDnsConfiguration.getStatus(netId); + statp->event->set_private_dns_modes(convertEnumType(privateDnsStatus.mode)); if (privateDnsStatus.mode == PrivateDnsMode::OFF) { *fallback = true; @@ -1256,7 +1319,7 @@ static int res_tls_send(res_state statp, const Slice query, const Slice answer, LOG(INFO) << __func__ << ": performing query over TLS"; - const auto response = sDnsTlsDispatcher.query(privateDnsStatus.validatedServers, mark, query, + const auto response = sDnsTlsDispatcher.query(privateDnsStatus.validatedServers, statp, query, answer, &resplen); LOG(INFO) << __func__ << ": TLS query result: " << static_cast<int>(response); @@ -1298,9 +1361,11 @@ static int res_tls_send(res_state statp, const Slice query, const Slice answer, } int resolv_res_nsend(const android_net_context* netContext, const uint8_t* msg, int msgLen, - uint8_t* ans, int ansLen, int* rcode, uint32_t flags) { + uint8_t* ans, int ansLen, int* rcode, uint32_t flags, + NetworkDnsEventReported* event) { + assert(event != nullptr); res_state res = res_get_state(); - res_setnetcontext(res, netContext); + res_setnetcontext(res, netContext, event); _resolv_populate_res_for_net(res); *rcode = NOERROR; return res_nsend(res, msg, msgLen, ans, ansLen, rcode, flags); diff --git a/resolv/res_send.h b/resolv/res_send.h index 7fc77cbe..fb80160f 100644 --- a/resolv/res_send.h +++ b/resolv/res_send.h @@ -17,7 +17,9 @@ #pragma once #include "netd_resolv/resolv.h" // struct android_net_context +#include "stats.pb.h" // Query dns with raw msg int resolv_res_nsend(const android_net_context* netContext, const uint8_t* msg, int msgLen, - uint8_t* ans, int ansLen, int* rcode, uint32_t flags); + uint8_t* ans, int ansLen, int* rcode, uint32_t flags, + android::net::NetworkDnsEventReported* event); diff --git a/resolv/resolv_cache.h b/resolv/resolv_cache.h index 0056076f..df21fb3c 100644 --- a/resolv/resolv_cache.h +++ b/resolv/resolv_cache.h @@ -32,10 +32,13 @@ #include <stddef.h> +#include <unordered_map> #include <vector> struct __res_state; +constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1; + /* sets the name server addresses to the provided res_state structure. The * name servers are retrieved from the cache which is associated * with the network to which the res_state structure is associated */ @@ -43,6 +46,9 @@ void _resolv_populate_res_for_net(struct __res_state* 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); + typedef enum { RESOLV_CACHE_UNSUPPORTED, /* the cache can't handle that kind of queries */ /* or the answer buffer is too small */ diff --git a/resolv/resolv_private.h b/resolv/resolv_private.h index 9fb0d483..3ecb6c10 100644 --- a/resolv/resolv_private.h +++ b/resolv/resolv_private.h @@ -64,6 +64,7 @@ #include "netd_resolv/resolv.h" #include "netd_resolv/stats.h" #include "resolv_static.h" +#include "stats.pb.h" // Linux defines MAXHOSTNAMELEN as 64, while the domain name limit in // RFC 1034 and RFC 1035 is 255 octets. @@ -118,6 +119,7 @@ struct __res_state { } _u; struct res_static rstatic[1]; bool use_local_nameserver; /* DNS-over-TLS bypass */ + android::net::NetworkDnsEventReported* event; }; typedef struct __res_state* res_state; @@ -226,7 +228,8 @@ void res_setservers(res_state, const sockaddr_union*, int); int res_getservers(res_state, sockaddr_union*, int); struct android_net_context; /* forward */ -void res_setnetcontext(res_state, const struct android_net_context*); +void res_setnetcontext(res_state, const struct android_net_context*, + android::net::NetworkDnsEventReported* event); int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hints, addrinfo** result); @@ -237,4 +240,16 @@ int herrnoToAiErrno(int herrno); // switch resolver log severity android::base::LogSeverity logSeverityStrToEnum(const std::string& logSeverityStr); +template <typename Dest> +Dest saturate_cast(int64_t x) { + using DestLimits = std::numeric_limits<Dest>; + if (x > DestLimits::max()) return DestLimits::max(); + if (x < DestLimits::min()) return DestLimits::min(); + return static_cast<Dest>(x); +} + +android::net::NsType getQueryType(const uint8_t* msg, size_t msgLen); + +android::net::IpVersion ipFamilyToIPVersion(int ipFamily); + #endif // NETD_RESOLV_PRIVATE_H diff --git a/resolv/stats.proto b/resolv/stats.proto index dd9992c5..60e20718 100644 --- a/resolv/stats.proto +++ b/resolv/stats.proto @@ -68,6 +68,8 @@ enum NsRcode { // NS_R_BADSIG = 16, NS_R_BADKEY = 17; NS_R_BADTIME = 18; + NS_R_INTERNAL_ERROR = 254; + NS_R_TIMEOUT = 255; } // Currently defined type values for resources and queries. @@ -144,11 +146,11 @@ enum IpVersion { IV_IPV6 = 2; } -enum TransportType { - TT_UNKNOWN = 0; - TT_UDP = 1; - TT_TCP = 2; - TT_DOT = 3; +enum Protocol { + PROTO_UNKNOWN = 0; + PROTO_UDP = 1; + PROTO_TCP = 2; + PROTO_DOT = 3; } enum PrivateDnsModes { @@ -158,21 +160,22 @@ enum PrivateDnsModes { PDM_STRICT = 3; } -enum Transport { +enum NetworkType { + NT_UNKNOWN = 0; // Indicates this network uses a Cellular transport. - TRANSPORT_DEFAULT = 0; // TRANSPORT_CELLULAR + NT_CELLULAR = 1; // Indicates this network uses a Wi-Fi transport. - TRANSPORT_WIFI = 1; + NT_WIFI = 2; // Indicates this network uses a Bluetooth transport. - TRANSPORT_BLUETOOTH = 2; + NT_BLUETOOTH = 3; // Indicates this network uses an Ethernet transport. - TRANSPORT_ETHERNET = 3; + NT_ETHERNET = 4; // Indicates this network uses a VPN transport. - TRANSPORT_VPN = 4; + NT_VPN = 5; // Indicates this network uses a Wi-Fi Aware transport. - TRANSPORT_WIFI_AWARE = 5; + NT_WIFI_AWARE = 6; // Indicates this network uses a LoWPAN transport. - TRANSPORT_LOWPAN = 6; + NT_LOWPAN = 7; } enum CacheStatus{ @@ -196,13 +199,13 @@ message DnsQueryEvent { optional IpVersion ip_version = 4; - optional TransportType transport = 5; + optional Protocol protocol = 5; // Number of DNS query retry times optional int32 retry_times = 6; // Ordinal number of name server. - optional int32 dns_server_count = 7; + optional int32 dns_server_index = 7; // Used only by TCP and DOT. True for new connections. optional bool connected = 8; @@ -238,7 +241,7 @@ message NetworkDnsEventReported { // Only valid for event_type = EVENT_RESNSEND. optional int32 res_nsend_flags = 5; - optional Transport network_type = 6; + optional NetworkType network_type = 6; // The DNS over TLS mode on a specific netId. optional PrivateDnsModes private_dns_modes = 7; @@ -246,4 +249,7 @@ message NetworkDnsEventReported { // Additional pass-through fields opaque to statsd. // The DNS resolver Mainline module can add new fields here without requiring an OS update. optional DnsQueryEvents dns_query_events = 8; -}
\ No newline at end of file + + // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom. + optional int32 sampling_rate_denom = 9; +} |
