]>
Commit | Line | Data |
---|---|---|
6bb38cd6 RG |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
22 | #include "dnsdist.hh" | |
23 | #include "dnsdist-lua.hh" | |
dc2fd93a | 24 | #include "dnsdist-dynblocks.hh" |
03b00917 | 25 | #include "dnsdist-rings.hh" |
6bb38cd6 RG |
26 | |
27 | #include "statnode.hh" | |
28 | ||
90b1e92f | 29 | static std::unordered_map<unsigned int, vector<boost::variant<string,double>>> getGenResponses(unsigned int top, boost::optional<int> labels, std::function<bool(const Rings::Response&)> pred) |
6bb38cd6 RG |
30 | { |
31 | setLuaNoSideEffect(); | |
90b1e92f | 32 | map<DNSName, unsigned int> counts; |
6bb38cd6 RG |
33 | unsigned int total=0; |
34 | { | |
cdcf7eeb | 35 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd | 36 | std::lock_guard<std::mutex> rl(shard->respLock); |
01f6920b | 37 | if(!labels) { |
cdcb5fbd | 38 | for(const auto& a : shard->respRing) { |
01f6920b RG |
39 | if(!pred(a)) |
40 | continue; | |
41 | counts[a.name]++; | |
42 | total++; | |
43 | } | |
6bb38cd6 | 44 | } |
01f6920b RG |
45 | else { |
46 | unsigned int lab = *labels; | |
b4b213da | 47 | for(const auto& a : shard->respRing) { |
01f6920b RG |
48 | if(!pred(a)) |
49 | continue; | |
6bb38cd6 | 50 | |
b4b213da RG |
51 | DNSName temp(a.name); |
52 | temp.trimToLabels(lab); | |
53 | counts[temp]++; | |
01f6920b RG |
54 | total++; |
55 | } | |
56 | } | |
6bb38cd6 RG |
57 | } |
58 | } | |
59 | // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl; | |
90b1e92f | 60 | vector<pair<unsigned int, DNSName>> rcounts; |
6bb38cd6 RG |
61 | rcounts.reserve(counts.size()); |
62 | for(const auto& c : counts) | |
63 | rcounts.push_back(make_pair(c.second, c.first.makeLowerCase())); | |
64 | ||
65 | sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, | |
66 | const decltype(rcounts)::value_type& b) { | |
67 | return b.first < a.first; | |
68 | }); | |
69 | ||
90b1e92f | 70 | std::unordered_map<unsigned int, vector<boost::variant<string,double>>> ret; |
6bb38cd6 RG |
71 | unsigned int count=1, rest=0; |
72 | for(const auto& rc : rcounts) { | |
73 | if(count==top+1) | |
74 | rest+=rc.first; | |
75 | else | |
76 | ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}}); | |
77 | } | |
78 | ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}}); | |
79 | return ret; | |
80 | } | |
81 | ||
f8327125 RG |
82 | typedef std::unordered_map<ComboAddress, unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual> counts_t; |
83 | ||
84 | static counts_t filterScore(const counts_t& counts, | |
90b1e92f | 85 | double delta, unsigned int rate) |
6bb38cd6 | 86 | { |
f8327125 RG |
87 | counts_t ret; |
88 | ||
6bb38cd6 | 89 | double lim = delta*rate; |
f8327125 RG |
90 | for(const auto& c : counts) { |
91 | if (c.second > lim) { | |
92 | ret[c.first] = c.second; | |
90b1e92f | 93 | } |
6bb38cd6 | 94 | } |
90b1e92f | 95 | |
f8327125 | 96 | return ret; |
6bb38cd6 RG |
97 | } |
98 | ||
99 | ||
100 | typedef std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> statvisitor_t; | |
101 | ||
102 | static void statNodeRespRing(statvisitor_t visitor, unsigned int seconds) | |
103 | { | |
104 | struct timespec cutoff, now; | |
105 | gettime(&now); | |
7ff8246f RG |
106 | cutoff = now; |
107 | cutoff.tv_sec -= seconds; | |
6bb38cd6 | 108 | |
6bb38cd6 | 109 | StatNode root; |
cdcf7eeb | 110 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd | 111 | std::lock_guard<std::mutex> rl(shard->respLock); |
01f6920b | 112 | |
cdcb5fbd | 113 | for(const auto& c : shard->respRing) { |
90b1e92f RG |
114 | if (now < c.when) |
115 | continue; | |
6bb38cd6 | 116 | |
90b1e92f RG |
117 | if (seconds && c.when < cutoff) |
118 | continue; | |
6bb38cd6 | 119 | |
7d07129c | 120 | root.submit(c.name, c.dh.rcode, boost::none); |
90b1e92f | 121 | } |
6bb38cd6 | 122 | } |
6bb38cd6 | 123 | |
90b1e92f RG |
124 | StatNode::Stat node; |
125 | root.visit([visitor](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) { | |
6bb38cd6 | 126 | visitor(*node_, self, children);}, node); |
6bb38cd6 RG |
127 | } |
128 | ||
129 | static vector<pair<unsigned int, std::unordered_map<string,string> > > getRespRing(boost::optional<int> rcode) | |
130 | { | |
131 | typedef std::unordered_map<string,string> entry_t; | |
132 | vector<pair<unsigned int, entry_t > > ret; | |
01f6920b | 133 | |
cdcf7eeb | 134 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd | 135 | std::lock_guard<std::mutex> rl(shard->respLock); |
01f6920b RG |
136 | |
137 | entry_t e; | |
138 | unsigned int count=1; | |
cdcb5fbd | 139 | for(const auto& c : shard->respRing) { |
01f6920b RG |
140 | if(rcode && (rcode.get() != c.dh.rcode)) |
141 | continue; | |
142 | e["qname"]=c.name.toString(); | |
143 | e["rcode"]=std::to_string(c.dh.rcode); | |
144 | ret.push_back(std::make_pair(count,e)); | |
145 | count++; | |
146 | } | |
6bb38cd6 | 147 | } |
01f6920b | 148 | |
6bb38cd6 RG |
149 | return ret; |
150 | } | |
151 | ||
90b1e92f | 152 | static counts_t exceedRespGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T) |
6bb38cd6 RG |
153 | { |
154 | counts_t counts; | |
155 | struct timespec cutoff, mintime, now; | |
156 | gettime(&now); | |
157 | cutoff = mintime = now; | |
158 | cutoff.tv_sec -= seconds; | |
159 | ||
cfe4b655 | 160 | counts.reserve(g_rings.getNumberOfResponseEntries()); |
01f6920b | 161 | |
cdcf7eeb | 162 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd RG |
163 | std::lock_guard<std::mutex> rl(shard->respLock); |
164 | for(const auto& c : shard->respRing) { | |
01f6920b | 165 | |
90b1e92f RG |
166 | if(seconds && c.when < cutoff) |
167 | continue; | |
168 | if(now < c.when) | |
169 | continue; | |
170 | ||
171 | T(counts, c); | |
172 | if(c.when < mintime) | |
173 | mintime = c.when; | |
174 | } | |
6bb38cd6 | 175 | } |
90b1e92f | 176 | |
6bb38cd6 | 177 | double delta = seconds ? seconds : DiffTime(now, mintime); |
f8327125 | 178 | return filterScore(counts, delta, rate); |
6bb38cd6 RG |
179 | } |
180 | ||
90b1e92f | 181 | static counts_t exceedQueryGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T) |
6bb38cd6 RG |
182 | { |
183 | counts_t counts; | |
184 | struct timespec cutoff, mintime, now; | |
185 | gettime(&now); | |
186 | cutoff = mintime = now; | |
187 | cutoff.tv_sec -= seconds; | |
188 | ||
cfe4b655 | 189 | counts.reserve(g_rings.getNumberOfQueryEntries()); |
01f6920b | 190 | |
cdcf7eeb | 191 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd RG |
192 | std::lock_guard<std::mutex> rl(shard->queryLock); |
193 | for(const auto& c : shard->queryRing) { | |
90b1e92f RG |
194 | if(seconds && c.when < cutoff) |
195 | continue; | |
196 | if(now < c.when) | |
197 | continue; | |
198 | T(counts, c); | |
199 | if(c.when < mintime) | |
200 | mintime = c.when; | |
201 | } | |
6bb38cd6 | 202 | } |
90b1e92f | 203 | |
6bb38cd6 | 204 | double delta = seconds ? seconds : DiffTime(now, mintime); |
f8327125 | 205 | return filterScore(counts, delta, rate); |
6bb38cd6 RG |
206 | } |
207 | ||
208 | ||
90b1e92f | 209 | static counts_t exceedRCode(unsigned int rate, int seconds, int rcode) |
6bb38cd6 RG |
210 | { |
211 | return exceedRespGen(rate, seconds, [rcode](counts_t& counts, const Rings::Response& r) | |
212 | { | |
213 | if(r.dh.rcode == rcode) | |
214 | counts[r.requestor]++; | |
215 | }); | |
216 | } | |
217 | ||
90b1e92f | 218 | static counts_t exceedRespByterate(unsigned int rate, int seconds) |
6bb38cd6 RG |
219 | { |
220 | return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r) | |
221 | { | |
222 | counts[r.requestor]+=r.size; | |
223 | }); | |
224 | } | |
225 | ||
226 | void setupLuaInspection() | |
227 | { | |
228 | g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) { | |
229 | setLuaNoSideEffect(); | |
230 | auto top = top_.get_value_or(10); | |
90b1e92f | 231 | map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts; |
6bb38cd6 RG |
232 | unsigned int total=0; |
233 | { | |
cdcf7eeb | 234 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd RG |
235 | std::lock_guard<std::mutex> rl(shard->queryLock); |
236 | for(const auto& c : shard->queryRing) { | |
01f6920b RG |
237 | counts[c.requestor]++; |
238 | total++; | |
239 | } | |
6bb38cd6 RG |
240 | } |
241 | } | |
90b1e92f | 242 | vector<pair<unsigned int, ComboAddress>> rcounts; |
6bb38cd6 RG |
243 | rcounts.reserve(counts.size()); |
244 | for(const auto& c : counts) | |
245 | rcounts.push_back(make_pair(c.second, c.first)); | |
246 | ||
247 | sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, | |
248 | const decltype(rcounts)::value_type& b) { | |
249 | return b.first < a.first; | |
250 | }); | |
251 | unsigned int count=1, rest=0; | |
252 | boost::format fmt("%4d %-40s %4d %4.1f%%\n"); | |
253 | for(const auto& rc : rcounts) { | |
254 | if(count==top+1) | |
255 | rest+=rc.first; | |
256 | else | |
257 | g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str(); | |
258 | } | |
259 | g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str(); | |
260 | }); | |
261 | ||
262 | g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) { | |
263 | setLuaNoSideEffect(); | |
90b1e92f | 264 | map<DNSName, unsigned int> counts; |
6bb38cd6 RG |
265 | unsigned int total=0; |
266 | if(!labels) { | |
cdcf7eeb | 267 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd RG |
268 | std::lock_guard<std::mutex> rl(shard->queryLock); |
269 | for(const auto& a : shard->queryRing) { | |
01f6920b RG |
270 | counts[a.name]++; |
271 | total++; | |
272 | } | |
273 | } | |
6bb38cd6 RG |
274 | } |
275 | else { | |
276 | unsigned int lab = *labels; | |
cdcf7eeb | 277 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd RG |
278 | std::lock_guard<std::mutex> rl(shard->queryLock); |
279 | for(auto a : shard->queryRing) { | |
01f6920b RG |
280 | a.name.trimToLabels(lab); |
281 | counts[a.name]++; | |
282 | total++; | |
283 | } | |
284 | } | |
6bb38cd6 RG |
285 | } |
286 | // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl; | |
90b1e92f | 287 | vector<pair<unsigned int, DNSName>> rcounts; |
6bb38cd6 RG |
288 | rcounts.reserve(counts.size()); |
289 | for(const auto& c : counts) | |
290 | rcounts.push_back(make_pair(c.second, c.first.makeLowerCase())); | |
291 | ||
292 | sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, | |
293 | const decltype(rcounts)::value_type& b) { | |
294 | return b.first < a.first; | |
295 | }); | |
296 | ||
90b1e92f | 297 | std::unordered_map<unsigned int, vector<boost::variant<string,double>>> ret; |
6bb38cd6 RG |
298 | unsigned int count=1, rest=0; |
299 | for(const auto& rc : rcounts) { | |
300 | if(count==top+1) | |
301 | rest+=rc.first; | |
302 | else | |
303 | ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}}); | |
304 | } | |
305 | ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}}); | |
306 | return ret; | |
307 | ||
308 | }); | |
309 | ||
310 | g_lua.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)"); | |
311 | ||
312 | g_lua.writeFunction("getResponseRing", []() { | |
313 | setLuaNoSideEffect(); | |
01f6920b RG |
314 | size_t totalEntries = 0; |
315 | std::vector<boost::circular_buffer<Rings::Response>> rings; | |
316 | rings.reserve(g_rings.getNumberOfShards()); | |
cdcf7eeb | 317 | for (const auto& shard : g_rings.d_shards) { |
01f6920b | 318 | { |
cdcb5fbd RG |
319 | std::lock_guard<std::mutex> rl(shard->respLock); |
320 | rings.push_back(shard->respRing); | |
01f6920b | 321 | } |
cdcb5fbd | 322 | totalEntries += rings.back().size(); |
6bb38cd6 RG |
323 | } |
324 | vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret; | |
01f6920b | 325 | ret.reserve(totalEntries); |
6bb38cd6 | 326 | decltype(ret)::value_type item; |
01f6920b RG |
327 | for (size_t idx = 0; idx < rings.size(); idx++) { |
328 | for(const auto& r : rings[idx]) { | |
329 | item["name"]=r.name.toString(); | |
330 | item["qtype"]=r.qtype; | |
331 | item["rcode"]=r.dh.rcode; | |
332 | item["usec"]=r.usec; | |
333 | ret.push_back(item); | |
334 | } | |
6bb38cd6 RG |
335 | } |
336 | return ret; | |
337 | }); | |
338 | ||
339 | g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) { | |
340 | return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; }); | |
341 | }); | |
342 | ||
343 | g_lua.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)"); | |
344 | ||
345 | ||
346 | g_lua.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) { | |
347 | return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; }); | |
348 | }); | |
349 | ||
350 | ||
351 | g_lua.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)"); | |
352 | ||
353 | g_lua.writeFunction("getTopBandwidth", [](unsigned int top) { | |
354 | setLuaNoSideEffect(); | |
355 | return g_rings.getTopBandwidth(top); | |
356 | }); | |
357 | ||
358 | g_lua.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)"); | |
359 | ||
360 | g_lua.writeFunction("delta", []() { | |
361 | setLuaNoSideEffect(); | |
362 | // we hold the lua lock already! | |
363 | for(const auto& d : g_confDelta) { | |
364 | struct tm tm; | |
365 | localtime_r(&d.first.tv_sec, &tm); | |
366 | char date[80]; | |
367 | strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm); | |
368 | g_outputBuffer += date; | |
369 | g_outputBuffer += d.second + "\n"; | |
370 | } | |
371 | }); | |
372 | ||
373 | g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) { | |
374 | setLuaNoSideEffect(); | |
375 | boost::optional<Netmask> nm; | |
376 | boost::optional<DNSName> dn; | |
377 | int msec=-1; | |
378 | ||
379 | vector<string> vec; | |
380 | auto str=boost::get<string>(&inp); | |
381 | if(str) | |
382 | vec.push_back(*str); | |
383 | else { | |
384 | auto v = boost::get<vector<pair<int, string> > >(inp); | |
385 | for(const auto& a: v) | |
386 | vec.push_back(a.second); | |
387 | } | |
388 | ||
389 | for(const auto& s : vec) { | |
390 | try | |
391 | { | |
392 | nm = Netmask(s); | |
393 | } | |
394 | catch(...) { | |
395 | if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) { | |
396 | ; | |
397 | } | |
398 | else { | |
399 | try { dn=DNSName(s); } | |
400 | catch(...) | |
401 | { | |
402 | g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask"; | |
403 | return; | |
404 | } | |
405 | } | |
406 | } | |
407 | } | |
408 | ||
01f6920b RG |
409 | std::vector<Rings::Query> qr; |
410 | std::vector<Rings::Response> rr; | |
cfe4b655 RG |
411 | qr.reserve(g_rings.getNumberOfQueryEntries()); |
412 | rr.reserve(g_rings.getNumberOfResponseEntries()); | |
cdcf7eeb | 413 | for (const auto& shard : g_rings.d_shards) { |
01f6920b | 414 | { |
cdcb5fbd RG |
415 | std::lock_guard<std::mutex> rl(shard->queryLock); |
416 | for (const auto& entry : shard->queryRing) { | |
01f6920b RG |
417 | qr.push_back(entry); |
418 | } | |
419 | } | |
420 | { | |
cdcb5fbd RG |
421 | std::lock_guard<std::mutex> rl(shard->respLock); |
422 | for (const auto& entry : shard->respRing) { | |
01f6920b RG |
423 | rr.push_back(entry); |
424 | } | |
425 | } | |
6bb38cd6 | 426 | } |
01f6920b | 427 | |
6bb38cd6 RG |
428 | sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) { |
429 | return b.when < a.when; | |
430 | }); | |
6bb38cd6 RG |
431 | |
432 | sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) { | |
433 | return b.when < a.when; | |
434 | }); | |
435 | ||
436 | unsigned int num=0; | |
437 | struct timespec now; | |
438 | gettime(&now); | |
439 | ||
440 | std::multimap<struct timespec, string> out; | |
441 | ||
442 | boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n"); | |
443 | g_outputBuffer+= (fmt % "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str(); | |
444 | ||
445 | if(msec==-1) { | |
446 | for(const auto& c : qr) { | |
447 | bool nmmatch=true, dnmatch=true; | |
448 | if(nm) | |
449 | nmmatch = nm->match(c.requestor); | |
450 | if(dn) | |
451 | dnmatch = c.name.isPartOf(*dn); | |
452 | if(nmmatch && dnmatch) { | |
453 | QType qt(c.qtype); | |
454 | out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % "" % htons(c.dh.id) % c.name.toString() % qt.getName() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % "Question").str() )) ; | |
455 | ||
456 | if(limit && *limit==++num) | |
457 | break; | |
458 | } | |
459 | } | |
460 | } | |
461 | num=0; | |
462 | ||
463 | ||
464 | string extra; | |
465 | for(const auto& c : rr) { | |
466 | bool nmmatch=true, dnmatch=true, msecmatch=true; | |
467 | if(nm) | |
468 | nmmatch = nm->match(c.requestor); | |
469 | if(dn) | |
470 | dnmatch = c.name.isPartOf(*dn); | |
471 | if(msec != -1) | |
472 | msecmatch=(c.usec/1000 > (unsigned int)msec); | |
473 | ||
474 | if(nmmatch && dnmatch && msecmatch) { | |
475 | QType qt(c.qtype); | |
476 | if(!c.dh.rcode) | |
477 | extra=". " +std::to_string(htons(c.dh.ancount))+ " answers"; | |
478 | else | |
479 | extra.clear(); | |
480 | if(c.usec != std::numeric_limits<decltype(c.usec)>::max()) | |
481 | out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % (c.usec/1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ; | |
482 | else | |
483 | out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ; | |
484 | ||
485 | if(limit && *limit==++num) | |
486 | break; | |
487 | } | |
488 | } | |
489 | ||
490 | for(const auto& p : out) { | |
491 | g_outputBuffer+=p.second; | |
492 | } | |
493 | }); | |
494 | ||
495 | g_lua.writeFunction("showResponseLatency", []() { | |
496 | setLuaNoSideEffect(); | |
497 | map<double, unsigned int> histo; | |
498 | double bin=100; | |
499 | for(int i=0; i < 15; ++i) { | |
500 | histo[bin]; | |
501 | bin*=2; | |
502 | } | |
503 | ||
504 | double totlat=0; | |
505 | unsigned int size=0; | |
506 | { | |
cdcf7eeb | 507 | for (const auto& shard : g_rings.d_shards) { |
cdcb5fbd RG |
508 | std::lock_guard<std::mutex> rl(shard->respLock); |
509 | for(const auto& r : shard->respRing) { | |
01f6920b RG |
510 | /* skip actively discovered timeouts */ |
511 | if (r.usec == std::numeric_limits<unsigned int>::max()) | |
512 | continue; | |
513 | ||
514 | ++size; | |
515 | auto iter = histo.lower_bound(r.usec); | |
516 | if(iter != histo.end()) | |
517 | iter->second++; | |
518 | else | |
519 | histo.rbegin()++; | |
520 | totlat+=r.usec; | |
521 | } | |
522 | } | |
6bb38cd6 RG |
523 | } |
524 | ||
525 | if (size == 0) { | |
526 | g_outputBuffer = "No traffic yet.\n"; | |
527 | return; | |
528 | } | |
529 | ||
530 | g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str(); | |
531 | double highest=0; | |
532 | ||
533 | for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { | |
534 | highest=std::max(highest, iter->second*1.0); | |
535 | } | |
536 | boost::format fmt("%7.2f\t%s\n"); | |
537 | g_outputBuffer += (fmt % "msec" % "").str(); | |
538 | ||
539 | for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { | |
540 | int stars = (70.0 * iter->second/highest); | |
541 | char c='*'; | |
542 | if(!stars && iter->second) { | |
543 | stars=1; // you get 1 . to show something is there.. | |
544 | if(70.0*iter->second/highest > 0.5) | |
545 | c=':'; | |
546 | else | |
547 | c='.'; | |
548 | } | |
549 | g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str(); | |
550 | } | |
551 | }); | |
552 | ||
553 | g_lua.writeFunction("showTCPStats", [] { | |
554 | setLuaNoSideEffect(); | |
781fd8fd | 555 | ostringstream ret; |
6bb38cd6 | 556 | boost::format fmt("%-10d %-10d %-10d %-10d\n"); |
781fd8fd RG |
557 | ret << (fmt % "Clients" % "MaxClients" % "Queued" % "MaxQueued") << endl; |
558 | ret << (fmt % g_tcpclientthreads->getThreadsCount() % g_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections) << endl; | |
559 | ret <<endl; | |
560 | ||
561 | ret << "Query distribution mode is: " << std::string(g_useTCPSinglePipe ? "single queue" : "per-thread queues") << endl; | |
562 | ret << endl; | |
563 | ||
564 | ret << "Frontends:" << endl; | |
cff9aa03 RG |
565 | fmt = boost::format("%-3d %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f"); |
566 | ret << (fmt % "#" % "Address" % "Connnections" % "Died reading query" % "Died sending response" % "Gave up" % "Client timeouts" % "Downstream timeouts" % "Avg queries/conn" % "Avg duration") << endl; | |
781fd8fd RG |
567 | |
568 | size_t counter = 0; | |
569 | for(const auto& f : g_frontends) { | |
cff9aa03 | 570 | ret << (fmt % counter % f->local.toStringWithPort() % f->tcpCurrentConnections % f->tcpDiedReadingQuery % f->tcpDiedSendingResponse % f->tcpGaveUp % f->tcpClientTimeouts % f->tcpDownstreamTimeouts % f->tcpAvgQueriesPerConnection % f->tcpAvgConnectionDuration) << endl; |
781fd8fd RG |
571 | ++counter; |
572 | } | |
573 | ret << endl; | |
574 | ||
575 | ret << "Backends:" << endl; | |
cff9aa03 RG |
576 | fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f"); |
577 | ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Avg queries/conn" % "Avg duration") << endl; | |
781fd8fd RG |
578 | |
579 | auto states = g_dstates.getLocal(); | |
580 | counter = 0; | |
581 | for(const auto& s : *states) { | |
cff9aa03 | 582 | ret << (fmt % counter % s->name % s->remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl; |
781fd8fd RG |
583 | ++counter; |
584 | } | |
585 | ||
586 | g_outputBuffer=ret.str(); | |
6bb38cd6 RG |
587 | }); |
588 | ||
589 | g_lua.writeFunction("dumpStats", [] { | |
590 | setLuaNoSideEffect(); | |
591 | vector<string> leftcolumn, rightcolumn; | |
592 | ||
593 | boost::format fmt("%-23s\t%+11s"); | |
594 | g_outputBuffer.clear(); | |
595 | auto entries = g_stats.entries; | |
596 | sort(entries.begin(), entries.end(), | |
597 | [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) { | |
598 | return a.first < b.first; | |
599 | }); | |
600 | boost::format flt(" %9.1f"); | |
601 | for(const auto& e : entries) { | |
602 | string second; | |
603 | if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second)) | |
604 | second=std::to_string((*val)->load()); | |
605 | else if (const auto& dval = boost::get<double*>(&e.second)) | |
606 | second=(flt % (**dval)).str(); | |
607 | else | |
608 | second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first)); | |
609 | ||
610 | if(leftcolumn.size() < g_stats.entries.size()/2) | |
611 | leftcolumn.push_back((fmt % e.first % second).str()); | |
612 | else | |
613 | rightcolumn.push_back((fmt % e.first % second).str()); | |
614 | } | |
615 | ||
616 | auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin(); | |
617 | boost::format clmn("%|0t|%1% %|39t|%2%\n"); | |
618 | ||
619 | for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) { | |
620 | string lentry, rentry; | |
621 | if(leftiter!= leftcolumn.end()) { | |
622 | lentry = *leftiter; | |
623 | leftiter++; | |
624 | } | |
625 | if(rightiter!= rightcolumn.end()) { | |
626 | rentry = *rightiter; | |
627 | rightiter++; | |
628 | } | |
629 | g_outputBuffer += (clmn % lentry % rentry).str(); | |
630 | } | |
631 | }); | |
632 | ||
633 | g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) { | |
634 | setLuaNoSideEffect(); | |
635 | return exceedRCode(rate, seconds, RCode::ServFail); | |
636 | }); | |
637 | g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) { | |
638 | setLuaNoSideEffect(); | |
639 | return exceedRCode(rate, seconds, RCode::NXDomain); | |
640 | }); | |
641 | ||
642 | g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) { | |
643 | setLuaNoSideEffect(); | |
644 | return exceedRespByterate(rate, seconds); | |
645 | }); | |
646 | ||
647 | g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) { | |
648 | setLuaNoSideEffect(); | |
649 | return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) { | |
650 | if(q.qtype==type) | |
651 | counts[q.requestor]++; | |
652 | }); | |
653 | }); | |
654 | ||
655 | g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) { | |
656 | setLuaNoSideEffect(); | |
657 | return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) { | |
658 | counts[q.requestor]++; | |
659 | }); | |
660 | }); | |
661 | ||
662 | g_lua.writeFunction("getRespRing", getRespRing); | |
663 | ||
664 | /* StatNode */ | |
665 | g_lua.registerFunction<StatNode, unsigned int()>("numChildren", | |
666 | [](StatNode& sn) -> unsigned int { | |
667 | return sn.children.size(); | |
668 | } ); | |
669 | g_lua.registerMember("fullname", &StatNode::fullname); | |
670 | g_lua.registerMember("labelsCount", &StatNode::labelsCount); | |
671 | g_lua.registerMember("servfails", &StatNode::Stat::servfails); | |
672 | g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains); | |
673 | g_lua.registerMember("queries", &StatNode::Stat::queries); | |
674 | ||
675 | g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) { | |
676 | statNodeRespRing(visitor, seconds ? *seconds : 0); | |
677 | }); | |
dc2fd93a RG |
678 | |
679 | /* DynBlockRulesGroup */ | |
680 | g_lua.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); }); | |
1d3ba133 | 681 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQueryRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) { |
dc2fd93a | 682 | if (group) { |
1d3ba133 | 683 | group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); |
dc2fd93a RG |
684 | } |
685 | }); | |
1d3ba133 | 686 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setResponseByteRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) { |
dc2fd93a | 687 | if (group) { |
1d3ba133 | 688 | group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); |
dc2fd93a RG |
689 | } |
690 | }); | |
23adffab RG |
691 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, DynBlockRulesGroup::smtVisitor_t visitor) { |
692 | if (group) { | |
693 | group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor); | |
694 | } | |
695 | }); | |
861ce85b RG |
696 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, dnsdist_ffi_stat_node_visitor_t visitor) { |
697 | if (group) { | |
698 | group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor); | |
699 | } | |
700 | }); | |
1d3ba133 | 701 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) { |
dc2fd93a | 702 | if (group) { |
1d3ba133 | 703 | group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); |
dc2fd93a RG |
704 | } |
705 | }); | |
1d3ba133 | 706 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQTypeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) { |
dc2fd93a | 707 | if (group) { |
1d3ba133 | 708 | group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); |
dc2fd93a RG |
709 | } |
710 | }); | |
b718792f RG |
711 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) { |
712 | if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) { | |
713 | for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) { | |
714 | group->excludeRange(Netmask(range.second)); | |
715 | } | |
716 | } | |
717 | else { | |
718 | group->excludeRange(Netmask(*boost::get<std::string>(&ranges))); | |
719 | } | |
720 | }); | |
721 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) { | |
722 | if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) { | |
723 | for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) { | |
724 | group->includeRange(Netmask(range.second)); | |
725 | } | |
726 | } | |
727 | else { | |
728 | group->includeRange(Netmask(*boost::get<std::string>(&ranges))); | |
729 | } | |
730 | }); | |
23adffab RG |
731 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeDomains", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> domains) { |
732 | if (domains.type() == typeid(std::vector<std::pair<int, std::string>>)) { | |
733 | for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&domains)) { | |
734 | group->excludeDomain(DNSName(range.second)); | |
735 | } | |
736 | } | |
737 | else { | |
738 | group->excludeDomain(DNSName(*boost::get<std::string>(&domains))); | |
739 | } | |
740 | }); | |
1d3ba133 RG |
741 | g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) { |
742 | group->apply(); | |
743 | }); | |
b718792f | 744 | g_lua.registerFunction("toString", &DynBlockRulesGroup::toString); |
6bb38cd6 | 745 | } |