OSDN Git Service

DO NOT MERGE Benchmarks for network metrics reporting
authorRobin Lee <rgl@google.com>
Tue, 13 Sep 2016 09:55:42 +0000 (18:55 +0900)
committerMichal Karpinski <mkarpinski@google.com>
Fri, 2 Dec 2016 15:38:04 +0000 (15:38 +0000)
Bug: 29748723
Test: this is an APCT test.

(cherry picked from commit 484dac1d071c7476895c4a3184e8dbdd7b63e524)

Change-Id: I102aeb84c92716f5e022b50792c2fd1cde98e286

21 files changed:
client/FwmarkClient.cpp
client/FwmarkClient.h
server/DnsProxyListener.cpp
server/DnsProxyListener.h
server/NetdNativeService.cpp
server/NetdNativeService.h
server/NetworkController.cpp
server/NetworkController.h
server/SockDiag.h
server/binder/android/net/INetd.aidl
tests/Android.mk
tests/benchmarks/Android.mk [new file with mode: 0644]
tests/benchmarks/connect_benchmark.cpp [new file with mode: 0644]
tests/benchmarks/dns_benchmark.cpp [new file with mode: 0644]
tests/benchmarks/main.cpp [new file with mode: 0644]
tests/dns_responder/Android.mk [new file with mode: 0644]
tests/dns_responder/dns_responder.cpp [moved from tests/dns_responder.cpp with 100% similarity]
tests/dns_responder/dns_responder.h [moved from tests/dns_responder.h with 100% similarity]
tests/dns_responder/dns_responder_client.cpp [new file with mode: 0644]
tests/dns_responder/dns_responder_client.h [new file with mode: 0644]
tests/netd_test.cpp

index a82f4c2..056dfc2 100644 (file)
@@ -32,7 +32,11 @@ const sockaddr_un FWMARK_SERVER_PATH = {AF_UNIX, "/dev/socket/fwmarkd"};
 }  // namespace
 
 bool FwmarkClient::shouldSetFwmark(int family) {
-    return (family == AF_INET || family == AF_INET6) && !getenv("ANDROID_NO_USE_FWMARK_CLIENT");
+    return (family == AF_INET || family == AF_INET6) && !getenv(ANDROID_NO_USE_FWMARK_CLIENT);
+}
+
+bool FwmarkClient::shouldReportConnectComplete(int family) {
+    return shouldSetFwmark(family) && !getenv(ANDROID_FWMARK_METRICS_ONLY);
 }
 
 FwmarkClient::FwmarkClient() : mChannel(-1) {
index df7686d..53f68d3 100644 (file)
@@ -27,6 +27,10 @@ public:
     // its SO_MARK set.
     static bool shouldSetFwmark(int family);
 
+    // Returns true if an additional call should be made after ON_CONNECT calls, to log extra
+    // information like latency and source IP.
+    static bool shouldReportConnectComplete(int family);
+
     FwmarkClient();
     ~FwmarkClient();
 
@@ -34,6 +38,15 @@ public:
     // Returns 0 on success or a negative errno value on failure.
     int send(FwmarkCommand* data, int fd);
 
+    // Env flag to control whether FwmarkClient sends any information at all about network events
+    // back to the system server through FwmarkServer.
+    static constexpr const char* ANDROID_NO_USE_FWMARK_CLIENT = "ANDROID_NO_USE_FWMARK_CLIENT";
+
+    // Env flag to control whether FwmarkClient should exclude detailed information like IP
+    // addresses and only send basic information necessary for marking sockets.
+    // Has no effect if ANDROID_NO_USE_FWMARK_CLIENT is set.
+    static constexpr const char* ANDROID_FWMARK_METRICS_ONLY = "ANDROID_FWMARK_METRICS_ONLY";
+
 private:
     int mChannel;
 };
index 2a5ecc0..7865dcb 100644 (file)
@@ -61,13 +61,14 @@ DnsProxyListener::DnsProxyListener(const NetworkController* netCtrl) :
 
 DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(
         SocketClient *c, char* host, char* service, struct addrinfo* hints,
-        const struct android_net_context& netcontext,
+        const struct android_net_context& netcontext, const int reportingLevel,
         const android::sp<android::net::metrics::INetdEventListener>& netdEventListener)
         : mClient(c),
           mHost(host),
           mService(service),
           mHints(hints),
           mNetContext(netcontext),
+          mReportingLevel(reportingLevel),
           mNetdEventListener(netdEventListener) {
 }
 
@@ -219,9 +220,23 @@ void DnsProxyListener::GetAddrInfoHandler::run() {
     }
     mClient->decRef();
     if (mNetdEventListener != nullptr) {
-        mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
-                                       INetdEventListener::EVENT_GETADDRINFO, (int32_t) rv,
-                                       latencyMs);
+        const int reportingLevel = mReportingLevel;
+        switch (reportingLevel) {
+            case 0:
+                // Skip reporting.
+                break;
+            case 1:
+                // Reporting is on. Send metrics.
+                mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
+                                              INetdEventListener::EVENT_GETADDRINFO, (int32_t) rv,
+                                              latencyMs);
+                break;
+            default:
+                ALOGW("Unknown metrics reporting level %d; skipping onDnsEvent", reportingLevel);
+                break;
+        }
+    } else {
+        ALOGW("Netd event listener is not available; skipping.");
     }
 }
 
@@ -289,9 +304,12 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
              netcontext.uid);
     }
 
+    const int metricsLevel = mDnsProxyListener->mNetCtrl->getMetricsReportingLevel();
+
     cli->incRef();
     DnsProxyListener::GetAddrInfoHandler* handler =
             new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext,
+                                                     metricsLevel,
                                                      mDnsProxyListener->getNetdEventListener());
     handler->start();
 
@@ -334,10 +352,11 @@ int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli,
     }
 
     uint32_t mark = mDnsProxyListener->mNetCtrl->getNetworkForDns(&netId, uid);
+    const int metricsLevel = mDnsProxyListener->mNetCtrl->getMetricsReportingLevel();
 
     cli->incRef();
     DnsProxyListener::GetHostByNameHandler* handler =
-            new DnsProxyListener::GetHostByNameHandler(cli, name, af, netId, mark,
+            new DnsProxyListener::GetHostByNameHandler(cli, name, af, netId, mark, metricsLevel,
                                                        mDnsProxyListener->getNetdEventListener());
     handler->start();
 
@@ -345,13 +364,14 @@ int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli,
 }
 
 DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(
-        SocketClient* c, char* name, int af, unsigned netId, uint32_t mark,
+        SocketClient* c, char* name, int af, unsigned netId, uint32_t mark, const int metricsLevel,
         const android::sp<android::net::metrics::INetdEventListener>& netdEventListener)
         : mClient(c),
           mName(name),
           mAf(af),
           mNetId(netId),
           mMark(mark),
+          mReportingLevel(metricsLevel),
           mNetdEventListener(netdEventListener) {
 }
 
@@ -404,8 +424,20 @@ void DnsProxyListener::GetHostByNameHandler::run() {
     mClient->decRef();
 
     if (mNetdEventListener != nullptr) {
-        mNetdEventListener->onDnsEvent(mNetId, INetdEventListener::EVENT_GETHOSTBYNAME,
-                                      h_errno, latencyMs);
+        const int reportingLevel = mReportingLevel;
+        switch (reportingLevel) {
+            case 0:
+                // Reporting is off.
+                break;
+            case 1:
+                // Reporting is on. Send metrics.
+                mNetdEventListener->onDnsEvent(mNetId, INetdEventListener::EVENT_GETHOSTBYNAME,
+                                              h_errno, latencyMs);
+                break;
+            default:
+                ALOGW("Unknown metrics reporting level %d; skipping onDnsEvent", reportingLevel);
+                break;
+        }
     }
 }
 
index d27c61c..4bb882c 100644 (file)
@@ -59,6 +59,7 @@ private:
                            char* service,
                            struct addrinfo* hints,
                            const struct android_net_context& netcontext,
+                           const int reportingLevel,
                            const android::sp<android::net::metrics::INetdEventListener>& listener);
         ~GetAddrInfoHandler();
 
@@ -72,6 +73,7 @@ private:
         char* mService; // owned
         struct addrinfo* mHints;  // owned
         struct android_net_context mNetContext;
+        const int mReportingLevel;
         android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
     };
 
@@ -92,6 +94,7 @@ private:
                             int af,
                             unsigned netId,
                             uint32_t mark,
+                            int reportingLevel,
                             const android::sp<android::net::metrics::INetdEventListener>& listener);
         ~GetHostByNameHandler();
         static void* threadStart(void* handler);
@@ -103,6 +106,7 @@ private:
         int mAf;
         unsigned mNetId;
         uint32_t mMark;
+        const int mReportingLevel;
         android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
     };
 
index f8f300a..e962362 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <android-base/stringprintf.h>
 #include <cutils/log.h>
+#include <cutils/properties.h>
 #include <utils/Errors.h>
 #include <utils/String16.h>
 
@@ -58,6 +59,17 @@ binder::Status checkPermission(const char *permission) {
     }
 }
 
+#define ENFORCE_DEBUGGABLE() {                              \
+    char value[PROPERTY_VALUE_MAX + 1];                     \
+    if (property_get("ro.debuggable", value, NULL) != 1     \
+            || value[0] != '1') {                           \
+        return binder::Status::fromExceptionCode(           \
+            binder::Status::EX_SECURITY,                    \
+            String8("Not available in production builds.")  \
+        );                                                  \
+    }                                                       \
+}
+
 #define ENFORCE_PERMISSION(permission) {                    \
     binder::Status status = checkPermission((permission));  \
     if (!status.isOk()) {                                   \
@@ -275,5 +287,27 @@ binder::Status NetdNativeService::setProcSysNet(
     return binder::Status::ok();
 }
 
+binder::Status NetdNativeService::getMetricsReportingLevel(int *reportingLevel) {
+    // This function intentionally does not lock, since the only thing it does is one read from an
+    // atomic_int.
+    ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL);
+    ENFORCE_DEBUGGABLE();
+
+    *reportingLevel = gCtls->netCtrl.getMetricsReportingLevel();
+    return binder::Status::ok();
+}
+
+binder::Status NetdNativeService::setMetricsReportingLevel(const int reportingLevel) {
+    // This function intentionally does not lock, since the only thing it does is one write to an
+    // atomic_int.
+    ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL);
+    ENFORCE_DEBUGGABLE();
+
+    if (int err = gCtls->netCtrl.setMetricsReportingLevel(reportingLevel)) {
+        return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT);
+    }
+    return binder::Status::ok();
+}
+
 }  // namespace net
 }  // namespace android
index 7e1d75e..dd01dbc 100644 (file)
@@ -59,6 +59,10 @@ class NetdNativeService : public BinderService<NetdNativeService>, public BnNetd
     binder::Status setProcSysNet(
             int32_t family, int32_t which, const std::string &ifname, const std::string &parameter,
             const std::string &value) override;
+
+    // Metrics reporting level set / get (internal use only).
+    binder::Status getMetricsReportingLevel(int *reportingLevel) override;
+    binder::Status setMetricsReportingLevel(const int reportingLevel) override;
 };
 
 }  // namespace net
index c891391..4dc73e7 100644 (file)
@@ -666,3 +666,16 @@ int NetworkController::modifyFallthroughLocked(unsigned vpnNetId, bool add) {
     }
     return 0;
 }
+
+int NetworkController::setMetricsReportingLevel(const int level) {
+    if (level < 0 || level > 1) {
+        ALOGE("Invalid reporting level %d", level);
+        return -EINVAL;
+    }
+    mReportingLevel = level;
+    return 0;
+}
+
+int NetworkController::getMetricsReportingLevel() const {
+    return mReportingLevel;
+}
index 195cdbd..ac044e3 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "utils/RWLock.h"
 
+#include <atomic>
 #include <list>
 #include <map>
 #include <set>
@@ -93,6 +94,9 @@ public:
 
     void dump(DumpWriter& dw);
 
+    int setMetricsReportingLevel(const int level);
+    int getMetricsReportingLevel() const;
+
 private:
     bool isValidNetwork(unsigned netId) const;
     Network* getNetworkLocked(unsigned netId) const;
@@ -113,6 +117,8 @@ private:
     std::map<unsigned, Network*> mNetworks;  // Map keys are NetIds.
     std::map<uid_t, Permission> mUsers;
     std::set<uid_t> mProtectableUsers;
+    std::atomic_int mReportingLevel{1};
+
 };
 
 #endif  // NETD_SERVER_NETWORK_CONTROLLER_H
index e561561..f227051 100644 (file)
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#ifndef _SOCK_DIAG_H
+#define _SOCK_DIAG_H
+
 #include <unistd.h>
 #include <sys/socket.h>
 
@@ -76,3 +79,5 @@ class SockDiag {
     void closeSocks() { close(mSock); close(mWriteSock); mSock = mWriteSock = -1; }
     static bool isLoopbackSocket(const inet_diag_msg *msg);
 };
+
+#endif  // _SOCK_DIAG_H
index 9df392a..ad1680b 100644 (file)
@@ -171,7 +171,7 @@ interface INetd {
     void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString,
             int prefixLength);
 
-    /*
+    /**
      * Set and get /proc/sys/net interface configuration parameters.
      *
      * @param family One of IPV4/IPV6 integers, indicating the desired address family directory.
@@ -188,4 +188,14 @@ interface INetd {
     void setProcSysNet(int family, int which, in @utf8InCpp String ifname,
             in @utf8InCpp String parameter, in @utf8InCpp String value);
     // TODO: add corresponding getProcSysNet().
+
+    /**
+     * Get/Set metrics reporting level.
+     *
+     * Reporting level is one of:
+     *     0 (NONE)
+     *     1 (METRICS)
+     */
+    int getMetricsReportingLevel();
+    void setMetricsReportingLevel(int level);
 }
index 8785a3f..b58b78e 100644 (file)
@@ -22,18 +22,21 @@ LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter
 EXTRA_LDLIBS := -lpthread
 LOCAL_SHARED_LIBRARIES += libbase libbinder libcutils liblog liblogwrap libnetdaidl libnetd_client \
                           libnetutils libutils
-LOCAL_STATIC_LIBRARIES += libtestUtil
+LOCAL_STATIC_LIBRARIES += libtestUtil libnetd_test_dnsresponder
 LOCAL_AIDL_INCLUDES := system/netd/server/binder
 LOCAL_C_INCLUDES += system/netd/include system/extras/tests/include system/netd/binder/include \
                     system/netd/server system/core/logwrapper/include \
+                    system/netd/tests/dns_responder \
                     system/core/libnetutils/include \
                     system/extras/tests/include bionic/libc/dns/include
 # netd_integration_test.cpp is currently empty and exists only so that we can do:
 # runtest -x system/netd/tests/netd_integration_test.cpp
 LOCAL_SRC_FILES := binder_test.cpp \
-                   dns_responder.cpp \
+                   dns_responder/dns_responder.cpp \
                    netd_integration_test.cpp \
                    netd_test.cpp \
                    ../server/NetdConstants.cpp
 LOCAL_MODULE_TAGS := eng tests
 include $(BUILD_NATIVE_TEST)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/tests/benchmarks/Android.mk b/tests/benchmarks/Android.mk
new file mode 100644 (file)
index 0000000..8743abc
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 2016 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+# APCT build target for metrics tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := netd_benchmark
+LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter
+# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
+LOCAL_CFLAGS += -Wno-varargs
+
+EXTRA_LDLIBS := -lpthread
+LOCAL_SHARED_LIBRARIES += libbase libbinder libcutils liblog liblogwrap libnetdaidl \
+                          libnetutils libutils libnetd_client
+LOCAL_STATIC_LIBRARIES += libnetd_test_dnsresponder libtestUtil
+
+LOCAL_AIDL_INCLUDES := system/netd/server/binder
+LOCAL_C_INCLUDES += system/netd/include \
+                    system/extras/tests/include \
+                    system/netd/binder/include \
+                    system/netd/client \
+                    system/netd/server \
+                    system/netd/tests/dns_responder \
+                    system/core/logwrapper/include \
+                    system/core/libnetutils/include \
+                    system/extras/tests/include bionic/libc/dns/include
+
+LOCAL_SRC_FILES := main.cpp \
+                   connect_benchmark.cpp \
+                   dns_benchmark.cpp \
+                   ../../server/NetdConstants.cpp
+
+LOCAL_MODULE_TAGS := eng tests
+
+include $(BUILD_NATIVE_BENCHMARK)
diff --git a/tests/benchmarks/connect_benchmark.cpp b/tests/benchmarks/connect_benchmark.cpp
new file mode 100644 (file)
index 0000000..9997d3a
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "connect_benchmark"
+
+#include <arpa/inet.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <time.h>
+
+#include <map>
+#include <functional>
+#include <thread>
+
+#include <android-base/stringprintf.h>
+#include <benchmark/benchmark.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+
+#include "FwmarkClient.h"
+#include "SockDiag.h"
+#include "Stopwatch.h"
+
+using android::base::StringPrintf;
+
+enum ReportingLevel {
+    NONE,
+    METRICS,
+    FULL
+};
+
+static int bindAndListen(int s) {
+    sockaddr_in6 sin6 = { .sin6_family = AF_INET6 };
+    if (bind(s, (sockaddr*) &sin6, sizeof(sin6)) == 0) {
+        if (listen(s, 1)) {
+            return -1;
+        }
+        sockaddr_in sin = {};
+        socklen_t len = sizeof(sin);
+        if (getsockname(s, (sockaddr*) &sin, &len)) {
+            return -1;
+        }
+        return ntohs(sin.sin_port);
+    } else {
+        return -1;
+    }
+}
+
+static void ipv4_loopback(benchmark::State& state, const bool waitBetweenRuns) {
+    const int listensocket = socket(AF_INET6, SOCK_STREAM, 0);
+    const int port = bindAndListen(listensocket);
+    if (port == -1) {
+        state.SkipWithError("Unable to bind server socket");
+        return;
+    }
+
+    // ALOGW("Listening on port = %d", port);
+    std::vector<uint64_t> latencies(state.max_iterations);
+    uint64_t iterations = 0;
+
+    while (state.KeepRunning()) {
+        int sock = socket(AF_INET, SOCK_STREAM, 0);
+        if (sock < 0) {
+            state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
+            break;
+        }
+
+        const Stopwatch stopwatch;
+
+        sockaddr_in server = { .sin_family = AF_INET, .sin_port = htons(port) };
+        if (auto ret = connect(sock, (sockaddr*) &server, sizeof(server))) {
+            state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
+            close(sock);
+            break;
+        }
+
+        if (waitBetweenRuns) {
+            latencies[iterations] = stopwatch.timeTaken() * 1e6L;
+            state.SetIterationTime(latencies[iterations] / 1e9L);
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+            ++iterations;
+        }
+
+        sockaddr_in6 client;
+        socklen_t clientlen = sizeof(client);
+        int accepted = accept(listensocket, (sockaddr *) &client, &clientlen);
+        if (accepted < 0) {
+            state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
+            close(sock);
+            break;
+        }
+
+        close(accepted);
+        close(sock);
+    }
+    close(listensocket);
+    // ALOGI("Finished test on port = %d", port);
+
+    if (iterations > 0) {
+        latencies.resize(iterations);
+        sort(latencies.begin(), latencies.end());
+        state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
+    }
+}
+
+static void ipv6_loopback(benchmark::State& state, const bool waitBetweenRuns) {
+    const int listensocket = socket(AF_INET6, SOCK_STREAM, 0);
+    const int port = bindAndListen(listensocket);
+    if (port == -1) {
+        state.SkipWithError("Unable to bind server socket");
+        return;
+    }
+
+    // ALOGW("Listening on port = %d", port);
+    std::vector<uint64_t> latencies(state.max_iterations);
+    uint64_t iterations = 0;
+
+    while (state.KeepRunning()) {
+        int sock = socket(AF_INET6, SOCK_STREAM, 0);
+        if (sock < 0) {
+            state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
+            break;
+        }
+
+        const Stopwatch stopwatch;
+
+        sockaddr_in6 server = { .sin6_family = AF_INET6, .sin6_port = htons(port) };
+        if (auto ret = connect(sock, (sockaddr*) &server, sizeof(server))) {
+            state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
+            close(sock);
+            break;
+        }
+
+        if (waitBetweenRuns) {
+            latencies[iterations] = stopwatch.timeTaken() * 1e6L;
+            state.SetIterationTime(latencies[iterations] / 1e9L);
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+            ++iterations;
+        }
+
+        sockaddr_in6 client;
+        socklen_t clientlen = sizeof(client);
+        int accepted = accept(listensocket, (sockaddr *) &client, &clientlen);
+        if (accepted < 0) {
+            state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
+            close(sock);
+            break;
+        }
+
+        close(accepted);
+        close(sock);
+    }
+    close(listensocket);
+    // ALOGI("Finished test on port = %d", port);
+
+    if (iterations > 0) {
+        latencies.resize(iterations);
+        sort(latencies.begin(), latencies.end());
+        state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
+    }
+}
+
+static void run_at_reporting_level(decltype(ipv4_loopback) benchmarkFunction,
+                                   ::benchmark::State& state, const ReportingLevel reportingLevel,
+                                   const bool waitBetweenRuns) {
+    // Our master thread (thread_index == 0) will control setup and teardown for other threads.
+    const bool isMaster = (state.thread_index == 0);
+
+    // Previous values of env variables used by fwmarkclient (only read/written by master thread)
+    const std::string savedSettings[] = {
+        FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT,
+        FwmarkClient::ANDROID_FWMARK_METRICS_ONLY
+    };
+    std::map<std::string, std::string> prevSettings;
+
+    // SETUP
+    if (isMaster) {
+        for (const auto setting : savedSettings) {
+            const char* prevEnvStr = getenv(setting.c_str());
+            if (prevEnvStr != nullptr) {
+                prevSettings[setting.c_str()] = prevEnvStr;
+            }
+        }
+        switch (reportingLevel) {
+            case NONE:
+                setenv(FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT, "", 1);
+                break;
+            case METRICS:
+                unsetenv(FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT);
+                setenv(FwmarkClient::ANDROID_FWMARK_METRICS_ONLY, "", 1);
+                break;
+            case FULL:
+                unsetenv(FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT);
+                unsetenv(FwmarkClient::ANDROID_FWMARK_METRICS_ONLY);
+                break;
+        }
+    }
+
+    // TEST
+    benchmarkFunction(state, waitBetweenRuns);
+
+    // TEARDOWN
+    if (isMaster) {
+        for (const auto setting : savedSettings) {
+            if (prevSettings.count(setting)) {
+                setenv(setting.c_str(), prevSettings[setting].c_str(), 1);
+            } else {
+                unsetenv(setting.c_str());
+            }
+        }
+    }
+}
+
+constexpr int MIN_THREADS = 1;
+constexpr int MAX_THREADS = 1;
+constexpr double MIN_TIME = 0.5 /* seconds */;
+
+static void ipv4_metrics_reporting_no_fwmark(::benchmark::State& state) {
+    run_at_reporting_level(ipv4_loopback, state, NONE, true);
+}
+BENCHMARK(ipv4_metrics_reporting_no_fwmark)->MinTime(MIN_TIME)->UseManualTime();
+
+// IPv4 metrics under low load
+static void ipv4_metrics_reporting_no_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv4_loopback, state, METRICS, true);
+}
+BENCHMARK(ipv4_metrics_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
+
+/*
+// TODO: uncomment once full reporting is available.
+static void ipv4_full_reporting_no_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv4_loopback, state, FULL, true);
+}
+BENCHMARK(ipv4_full_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
+*/
+
+// IPv4 benchmarks under high load
+static void ipv4_metrics_reporting_high_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv4_loopback, state, METRICS, false);
+}
+BENCHMARK(ipv4_metrics_reporting_high_load)
+    ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
+
+/*
+// TODO: uncomment once full reporting is available.
+static void ipv4_full_reporting_high_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv4_loopback, state, FULL, false);
+}
+BENCHMARK(ipv4_full_reporting_high_load)
+    ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
+*/
+
+// IPv6 raw connect() without using fwmark
+static void ipv6_metrics_reporting_no_fwmark(::benchmark::State& state) {
+    run_at_reporting_level(ipv6_loopback, state, NONE, true);
+}
+BENCHMARK(ipv6_metrics_reporting_no_fwmark)->MinTime(MIN_TIME)->UseManualTime();
+
+// IPv6 metrics under low load
+static void ipv6_metrics_reporting_no_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv6_loopback, state, METRICS, true);
+}
+BENCHMARK(ipv6_metrics_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
+
+/*
+// TODO: uncomment once full reporting is available.
+static void ipv6_full_reporting_no_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv6_loopback, state, FULL, true);
+}
+BENCHMARK(ipv6_full_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
+*/
+
+// IPv6 benchmarks under high load
+static void ipv6_metrics_reporting_high_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv6_loopback, state, METRICS, false);
+}
+BENCHMARK(ipv6_metrics_reporting_high_load)
+    ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
+
+/*
+// TODO: uncomment once full reporting is available.
+static void ipv6_full_reporting_high_load(::benchmark::State& state) {
+    run_at_reporting_level(ipv6_loopback, state, FULL, false);
+}
+BENCHMARK(ipv6_full_reporting_high_load)
+    ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
+*/
diff --git a/tests/benchmarks/dns_benchmark.cpp b/tests/benchmarks/dns_benchmark.cpp
new file mode 100644 (file)
index 0000000..4307861
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "dns_benchmark"
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <android-base/stringprintf.h>
+#include <benchmark/benchmark.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "dns_responder_client.h"
+#include "NetdClient.h"
+
+using android::base::StringPrintf;
+
+constexpr int MIN_THREADS = 1;
+constexpr int MAX_THREADS = 32;
+
+class DnsFixture : public ::benchmark::Fixture {
+protected:
+    static constexpr unsigned num_hosts = 1000;
+    DnsResponderClient dns;
+    std::vector<DnsResponderClient::Mapping> mappings;
+    std::vector<std::unique_ptr<test::DNSResponder>> mDns;
+
+public:
+    void SetUp(const ::benchmark::State& state) override {
+        if (state.thread_index == 0) {
+            dns.SetUp();
+
+            std::vector<std::string> domains = { "example.com" };
+            std::vector<std::string> servers;
+            dns.SetupMappings(num_hosts, domains, &mappings);
+
+            dns.SetupDNSServers(MAXNS, mappings, &mDns, &servers);
+
+            const std::vector<int> mDefaultParams_Binder = { 300, 25, 8, 8 };
+            dns.SetResolversForNetwork(servers, domains, mDefaultParams_Binder);
+        }
+    }
+
+    void TearDown(const ::benchmark::State& state) override {
+        if (state.thread_index == 0) {
+            dns.ShutdownDNSServers(&mDns);
+            dns.TearDown();
+        }
+    }
+
+    std::vector<DnsResponderClient::Mapping> const& getMappings() const {
+        return mappings;
+    }
+
+    android::sp<android::net::INetd> getNetd() const {
+        return dns.mNetdSrv;
+    }
+
+    void getaddrinfo_until_done(benchmark::State &state) {
+        while (state.KeepRunning()) {
+            const uint32_t ofs = arc4random_uniform(getMappings().size());
+            const auto& mapping = getMappings()[ofs];
+            addrinfo* result = nullptr;
+            if (getaddrinfo(mapping.host.c_str(), nullptr, nullptr, &result)) {
+                state.SkipWithError(StringPrintf("getaddrinfo failed with errno=%d",
+                        errno).c_str());
+                break;
+            }
+            if (result) {
+                freeaddrinfo(result);
+                result = nullptr;
+            }
+        }
+    }
+
+    void benchmark_at_reporting_level(benchmark::State &state, int metricsLevel) {
+        const bool isMaster = (state.thread_index == 0);
+        int oldMetricsLevel;
+
+        // SETUP
+        if (isMaster) {
+            auto rv = getNetd()->getMetricsReportingLevel(&oldMetricsLevel);
+            if (!rv.isOk()) {
+                state.SkipWithError(StringPrintf("Failed saving metrics reporting level: %s",
+                        rv.toString8().string()).c_str());
+                return;
+            }
+            rv = getNetd()->setMetricsReportingLevel(metricsLevel);
+            if (!rv.isOk()) {
+                state.SkipWithError(StringPrintf("Failed changing metrics reporting: %s",
+                        rv.toString8().string()).c_str());
+                return;
+            }
+        }
+
+        // TEST
+        getaddrinfo_until_done(state);
+
+        // TEARDOWN
+        if (isMaster) {
+            auto rv = getNetd()->setMetricsReportingLevel(oldMetricsLevel);
+            if (!rv.isOk()) {
+                state.SkipWithError(StringPrintf("Failed restoring metrics reporting level: %s",
+                        rv.toString8().string()).c_str());
+                return;
+            }
+        }
+    }
+};
+
+// DNS calls without any metrics logged or sent.
+BENCHMARK_DEFINE_F(DnsFixture, getaddrinfo_log_nothing)(benchmark::State& state) {
+    benchmark_at_reporting_level(state, 0);
+}
+BENCHMARK_REGISTER_F(DnsFixture, getaddrinfo_log_nothing)
+    ->ThreadRange(MIN_THREADS, MAX_THREADS)
+    ->UseRealTime();
+
+// DNS calls with metrics only (netId, latency, return code) sent to the system server.
+BENCHMARK_DEFINE_F(DnsFixture, getaddrinfo_log_metrics)(benchmark::State& state) {
+    benchmark_at_reporting_level(state, 1);
+}
+BENCHMARK_REGISTER_F(DnsFixture, getaddrinfo_log_metrics)
+    ->ThreadRange(MIN_THREADS, MAX_THREADS)
+    ->UseRealTime();
+
+// DNS calls with all information logged and sent to the system server.
+BENCHMARK_DEFINE_F(DnsFixture, getaddrinfo_log_everything)(benchmark::State& state) {
+    benchmark_at_reporting_level(state, 2);
+}
+BENCHMARK_REGISTER_F(DnsFixture, getaddrinfo_log_everything)
+    ->ThreadRange(MIN_THREADS, MAX_THREADS)
+    ->UseRealTime();
diff --git a/tests/benchmarks/main.cpp b/tests/benchmarks/main.cpp
new file mode 100644 (file)
index 0000000..a0157bc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 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 <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/tests/dns_responder/Android.mk b/tests/dns_responder/Android.mk
new file mode 100644 (file)
index 0000000..0a1ff4d
--- /dev/null
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2016 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+# TODO describe library here
+include $(CLEAR_VARS)
+LOCAL_MODULE := libnetd_test_dnsresponder
+LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter
+# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
+LOCAL_CFLAGS += -Wno-varargs
+
+EXTRA_LDLIBS := -lpthread
+LOCAL_SHARED_LIBRARIES += libbase libbinder libcutils liblog liblogwrap libnetdaidl libnetd_client \
+                          libnetutils libutils
+
+LOCAL_C_INCLUDES += system/netd/include \
+                    system/extras/tests/include \
+                    system/netd/binder/include \
+                    system/netd/server \
+                    system/netd/tests/dns_responder \
+                    system/core/logwrapper/include \
+                    system/core/libnetutils/include \
+                    system/extras/tests/include \
+                    bionic/libc/dns/include
+
+LOCAL_SRC_FILES := dns_responder.cpp \
+                   dns_responder_client.cpp \
+                   ../../server/NetdConstants.cpp
+
+LOCAL_MODULE_TAGS := eng tests
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/tests/dns_responder/dns_responder_client.cpp b/tests/dns_responder/dns_responder_client.cpp
new file mode 100644 (file)
index 0000000..ff5b556
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016 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 "dns_responder_client.h"
+
+#include <android-base/stringprintf.h>
+
+// TODO: make this dynamic and stop depending on implementation details.
+#define TEST_OEM_NETWORK "oem29"
+#define TEST_NETID 30
+
+// TODO: move this somewhere shared.
+static const char* ANDROID_DNS_MODE = "ANDROID_DNS_MODE";
+
+// The only response code used in this class. See
+// frameworks/base/services/java/com/android/server/NetworkManagementService.java
+// for others.
+static constexpr int ResponseCodeOK = 200;
+
+using android::base::StringPrintf;
+
+static int netdCommand(const char* sockname, const char* command) {
+    int sock = socket_local_client(sockname,
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    if (sock < 0) {
+        perror("Error connecting");
+        return -1;
+    }
+
+    // FrameworkListener expects the whole command in one read.
+    char buffer[256];
+    int nwritten = snprintf(buffer, sizeof(buffer), "0 %s", command);
+    if (write(sock, buffer, nwritten + 1) < 0) {
+        perror("Error sending netd command");
+        close(sock);
+        return -1;
+    }
+
+    int nread = read(sock, buffer, sizeof(buffer));
+    if (nread < 0) {
+        perror("Error reading response");
+        close(sock);
+        return -1;
+    }
+    close(sock);
+    return atoi(buffer);
+}
+
+static bool expectNetdResult(int expected, const char* sockname, const char* format, ...) {
+    char command[256];
+    va_list args;
+    va_start(args, format);
+    vsnprintf(command, sizeof(command), format, args);
+    va_end(args);
+    int result = netdCommand(sockname, command);
+    if (expected != result) {
+        return false;
+    }
+    return (200 <= expected && expected < 300);
+}
+
+void DnsResponderClient::SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+        std::vector<Mapping>* mappings) {
+    mappings->resize(num_hosts * domains.size());
+    auto mappings_it = mappings->begin();
+    for (unsigned i = 0 ; i < num_hosts ; ++i) {
+        for (const auto& domain : domains) {
+            mappings_it->host = StringPrintf("host%u", i);
+            mappings_it->entry = StringPrintf("%s.%s.", mappings_it->host.c_str(),
+                    domain.c_str());
+            mappings_it->ip4 = StringPrintf("192.0.2.%u", i%253 + 1);
+            mappings_it->ip6 = StringPrintf("2001:db8::%x", i%65534 + 1);
+            ++mappings_it;
+        }
+    }
+}
+
+bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& servers,
+        const std::vector<std::string>& domains, const std::vector<int>& params) {
+    auto rv = mNetdSrv->setResolverConfiguration(TEST_NETID, servers, domains, params);
+    return rv.isOk();
+}
+
+bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& searchDomains,
+            const std::vector<std::string>& servers, const std::string& params) {
+    std::string cmd = StringPrintf("resolver setnetdns %d \"", mOemNetId);
+    if (!searchDomains.empty()) {
+        cmd += searchDomains[0].c_str();
+        for (size_t i = 1 ; i < searchDomains.size() ; ++i) {
+            cmd += " ";
+            cmd += searchDomains[i];
+        }
+    }
+    cmd += "\"";
+
+    for (const auto& str : servers) {
+        cmd += " ";
+        cmd += str;
+    }
+
+    if (!params.empty()) {
+        cmd += " --params \"";
+        cmd += params;
+        cmd += "\"";
+    }
+
+    int rv = netdCommand("netd", cmd.c_str());
+    if (rv != ResponseCodeOK) {
+        return false;
+    }
+    return true;
+}
+
+void DnsResponderClient::SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+        std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+        std::vector<std::string>* servers) {
+    const char* listen_srv = "53";
+    dns->resize(num_servers);
+    servers->resize(num_servers);
+    for (unsigned i = 0 ; i < num_servers ; ++i) {
+        auto& server = (*servers)[i];
+        auto& d = (*dns)[i];
+        server = StringPrintf("127.0.0.%u", i + 100);
+        d = std::make_unique<test::DNSResponder>(server, listen_srv, 250,
+                ns_rcode::ns_r_servfail, 1.0);
+        for (const auto& mapping : mappings) {
+            d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
+            d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
+        }
+        d->startServer();
+    }
+}
+
+void DnsResponderClient::ShutdownDNSServers(std::vector<std::unique_ptr<test::DNSResponder>>* dns) {
+    for (const auto& d : *dns) {
+        d->stopServer();
+    }
+    dns->clear();
+}
+
+int DnsResponderClient::SetupOemNetwork() {
+    netdCommand("netd", "network destroy " TEST_OEM_NETWORK);
+    if (!expectNetdResult(ResponseCodeOK, "netd",
+                         "network create %s", TEST_OEM_NETWORK)) {
+        return -1;
+    }
+    int oemNetId = TEST_NETID;
+    setNetworkForProcess(oemNetId);
+    if ((unsigned) oemNetId != getNetworkForProcess()) {
+        return -1;
+    }
+    return oemNetId;
+}
+
+void DnsResponderClient::TearDownOemNetwork(int oemNetId) {
+    if (oemNetId != -1) {
+        expectNetdResult(ResponseCodeOK, "netd",
+                         "network destroy %s", TEST_OEM_NETWORK);
+    }
+}
+
+void DnsResponderClient::SetUp() {
+    // Ensure resolutions go via proxy.
+    setenv(ANDROID_DNS_MODE, "", 1);
+    mOemNetId = SetupOemNetwork();
+
+    // binder setup
+    auto binder = android::defaultServiceManager()->getService(android::String16("netd"));
+    mNetdSrv = android::interface_cast<android::net::INetd>(binder);
+}
+
+void DnsResponderClient::TearDown() {
+    TearDownOemNetwork(mOemNetId);
+}
diff --git a/tests/dns_responder/dns_responder_client.h b/tests/dns_responder/dns_responder_client.h
new file mode 100644 (file)
index 0000000..d8f3710
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef DNS_RESPONDER_CLIENT_H
+#define DNS_RESPONDER_CLIENT_H
+
+#include "SockDiag.h"
+
+#include <cutils/sockets.h>
+
+#include <private/android_filesystem_config.h>
+#include <utils/StrongPointer.h>
+
+#include "android/net/INetd.h"
+#include "binder/IServiceManager.h"
+#include "NetdClient.h"
+#include "dns_responder.h"
+#include "resolv_params.h"
+
+class DnsResponderClient {
+public:
+    struct Mapping {
+        std::string host;
+        std::string entry;
+        std::string ip4;
+        std::string ip6;
+    };
+
+    virtual ~DnsResponderClient() = default;
+
+    void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+            std::vector<Mapping>* mappings);
+
+    bool SetResolversForNetwork(const std::vector<std::string>& servers,
+            const std::vector<std::string>& domains, const std::vector<int>& params);
+
+    bool SetResolversForNetwork(const std::vector<std::string>& searchDomains,
+            const std::vector<std::string>& servers, const std::string& params);
+
+    static void SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+            std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+            std::vector<std::string>* servers);
+
+    static void ShutdownDNSServers(std::vector<std::unique_ptr<test::DNSResponder>>* dns);
+
+    static int SetupOemNetwork();
+
+    static void TearDownOemNetwork(int oemNetId);
+
+    virtual void SetUp();
+
+    virtual void TearDown();
+
+public:
+    android::sp<android::net::INetd> mNetdSrv = nullptr;
+    int mOemNetId = -1;
+};
+
+#endif  // DNS_RESPONDER_CLIENT_H
index 1096fc7..6c7fdac 100644 (file)
@@ -47,6 +47,7 @@
 #include <testUtil.h>
 
 #include "dns_responder.h"
+#include "dns_responder_client.h"
 #include "resolv_params.h"
 #include "ResolverStats.h"
 
@@ -79,51 +80,6 @@ bool UnorderedCompareArray(const A& a, const B& b) {
     return true;
 }
 
-// The only response code used in this test, see
-// frameworks/base/services/java/com/android/server/NetworkManagementService.java
-// for others.
-static constexpr int ResponseCodeOK = 200;
-
-// Returns ResponseCode.
-int netdCommand(const char* sockname, const char* command) {
-    int sock = socket_local_client(sockname,
-                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                   SOCK_STREAM);
-    if (sock < 0) {
-        perror("Error connecting");
-        return -1;
-    }
-
-    // FrameworkListener expects the whole command in one read.
-    char buffer[256];
-    int nwritten = snprintf(buffer, sizeof(buffer), "0 %s", command);
-    if (write(sock, buffer, nwritten + 1) < 0) {
-        perror("Error sending netd command");
-        close(sock);
-        return -1;
-    }
-
-    int nread = read(sock, buffer, sizeof(buffer));
-    if (nread < 0) {
-        perror("Error reading response");
-        close(sock);
-        return -1;
-    }
-    close(sock);
-    return atoi(buffer);
-}
-
-bool expectNetdResult(int expected, const char* sockname, const char* format, ...) {
-    char command[256];
-    va_list args;
-    va_start(args, format);
-    vsnprintf(command, sizeof(command), format, args);
-    va_end(args);
-    int result = netdCommand(sockname, command);
-    EXPECT_EQ(expected, result) << command;
-    return (200 <= expected && expected < 300);
-}
-
 class AddrInfo {
   public:
     AddrInfo() : ai_(nullptr), error_(0) {}
@@ -168,131 +124,15 @@ class AddrInfo {
     int error_;
 };
 
-class ResolverTest : public ::testing::Test {
+class ResolverTest : public ::testing::Test, public DnsResponderClient {
 protected:
-    struct Mapping {
-        std::string host;
-        std::string entry;
-        std::string ip4;
-        std::string ip6;
-    };
-
     virtual void SetUp() {
         // Ensure resolutions go via proxy.
-        setenv("ANDROID_DNS_MODE", "", 1);
-        uid = getuid();
-        pid = getpid();
-        SetupOemNetwork();
-
-        // binder setup
-        auto binder = android::defaultServiceManager()->getService(android::String16("netd"));
-        ASSERT_TRUE(binder != nullptr);
-        mNetdSrv = android::interface_cast<android::net::INetd>(binder);
+        DnsResponderClient::SetUp();
     }
 
     virtual void TearDown() {
-        TearDownOemNetwork();
-        netdCommand("netd", "network destroy " TEST_OEM_NETWORK);
-    }
-
-    void SetupOemNetwork() {
-        netdCommand("netd", "network destroy " TEST_OEM_NETWORK);
-        if (expectNetdResult(ResponseCodeOK, "netd",
-                             "network create %s", TEST_OEM_NETWORK)) {
-            oemNetId = TEST_NETID;
-        }
-        setNetworkForProcess(oemNetId);
-        ASSERT_EQ((unsigned) oemNetId, getNetworkForProcess());
-    }
-
-    void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
-            std::vector<Mapping>* mappings) const {
-        mappings->resize(num_hosts * domains.size());
-        auto mappings_it = mappings->begin();
-        for (unsigned i = 0 ; i < num_hosts ; ++i) {
-            for (const auto& domain : domains) {
-                ASSERT_TRUE(mappings_it != mappings->end());
-                mappings_it->host = StringPrintf("host%u", i);
-                mappings_it->entry = StringPrintf("%s.%s.", mappings_it->host.c_str(),
-                        domain.c_str());
-                mappings_it->ip4 = StringPrintf("192.0.2.%u", i%253 + 1);
-                mappings_it->ip6 = StringPrintf("2001:db8::%x", i%65534 + 1);
-                ++mappings_it;
-            }
-        }
-    }
-
-    void SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
-            std::vector<std::unique_ptr<test::DNSResponder>>* dns,
-            std::vector<std::string>* servers) const {
-        ASSERT_TRUE(num_servers != 0 && num_servers < 100);
-        const char* listen_srv = "53";
-        dns->resize(num_servers);
-        servers->resize(num_servers);
-        for (unsigned i = 0 ; i < num_servers ; ++i) {
-            auto& server = (*servers)[i];
-            auto& d = (*dns)[i];
-            server = StringPrintf("127.0.0.%u", i + 100);
-            d = std::make_unique<test::DNSResponder>(server, listen_srv, 250,
-                    ns_rcode::ns_r_servfail, 1.0);
-            ASSERT_TRUE(d.get() != nullptr);
-            for (const auto& mapping : mappings) {
-                d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
-                d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
-            }
-            ASSERT_TRUE(d->startServer());
-        }
-    }
-
-    void ShutdownDNSServers(std::vector<std::unique_ptr<test::DNSResponder>>* dns) const {
-        for (const auto& d : *dns) {
-            ASSERT_TRUE(d.get() != nullptr);
-            d->stopServer();
-        }
-        dns->clear();
-    }
-
-    void TearDownOemNetwork() {
-        if (oemNetId != -1) {
-            expectNetdResult(ResponseCodeOK, "netd",
-                             "network destroy %s", TEST_OEM_NETWORK);
-        }
-    }
-
-    bool SetResolversForNetwork(const std::vector<std::string>& servers,
-            const std::vector<std::string>& domains, const std::vector<int>& params) {
-        auto rv = mNetdSrv->setResolverConfiguration(TEST_NETID, servers, domains, params);
-        return rv.isOk();
-    }
-
-    bool SetResolversForNetwork(const std::vector<std::string>& searchDomains,
-            const std::vector<std::string>& servers, const std::string& params) {
-        std::string cmd = StringPrintf("resolver setnetdns %d \"", oemNetId);
-        if (!searchDomains.empty()) {
-            cmd += searchDomains[0].c_str();
-            for (size_t i = 1 ; i < searchDomains.size() ; ++i) {
-                cmd += " ";
-                cmd += searchDomains[i];
-            }
-        }
-        cmd += "\"";
-
-        for (const auto& str : servers) {
-            cmd += " ";
-            cmd += str;
-        }
-
-        if (!params.empty()) {
-            cmd += " --params \"";
-            cmd += params;
-            cmd += "\"";
-        }
-
-        int rv = netdCommand("netd", cmd.c_str());
-        if (rv != ResponseCodeOK) {
-            return false;
-        }
-        return true;
+        DnsResponderClient::TearDown();
     }
 
     bool GetResolverInfo(std::vector<std::string>* servers, std::vector<std::string>* domains,
@@ -368,7 +208,7 @@ protected:
         std::vector<std::string> domains = { "example.com" };
         std::vector<std::unique_ptr<test::DNSResponder>> dns;
         std::vector<std::string> servers;
-        std::vector<Mapping> mappings;
+        std::vector<DnsResponderClient::Mapping> mappings;
         ASSERT_NO_FATAL_FAILURE(SetupMappings(num_hosts, domains, &mappings));
         ASSERT_NO_FATAL_FAILURE(SetupDNSServers(MAXNS, mappings, &dns, &servers));
 
@@ -407,10 +247,6 @@ protected:
         ASSERT_NO_FATAL_FAILURE(ShutdownDNSServers(&dns));
     }
 
-    int pid;
-    int uid;
-    int oemNetId = -1;
-    android::sp<android::net::INetd> mNetdSrv = nullptr;
     const std::vector<std::string> mDefaultSearchDomains = { "example.com" };
     // <sample validity in s> <success threshold in percent> <min samples> <max samples>
     const std::string mDefaultParams = "300 25 8 8";