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 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 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 deque
<uint16_t> d_available
;
167 void setSocketBuffer(int fd
, int optname
, uint32_t size
)
170 socklen_t len
=sizeof(psize
);
172 if(!getsockopt(fd
, SOL_SOCKET
, optname
, (char*)&psize
, &len
) && psize
> size
) {
173 cerr
<<"Not decreasing socket buffer size from "<<psize
<<" to "<<size
<<endl
;
177 if (setsockopt(fd
, SOL_SOCKET
, optname
, (char*)&size
, sizeof(size
)) < 0 )
178 cerr
<<"Warning: unable to raise socket buffer size to "<<size
<<": "<<stringerror()<<endl
;
181 static void setSocketReceiveBuffer(int fd
, uint32_t size
)
183 setSocketBuffer(fd
, SO_RCVBUF
, size
);
186 static void setSocketSendBuffer(int fd
, uint32_t size
)
188 setSocketBuffer(fd
, SO_SNDBUF
, size
);
192 struct AssignedIDTag
{};
193 struct QuestionTag
{};
197 QuestionData() : d_assignedID(-1), d_origRcode(-1), d_newRcode(-1), d_norecursionavailable(false), d_origlate(false), d_newlate(false)
200 QuestionIdentifier d_qi
;
202 MOADNSParser::answers_t d_origAnswers
, d_newAnswers
;
203 int d_origRcode
, d_newRcode
;
204 struct timeval d_resentTime
;
205 bool d_norecursionavailable
;
206 bool d_origlate
, d_newlate
;
209 typedef multi_index_container
<
212 ordered_unique
<tag
<QuestionTag
>, BOOST_MULTI_INDEX_MEMBER(QuestionData
, QuestionIdentifier
, d_qi
) > ,
213 ordered_unique
<tag
<AssignedIDTag
>, BOOST_MULTI_INDEX_MEMBER(QuestionData
, int, d_assignedID
) >
220 unsigned int s_questions
, s_origanswers
, s_weanswers
, s_wetimedout
, s_perfect
, s_mostly
, s_origtimedout
;
221 unsigned int s_wenever
, s_orignever
;
222 unsigned int s_webetter
, s_origbetter
, s_norecursionavailable
;
223 unsigned int s_weunmatched
, s_origunmatched
;
224 unsigned int s_wednserrors
, s_origdnserrors
, s_duplicates
;
228 void WeOrigSlowQueriesDelta(int& weOutstanding
, int& origOutstanding
, int& weSlow
, int& origSlow
)
231 gettimeofday(&now
, 0);
233 weOutstanding
=origOutstanding
=weSlow
=origSlow
=0;
235 for(qids_t::iterator i
=qids
.begin(); i
!=qids
.end(); ++i
) {
236 double dt
=DiffTime(i
->d_resentTime
, now
);
238 if(i
->d_newRcode
== -1)
240 if(i
->d_origRcode
== -1)
244 if(i
->d_newRcode
== -1) {
254 if(i
->d_origRcode
== -1) {
268 void compactAnswerSet(MOADNSParser::answers_t orig
, set
<DNSRecord
>& compacted
)
270 for(MOADNSParser::answers_t::const_iterator i
=orig
.begin(); i
!= orig
.end(); ++i
)
271 if(i
->first
.d_place
==DNSResourceRecord::ANSWER
)
272 compacted
.insert(i
->first
);
275 bool isRcodeOk(int rcode
)
277 return rcode
==0 || rcode
==3;
280 set
<pair
<DNSName
,uint16_t> > s_origbetterset
;
282 bool isRootReferral(const MOADNSParser::answers_t
& answers
)
288 for(MOADNSParser::answers_t::const_iterator iter
= answers
.begin(); iter
!= answers
.end(); ++iter
) {
289 // cerr<<(int)iter->first.d_place<<", "<<iter->first.d_name<<" "<<iter->first.d_type<<", # "<<answers.size()<<endl;
290 if(iter
->first
.d_place
!=2)
292 if(!iter
->first
.d_name
.isRoot() || iter
->first
.d_type
!=QType::NS
)
298 vector
<uint32_t> flightTimes
;
299 void accountFlightTime(qids_t::const_iterator iter
)
301 if(flightTimes
.empty())
302 flightTimes
.resize(2050);
305 gettimeofday(&now
, 0);
306 unsigned int mdiff
= 1000*DiffTime(iter
->d_resentTime
, now
);
307 if(mdiff
> flightTimes
.size()-2)
308 mdiff
= flightTimes
.size()-1;
310 flightTimes
[mdiff
]++;
313 uint64_t countLessThan(unsigned int msec
)
316 for(unsigned int i
= 0 ; i
< msec
&& i
< flightTimes
.size() ; ++i
) {
317 ret
+= flightTimes
[i
];
322 void emitFlightTimes()
324 uint64_t totals
= countLessThan(flightTimes
.size());
325 unsigned int limits
[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes
.size()};
327 cout
.setf(std::ios::fixed
);
329 for(unsigned int i
=0 ; i
< sizeof(limits
)/sizeof(limits
[0]); ++i
) {
330 if(limits
[i
]!=flightTimes
.size())
331 cout
<<"Within "<<limits
[i
]<<" msec: ";
333 cout
<<"Beyond "<<limits
[i
]-2<<" msec: ";
334 uint64_t here
= countLessThan(limits
[i
]);
335 cout
<<100.0*here
/totals
<<"% ("<<100.0*(here
-sofar
)/totals
<<"%)"<<endl
;
341 void measureResultAndClean(qids_t::const_iterator iter
)
343 const QuestionData
& qd
=*iter
;
344 accountFlightTime(iter
);
346 set
<DNSRecord
> canonicOrig
, canonicNew
;
347 compactAnswerSet(qd
.d_origAnswers
, canonicOrig
);
348 compactAnswerSet(qd
.d_newAnswers
, canonicNew
);
351 cout
<<qd
.d_qi
<<", orig rcode: "<<qd
.d_origRcode
<<", ours: "<<qd
.d_newRcode
;
352 cout
<<", "<<canonicOrig
.size()<< " vs " << canonicNew
.size()<<", perfect: ";
355 if(canonicOrig
==canonicNew
) {
364 if(qd
.d_norecursionavailable
)
366 cout
<<"\t* original nameserver did not provide recursion for this question *"<<endl
;
367 if(qd
.d_origRcode
== qd
.d_newRcode
) {
369 cout
<<"\t* mostly correct *"<<endl
;
373 if(!isRcodeOk(qd
.d_origRcode
) && isRcodeOk(qd
.d_newRcode
)) {
375 cout
<<"\t* we better *"<<endl
;
378 if(isRcodeOk(qd
.d_origRcode
) && !isRcodeOk(qd
.d_newRcode
) && !isRootReferral(qd
.d_origAnswers
)) {
380 cout
<<"\t* orig better *"<<endl
;
383 if(s_origbetterset
.insert(make_pair(qd
.d_qi
.d_qname
, qd
.d_qi
.d_qtype
)).second
) {
384 cout
<<"orig better: " << qd
.d_qi
.d_qname
<<" "<< qd
.d_qi
.d_qtype
<<endl
;
389 cout
<<"orig: rcode="<<qd
.d_origRcode
<<"\n";
390 for(set
<DNSRecord
>::const_iterator i
=canonicOrig
.begin(); i
!=canonicOrig
.end(); ++i
)
391 cout
<<"\t"<<i
->d_name
<<"\t"<<DNSRecordContent::NumberToType(i
->d_type
)<<"\t'" << (i
->d_content
? i
->d_content
->getZoneRepresentation() : "") <<"'\n";
392 cout
<<"new: rcode="<<qd
.d_newRcode
<<"\n";
393 for(set
<DNSRecord
>::const_iterator i
=canonicNew
.begin(); i
!=canonicNew
.end(); ++i
)
394 cout
<<"\t"<<i
->d_name
<<"\t"<<DNSRecordContent::NumberToType(i
->d_type
)<<"\t'" << (i
->d_content
? i
->d_content
->getZoneRepresentation() : "") <<"'\n";
401 int releaseID
=qd
.d_assignedID
;
402 qids
.erase(iter
); // qd invalid now
403 s_idmanager
.releaseID(releaseID
);
407 std::unique_ptr
<Socket
> s_socket
= nullptr;
409 void receiveFromReference()
414 int res
=waitForData(s_socket
->getHandle(), g_timeoutMsec
/1000, 1000*(g_timeoutMsec
%1000));
416 if(res
< 0 || res
==0)
419 while(s_socket
->recvFromAsync(packet
, remote
)) {
422 MOADNSParser
mdp(false, packet
.c_str(), packet
.length());
423 if(!mdp
.d_header
.qr
) {
424 cout
<<"Received a question from our reference nameserver!"<<endl
;
428 typedef qids_t::index
<AssignedIDTag
>::type qids_by_id_index_t
;
429 qids_by_id_index_t
& idindex
=qids
.get
<AssignedIDTag
>();
430 qids_by_id_index_t::const_iterator found
=idindex
.find(ntohs(mdp
.d_header
.id
));
431 if(found
== idindex
.end()) {
433 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
;
438 QuestionData qd
=*found
; // we have to make a copy because we reinsert below
439 qd
.d_newAnswers
=mdp
.d_answers
;
440 qd
.d_newRcode
=mdp
.d_header
.rcode
;
441 idindex
.replace(found
, qd
);
442 if(qd
.d_origRcode
!=-1) {
443 qids_t::const_iterator iter
= qids
.project
<0>(found
);
444 measureResultAndClean(iter
);
447 catch(const MOADNSException
&mde
)
451 catch(std::out_of_range
&e
)
455 catch(std::exception
& e
)
461 catch(std::exception
& e
)
463 cerr
<<"Receiver function died: "<<e
.what()<<endl
;
468 cerr
<<"Receiver function died with unknown exception"<<endl
;
475 gettimeofday(&now
, 0);
477 for(qids_t::iterator i
=qids
.begin(); i
!=qids
.end(); ) {
478 if(DiffTime(i
->d_resentTime
, now
) < 10)
481 s_idmanager
.releaseID(i
->d_assignedID
);
482 if(i
->d_newRcode
==-1) {
485 if(i
->d_origRcode
==-1) {
494 void printStats(uint64_t origWaitingFor
=0, uint64_t weWaitingFor
=0)
496 format
headerfmt ("%|9t|Questions - Pend. - Drop = Answers = (On time + Late) = (Err + Ok)\n");
497 format
datafmt("%s%|9t|%d %|21t|%d %|29t|%d %|36t|%d %|47t|%d %|57t|%d %|66t|%d %|72t|%d\n");
500 cerr
<<(datafmt
% "Orig" % s_questions
% origWaitingFor
% s_orignever
% s_origanswers
% 0 % s_origtimedout
% 0 % 0);
501 cerr
<<(datafmt
% "Refer." % s_questions
% weWaitingFor
% s_wenever
% s_weanswers
% 0 % s_wetimedout
% 0 % 0);
503 cerr
<<weWaitingFor
<<" queries that could still come in on time, "<<qids
.size()<<" outstanding"<<endl
;
505 cerr
<<"we late: "<<s_wetimedout
<<", orig late: "<< s_origtimedout
<<", "<<s_questions
<<" questions sent, "<<s_origanswers
506 <<" original answers, "<<s_perfect
<<" perfect, "<<s_mostly
<<" mostly correct"<<", "<<s_webetter
<<" we better, "<<s_origbetter
<<" orig better ("<<s_origbetterset
.size()<<" diff)"<<endl
;
507 cerr
<<"we never: "<<s_wenever
<<", orig never: "<<s_orignever
<<endl
;
508 cerr
<<"original questions from IP addresses for which recursion was not available: "<<s_norecursionavailable
<<endl
;
509 cerr
<<"Unmatched from us: "<<s_weunmatched
<<", unmatched from original: "<<s_origunmatched
<< " ( - decoding err: "<<s_origunmatched
-s_origdnserrors
<<")"<<endl
;
510 cerr
<<"DNS decoding errors from us: "<<s_wednserrors
<<", from original: "<<s_origdnserrors
<<", exact duplicates from client: "<<s_duplicates
<<endl
<<endl
;
519 gettimeofday(&now
, 0);
521 if(DiffTime(last
, now
) < 0.3)
524 int weWaitingFor
, origWaitingFor
, weSlow
, origSlow
;
525 WeOrigSlowQueriesDelta(weWaitingFor
, origWaitingFor
, weSlow
, origSlow
);
528 if( weWaitingFor
> 1000) {
529 cerr
<<"Too many questions ("<<weWaitingFor
<<") outstanding, throttling"<<endl
;
533 else if(weWaitingFor
< 750) {
534 cerr
<<"Unthrottling ("<<weWaitingFor
<<")"<<endl
;
538 if(DiffTime(last
, now
) < 2)
544 Questions - Pend. - Drop = Answers = (On time + Late) = (Err + Ok)
545 Orig 9 21 29 36 47 57 66 72
550 printStats(origWaitingFor
, weWaitingFor
);
554 static void generateOptRR(const std::string
& optRData
, string
& res
)
556 const uint8_t name
= 0;
563 dh
.d_type
= htons(QType::OPT
);
564 dh
.d_class
= htons(1280);
565 memcpy(&dh
.d_ttl
, &edns0
, sizeof edns0
);
566 dh
.d_clen
= htons((uint16_t) optRData
.length());
567 res
.assign((const char *) &name
, sizeof name
);
568 res
.append((const char *) &dh
, sizeof dh
);
569 res
.append(optRData
.c_str(), optRData
.length());
572 static void addECSOption(char* packet
, const size_t packetSize
, uint16_t* len
, const ComboAddress
& remote
, int stamp
)
575 struct dnsheader
* dh
= (struct dnsheader
*) packet
;
579 eso
.source
= Netmask(remote
);
581 ComboAddress
stamped(remote
);
582 *((char*)&stamped
.sin4
.sin_addr
.s_addr
)=stamp
;
583 eso
.source
= Netmask(stamped
);
585 string optRData
=makeEDNSSubnetOptsString(eso
);
587 generateEDNSOption(EDNSOptionCode::ECS
, optRData
, record
);
588 generateOptRR(record
, EDNSRR
);
591 uint16_t arcount
= ntohs(dh
->arcount
);
592 /* does it fit in the existing buffer? */
593 if (packetSize
> *len
&& (packetSize
- *len
) > EDNSRR
.size()) {
595 dh
->arcount
= htons(arcount
);
596 memcpy(packet
+ *len
, EDNSRR
.c_str(), EDNSRR
.size());
597 *len
+= EDNSRR
.size();
601 static bool g_rdSelector
;
602 static uint16_t g_pcapDnsPort
;
604 static bool sendPacketFromPR(PcapPacketReader
& pr
, const ComboAddress
& remote
, int stamp
)
607 if (pr
.d_len
<= sizeof(dnsheader
)) {
610 if (pr
.d_len
> std::numeric_limits
<uint16_t>::max()) {
611 /* too large for an DNS UDP query, something is not right */
614 dnsheader
* dh
=const_cast<dnsheader
*>(reinterpret_cast<const dnsheader
*>(pr
.d_payload
));
615 if((ntohs(pr
.d_udp
->uh_dport
)!=g_pcapDnsPort
&& ntohs(pr
.d_udp
->uh_sport
)!=g_pcapDnsPort
) || dh
->rd
!= g_rdSelector
)
620 // yes, we send out ALWAYS. Even if we don't do anything with it later,
621 if(!dh
->qr
) { // this is to stress out the reference server with all the pain
623 qd
.d_assignedID
= s_idmanager
.getID();
625 dh
->id
=htons(qd
.d_assignedID
);
626 // dh->rd=1; // useful to replay traffic to auths to a recursor
627 uint16_t dlen
= pr
.d_len
;
630 static_assert(sizeof(pr
.d_buffer
) >= 1500, "The size of the underlying buffer should be at least 1500 bytes");
632 /* the existing packet is larger than the maximum size we are willing to send, and it won't get better by adding ECS */
635 addECSOption((char*)pr
.d_payload
, 1500, &dlen
, pr
.getSource(), stamp
);
639 s_socket
->sendTo((const char*)pr
.d_payload
, dlen
, remote
);
643 MOADNSParser
mdp(false, (const char*)pr
.d_payload
, pr
.d_len
);
644 QuestionIdentifier qi
=QuestionIdentifier::create(pr
.getSource(), pr
.getDest(), mdp
);
646 if(!mdp
.d_header
.qr
) {
650 cout
<<"Saw an exact duplicate question in PCAP "<<qi
<< endl
;
652 s_idmanager
.releaseID(qd
.d_assignedID
); // release = puts at back of pool
655 // new question - ID assigned above already
657 gettimeofday(&qd
.d_resentTime
,0);
662 qids_t::const_iterator iter
=qids
.find(qi
);
663 if(iter
!= qids
.end()) {
664 QuestionData eqd
=*iter
;
665 eqd
.d_origAnswers
=mdp
.d_answers
;
666 eqd
.d_origRcode
=mdp
.d_header
.rcode
;
669 s_norecursionavailable
++;
670 eqd
.d_norecursionavailable
=true;
672 qids
.replace(iter
, eqd
);
674 if(eqd
.d_newRcode
!=-1) {
675 measureResultAndClean(iter
);
683 cout
<<"Unmatched original answer "<<qi
<<endl
;
687 catch(const MOADNSException
&mde
)
690 cerr
<<"Error parsing packet: "<<mde
.what()<<endl
;
691 s_idmanager
.releaseID(qd
.d_assignedID
); // not added to qids for cleanup
694 catch(std::exception
&e
)
697 cerr
<<"Error parsing packet: "<<e
.what()<<endl
;
699 s_idmanager
.releaseID(qd
.d_assignedID
); // not added to qids for cleanup
706 void usage(po::options_description
&desc
) {
707 cerr
<< "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl
;
708 cerr
<< desc
<< "\n";
711 int main(int argc
, char** argv
)
714 po::options_description
desc("Allowed options");
716 ("help,h", "produce help message")
717 ("version", "show version number")
718 ("packet-limit", po::value
<uint32_t>()->default_value(0), "stop after this many packets")
719 ("pcap-dns-port", po::value
<uint16_t>()->default_value(53), "look at packets from or to this port in the PCAP (defaults to 53)")
720 ("quiet", po::value
<bool>()->default_value(true), "don't be too noisy")
721 ("recursive", po::value
<bool>()->default_value(true), "look at recursion desired packets, or not (defaults true)")
722 ("speedup", po::value
<float>()->default_value(1), "replay at this speedup")
723 ("timeout-msec", po::value
<uint32_t>()->default_value(500), "wait at least this many milliseconds for a reply")
724 ("ecs-stamp", "Add original IP address to ECS in replay")
725 ("ecs-mask", po::value
<uint16_t>(), "Replace first octet of src IP address with this value in ECS")
726 ("source-ip", po::value
<string
>()->default_value(""), "IP to send the replayed packet from")
727 ("source-port", po::value
<uint16_t>()->default_value(0), "Port to send the replayed packet from");
729 po::options_description alloptions
;
730 po::options_description
hidden("hidden options");
732 ("pcap-source", po::value
<string
>(), "PCAP source file")
733 ("target-ip", po::value
<string
>()->default_value("127.0.0.1"), "target-ip")
734 ("target-port", po::value
<uint16_t>()->default_value(5300), "target port");
736 alloptions
.add(desc
).add(hidden
);
737 po::positional_options_description p
;
738 p
.add("pcap-source", 1);
739 p
.add("target-ip", 1);
740 p
.add("target-port", 1);
742 po::store(po::command_line_parser(argc
, argv
).options(alloptions
).positional(p
).run(), g_vm
);
747 if (g_vm
.count("help")) {
752 if (g_vm
.count("version")) {
753 cerr
<<"dnsreplay "<<VERSION
<<endl
;
757 if(!g_vm
.count("pcap-source")) {
758 cerr
<<"Fatal, need to specify at least a PCAP source file"<<endl
;
763 uint32_t packetLimit
= g_vm
["packet-limit"].as
<uint32_t>();
765 g_rdSelector
= g_vm
["recursive"].as
<bool>();
766 g_pcapDnsPort
= g_vm
["pcap-dns-port"].as
<uint16_t>();
768 g_quiet
= g_vm
["quiet"].as
<bool>();
770 signal(SIGINT
, pleaseQuitHandler
);
771 float speedup
=g_vm
["speedup"].as
<float>();
772 g_timeoutMsec
=g_vm
["timeout-msec"].as
<uint32_t>();
774 PcapPacketReader
pr(g_vm
["pcap-source"].as
<string
>());
775 s_socket
= make_unique
<Socket
>(AF_INET
, SOCK_DGRAM
);
777 s_socket
->setNonBlocking();
779 if(g_vm
.count("source-ip") && !g_vm
["source-ip"].as
<string
>().empty())
780 s_socket
->bind(ComboAddress(g_vm
["source-ip"].as
<string
>(), g_vm
["source-port"].as
<uint16_t>()));
782 setSocketReceiveBuffer(s_socket
->getHandle(), 2000000);
783 setSocketSendBuffer(s_socket
->getHandle(), 2000000);
785 ComboAddress
remote(g_vm
["target-ip"].as
<string
>(),
786 g_vm
["target-port"].as
<uint16_t>());
789 if(g_vm
.count("ecs-stamp") && g_vm
.count("ecs-mask"))
790 stamp
=g_vm
["ecs-mask"].as
<uint16_t>();
792 cerr
<<"Replaying packets to: '"<<g_vm
["target-ip"].as
<string
>()<<"', port "<<g_vm
["target-port"].as
<uint16_t>()<<endl
;
795 struct timeval mental_time
;
796 mental_time
.tv_sec
=0; mental_time
.tv_usec
=0;
798 if(!pr
.getUDPPacket()) // we do this here so we error out more cleanly on no packets
800 unsigned int count
=0;
804 cerr
<<"Interrupted from terminal"<<endl
;
810 struct timeval packet_ts
;
811 packet_ts
.tv_sec
= 0;
812 packet_ts
.tv_usec
= 0;
814 while(packet_ts
< mental_time
) {
815 if(!first
&& !pr
.getUDPPacket()) // otherwise we miss the first packet
819 packet_ts
.tv_sec
= pr
.d_pheader
.ts
.tv_sec
;
820 packet_ts
.tv_usec
= pr
.d_pheader
.ts
.tv_usec
;
822 if(sendPacketFromPR(pr
, remote
, stamp
))
825 if(packetLimit
&& count
>= packetLimit
)
828 mental_time
=packet_ts
;
829 struct timeval then
, now
;
830 gettimeofday(&then
,0);
832 receiveFromReference();
834 gettimeofday(&now
, 0);
836 mental_time
= mental_time
+ speedup
* (now
-then
);
840 receiveFromReference();
844 catch(std::exception
& e
)
846 cerr
<<"Fatal: "<<e
.what()<<endl
;