} // 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) {
// 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();
// 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;
};
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) {
}
}
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.");
}
}
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();
}
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();
}
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) {
}
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;
+ }
}
}
char* service,
struct addrinfo* hints,
const struct android_net_context& netcontext,
+ const int reportingLevel,
const android::sp<android::net::metrics::INetdEventListener>& listener);
~GetAddrInfoHandler();
char* mService; // owned
struct addrinfo* mHints; // owned
struct android_net_context mNetContext;
+ const int mReportingLevel;
android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
};
int af,
unsigned netId,
uint32_t mark,
+ int reportingLevel,
const android::sp<android::net::metrics::INetdEventListener>& listener);
~GetHostByNameHandler();
static void* threadStart(void* handler);
int mAf;
unsigned mNetId;
uint32_t mMark;
+ const int mReportingLevel;
android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
};
#include <android-base/stringprintf.h>
#include <cutils/log.h>
+#include <cutils/properties.h>
#include <utils/Errors.h>
#include <utils/String16.h>
}
}
+#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()) { \
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
binder::Status setProcSysNet(
int32_t family, int32_t which, const std::string &ifname, const std::string ¶meter,
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
}
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;
+}
#include "utils/RWLock.h"
+#include <atomic>
#include <list>
#include <map>
#include <set>
void dump(DumpWriter& dw);
+ int setMetricsReportingLevel(const int level);
+ int getMetricsReportingLevel() const;
+
private:
bool isValidNetwork(unsigned netId) const;
Network* getNetworkLocked(unsigned netId) const;
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
* limitations under the License.
*/
+#ifndef _SOCK_DIAG_H
+#define _SOCK_DIAG_H
+
#include <unistd.h>
#include <sys/socket.h>
void closeSocks() { close(mSock); close(mWriteSock); mSock = mWriteSock = -1; }
static bool isLoopbackSocket(const inet_diag_msg *msg);
};
+
+#endif // _SOCK_DIAG_H
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.
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);
}
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))
--- /dev/null
+#
+# 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)
--- /dev/null
+/*
+ * 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();
+*/
--- /dev/null
+/*
+ * 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();
--- /dev/null
+/*
+ * 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();
--- /dev/null
+#
+# 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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+#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
#include <testUtil.h>
#include "dns_responder.h"
+#include "dns_responder_client.h"
#include "resolv_params.h"
#include "ResolverStats.h"
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) {}
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,
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));
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";