• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
25 namespace android::net {
26 
27 using base::StringPrintf;
28 using netdutils::DumpWriter;
29 using netdutils::IPAddress;
30 using netdutils::IPSockAddr;
31 using netdutils::ScopedIndent;
32 using std::chrono::duration_cast;
33 using std::chrono::microseconds;
34 using std::chrono::milliseconds;
35 using std::chrono::seconds;
36 
37 namespace {
38 
39 static constexpr IPAddress INVALID_IPADDRESS = IPAddress();
40 
rcodeToName(int rcode)41 std::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 
ensureNoInvalidIp(const std::vector<IPSockAddr> & servers)62 bool 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.
operator ==(const StatsData & o) const75 bool 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 
toString() const80 std::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 
StatsRecords(const IPSockAddr & ipSockAddr,size_t size)96 StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size)
97     : mCapacity(size), mStatsData(ipSockAddr) {}
98 
push(const Record & record)99 void 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 
updateStatsData(const Record & record,const bool add)109 void 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 
setServers(const std::vector<netdutils::IPSockAddr> & servers,Protocol protocol)123 bool 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 
addStats(const IPSockAddr & ipSockAddr,const DnsQueryEvent & record)147 bool 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 
getStats(Protocol protocol) const163 std::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 
dump(DumpWriter & dw)174 void 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
200