blob: 970e30e92decb72e48fcf6b2d839bd1c1ba4d806 [file] [log] [blame]
Mike Yue655b1d2019-08-28 17:49:59 +08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18#define LOG_TAG "resolv"
19
20#include "DnsStats.h"
21
22#include <android-base/logging.h>
23#include <android-base/stringprintf.h>
24
25namespace android::net {
26
27using base::StringPrintf;
28using netdutils::DumpWriter;
29using netdutils::IPAddress;
30using netdutils::IPSockAddr;
31using netdutils::ScopedIndent;
32using std::chrono::duration_cast;
33using std::chrono::microseconds;
34using std::chrono::milliseconds;
35using std::chrono::seconds;
36
37namespace {
38
39static constexpr IPAddress INVALID_IPADDRESS = IPAddress();
40
41std::string rcodeToName(int rcode) {
42 // clang-format off
43 switch (rcode) {
44 case NS_R_NO_ERROR: return "NOERROR";
45 case NS_R_FORMERR: return "FORMERR";
46 case NS_R_SERVFAIL: return "SERVFAIL";
47 case NS_R_NXDOMAIN: return "NXDOMAIN";
48 case NS_R_NOTIMPL: return "NOTIMP";
49 case NS_R_REFUSED: return "REFUSED";
50 case NS_R_YXDOMAIN: return "YXDOMAIN";
51 case NS_R_YXRRSET: return "YXRRSET";
52 case NS_R_NXRRSET: return "NXRRSET";
53 case NS_R_NOTAUTH: return "NOTAUTH";
54 case NS_R_NOTZONE: return "NOTZONE";
55 case NS_R_INTERNAL_ERROR: return "INTERNAL_ERROR";
56 case NS_R_TIMEOUT: return "TIMEOUT";
57 default: return StringPrintf("UNKNOWN(%d)", rcode);
58 }
59 // clang-format on
60}
61
62bool ensureNoInvalidIp(const std::vector<IPSockAddr>& servers) {
63 for (const auto& server : servers) {
64 if (server.ip() == INVALID_IPADDRESS || server.port() == 0) {
65 LOG(WARNING) << "Invalid server: " << server;
66 return false;
67 }
68 }
69 return true;
70}
71
72} // namespace
73
74// The comparison ignores the last update time.
75bool StatsData::operator==(const StatsData& o) const {
76 return std::tie(serverSockAddr, total, rcodeCounts, latencyUs) ==
77 std::tie(o.serverSockAddr, o.total, o.rcodeCounts, o.latencyUs);
78}
79
80std::string StatsData::toString() const {
81 if (total == 0) return StringPrintf("%s <no data>", serverSockAddr.ip().toString().c_str());
82
83 const auto now = std::chrono::steady_clock::now();
84 const int meanLatencyMs = duration_cast<milliseconds>(latencyUs).count() / total;
85 const int lastUpdateSec = duration_cast<seconds>(now - lastUpdate).count();
86 std::string buf;
87 for (const auto& [rcode, counts] : rcodeCounts) {
88 if (counts != 0) {
89 buf += StringPrintf("%s:%d ", rcodeToName(rcode).c_str(), counts);
90 }
91 }
92 return StringPrintf("%s (%d, %dms, [%s], %ds)", serverSockAddr.ip().toString().c_str(), total,
93 meanLatencyMs, buf.c_str(), lastUpdateSec);
94}
95
96StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size)
97 : mCapacity(size), mStatsData(ipSockAddr) {}
98
99void StatsRecords::push(const Record& record) {
100 updateStatsData(record, true);
101 mRecords.push_back(record);
102
103 if (mRecords.size() > mCapacity) {
104 updateStatsData(mRecords.front(), false);
105 mRecords.pop_front();
106 }
107}
108
109void StatsRecords::updateStatsData(const Record& record, const bool add) {
110 const int rcode = record.rcode;
111 if (add) {
112 mStatsData.total += 1;
113 mStatsData.rcodeCounts[rcode] += 1;
114 mStatsData.latencyUs += record.latencyUs;
115 } else {
116 mStatsData.total -= 1;
117 mStatsData.rcodeCounts[rcode] -= 1;
118 mStatsData.latencyUs -= record.latencyUs;
119 }
120 mStatsData.lastUpdate = std::chrono::steady_clock::now();
121}
122
123bool DnsStats::setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol) {
124 if (!ensureNoInvalidIp(servers)) return false;
125
126 ServerStatsMap& statsMap = mStats[protocol];
127 for (const auto& server : servers) {
128 statsMap.try_emplace(server, StatsRecords(server, kLogSize));
129 }
130
131 // Clean up the map to eliminate the nodes not belonging to the given list of servers.
132 const auto cleanup = [&](ServerStatsMap* statsMap) {
133 ServerStatsMap tmp;
134 for (const auto& server : servers) {
135 if (statsMap->find(server) != statsMap->end()) {
136 tmp.insert(statsMap->extract(server));
137 }
138 }
139 statsMap->swap(tmp);
140 };
141
142 cleanup(&statsMap);
143
144 return true;
145}
146
147bool DnsStats::addStats(const IPSockAddr& ipSockAddr, const DnsQueryEvent& record) {
148 if (ipSockAddr.ip() == INVALID_IPADDRESS) return false;
149
150 for (auto& [serverSockAddr, statsRecords] : mStats[record.protocol()]) {
151 if (serverSockAddr == ipSockAddr) {
152 const StatsRecords::Record rec = {
153 .rcode = record.rcode(),
154 .latencyUs = microseconds(record.latency_micros()),
155 };
156 statsRecords.push(rec);
157 return true;
158 }
159 }
160 return false;
161}
162
163std::vector<StatsData> DnsStats::getStats(Protocol protocol) const {
164 std::vector<StatsData> ret;
165
166 if (mStats.find(protocol) != mStats.end()) {
167 for (const auto& [_, statsRecords] : mStats.at(protocol)) {
168 ret.push_back(statsRecords.getStatsData());
169 }
170 }
171 return ret;
172}
173
174void DnsStats::dump(DumpWriter& dw) {
175 const auto dumpStatsMap = [&](ServerStatsMap& statsMap) {
176 ScopedIndent indentLog(dw);
177 if (statsMap.size() == 0) {
178 dw.println("<no server>");
179 return;
180 }
181 for (const auto& [_, statsRecords] : statsMap) {
182 dw.println("%s", statsRecords.getStatsData().toString().c_str());
183 }
184 };
185
186 dw.println("Server statistics: (total, RTT avg, {rcode:counts}, last update)");
187 ScopedIndent indentStats(dw);
188
189 dw.println("over UDP");
190 dumpStatsMap(mStats[PROTO_UDP]);
191
192 dw.println("over TLS");
193 dumpStatsMap(mStats[PROTO_DOT]);
194
195 dw.println("over TCP");
196 dumpStatsMap(mStats[PROTO_TCP]);
197}
198
199} // namespace android::net