]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsscope.cc
dnsdist: Fix DNS over plain HTTP broken by `reloadAllCertificates()`
[thirdparty/pdns.git] / pdns / dnsscope.cc
CommitLineData
12471842
PL
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 */
da327a1b 22#define __FAVOR_BSD
870a0fe4
AT
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
2fd685b1 26#ifdef HAVE_BOOST_GE_148
ef890b79 27#include "histog.hh"
75709ae8 28#endif
ef890b79 29
a640a9d4
BH
30#include "statbag.hh"
31#include "dnspcap.hh"
32#include "dnsparser.hh"
68ca579c 33#include "dnsname.hh"
a48e03da 34#include <boost/format.hpp>
a640a9d4
BH
35#include <map>
36#include <set>
37#include <fstream>
da327a1b 38#include <algorithm>
9a450843 39#include "anadns.hh"
611a846e 40#include <boost/program_options.hpp>
ef890b79 41#include <unordered_set>
611a846e 42#include <boost/logic/tribool.hpp>
ce9309b7 43#include "arguments.hh"
10f4eea8 44#include "namespaces.hh"
168eb01c 45#include "dnsrecords.hh"
6264512b 46#include "statnode.hh"
a640a9d4 47
611a846e 48namespace po = boost::program_options;
49po::variables_map g_vm;
a640a9d4 50
ce9309b7 51ArgvMap& arg()
bb85386d 52{
ce9309b7 53 static ArgvMap theArg;
54 return theArg;
55}
611a846e 56StatBag S;
a640a9d4 57
9ad511a7 58
59
a640a9d4
BH
60struct QuestionData
61{
62 QuestionData() : d_qcount(0), d_answercount(0)
63 {
64 d_firstquestiontime.tv_sec=0;
65 }
66
67 int d_qcount;
68 int d_answercount;
69
f79a7a88 70 struct pdns_timeval d_firstquestiontime;
a640a9d4
BH
71};
72
9a450843 73typedef map<QuestionIdentifier, QuestionData> statmap_t;
a640a9d4
BH
74statmap_t statmap;
75
050e6877 76static unsigned int liveQuestions()
ad79b1b5 77{
78 unsigned int ret=0;
ef7cd021 79 for(statmap_t::value_type& val : statmap) {
ad79b1b5 80 if(!val.second.d_answercount)
81 ret++;
82 // if(val.second.d_qcount > val.second.d_answercount)
83 // ret+= val.second.d_qcount - val.second.d_answercount;
84 }
85 return ret;
86}
87
88struct LiveCounts
89{
90 unsigned int questions;
91 unsigned int answers;
92 unsigned int outstanding;
93
94 LiveCounts()
95 {
96 questions=answers=outstanding=0;
97 }
98
99 LiveCounts operator-(const LiveCounts& rhs)
100 {
101 LiveCounts ret;
102 ret.questions = questions - rhs.questions;
103 ret.answers = answers - rhs.answers;
104 ret.outstanding = outstanding;
105 return ret;
106 }
107};
108
d73de874 109static void visitor(const StatNode* node, const StatNode::Stat& /* selfstat */, const StatNode::Stat& childstat)
6264512b 110{
111 // 20% servfails, >100 children, on average less than 2 copies of a query
112 // >100 different subqueries
113 double dups=1.0*childstat.queries/node->children.size();
114 if(dups > 2.0)
115 return;
116 if(1.0*childstat.servfails / childstat.queries > 0.2 && node->children.size()>100) {
117 cout<<node->fullname<<", servfails: "<<childstat.servfails<<", nxdomains: "<<childstat.nxdomains<<", remotes: "<<childstat.remotes.size()<<", children: "<<node->children.size()<<", childstat.queries: "<<childstat.queries;
118 cout<<", dups2: "<<dups<<endl;
119 for(const StatNode::Stat::remotes_t::value_type& rem : childstat.remotes) {
120 cout<<"source: "<<node->fullname<<"\t"<<rem.first.toString()<<"\t"<<rem.second<<endl;
121 }
122 }
123}
ce9309b7 124
050e6877 125static const struct timeval operator-(const struct pdns_timeval& lhs, const struct pdns_timeval& rhs)
ef890b79 126{
54c38ce0 127 struct timeval a{lhs.tv_sec, static_cast<suseconds_t>(lhs.tv_usec)}, b{rhs.tv_sec, static_cast<suseconds_t>(rhs.tv_usec)};
ef890b79 128 return operator-(a,b);
129}
130
131
a640a9d4
BH
132int main(int argc, char** argv)
133try
134{
611a846e 135 po::options_description desc("Allowed options"), hidden, alloptions;
136 desc.add_options()
137 ("help,h", "produce help message")
0670347d 138 ("version", "print version number")
611a846e 139 ("rd", po::value<bool>(), "If set to true, only process RD packets, to false only non-RD, unset: both")
140 ("ipv4", po::value<bool>()->default_value(true), "Process IPv4 packets")
141 ("ipv6", po::value<bool>()->default_value(true), "Process IPv6 packets")
2fd685b1 142#ifdef HAVE_BOOST_GE_148
ef890b79 143 ("log-histogram", "Write a log-histogram to file 'log-histogram'")
144 ("full-histogram", po::value<double>(), "Write a log-histogram to file 'full-histogram' with this millisecond bin size")
75709ae8 145#endif
7211d47b 146 ("filter-name,f", po::value<string>(), "Do statistics only for queries within this domain")
9a07e342 147 ("load-stats,l", po::value<string>()->default_value(""), "if set, emit per-second load statistics (questions, answers, outstanding)")
ef890b79 148 ("no-servfail-stats", "Don't include servfails in response time stats")
e9ca1b28 149 ("port", po::value<uint16_t>()->default_value(0), "The source and destination port to consider. Default is looking at packets from and to ports 53 and 5300")
ef890b79 150 ("servfail-tree", "Figure out subtrees that generate servfails")
151 ("stats-dir", po::value<string>()->default_value("."), "Directory where statistics will be saved")
be427910 152 ("write-failures,w", po::value<string>()->default_value(""), "if set, write weird packets to this PCAP file")
611a846e 153 ("verbose,v", "be verbose");
bb85386d 154
611a846e 155 hidden.add_options()
156 ("files", po::value<vector<string> >(), "files");
157
bb85386d 158 alloptions.add(desc).add(hidden);
611a846e 159
160 po::positional_options_description p;
161 p.add("files", -1);
162
163 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
164 po::notify(g_vm);
bb85386d 165
be427910 166 vector<string> files;
bb85386d
FM
167 if(g_vm.count("files"))
168 files = g_vm["files"].as<vector<string> >();
0670347d
PL
169
170 if(g_vm.count("version")) {
171 cerr<<"dnsscope "<<VERSION<<endl;
172 exit(0);
173 }
174
82b4a662 175 if(files.empty() || g_vm.count("help")) {
87aa8d9f 176 cerr<<"Syntax: dnsscope filename.pcap [filename2.pcap...]"<<endl;
611a846e 177 cout << desc << endl;
0670347d 178 exit(0);
1cf46d65 179 }
a640a9d4 180
7211d47b 181 DNSName filtername;
182 if(g_vm.count("filter-name"))
183 filtername = DNSName(g_vm["filter-name"].as<string>());
184 uint32_t nameMismatch = 0;
185
9ad511a7 186 StatNode root;
187
be427910 188 bool verbose = g_vm.count("verbose");
189
611a846e 190 bool haveRDFilter=0, rdFilter=0;
191 if(g_vm.count("rd")) {
192 rdFilter = g_vm["rd"].as<bool>();
193 haveRDFilter=1;
ad79b1b5 194 cout<<"Filtering on recursion desired="<<rdFilter<<endl;
611a846e 195 }
ad79b1b5 196 else
197 cout<<"Warning, looking at both RD and non-RD traffic!"<<endl;
611a846e 198
199 bool doIPv4 = g_vm["ipv4"].as<bool>();
200 bool doIPv6 = g_vm["ipv6"].as<bool>();
9ad511a7 201 bool doServFailTree = g_vm.count("servfail-tree");
ef890b79 202 bool noservfailstats = g_vm.count("no-servfail-stats");
eb20f481
OM
203 int dnserrors = 0;
204 int parsefail = 0;
a640a9d4
BH
205 typedef map<uint32_t,uint32_t> cumul_t;
206 cumul_t cumul;
eb20f481
OM
207 unsigned int untracked = 0;
208 unsigned int nonRDQueries = 0;
209 unsigned int queries = 0;
210 unsigned int ipv4DNSPackets = 0;
211 unsigned int ipv6DNSPackets = 0;
212 unsigned int fragmented = 0;
213 unsigned int rdNonRAAnswers = 0;
214 unsigned int answers = 0;
215 unsigned int nonDNSIP = 0;
216 unsigned int rdFilterMismatch = 0;
217 unsigned int dnssecOK = 0;
218 unsigned int edns = 0;
219 unsigned int dnssecCD = 0;
220 unsigned int dnssecAD = 0;
221 unsigned int reuses = 0;
a640a9d4
BH
222 typedef map<uint16_t,uint32_t> rcodes_t;
223 rcodes_t rcodes;
bb85386d 224
54007885 225 time_t lowestTime=0, highestTime=0;
ad79b1b5 226 time_t lastsec=0;
227 LiveCounts lastcounts;
ef890b79 228 std::unordered_set<ComboAddress, ComboAddress::addressOnlyHash> requestors, recipients, rdnonra;
ad79b1b5 229 typedef vector<pair<time_t, LiveCounts> > pcounts_t;
230 pcounts_t pcounts;
e9ca1b28 231 const uint16_t port = g_vm["port"].as<uint16_t>();
168eb01c 232 OPTRecordContent::report();
ef890b79 233
82b4a662 234 for(unsigned int fno=0; fno < files.size(); ++fno) {
235 PcapPacketReader pr(files[fno]);
c2826d2e 236 std::unique_ptr<PcapPacketWriter> pw=nullptr;
82b4a662 237 if(!g_vm["write-failures"].as<string>().empty())
2bbc9eb0
RP
238 pw = std::make_unique<PcapPacketWriter>(g_vm["write-failures"].as<string>(), pr);
239
168eb01c 240 EDNSOpts edo;
82b4a662 241 while(pr.getUDPPacket()) {
242
e9ca1b28
RG
243 if (pr.d_len <= 12) {
244 // non-DNS ip
245 nonDNSIP++;
246 continue;
247 }
248 if (port > 0 &&
249 (ntohs(pr.d_udp->uh_dport) != port && ntohs(pr.d_udp->uh_sport) != port)) {
250 // non-DNS ip
251 nonDNSIP++;
252 continue;
253 }
254
255 if (port == 0 &&
256 (ntohs(pr.d_udp->uh_dport) != 5300 && ntohs(pr.d_udp->uh_sport) != 5300 &&
257 ntohs(pr.d_udp->uh_dport) != 53 && ntohs(pr.d_udp->uh_sport) != 53)) {
258 // non-DNS ip
259 nonDNSIP++;
260 continue;
261 }
262
263 try {
264 if ((pr.d_ip->ip_v == 4 && !doIPv4) || (pr.d_ip->ip_v == 6 && !doIPv6)) {
265 continue;
266 }
267
268 if (pr.d_ip->ip_v == 4) {
269 uint16_t frag = ntohs(pr.d_ip->ip_off);
270 if((frag & IP_MF) || (frag & IP_OFFMASK)) { // more fragments or IS a fragment
271 fragmented++;
7211d47b 272 continue;
273 }
e9ca1b28
RG
274 }
275 uint16_t qtype;
276 DNSName qname((const char*)pr.d_payload, pr.d_len, 12, false, &qtype);
277 struct dnsheader header;
278 memcpy(&header, (struct dnsheader*)pr.d_payload, 12);
279
280 if(haveRDFilter && header.rd != rdFilter) {
281 rdFilterMismatch++;
282 continue;
283 }
284
285 if(!filtername.empty() && !qname.isPartOf(filtername)) {
286 nameMismatch++;
287 continue;
288 }
289
290 if(!header.qr) {
291 uint16_t udpsize, z;
292 if(getEDNSUDPPayloadSizeAndZ((const char*)pr.d_payload, pr.d_len, &udpsize, &z)) {
293 edns++;
294 if(z & EDNSOpts::DNSSECOK)
295 dnssecOK++;
296 if(header.cd)
297 dnssecCD++;
298 if(header.ad)
299 dnssecAD++;
300 }
301 }
302
303 if(pr.d_ip->ip_v == 4)
304 ++ipv4DNSPackets;
305 else
306 ++ipv6DNSPackets;
307
308 if(pr.d_pheader.ts.tv_sec != lastsec) {
309 LiveCounts lc;
310 if(lastsec) {
311 lc.questions = queries;
312 lc.answers = answers;
313 lc.outstanding = liveQuestions();
314
315 LiveCounts diff = lc - lastcounts;
316 pcounts.emplace_back(pr.d_pheader.ts.tv_sec, diff);
317 }
318 lastsec = pr.d_pheader.ts.tv_sec;
319 lastcounts = lc;
320 }
321
322 if(lowestTime) { lowestTime = min((time_t)lowestTime, (time_t)pr.d_pheader.ts.tv_sec); }
323 else { lowestTime = pr.d_pheader.ts.tv_sec; }
324 highestTime=max((time_t)highestTime, (time_t)pr.d_pheader.ts.tv_sec);
325
326 QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), header, qname, qtype);
327
328 if(!header.qr) { // question
329 // cout<<"Query "<<qi<<endl;
330 if(!header.rd)
331 nonRDQueries++;
332 queries++;
333
334 ComboAddress rem = pr.getSource();
335 rem.sin4.sin_port=0;
336 requestors.insert(rem);
337
338 QuestionData& qd=statmap[qi];
339
340 if(!qd.d_firstquestiontime.tv_sec)
341 qd.d_firstquestiontime=pr.d_pheader.ts;
342 else {
343 auto delta=makeFloat(pr.d_pheader.ts - qd.d_firstquestiontime);
344 // cout<<"Reuse of "<<qi<<", delta t="<<delta<<", count="<<qd.d_qcount<<endl;
345 if(delta > 2.0) {
346 // cout<<"Resetting old entry for "<<qi<<", too old"<<endl;
347 qd.d_qcount=0;
348 qd.d_answercount=0;
349 qd.d_firstquestiontime=pr.d_pheader.ts;
ef890b79 350 }
351 }
e9ca1b28
RG
352 if(qd.d_qcount++)
353 reuses++;
354 }
355 else { // answer
356 // cout<<"Response "<<qi<<endl;
357 rcodes[header.rcode]++;
358 answers++;
359 if(header.rd && !header.ra) {
360 rdNonRAAnswers++;
361 rdnonra.insert(pr.getDest());
362 }
363
364 if(header.ra) {
365 ComboAddress rem = pr.getDest();
366 rem.sin4.sin_port=0;
367 recipients.insert(rem);
368 }
168eb01c 369
e9ca1b28
RG
370 QuestionData& qd=statmap[qi];
371 if(!qd.d_qcount) {
372 // cout<<"Untracked answer: "<<qi<<endl;
373 untracked++;
374 }
bb85386d 375
e9ca1b28 376 qd.d_answercount++;
82b4a662 377
e9ca1b28
RG
378 if(qd.d_qcount) {
379 uint32_t usecs= (pr.d_pheader.ts.tv_sec - qd.d_firstquestiontime.tv_sec) * 1000000 +
380 (pr.d_pheader.ts.tv_usec - qd.d_firstquestiontime.tv_usec) ;
381
382 // cout<<"Usecs for "<<qi<<": "<<usecs<<endl;
383 if(!noservfailstats || header.rcode != 2)
384 cumul[usecs]++;
385
e9ca1b28
RG
386 ComboAddress rem = pr.getDest();
387 rem.sin4.sin_port=0;
388
389 if(doServFailTree)
390 root.submit(qname, header.rcode, pr.d_len, false, rem);
e32a8d46 391 }
ad79b1b5 392
e9ca1b28
RG
393 if(!qd.d_qcount || qd.d_qcount == qd.d_answercount) {
394 // cout<<"Clearing state for "<<qi<<endl<<endl;
395 statmap.erase(qi);
396 }
397 else {
398 // cout<<"State for qi remains open, qcount="<<qd.d_qcount<<", answercount="<<qd.d_answercount<<endl;
399 }
400 }
a640a9d4 401 }
e9ca1b28
RG
402 catch(std::exception& e) {
403 if(verbose)
404 cout<<"error parsing packet: "<<e.what()<<endl;
405
406 if(pw)
407 pw->write();
408 parsefail++;
409 continue;
a640a9d4
BH
410 }
411 }
82b4a662 412
e9ca1b28 413 cout<<"PCAP contained "<<pr.d_correctpackets<<" correct packets, "<<pr.d_runts<<" runts, "<< pr.d_oversized<<" oversize, "<<pr.d_nonetheripudp<<" non-UDP.\n";
a640a9d4 414 }
ef890b79 415
416 /*
417 cout<<"Open when done: "<<endl;
418 for(const auto& a : statmap) {
419 cout<<a.first<<": qcount="<<a.second.d_qcount<<", answercount="<<a.second.d_answercount<<endl;
420 }
421 */
bb85386d 422
a5d9353e 423 cout<<"Timespan: "<<(highestTime-lowestTime)/3600.0<<" hours"<<endl;
1aac6e17 424
ef890b79 425 cout<<nonDNSIP<<" non-DNS UDP, "<<dnserrors<<" dns decoding errors, "<<parsefail<<" packets failed to parse"<<endl;
ad79b1b5 426 cout<<"Ignored fragment packets: "<<fragmented<<endl;
d2d8cafd 427 cout<<"Dropped DNS packets based on recursion-desired filter: "<<rdFilterMismatch<<endl;
7211d47b 428 if(!filtername.empty())
429 cout <<"Dropped DNS packets because not part of '"<<filtername<<"': "<<nameMismatch << endl;
d2d8cafd 430 cout<<"DNS IPv4: "<<ipv4DNSPackets<<" packets, IPv6: "<<ipv6DNSPackets<<" packets"<<endl;
ce9309b7 431 cout<<"Questions: "<<queries<<", answers: "<<answers<<endl;
ef890b79 432 cout<<"Reuses of same state entry: "<<reuses<<endl;
a640a9d4 433 unsigned int unanswered=0;
ad79b1b5 434
435
436 // ofstream openf("openf");
a640a9d4 437 for(statmap_t::const_iterator i=statmap.begin(); i!=statmap.end(); ++i) {
7aba88c0 438 if(!i->second.d_answercount) {
a640a9d4 439 unanswered++;
7aba88c0 440 }
ad79b1b5 441 //openf<< i->first.d_source.toStringWithPort()<<' ' <<i->first.d_dest.toStringWithPort()<<' '<<i->first.d_id<<' '<<i->first.d_qname <<" " <<i->first.d_qtype<< " "<<i->second.d_qcount <<" " <<i->second.d_answercount<<endl;
a640a9d4
BH
442 }
443
a5d9353e 444 cout<< boost::format("%d (%.02f%% of all) queries did not request recursion") % nonRDQueries % ((nonRDQueries*100.0)/queries) << endl;
012de544 445 cout<< rdNonRAAnswers << " answers had recursion desired bit set, but recursion available=0 (for "<<rdnonra.size()<<" remotes)"<<endl;
a5d9353e 446 cout<<statmap.size()<<" queries went unanswered, of which "<< statmap.size()-unanswered<<" were answered on exact retransmit"<<endl;
447 cout<<untracked<<" responses could not be matched to questions"<<endl;
168eb01c 448 cout<<edns <<" questions requested EDNS processing, do=1: "<<dnssecOK<<", ad=1: "<<dnssecAD<<", cd=1: "<<dnssecCD<<endl;
ce9309b7 449
450 if(answers) {
451 cout<<(boost::format("%1% %|25t|%2%") % "Rcode" % "Count\n");
452 for(rcodes_t::const_iterator i=rcodes.begin(); i!=rcodes.end(); ++i)
453 cout<<(boost::format("%s %|25t|%d %|35t|(%.1f%%)") % RCode::to_s(i->first) % i->second % (i->second*100.0/answers))<<endl;
454 }
a640a9d4
BH
455
456 uint32_t sum=0;
1ffb2c7f 457 // ofstream stats("stats");
e075bae3 458 uint32_t totpairs=0;
a640a9d4
BH
459 double tottime=0;
460 for(cumul_t::const_iterator i=cumul.begin(); i!=cumul.end(); ++i) {
1ffb2c7f 461 // stats<<i->first<<"\t"<<(sum+=i->second)<<"\n";
e075bae3 462 totpairs+=i->second;
a640a9d4
BH
463 tottime+=i->first*i->second;
464 }
bb85386d 465
a640a9d4
BH
466 typedef map<uint32_t, bool> done_t;
467 done_t done;
ef890b79 468 for(auto a : {50, 100, 200, 300, 400, 800, 1000, 2000, 4000, 8000, 32000, 64000, 256000, 1024000, 2048000})
469 done[a]=false;
a640a9d4 470
843c7356 471 cout.setf(std::ios::fixed);
ef890b79 472 cout.precision(4);
a640a9d4 473 sum=0;
ef890b79 474
2fd685b1 475#ifdef HAVE_BOOST_GE_148
ef890b79 476 if(g_vm.count("log-histogram")) {
477 string fname = g_vm["stats-dir"].as<string>()+"/log-histogram";
478 ofstream loglog(fname);
479 if(!loglog)
480 throw runtime_error("Unable to write statistics to "+fname);
481
482 writeLogHistogramFile(cumul, loglog);
483 }
484
485 if(g_vm.count("full-histogram")) {
486 string fname=g_vm["stats-dir"].as<string>()+"/full-histogram";
487 ofstream loglog(fname);
488 if(!loglog)
489 throw runtime_error("Unable to write statistics to "+fname);
490 writeFullHistogramFile(cumul, g_vm["full-histogram"].as<double>(), loglog);
491 }
75709ae8 492#endif
ef890b79 493
bb85386d 494
ef890b79 495 sum=0;
a640a9d4 496 double lastperc=0, perc=0;
e075bae3 497 uint64_t lastsum=0;
a640a9d4 498
a9edfc57 499 for(cumul_t::const_iterator i=cumul.begin(); i!=cumul.end(); ++i) {
e075bae3 500 for(done_t::iterator j=done.begin(); j!=done.end(); ++j) {
1aac6e17 501 if(!j->second && i->first > j->first) {
4957a608
BH
502 j->second=true;
503
e075bae3 504 perc=sum*100.0/totpairs;
4957a608 505 if(j->first < 1024)
35f4f574 506 cout<< perc <<"% of questions answered within " << j->first << " us (";
4957a608 507 else
9273c94d 508 cout<< perc <<"% of questions answered within " << j->first/1000.0 << " ms (";
bb85386d 509
4957a608 510 cout<<perc-lastperc<<"%)\n";
e075bae3 511 lastperc=sum*100.0/totpairs;
512 lastsum=sum;
1aac6e17 513 }
e075bae3 514 }
a9edfc57 515 sum+=i->second;
516 }
517
518 for(auto j = done.begin(); j != done.end(); ++j) {
519 if(!j->second) {
520 perc=sum*100.0/totpairs;
521 if(j->first < 1024)
35f4f574 522 cout<< perc <<"% of questions answered within " << j->first << " us (";
a9edfc57 523 else
9273c94d 524 cout<< perc <<"% of questions answered within " << j->first/1000.0 << " ms (";
bb85386d 525
a9edfc57 526 cout<<perc-lastperc<<"%)\n";
527 lastperc=sum*100.0/totpairs;
528 lastsum=sum;
529 break;
530 }
a640a9d4 531 }
bb85386d 532
e075bae3 533 cout<< (totpairs-lastsum)<<" responses ("<<((totpairs-lastsum)*100.0/answers) <<"%) older than "<< (done.rbegin()->first/1000000.0) <<" seconds"<<endl;
534 if(totpairs)
35f4f574 535 cout<<"Average non-late response time: "<<tottime/totpairs<<" us"<<endl;
9a07e342 536
537 if(!g_vm["load-stats"].as<string>().empty()) {
538 ofstream load(g_vm["load-stats"].as<string>().c_str());
bb85386d 539 if(!load)
9a07e342 540 throw runtime_error("Error writing load statistics to "+g_vm["load-stats"].as<string>());
ef7cd021 541 for(pcounts_t::value_type& val : pcounts) {
bb85386d 542 load<<val.first<<'\t'<<val.second.questions<<'\t'<<val.second.answers<<'\t'<<val.second.outstanding<<'\n';
9a07e342 543 }
544 }
ce9309b7 545
546
547 cout<<"Saw questions from "<<requestors.size()<<" distinct remotes, answers to "<<recipients.size()<<endl;
548 ofstream remotes("remotes");
ef7cd021 549 for(const ComboAddress& rem : requestors) {
ce9309b7 550 remotes<<rem.toString()<<'\n';
551 }
552
553 vector<ComboAddress> diff;
6264512b 554 set_difference(requestors.begin(), requestors.end(), recipients.begin(), recipients.end(), back_inserter(diff), ComboAddress::addressOnlyLessThan());
012de544 555 cout<<"Saw "<<diff.size()<<" unique remotes asking questions, but not getting RA answers"<<endl;
bb85386d 556
ce9309b7 557 ofstream ignored("ignored");
ef7cd021 558 for(const ComboAddress& rem : diff) {
ce9309b7 559 ignored<<rem.toString()<<'\n';
560 }
561 ofstream rdnonrafs("rdnonra");
ef7cd021 562 for(const ComboAddress& rem : rdnonra) {
ce9309b7 563 rdnonrafs<<rem.toString()<<'\n';
564 }
9ad511a7 565
566 if(doServFailTree) {
567 StatNode::Stat node;
568 root.visit(visitor, node);
569 }
570
a640a9d4 571}
5172cb78 572catch(std::exception& e)
a640a9d4
BH
573{
574 cerr<<"Fatal: "<<e.what()<<endl;
575}