]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnstcpbench.cc
update pdns.conf-dist with udp-truncation-threshold setting
[thirdparty/pdns.git] / pdns / dnstcpbench.cc
CommitLineData
22f4bd59 1/*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2013 PowerDNS.COM BV
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17*/
4106b16e 18#include <boost/accumulators/statistics/median.hpp>
19#include <boost/accumulators/statistics/mean.hpp>
20#include <boost/accumulators/accumulators.hpp>
21
22#include <boost/accumulators/statistics.hpp>
23
a51b52c9 24#include "dnsparser.hh"
25#include "sstuff.hh"
26#include "misc.hh"
27#include "dnswriter.hh"
28#include "dnsrecords.hh"
29#include "statbag.hh"
a8f90a6a 30#include <netinet/tcp.h>
a51b52c9 31#include <boost/array.hpp>
36658df5 32#include <boost/program_options.hpp>
4106b16e 33#include <boost/foreach.hpp>
a51b52c9 34
36658df5 35StatBag S;
36namespace po = boost::program_options;
4106b16e 37
36658df5 38po::variables_map g_vm;
39bool g_verbose;
a51b52c9 40bool g_onlyTCP;
a8f90a6a 41bool g_tcpNoDelay;
36658df5 42unsigned int g_timeoutMsec;
43AtomicCounter g_networkErrors, g_otherErrors, g_OK, g_truncates, g_authAnswers, g_timeOuts;
1d74012c 44ComboAddress g_dest;
f7896b52 45
4106b16e 46unsigned int makeUsec(const struct timeval& tv)
47{
48 return 1000000*tv.tv_sec + tv.tv_usec;
49}
50
22f4bd59 51/* On Linux, run echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
52 to prevent running out of free TCP ports */
53
54struct BenchQuery
55{
4106b16e 56 BenchQuery(const std::string& qname_, uint16_t qtype_) : qname(qname_), qtype(qtype_), udpUsec(0), tcpUsec(0), answerSecond(0) {}
22f4bd59 57 BenchQuery(){}
58 std::string qname;
59 uint16_t qtype;
4106b16e 60 uint32_t udpUsec, tcpUsec;
61 time_t answerSecond;
22f4bd59 62};
f7896b52 63
22f4bd59 64void doQuery(BenchQuery* q)
f7896b52 65try
a51b52c9 66{
67 vector<uint8_t> packet;
1d74012c 68 DNSPacketWriter pw(packet, q->qname, q->qtype);
36658df5 69 int res;
a51b52c9 70 string reply;
71
4106b16e 72 struct timeval tv, now;
73 gettimeofday(&tv, 0);
74
a51b52c9 75 if(!g_onlyTCP) {
1d74012c 76 Socket udpsock((AddressFamily)g_dest.sin4.sin_family, Datagram);
a51b52c9 77
1d74012c 78 udpsock.sendTo(string((char*)&*packet.begin(), (char*)&*packet.end()), g_dest);
a51b52c9 79 ComboAddress origin;
36658df5 80 res = waitForData(udpsock.getHandle(), 0, 1000 * g_timeoutMsec);
81 if(res < 0)
82 throw NetworkError("Error waiting for response");
83 if(!res) {
84 g_timeOuts++;
85 return;
86 }
87
a51b52c9 88 udpsock.recvFrom(reply, origin);
4106b16e 89
90 gettimeofday(&now, 0);
91 q->udpUsec = makeUsec(now - tv);
92 tv=now;
93
a51b52c9 94 MOADNSParser mdp(reply);
95 if(!mdp.d_header.tc)
96 return;
f7896b52 97 g_truncates++;
a51b52c9 98 }
99
1d74012c 100 Socket sock((AddressFamily)g_dest.sin4.sin_family, Stream);
f7896b52 101 int tmp=1;
102 if(setsockopt(sock.getHandle(),SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
103 throw runtime_error("Unable to set socket reuse: "+string(strerror(errno)));
a8f90a6a 104
105 if(g_tcpNoDelay && setsockopt(sock.getHandle(), IPPROTO_TCP, TCP_NODELAY,(char*)&tmp,sizeof tmp)<0)
106 throw runtime_error("Unable to set socket no delay: "+string(strerror(errno)));
a51b52c9 107
1d74012c 108 sock.connect(g_dest);
36658df5 109 uint16_t len = htons(packet.size());
110 string tcppacket((char*)& len, 2);
111 tcppacket.append((char*)&*packet.begin(), (char*)&*packet.end());
112
113 sock.writen(tcppacket);
114
115 res = waitForData(sock.getHandle(), 0, 1000 * g_timeoutMsec);
116 if(res < 0)
117 throw NetworkError("Error waiting for response");
118 if(!res) {
119 g_timeOuts++;
120 return;
121 }
a51b52c9 122
123 if(sock.read((char *) &len, 2) != 2)
124 throw AhuException("tcp read failed");
125
126 len=ntohs(len);
127 char *creply = new char[len];
128 int n=0;
129 int numread;
130 while(n<len) {
131 numread=sock.read(creply+n, len-n);
132 if(numread<0)
133 throw AhuException("tcp read failed");
134 n+=numread;
135 }
136
137 reply=string(creply, len);
138 delete[] creply;
139
4106b16e 140 gettimeofday(&now, 0);
141 q->tcpUsec = makeUsec(now - tv);
142 q->answerSecond = now.tv_sec;
143
a51b52c9 144 MOADNSParser mdp(reply);
f7896b52 145 // cout<<"Had correct TCP/IP response, "<<mdp.d_answers.size()<<" answers, aabit="<<mdp.d_header.aa<<endl;
36658df5 146 if(mdp.d_header.aa)
147 g_authAnswers++;
f7896b52 148 g_OK++;
149}
150catch(NetworkError& ne)
151{
152 cerr<<"Network error: "<<ne.what()<<endl;
153 g_networkErrors++;
154}
155catch(...)
156{
157 g_otherErrors++;
a51b52c9 158}
159
160/* read queries from stdin, put in vector
161 launch n worker threads, each picks a query using AtomicCounter
162 If a worker reaches the end of its queue, it stops */
163
164AtomicCounter g_pos;
682549f2 165
22f4bd59 166vector<BenchQuery> g_queries;
1d74012c 167
682549f2 168static void* worker(void*)
a51b52c9 169{
a51b52c9 170 for(;;) {
f7896b52 171 unsigned int pos = g_pos++;
172 if(pos >= g_queries.size())
a51b52c9 173 break;
1d74012c 174
175 doQuery(&g_queries[pos]); // this is safe as long as nobody *inserts* to g_queries
a51b52c9 176 }
177 return 0;
178}
179
a51b52c9 180int main(int argc, char** argv)
181try
182{
36658df5 183 po::options_description desc("Allowed options"), hidden, alloptions;
184 desc.add_options()
185 ("help,h", "produce help message")
186 ("verbose,v", "be verbose")
187 ("udp-first,u", "try UDP first")
74f463af 188 ("file,f", po::value<string>(), "source file - if not specified, defaults to stdin")
a8f90a6a 189 ("tcp-no-delay", po::value<bool>()->default_value(true), "use TCP_NODELAY socket option")
36658df5 190 ("timeout-msec", po::value<int>()->default_value(10), "wait for this amount of milliseconds for an answer")
191 ("workers", po::value<int>()->default_value(100), "number of parallel workers");
192
193 hidden.add_options()
194 ("remote-host", po::value<string>(), "remote-host")
195 ("remote-port", po::value<int>()->default_value(53), "remote-port");
196 alloptions.add(desc).add(hidden);
197
198 po::positional_options_description p;
199 p.add("remote-host", 1);
200 p.add("remote-port", 1);
201
202 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
203 po::notify(g_vm);
204
205 if(g_vm.count("help")) {
206 cout << desc<<endl;
207 exit(EXIT_SUCCESS);
208 }
a8f90a6a 209 g_tcpNoDelay = g_vm["tcp-no-delay"].as<bool>();
210
36658df5 211 g_onlyTCP = !g_vm.count("udp-first");
212 g_verbose = g_vm.count("verbose");
213 g_timeoutMsec = g_vm["timeout-msec"].as<int>();
214
a51b52c9 215 reportAllTypes();
f7896b52 216
36658df5 217 if(g_vm["remote-host"].empty()) {
f7896b52 218 cerr<<"Syntax: tcpbench remote [port] < queries"<<endl;
219 cerr<<"Where queries is one query per line, format: qname qtype, just 1 space"<<endl;
36658df5 220 cerr<<desc<<endl;
f7896b52 221 exit(EXIT_FAILURE);
222 }
f7896b52 223
36658df5 224 g_dest = ComboAddress(g_vm["remote-host"].as<string>().c_str(), g_vm["remote-port"].as<int>());
225
226 unsigned int numworkers=g_vm["workers"].as<int>();
227
228 if(g_verbose) {
229 cout<<"Sending queries to: "<<g_dest.toStringWithPort()<<endl;
230 cout<<"Attempting UDP first: " << (g_onlyTCP ? "no" : "yes") <<endl;
231 cout<<"Timeout: "<< g_timeoutMsec<<"msec"<<endl;
a8f90a6a 232 cout << "Using TCP_NODELAY: "<<g_tcpNoDelay<<endl;
36658df5 233 }
234
235
a51b52c9 236 pthread_t workers[numworkers];
237
74f463af 238 FILE* fp;
239 if(!g_vm.count("file"))
240 fp=fdopen(0, "r");
241 else {
242 fp=fopen(g_vm["file"].as<string>().c_str(), "r");
243 if(!fp)
244 unixDie("Unable to open "+g_vm["file"].as<string>()+" for input");
245 }
f7896b52 246 pair<string, string> q;
247 string line;
248 while(stringfgets(fp, line)) {
249 trim_right(line);
250 q=splitField(line, ' ');
22f4bd59 251 g_queries.push_back(BenchQuery(q.first, DNSRecordContent::TypeToNumber(q.second)));
a51b52c9 252 }
f7896b52 253 fclose(fp);
254
a51b52c9 255 for(unsigned int n = 0; n < numworkers; ++n) {
256 pthread_create(&workers[n], 0, worker, 0);
257 }
258 for(unsigned int n = 0; n < numworkers; ++n) {
259 void* status;
260 pthread_join(workers[n], &status);
261 }
4106b16e 262
263 using namespace boost::accumulators;
264 typedef accumulator_set<
265 unsigned int
266 , stats<boost::accumulators::tag::median(with_p_square_quantile),
267 boost::accumulators::tag::mean(immediate)
268 >
269 > acc_t;
270
271 acc_t udpspeeds, tcpspeeds, qps;
272
273 typedef map<time_t, uint32_t> counts_t;
274 counts_t counts;
275
276 BOOST_FOREACH(const BenchQuery& bq, g_queries) {
277 counts[bq.answerSecond]++;
278 udpspeeds(bq.udpUsec);
279 tcpspeeds(bq.tcpUsec);
280 }
281
282 BOOST_FOREACH(const counts_t::value_type& val, counts) {
283 qps(val.second);
284 }
285
286 cout<<"Average qps: "<<mean(qps)<<", median qps: "<<median(qps)<<endl;
287 cout<<"Average UDP latency: "<<mean(udpspeeds)<<"usec, median: "<<median(udpspeeds)<<"usec"<<endl;
288 cout<<"Average TCP latency: "<<mean(tcpspeeds)<<"usec, median: "<<median(tcpspeeds)<<"usec"<<endl;
289
f7896b52 290 cout<<"OK: "<<g_OK<<", network errors: "<<g_networkErrors<<", other errors: "<<g_otherErrors<<endl;
36658df5 291 cout<<"Timeouts: "<<g_timeOuts<<endl;
292 cout<<"Truncateds: "<<g_truncates<<", auth answers: "<<g_authAnswers<<endl;
a51b52c9 293}
294catch(std::exception &e)
295{
296 cerr<<"Fatal: "<<e.what()<<endl;
297}