2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
23 Replay all recursion-desired DNS questions to a specified IP address.
25 Track all outgoing questions, remap id to one of ours.
26 Also track all recorded answers, and map them to that same id, the 'expectation'.
28 When we see a question, parse it, give it a QuestionIdentifier, and and an id from the free-id list.
30 When we see an answer in the tcpdump, parse it, make QI, and add it to the original QI
33 When we see an answer from the socket, use the id to match it up to the original QI
36 There is one central object, which has (when complete)
42 What to do with timeouts. We keep around at most 65536 outstanding answers.
52 } while(time_of_last_packet_sent < mental_clock)
53 mental_clock=time_of_last_packet_sent;
55 wait for a response packet for 0.1 seconds
56 note how much time has passed
57 mental_clock+=time_passed;
70 #include <boost/program_options.hpp>
71 #include "dnsrecords.hh"
72 #include "ednssubnet.hh"
73 #include "ednsoptions.hh"
78 #include <boost/format.hpp>
79 #include <boost/utility.hpp>
80 #include <boost/multi_index_container.hpp>
81 #include <boost/multi_index/ordered_index.hpp>
82 #include <boost/multi_index/key_extractors.hpp>
84 #include "namespaces.hh"
85 using namespace ::boost::multi_index
;
86 #include "namespaces.hh"
92 namespace po
= boost::program_options
;
94 po::variables_map g_vm
;
96 static const struct timeval
operator*(float fact
, const struct timeval
& rhs
)
98 // cout<<"In: "<<rhs.tv_sec<<" + "<<rhs.tv_usec<<"\n";
100 if( (2000000000 / fact
) < rhs
.tv_usec
) {
101 double d
=1.0 * rhs
.tv_usec
* fact
;
102 ret
.tv_sec
=fact
* rhs
.tv_sec
;
103 ret
.tv_sec
+=(int) (d
/1000000);
107 ret
.tv_usec
=(unsigned int)(1000000*d
);
110 cout
<<"out complex: "<<ret
.tv_sec
<<" + "<<ret
.tv_usec
<<"\n";
115 ret
.tv_sec
=rhs
.tv_sec
* fact
;
116 ret
.tv_usec
=rhs
.tv_usec
* fact
;
119 // cout<<"out simple: "<<ret.tv_sec<<" + "<<ret.tv_usec<<"\n";
124 static void pleaseQuitHandler(int)
129 class DNSIDManager
: public boost::noncopyable
134 for(unsigned int i
=0; i
< 65536; ++i
)
135 d_available
.push_back(i
);
141 if(!d_available
.empty()) {
142 ret
=d_available
.front();
146 throw runtime_error("out of ids!"); // XXX FIXME
151 uint16_t ret
=peakID();
152 d_available
.pop_front();
156 void releaseID(uint16_t id
)
158 d_available
.push_back(id
);
162 std::deque
<uint16_t> d_available
;
166 struct AssignedIDTag
{};
167 struct QuestionTag
{};
171 QuestionData() : d_assignedID(-1), d_origRcode(-1), d_newRcode(-1), d_norecursionavailable(false), d_origlate(false), d_newlate(false)
174 QuestionIdentifier d_qi
;
176 MOADNSParser::answers_t d_origAnswers
, d_newAnswers
;
177 int d_origRcode
, d_newRcode
;
178 struct timeval d_resentTime
{};
179 bool d_norecursionavailable
;
180 bool d_origlate
, d_newlate
;
183 typedef multi_index_container
<
186 ordered_unique
<tag
<QuestionTag
>, BOOST_MULTI_INDEX_MEMBER(QuestionData
, QuestionIdentifier
, d_qi
) > ,
187 ordered_unique
<tag
<AssignedIDTag
>, BOOST_MULTI_INDEX_MEMBER(QuestionData
, int, d_assignedID
) >
194 unsigned int s_questions
, s_origanswers
, s_weanswers
, s_wetimedout
, s_perfect
, s_mostly
, s_origtimedout
;
195 unsigned int s_wenever
, s_orignever
;
196 unsigned int s_webetter
, s_origbetter
, s_norecursionavailable
;
197 unsigned int s_weunmatched
, s_origunmatched
;
198 unsigned int s_wednserrors
, s_origdnserrors
, s_duplicates
;
202 static void WeOrigSlowQueriesDelta(int& weOutstanding
, int& origOutstanding
, int& weSlow
, int& origSlow
)
205 gettimeofday(&now
, 0);
207 weOutstanding
=origOutstanding
=weSlow
=origSlow
=0;
209 for(qids_t::iterator i
=qids
.begin(); i
!=qids
.end(); ++i
) {
210 double dt
=DiffTime(i
->d_resentTime
, now
);
212 if(i
->d_newRcode
== -1)
214 if(i
->d_origRcode
== -1)
218 if(i
->d_newRcode
== -1) {
228 if(i
->d_origRcode
== -1) {
242 static void compactAnswerSet(MOADNSParser::answers_t orig
, set
<DNSRecord
>& compacted
)
244 for(MOADNSParser::answers_t::const_iterator i
=orig
.begin(); i
!= orig
.end(); ++i
)
245 if(i
->first
.d_place
==DNSResourceRecord::ANSWER
)
246 compacted
.insert(i
->first
);
249 static bool isRcodeOk(int rcode
)
251 return rcode
==0 || rcode
==3;
254 set
<pair
<DNSName
,uint16_t> > s_origbetterset
;
256 static bool isRootReferral(const MOADNSParser::answers_t
& answers
)
262 for(MOADNSParser::answers_t::const_iterator iter
= answers
.begin(); iter
!= answers
.end(); ++iter
) {
263 // cerr<<(int)iter->first.d_place<<", "<<iter->first.d_name<<" "<<iter->first.d_type<<", # "<<answers.size()<<endl;
264 if(iter
->first
.d_place
!=2)
266 if(!iter
->first
.d_name
.isRoot() || iter
->first
.d_type
!=QType::NS
)
272 vector
<uint32_t> flightTimes
;
273 static void accountFlightTime(qids_t::const_iterator iter
)
275 if(flightTimes
.empty())
276 flightTimes
.resize(2050);
279 gettimeofday(&now
, 0);
280 unsigned int mdiff
= 1000*DiffTime(iter
->d_resentTime
, now
);
281 if(mdiff
> flightTimes
.size()-2)
282 mdiff
= flightTimes
.size()-1;
284 flightTimes
[mdiff
]++;
287 static uint64_t countLessThan(unsigned int msec
)
290 for(unsigned int i
= 0 ; i
< msec
&& i
< flightTimes
.size() ; ++i
) {
291 ret
+= flightTimes
[i
];
296 static void emitFlightTimes()
298 uint64_t totals
= countLessThan(flightTimes
.size());
300 // Avoid division by zero below
303 unsigned int limits
[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes
.size()};
305 cout
.setf(std::ios::fixed
);
307 for(unsigned int i
=0 ; i
< sizeof(limits
)/sizeof(limits
[0]); ++i
) {
308 if(limits
[i
]!=flightTimes
.size())
309 cout
<<"Within "<<limits
[i
]<<" ms: ";
311 cout
<<"Beyond "<<limits
[i
]-2<<" ms: ";
312 uint64_t here
= countLessThan(limits
[i
]);
313 cout
<<100.0*here
/totals
<<"% ("<<100.0*(here
-sofar
)/totals
<<"%)"<<endl
;
319 static void measureResultAndClean(qids_t::const_iterator iter
)
321 const QuestionData
& qd
=*iter
;
322 accountFlightTime(iter
);
324 set
<DNSRecord
> canonicOrig
, canonicNew
;
325 compactAnswerSet(qd
.d_origAnswers
, canonicOrig
);
326 compactAnswerSet(qd
.d_newAnswers
, canonicNew
);
329 cout
<<qd
.d_qi
<<", orig rcode: "<<qd
.d_origRcode
<<", ours: "<<qd
.d_newRcode
;
330 cout
<<", "<<canonicOrig
.size()<< " vs " << canonicNew
.size()<<", perfect: ";
333 if(canonicOrig
==canonicNew
) {
342 if(qd
.d_norecursionavailable
)
344 cout
<<"\t* original nameserver did not provide recursion for this question *"<<endl
;
345 if(qd
.d_origRcode
== qd
.d_newRcode
) {
347 cout
<<"\t* mostly correct *"<<endl
;
351 if(!isRcodeOk(qd
.d_origRcode
) && isRcodeOk(qd
.d_newRcode
)) {
353 cout
<<"\t* we better *"<<endl
;
356 if(isRcodeOk(qd
.d_origRcode
) && !isRcodeOk(qd
.d_newRcode
) && !isRootReferral(qd
.d_origAnswers
)) {
358 cout
<<"\t* orig better *"<<endl
;
361 if (s_origbetterset
.emplace(qd
.d_qi
.d_qname
, qd
.d_qi
.d_qtype
).second
) {
362 cout
<<"orig better: " << qd
.d_qi
.d_qname
<<" "<< qd
.d_qi
.d_qtype
<<endl
;
367 cout
<<"orig: rcode="<<qd
.d_origRcode
<<"\n";
368 for(set
<DNSRecord
>::const_iterator i
=canonicOrig
.begin(); i
!=canonicOrig
.end(); ++i
)
369 cout
<<"\t"<<i
->d_name
<<"\t"<<DNSRecordContent::NumberToType(i
->d_type
)<<"\t'" << (i
->getContent() ? i
->getContent()->getZoneRepresentation() : "") <<"'\n";
370 cout
<<"new: rcode="<<qd
.d_newRcode
<<"\n";
371 for(set
<DNSRecord
>::const_iterator i
=canonicNew
.begin(); i
!=canonicNew
.end(); ++i
)
372 cout
<<"\t"<<i
->d_name
<<"\t"<<DNSRecordContent::NumberToType(i
->d_type
)<<"\t'" << (i
->getContent() ? i
->getContent()->getZoneRepresentation() : "") <<"'\n";
379 int releaseID
=qd
.d_assignedID
;
380 qids
.erase(iter
); // qd invalid now
381 s_idmanager
.releaseID(releaseID
);
385 std::unique_ptr
<Socket
> s_socket
= nullptr;
387 static void receiveFromReference()
392 int res
=waitForData(s_socket
->getHandle(), g_timeoutMsec
/1000, 1000*(g_timeoutMsec
%1000));
394 if(res
< 0 || res
==0)
397 while (s_socket
->recvFromAsync(packet
, remote
)) {
400 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
401 MOADNSParser
mdp(false, reinterpret_cast<const char*>(packet
.data()), packet
.size());
402 if(!mdp
.d_header
.qr
) {
403 cout
<<"Received a question from our reference nameserver!"<<endl
;
407 typedef qids_t::index
<AssignedIDTag
>::type qids_by_id_index_t
;
408 qids_by_id_index_t
& idindex
=qids
.get
<AssignedIDTag
>();
409 qids_by_id_index_t::const_iterator found
=idindex
.find(ntohs(mdp
.d_header
.id
));
410 if(found
== idindex
.end()) {
412 cout
<<"Received an answer ("<<mdp
.d_qname
<<") from reference nameserver with id "<<mdp
.d_header
.id
<<" which we can't match to a question!"<<endl
;
417 QuestionData qd
=*found
; // we have to make a copy because we reinsert below
418 qd
.d_newAnswers
=mdp
.d_answers
;
419 qd
.d_newRcode
=mdp
.d_header
.rcode
;
420 idindex
.replace(found
, qd
);
421 if(qd
.d_origRcode
!=-1) {
422 qids_t::const_iterator iter
= qids
.project
<0>(found
);
423 measureResultAndClean(iter
);
426 catch(const MOADNSException
&mde
)
430 catch(std::out_of_range
&e
)
434 catch(std::exception
& e
)
440 catch(std::exception
& e
)
442 cerr
<<"Receiver function died: "<<e
.what()<<endl
;
447 cerr
<<"Receiver function died with unknown exception"<<endl
;
451 static void pruneQids()
454 gettimeofday(&now
, 0);
456 for(qids_t::iterator i
=qids
.begin(); i
!=qids
.end(); ) {
457 if(DiffTime(i
->d_resentTime
, now
) < 10)
460 s_idmanager
.releaseID(i
->d_assignedID
);
461 if(i
->d_newRcode
==-1) {
464 if(i
->d_origRcode
==-1) {
473 static void printStats(uint64_t origWaitingFor
=0, uint64_t weWaitingFor
=0)
475 boost::format
headerfmt ("%|9t|Questions - Pend. - Drop = Answers = (On time + Late) = (Err + Ok)\n");
476 boost::format
datafmt("%s%|9t|%d %|21t|%d %|29t|%d %|36t|%d %|47t|%d %|57t|%d %|66t|%d %|72t|%d\n");
479 cerr
<<(datafmt
% "Orig" % s_questions
% origWaitingFor
% s_orignever
% s_origanswers
% 0 % s_origtimedout
% 0 % 0);
480 cerr
<<(datafmt
% "Refer." % s_questions
% weWaitingFor
% s_wenever
% s_weanswers
% 0 % s_wetimedout
% 0 % 0);
482 cerr
<<weWaitingFor
<<" queries that could still come in on time, "<<qids
.size()<<" outstanding"<<endl
;
484 cerr
<<"we late: "<<s_wetimedout
<<", orig late: "<< s_origtimedout
<<", "<<s_questions
<<" questions sent, "<<s_origanswers
485 <<" original answers, "<<s_perfect
<<" perfect, "<<s_mostly
<<" mostly correct"<<", "<<s_webetter
<<" we better, "<<s_origbetter
<<" orig better ("<<s_origbetterset
.size()<<" diff)"<<endl
;
486 cerr
<<"we never: "<<s_wenever
<<", orig never: "<<s_orignever
<<endl
;
487 cerr
<<"original questions from IP addresses for which recursion was not available: "<<s_norecursionavailable
<<endl
;
488 cerr
<<"Unmatched from us: "<<s_weunmatched
<<", unmatched from original: "<<s_origunmatched
<< " ( - decoding err: "<<s_origunmatched
-s_origdnserrors
<<")"<<endl
;
489 cerr
<<"DNS decoding errors from us: "<<s_wednserrors
<<", from original: "<<s_origdnserrors
<<", exact duplicates from client: "<<s_duplicates
<<endl
<<endl
;
493 static void houseKeeping()
498 gettimeofday(&now
, 0);
500 if(DiffTime(last
, now
) < 0.3)
503 int weWaitingFor
, origWaitingFor
, weSlow
, origSlow
;
504 WeOrigSlowQueriesDelta(weWaitingFor
, origWaitingFor
, weSlow
, origSlow
);
507 if( weWaitingFor
> 1000) {
508 cerr
<<"Too many questions ("<<weWaitingFor
<<") outstanding, throttling"<<endl
;
512 else if(weWaitingFor
< 750) {
513 cerr
<<"Unthrottling ("<<weWaitingFor
<<")"<<endl
;
517 if(DiffTime(last
, now
) < 2)
523 Questions - Pend. - Drop = Answers = (On time + Late) = (Err + Ok)
524 Orig 9 21 29 36 47 57 66 72
529 printStats(origWaitingFor
, weWaitingFor
);
533 static void generateOptRR(const std::string
& optRData
, string
& res
)
535 const uint8_t name
= 0;
542 dh
.d_type
= htons(QType::OPT
);
543 dh
.d_class
= htons(1280);
544 memcpy(&dh
.d_ttl
, &edns0
, sizeof edns0
);
545 dh
.d_clen
= htons((uint16_t) optRData
.length());
546 res
.assign((const char *) &name
, sizeof name
);
547 res
.append((const char *) &dh
, sizeof dh
);
548 res
.append(optRData
.c_str(), optRData
.length());
551 static void addECSOption(char* packet
, const size_t packetSize
, uint16_t* len
, const ComboAddress
& remote
, int stamp
)
554 struct dnsheader
* dh
= (struct dnsheader
*) packet
;
558 eso
.source
= Netmask(remote
);
560 ComboAddress
stamped(remote
);
561 *((char*)&stamped
.sin4
.sin_addr
.s_addr
)=stamp
;
562 eso
.source
= Netmask(stamped
);
564 string optRData
=makeEDNSSubnetOptsString(eso
);
566 generateEDNSOption(EDNSOptionCode::ECS
, optRData
, record
);
567 generateOptRR(record
, EDNSRR
);
570 uint16_t arcount
= ntohs(dh
->arcount
);
571 /* does it fit in the existing buffer? */
572 if (packetSize
> *len
&& (packetSize
- *len
) > EDNSRR
.size()) {
574 dh
->arcount
= htons(arcount
);
575 memcpy(packet
+ *len
, EDNSRR
.c_str(), EDNSRR
.size());
576 *len
+= EDNSRR
.size();
580 static bool checkIPTransparentUsability()
582 #ifdef IP_TRANSPARENT
584 auto s
= Socket(SSocket(AF_INET
, SOCK_DGRAM
, 0));
585 SSetsockopt(s
.getHandle(), IPPROTO_IP
, IP_TRANSPARENT
, 1);
588 catch (const std::exception
& e
) {
589 cerr
<< "Error while checking whether IP_TRANSPARENT (required for '--source-from-pcap') is working properly: " << e
.what() << endl
;
591 #endif /* IP_TRANSPARENT */
596 static bool g_rdSelector
;
597 static uint16_t g_pcapDnsPort
;
599 static bool sendPacketFromPR(PcapPacketReader
& pr
, const ComboAddress
& remote
, int stamp
, [[maybe_unused
]] bool usePCAPSourceIP
)
602 if (pr
.d_len
<= sizeof(dnsheader
)) {
605 if (pr
.d_len
> std::numeric_limits
<uint16_t>::max()) {
606 /* too large for an DNS UDP query, something is not right */
609 dnsheader
* dh
=const_cast<dnsheader
*>(reinterpret_cast<const dnsheader
*>(pr
.d_payload
));
610 if((ntohs(pr
.d_udp
->uh_dport
)!=g_pcapDnsPort
&& ntohs(pr
.d_udp
->uh_sport
)!=g_pcapDnsPort
) || dh
->rd
!= g_rdSelector
)
615 // yes, we send out ALWAYS. Even if we don't do anything with it later,
616 if(!dh
->qr
) { // this is to stress out the reference server with all the pain
618 qd
.d_assignedID
= s_idmanager
.getID();
620 dh
->id
=htons(qd
.d_assignedID
);
621 // dh->rd=1; // useful to replay traffic to auths to a recursor
622 uint16_t dlen
= pr
.d_len
;
625 static_assert(sizeof(pr
.d_readbuffer
) >= 1500, "The size of the underlying buffer should be at least 1500 bytes");
627 /* the existing packet is larger than the maximum size we are willing to send, and it won't get better by adding ECS */
630 addECSOption((char*)pr
.d_payload
, 1500, &dlen
, pr
.getSource(), stamp
);
633 #ifdef IP_TRANSPARENT
634 if (usePCAPSourceIP
) {
635 auto s
= Socket(SSocket(AF_INET
, SOCK_DGRAM
, 0));
636 SSetsockopt(s
.getHandle(), IPPROTO_IP
, IP_TRANSPARENT
, 1);
637 SBind(s
.getHandle(), pr
.getSource());
638 sendto(s
.getHandle(), reinterpret_cast<const char*>(pr
.d_payload
), dlen
, 0, reinterpret_cast<const struct sockaddr
*>(&remote
), remote
.getSocklen());
641 #endif /* IP_TRANSPARENT */
642 s_socket
->sendTo((const char*)pr
.d_payload
, dlen
, remote
);
643 #ifdef IP_TRANSPARENT
645 #endif /* IP_TRANSPARENT */
649 MOADNSParser
mdp(false, (const char*)pr
.d_payload
, pr
.d_len
);
650 QuestionIdentifier qi
=QuestionIdentifier::create(pr
.getSource(), pr
.getDest(), mdp
);
652 if(!mdp
.d_header
.qr
) {
656 cout
<<"Saw an exact duplicate question in PCAP "<<qi
<< endl
;
658 s_idmanager
.releaseID(qd
.d_assignedID
); // release = puts at back of pool
661 // new question - ID assigned above already
663 gettimeofday(&qd
.d_resentTime
,0);
668 qids_t::const_iterator iter
=qids
.find(qi
);
669 if(iter
!= qids
.end()) {
670 QuestionData eqd
=*iter
;
671 eqd
.d_origAnswers
=mdp
.d_answers
;
672 eqd
.d_origRcode
=mdp
.d_header
.rcode
;
675 s_norecursionavailable
++;
676 eqd
.d_norecursionavailable
=true;
678 qids
.replace(iter
, eqd
);
680 if(eqd
.d_newRcode
!=-1) {
681 measureResultAndClean(iter
);
689 cout
<<"Unmatched original answer "<<qi
<<endl
;
693 catch (const MOADNSException
& mde
) {
695 cerr
<<"Error parsing packet: "<<mde
.what()<<endl
;
697 s_idmanager
.releaseID(qd
.d_assignedID
); // not added to qids for cleanup
700 catch (std::exception
& e
) {
702 cerr
<<"Error parsing packet: "<<e
.what()<<endl
;
705 s_idmanager
.releaseID(qd
.d_assignedID
); // not added to qids for cleanup
712 static void usage(po::options_description
&desc
) {
713 cerr
<< "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl
;
714 cerr
<< desc
<< "\n";
717 int main(int argc
, char** argv
)
720 po::options_description
desc("Allowed options");
722 ("help,h", "produce help message")
723 ("version", "show version number")
724 ("packet-limit", po::value
<uint32_t>()->default_value(0), "stop after this many packets")
725 ("pcap-dns-port", po::value
<uint16_t>()->default_value(53), "look at packets from or to this port in the PCAP (defaults to 53)")
726 ("quiet", po::value
<bool>()->default_value(true), "don't be too noisy")
727 ("recursive", po::value
<bool>()->default_value(true), "look at recursion desired packets, or not (defaults true)")
728 ("speedup", po::value
<float>()->default_value(1), "replay at this speedup")
729 ("timeout-msec", po::value
<uint32_t>()->default_value(500), "wait at least this many milliseconds for a reply")
730 ("ecs-stamp", "Add original IP address to ECS in replay")
731 ("ecs-mask", po::value
<uint16_t>(), "Replace first octet of src IP address with this value in ECS")
732 ("source-ip", po::value
<string
>()->default_value(""), "IP to send the replayed packet from")
733 ("source-from-pcap", po::value
<bool>()->default_value(false), "Send the replayed packets from the source IP present in the PCAP (requires IP_TRANSPARENT support)")
734 ("source-port", po::value
<uint16_t>()->default_value(0), "Port to send the replayed packet from");
736 po::options_description alloptions
;
737 po::options_description
hidden("hidden options");
739 ("pcap-source", po::value
<string
>(), "PCAP source file")
740 ("target-ip", po::value
<string
>()->default_value("127.0.0.1"), "target-ip")
741 ("target-port", po::value
<uint16_t>()->default_value(5300), "target port");
743 alloptions
.add(desc
).add(hidden
);
744 po::positional_options_description p
;
745 p
.add("pcap-source", 1);
746 p
.add("target-ip", 1);
747 p
.add("target-port", 1);
749 po::store(po::command_line_parser(argc
, argv
).options(alloptions
).positional(p
).run(), g_vm
);
754 if (g_vm
.count("help")) {
759 if (g_vm
.count("version")) {
760 cerr
<<"dnsreplay "<<VERSION
<<endl
;
764 if(!g_vm
.count("pcap-source")) {
765 cerr
<<"Fatal, need to specify at least a PCAP source file"<<endl
;
770 uint32_t packetLimit
= g_vm
["packet-limit"].as
<uint32_t>();
772 g_rdSelector
= g_vm
["recursive"].as
<bool>();
773 g_pcapDnsPort
= g_vm
["pcap-dns-port"].as
<uint16_t>();
775 g_quiet
= g_vm
["quiet"].as
<bool>();
777 signal(SIGINT
, pleaseQuitHandler
);
778 float speedup
=g_vm
["speedup"].as
<float>();
779 g_timeoutMsec
=g_vm
["timeout-msec"].as
<uint32_t>();
781 PcapPacketReader
pr(g_vm
["pcap-source"].as
<string
>());
782 s_socket
= make_unique
<Socket
>(AF_INET
, SOCK_DGRAM
);
784 s_socket
->setNonBlocking();
786 if(g_vm
.count("source-ip") && !g_vm
["source-ip"].as
<string
>().empty()) {
787 s_socket
->bind(ComboAddress(g_vm
["source-ip"].as
<string
>(), g_vm
["source-port"].as
<uint16_t>()));
791 setSocketReceiveBuffer(s_socket
->getHandle(), 2000000);
793 catch (const std::exception
& e
) {
794 cerr
<<e
.what()<<endl
;
797 setSocketSendBuffer(s_socket
->getHandle(), 2000000);
799 catch (const std::exception
& e
) {
800 cerr
<<e
.what()<<endl
;
803 ComboAddress
remote(g_vm
["target-ip"].as
<string
>(),
804 g_vm
["target-port"].as
<uint16_t>());
807 if(g_vm
.count("ecs-stamp") && g_vm
.count("ecs-mask"))
808 stamp
=g_vm
["ecs-mask"].as
<uint16_t>();
810 cerr
<<"Replaying packets to: '"<<g_vm
["target-ip"].as
<string
>()<<"', port "<<g_vm
["target-port"].as
<uint16_t>()<<endl
;
812 bool usePCAPSourceIP
= g_vm
["source-from-pcap"].as
<bool>();
813 if (usePCAPSourceIP
&& !checkIPTransparentUsability()) {
814 cerr
<< "--source-from-pcap requested but IP_TRANSPARENT support is unavailable or not working properly" << endl
;
819 struct timeval mental_time
;
820 mental_time
.tv_sec
=0; mental_time
.tv_usec
=0;
822 if(!pr
.getUDPPacket()) // we do this here so we error out more cleanly on no packets
824 unsigned int count
=0;
828 cerr
<<"Interrupted from terminal"<<endl
;
834 struct timeval packet_ts
;
835 packet_ts
.tv_sec
= 0;
836 packet_ts
.tv_usec
= 0;
838 while(packet_ts
< mental_time
) {
839 if(!first
&& !pr
.getUDPPacket()) // otherwise we miss the first packet
843 packet_ts
.tv_sec
= pr
.d_pheader
.ts
.tv_sec
;
844 packet_ts
.tv_usec
= pr
.d_pheader
.ts
.tv_usec
;
846 if (sendPacketFromPR(pr
, remote
, stamp
, usePCAPSourceIP
)) {
850 if(packetLimit
&& count
>= packetLimit
)
853 mental_time
=packet_ts
;
854 struct timeval then
, now
;
855 gettimeofday(&then
,0);
857 receiveFromReference();
859 gettimeofday(&now
, 0);
861 mental_time
= mental_time
+ speedup
* (now
-then
);
865 receiveFromReference();
869 catch(std::exception
& e
)
871 cerr
<<"Fatal: "<<e
.what()<<endl
;