]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsscope.cc
pkcs11signers: Use emplace_back for attributes
[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
75709ae8 26#if 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")
75709ae8 142#if 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")
149 ("servfail-tree", "Figure out subtrees that generate servfails")
150 ("stats-dir", po::value<string>()->default_value("."), "Directory where statistics will be saved")
be427910 151 ("write-failures,w", po::value<string>()->default_value(""), "if set, write weird packets to this PCAP file")
611a846e 152 ("verbose,v", "be verbose");
bb85386d 153
611a846e 154 hidden.add_options()
155 ("files", po::value<vector<string> >(), "files");
156
bb85386d 157 alloptions.add(desc).add(hidden);
611a846e 158
159 po::positional_options_description p;
160 p.add("files", -1);
161
162 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
163 po::notify(g_vm);
bb85386d 164
be427910 165 vector<string> files;
bb85386d
FM
166 if(g_vm.count("files"))
167 files = g_vm["files"].as<vector<string> >();
0670347d
PL
168
169 if(g_vm.count("version")) {
170 cerr<<"dnsscope "<<VERSION<<endl;
171 exit(0);
172 }
173
82b4a662 174 if(files.empty() || g_vm.count("help")) {
87aa8d9f 175 cerr<<"Syntax: dnsscope filename.pcap [filename2.pcap...]"<<endl;
611a846e 176 cout << desc << endl;
0670347d 177 exit(0);
1cf46d65 178 }
a640a9d4 179
7211d47b 180 DNSName filtername;
181 if(g_vm.count("filter-name"))
182 filtername = DNSName(g_vm["filter-name"].as<string>());
183 uint32_t nameMismatch = 0;
184
9ad511a7 185 StatNode root;
186
be427910 187 bool verbose = g_vm.count("verbose");
188
611a846e 189 bool haveRDFilter=0, rdFilter=0;
190 if(g_vm.count("rd")) {
191 rdFilter = g_vm["rd"].as<bool>();
192 haveRDFilter=1;
ad79b1b5 193 cout<<"Filtering on recursion desired="<<rdFilter<<endl;
611a846e 194 }
ad79b1b5 195 else
196 cout<<"Warning, looking at both RD and non-RD traffic!"<<endl;
611a846e 197
198 bool doIPv4 = g_vm["ipv4"].as<bool>();
199 bool doIPv6 = g_vm["ipv6"].as<bool>();
9ad511a7 200 bool doServFailTree = g_vm.count("servfail-tree");
ef890b79 201 bool noservfailstats = g_vm.count("no-servfail-stats");
202 int dnserrors=0, parsefail=0;
a640a9d4
BH
203 typedef map<uint32_t,uint32_t> cumul_t;
204 cumul_t cumul;
e075bae3 205 unsigned int untracked=0, errorresult=0, nonRDQueries=0, queries=0;
d2d8cafd 206 unsigned int ipv4DNSPackets=0, ipv6DNSPackets=0, fragmented=0, rdNonRAAnswers=0;
207 unsigned int answers=0, nonDNSIP=0, rdFilterMismatch=0;
168eb01c 208 unsigned int dnssecOK=0, edns=0;
209 unsigned int dnssecCD=0, dnssecAD=0;
ef890b79 210 unsigned int reuses=0;
a640a9d4
BH
211 typedef map<uint16_t,uint32_t> rcodes_t;
212 rcodes_t rcodes;
bb85386d 213
54007885 214 time_t lowestTime=0, highestTime=0;
ad79b1b5 215 time_t lastsec=0;
216 LiveCounts lastcounts;
ef890b79 217 std::unordered_set<ComboAddress, ComboAddress::addressOnlyHash> requestors, recipients, rdnonra;
ad79b1b5 218 typedef vector<pair<time_t, LiveCounts> > pcounts_t;
219 pcounts_t pcounts;
168eb01c 220 OPTRecordContent::report();
ef890b79 221
82b4a662 222 for(unsigned int fno=0; fno < files.size(); ++fno) {
223 PcapPacketReader pr(files[fno]);
c2826d2e 224 std::unique_ptr<PcapPacketWriter> pw=nullptr;
82b4a662 225 if(!g_vm["write-failures"].as<string>().empty())
2bbc9eb0
RP
226 pw = std::make_unique<PcapPacketWriter>(g_vm["write-failures"].as<string>(), pr);
227
168eb01c 228 EDNSOpts edo;
82b4a662 229 while(pr.getUDPPacket()) {
230
231 if((ntohs(pr.d_udp->uh_dport)==5300 || ntohs(pr.d_udp->uh_sport)==5300 ||
232 ntohs(pr.d_udp->uh_dport)==53 || ntohs(pr.d_udp->uh_sport)==53) &&
233 pr.d_len > 12) {
234 try {
235 if((pr.d_ip->ip_v == 4 && !doIPv4) || (pr.d_ip->ip_v == 6 && !doIPv6))
236 continue;
237 if(pr.d_ip->ip_v == 4) {
238 uint16_t frag = ntohs(pr.d_ip->ip_off);
239 if((frag & IP_MF) || (frag & IP_OFFMASK)) { // more fragments or IS a fragment
240 fragmented++;
241 continue;
242 }
243 }
ef890b79 244 uint16_t qtype;
245 DNSName qname((const char*)pr.d_payload, pr.d_len, 12, false, &qtype);
246 struct dnsheader header;
247 memcpy(&header, (struct dnsheader*)pr.d_payload, 12);
248
249 if(haveRDFilter && header.rd != rdFilter) {
82b4a662 250 rdFilterMismatch++;
be427910 251 continue;
252 }
bb85386d 253
7211d47b 254 if(!filtername.empty() && !qname.isPartOf(filtername)) {
255 nameMismatch++;
256 continue;
257 }
bb85386d 258
ef890b79 259 if(!header.qr) {
260 uint16_t udpsize, z;
261 if(getEDNSUDPPayloadSizeAndZ((const char*)pr.d_payload, pr.d_len, &udpsize, &z)) {
262 edns++;
263 if(z & EDNSOpts::DNSSECOK)
264 dnssecOK++;
265 if(header.cd)
266 dnssecCD++;
267 if(header.ad)
268 dnssecAD++;
269 }
270 }
168eb01c 271
bb85386d 272 if(pr.d_ip->ip_v == 4)
82b4a662 273 ++ipv4DNSPackets;
274 else
275 ++ipv6DNSPackets;
bb85386d 276
82b4a662 277 if(pr.d_pheader.ts.tv_sec != lastsec) {
278 LiveCounts lc;
279 if(lastsec) {
280 lc.questions = queries;
281 lc.answers = answers;
bb85386d 282 lc.outstanding = liveQuestions();
82b4a662 283
284 LiveCounts diff = lc - lastcounts;
e32a8d46
RP
285 pcounts.emplace_back(pr.d_pheader.ts.tv_sec, diff);
286 }
287 lastsec = pr.d_pheader.ts.tv_sec;
288 lastcounts = lc;
289 }
ad79b1b5 290
54007885
PD
291 if(lowestTime) { lowestTime = min((time_t)lowestTime, (time_t)pr.d_pheader.ts.tv_sec); }
292 else { lowestTime = pr.d_pheader.ts.tv_sec; }
82b4a662 293 highestTime=max((time_t)highestTime, (time_t)pr.d_pheader.ts.tv_sec);
4957a608 294
ef890b79 295 QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), header, qname, qtype);
4957a608 296
ef890b79 297 if(!header.qr) { // question
298 // cout<<"Query "<<qi<<endl;
299 if(!header.rd)
82b4a662 300 nonRDQueries++;
301 queries++;
fa7c37fe 302
82b4a662 303 ComboAddress rem = pr.getSource();
304 rem.sin4.sin_port=0;
bb85386d 305 requestors.insert(rem);
ce9309b7 306
ef890b79 307 QuestionData& qd=statmap[qi];
bb85386d 308
82b4a662 309 if(!qd.d_firstquestiontime.tv_sec)
310 qd.d_firstquestiontime=pr.d_pheader.ts;
ef890b79 311 else {
312 auto delta=makeFloat(pr.d_pheader.ts - qd.d_firstquestiontime);
313 // cout<<"Reuse of "<<qi<<", delta t="<<delta<<", count="<<qd.d_qcount<<endl;
314 if(delta > 2.0) {
315 // cout<<"Resetting old entry for "<<qi<<", too old"<<endl;
316 qd.d_qcount=0;
317 qd.d_answercount=0;
318 qd.d_firstquestiontime=pr.d_pheader.ts;
319 }
320 }
321 if(qd.d_qcount++)
322 reuses++;
ce9309b7 323 }
82b4a662 324 else { // answer
ef890b79 325 // cout<<"Response "<<qi<<endl;
326 rcodes[header.rcode]++;
82b4a662 327 answers++;
ef890b79 328 if(header.rd && !header.ra) {
82b4a662 329 rdNonRAAnswers++;
330 rdnonra.insert(pr.getDest());
331 }
bb85386d 332
ef890b79 333 if(header.ra) {
82b4a662 334 ComboAddress rem = pr.getDest();
335 rem.sin4.sin_port=0;
bb85386d 336 recipients.insert(rem);
82b4a662 337 }
338
339 QuestionData& qd=statmap[qi];
ef890b79 340 if(!qd.d_qcount) {
341 // cout<<"Untracked answer: "<<qi<<endl;
82b4a662 342 untracked++;
ef890b79 343 }
82b4a662 344
345 qd.d_answercount++;
346
347 if(qd.d_qcount) {
bb85386d 348 uint32_t usecs= (pr.d_pheader.ts.tv_sec - qd.d_firstquestiontime.tv_sec) * 1000000 +
82b4a662 349 (pr.d_pheader.ts.tv_usec - qd.d_firstquestiontime.tv_usec) ;
ef890b79 350
351 // cout<<"Usecs for "<<qi<<": "<<usecs<<endl;
352 if(!noservfailstats || header.rcode != 2)
353 cumul[usecs]++;
bb85386d
FM
354
355 if(header.rcode != 0 && header.rcode!=3)
82b4a662 356 errorresult++;
357 ComboAddress rem = pr.getDest();
358 rem.sin4.sin_port=0;
9ad511a7 359
82b4a662 360 if(doServFailTree)
6ecfaee2 361 root.submit(qname, header.rcode, pr.d_len, false, rem);
82b4a662 362 }
4957a608 363
ef890b79 364 if(!qd.d_qcount || qd.d_qcount == qd.d_answercount) {
365 // cout<<"Clearing state for "<<qi<<endl<<endl;
82b4a662 366 statmap.erase(qi);
ef890b79 367 }
eace2c24
RG
368 else {
369 // cout<<"State for qi remains open, qcount="<<qd.d_qcount<<", answercount="<<qd.d_answercount<<endl;
370 }
82b4a662 371 }
82b4a662 372 }
373 catch(std::exception& e) {
374 if(verbose)
375 cout<<"error parsing packet: "<<e.what()<<endl;
376
377 if(pw)
378 pw->write();
ef890b79 379 parsefail++;
82b4a662 380 continue;
381 }
a640a9d4 382 }
82b4a662 383 else { // non-DNS ip
384 nonDNSIP++;
a640a9d4
BH
385 }
386 }
82b4a662 387 cout<<"PCAP contained "<<pr.d_correctpackets<<" correct packets, "<<pr.d_runts<<" runts, "<< pr.d_oversized<<" oversize, "<<pr.d_nonetheripudp<<" non-UDP.\n";
388
a640a9d4 389 }
ef890b79 390
391 /*
392 cout<<"Open when done: "<<endl;
393 for(const auto& a : statmap) {
394 cout<<a.first<<": qcount="<<a.second.d_qcount<<", answercount="<<a.second.d_answercount<<endl;
395 }
396 */
bb85386d 397
a5d9353e 398 cout<<"Timespan: "<<(highestTime-lowestTime)/3600.0<<" hours"<<endl;
1aac6e17 399
ef890b79 400 cout<<nonDNSIP<<" non-DNS UDP, "<<dnserrors<<" dns decoding errors, "<<parsefail<<" packets failed to parse"<<endl;
ad79b1b5 401 cout<<"Ignored fragment packets: "<<fragmented<<endl;
d2d8cafd 402 cout<<"Dropped DNS packets based on recursion-desired filter: "<<rdFilterMismatch<<endl;
7211d47b 403 if(!filtername.empty())
404 cout <<"Dropped DNS packets because not part of '"<<filtername<<"': "<<nameMismatch << endl;
d2d8cafd 405 cout<<"DNS IPv4: "<<ipv4DNSPackets<<" packets, IPv6: "<<ipv6DNSPackets<<" packets"<<endl;
ce9309b7 406 cout<<"Questions: "<<queries<<", answers: "<<answers<<endl;
ef890b79 407 cout<<"Reuses of same state entry: "<<reuses<<endl;
a640a9d4 408 unsigned int unanswered=0;
ad79b1b5 409
410
411 // ofstream openf("openf");
a640a9d4 412 for(statmap_t::const_iterator i=statmap.begin(); i!=statmap.end(); ++i) {
7aba88c0 413 if(!i->second.d_answercount) {
a640a9d4 414 unanswered++;
7aba88c0 415 }
ad79b1b5 416 //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
417 }
418
a5d9353e 419 cout<< boost::format("%d (%.02f%% of all) queries did not request recursion") % nonRDQueries % ((nonRDQueries*100.0)/queries) << endl;
012de544 420 cout<< rdNonRAAnswers << " answers had recursion desired bit set, but recursion available=0 (for "<<rdnonra.size()<<" remotes)"<<endl;
a5d9353e 421 cout<<statmap.size()<<" queries went unanswered, of which "<< statmap.size()-unanswered<<" were answered on exact retransmit"<<endl;
422 cout<<untracked<<" responses could not be matched to questions"<<endl;
168eb01c 423 cout<<edns <<" questions requested EDNS processing, do=1: "<<dnssecOK<<", ad=1: "<<dnssecAD<<", cd=1: "<<dnssecCD<<endl;
ce9309b7 424
425 if(answers) {
426 cout<<(boost::format("%1% %|25t|%2%") % "Rcode" % "Count\n");
427 for(rcodes_t::const_iterator i=rcodes.begin(); i!=rcodes.end(); ++i)
428 cout<<(boost::format("%s %|25t|%d %|35t|(%.1f%%)") % RCode::to_s(i->first) % i->second % (i->second*100.0/answers))<<endl;
429 }
a640a9d4
BH
430
431 uint32_t sum=0;
1ffb2c7f 432 // ofstream stats("stats");
e075bae3 433 uint32_t totpairs=0;
a640a9d4
BH
434 double tottime=0;
435 for(cumul_t::const_iterator i=cumul.begin(); i!=cumul.end(); ++i) {
1ffb2c7f 436 // stats<<i->first<<"\t"<<(sum+=i->second)<<"\n";
e075bae3 437 totpairs+=i->second;
a640a9d4
BH
438 tottime+=i->first*i->second;
439 }
bb85386d 440
a640a9d4
BH
441 typedef map<uint32_t, bool> done_t;
442 done_t done;
ef890b79 443 for(auto a : {50, 100, 200, 300, 400, 800, 1000, 2000, 4000, 8000, 32000, 64000, 256000, 1024000, 2048000})
444 done[a]=false;
a640a9d4 445
843c7356 446 cout.setf(std::ios::fixed);
ef890b79 447 cout.precision(4);
a640a9d4 448 sum=0;
ef890b79 449
75709ae8 450#if HAVE_BOOST_GE_148
ef890b79 451 if(g_vm.count("log-histogram")) {
452 string fname = g_vm["stats-dir"].as<string>()+"/log-histogram";
453 ofstream loglog(fname);
454 if(!loglog)
455 throw runtime_error("Unable to write statistics to "+fname);
456
457 writeLogHistogramFile(cumul, loglog);
458 }
459
460 if(g_vm.count("full-histogram")) {
461 string fname=g_vm["stats-dir"].as<string>()+"/full-histogram";
462 ofstream loglog(fname);
463 if(!loglog)
464 throw runtime_error("Unable to write statistics to "+fname);
465 writeFullHistogramFile(cumul, g_vm["full-histogram"].as<double>(), loglog);
466 }
75709ae8 467#endif
ef890b79 468
bb85386d 469
ef890b79 470 sum=0;
a640a9d4 471 double lastperc=0, perc=0;
e075bae3 472 uint64_t lastsum=0;
a640a9d4 473
a9edfc57 474 for(cumul_t::const_iterator i=cumul.begin(); i!=cumul.end(); ++i) {
e075bae3 475 for(done_t::iterator j=done.begin(); j!=done.end(); ++j) {
1aac6e17 476 if(!j->second && i->first > j->first) {
4957a608
BH
477 j->second=true;
478
e075bae3 479 perc=sum*100.0/totpairs;
4957a608 480 if(j->first < 1024)
35f4f574 481 cout<< perc <<"% of questions answered within " << j->first << " us (";
4957a608 482 else
9273c94d 483 cout<< perc <<"% of questions answered within " << j->first/1000.0 << " ms (";
bb85386d 484
4957a608 485 cout<<perc-lastperc<<"%)\n";
e075bae3 486 lastperc=sum*100.0/totpairs;
487 lastsum=sum;
1aac6e17 488 }
e075bae3 489 }
a9edfc57 490 sum+=i->second;
491 }
492
493 for(auto j = done.begin(); j != done.end(); ++j) {
494 if(!j->second) {
495 perc=sum*100.0/totpairs;
496 if(j->first < 1024)
35f4f574 497 cout<< perc <<"% of questions answered within " << j->first << " us (";
a9edfc57 498 else
9273c94d 499 cout<< perc <<"% of questions answered within " << j->first/1000.0 << " ms (";
bb85386d 500
a9edfc57 501 cout<<perc-lastperc<<"%)\n";
502 lastperc=sum*100.0/totpairs;
503 lastsum=sum;
504 break;
505 }
a640a9d4 506 }
bb85386d 507
e075bae3 508 cout<< (totpairs-lastsum)<<" responses ("<<((totpairs-lastsum)*100.0/answers) <<"%) older than "<< (done.rbegin()->first/1000000.0) <<" seconds"<<endl;
509 if(totpairs)
35f4f574 510 cout<<"Average non-late response time: "<<tottime/totpairs<<" us"<<endl;
9a07e342 511
512 if(!g_vm["load-stats"].as<string>().empty()) {
513 ofstream load(g_vm["load-stats"].as<string>().c_str());
bb85386d 514 if(!load)
9a07e342 515 throw runtime_error("Error writing load statistics to "+g_vm["load-stats"].as<string>());
ef7cd021 516 for(pcounts_t::value_type& val : pcounts) {
bb85386d 517 load<<val.first<<'\t'<<val.second.questions<<'\t'<<val.second.answers<<'\t'<<val.second.outstanding<<'\n';
9a07e342 518 }
519 }
ce9309b7 520
521
522 cout<<"Saw questions from "<<requestors.size()<<" distinct remotes, answers to "<<recipients.size()<<endl;
523 ofstream remotes("remotes");
ef7cd021 524 for(const ComboAddress& rem : requestors) {
ce9309b7 525 remotes<<rem.toString()<<'\n';
526 }
527
528 vector<ComboAddress> diff;
6264512b 529 set_difference(requestors.begin(), requestors.end(), recipients.begin(), recipients.end(), back_inserter(diff), ComboAddress::addressOnlyLessThan());
012de544 530 cout<<"Saw "<<diff.size()<<" unique remotes asking questions, but not getting RA answers"<<endl;
bb85386d 531
ce9309b7 532 ofstream ignored("ignored");
ef7cd021 533 for(const ComboAddress& rem : diff) {
ce9309b7 534 ignored<<rem.toString()<<'\n';
535 }
536 ofstream rdnonrafs("rdnonra");
ef7cd021 537 for(const ComboAddress& rem : rdnonra) {
ce9309b7 538 rdnonrafs<<rem.toString()<<'\n';
539 }
9ad511a7 540
541 if(doServFailTree) {
542 StatNode::Stat node;
543 root.visit(visitor, node);
544 }
545
a640a9d4 546}
5172cb78 547catch(std::exception& e)
a640a9d4
BH
548{
549 cerr<<"Fatal: "<<e.what()<<endl;
550}