]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/calidns.cc
rec: Don't account chained queries more than once
[thirdparty/pdns.git] / pdns / calidns.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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
7d8a5de7 25#include <iostream>
26#include "dnsparser.hh"
27#include "sstuff.hh"
28#include "misc.hh"
29#include "dnswriter.hh"
30#include "dnsrecords.hh"
31#include <thread>
32#include <atomic>
33#include "statbag.hh"
34#include <fstream>
c1e527e3 35#include <poll.h>
36#include <memory>
c7c8bdee 37#include <boost/program_options.hpp>
7d8a5de7 38using std::thread;
c1e527e3 39using std::unique_ptr;
7d8a5de7 40
41StatBag S;
42
3df2e6c4 43std::atomic<unsigned int> g_recvcounter, g_recvbytes;
7d8a5de7 44volatile bool g_done;
45
c7c8bdee
PL
46namespace po = boost::program_options;
47po::variables_map g_vm;
48
c1e527e3 49void* recvThread(const vector<Socket*>* sockets)
7d8a5de7 50{
c1e527e3 51 vector<pollfd> rfds, fds;
52 for(const auto& s : *sockets) {
53 struct pollfd pfd;
54 pfd.fd = s->getHandle();
55 pfd.events = POLLIN;
56 pfd.revents = 0;
57 rfds.push_back(pfd);
58 }
59
60 int err;
61
62 vector<struct mmsghdr> buf(100);
63 for(auto& m : buf) {
64 fillMSGHdr(&m.msg_hdr, new struct iovec, new char[512], 512, new char[1500], 1500, new ComboAddress("127.0.0.1"));
65 }
66
7d8a5de7 67 while(!g_done) {
c1e527e3 68 fds=rfds;
69
70 err = poll(&fds[0], fds.size(), -1);
71 if(err < 0) {
72 if(errno==EINTR)
73 continue;
74 unixDie("Unable to poll for new UDP events");
75 }
c1e527e3 76
28c266da 77 for(auto &pfd : fds) {
c1e527e3 78 if(pfd.revents & POLLIN) {
79
80 if((err=recvmmsg(pfd.fd, &buf[0], buf.size(), MSG_WAITFORONE, 0)) < 0 ) {
81 if(errno != EAGAIN)
82 cerr<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
83 unixDie("recvmmsg");
84 continue;
85 }
3df2e6c4 86 g_recvcounter+=err;
87 for(int n=0; n < err; ++n)
88 g_recvbytes += buf[n].msg_len;
c1e527e3 89 }
7d8a5de7 90 }
7d8a5de7 91 }
c1e527e3 92
7d8a5de7 93 return 0;
94}
95
96
97void setSocketBuffer(int fd, int optname, uint32_t size)
98{
99 uint32_t psize=0;
100 socklen_t len=sizeof(psize);
101
102 if(!getsockopt(fd, SOL_SOCKET, optname, (char*)&psize, &len) && psize > size) {
103 cerr<<"Not decreasing socket buffer size from "<<psize<<" to "<<size<<endl;
104 return;
105 }
106
107 if (setsockopt(fd, SOL_SOCKET, optname, (char*)&size, sizeof(size)) < 0 )
108 cerr<<"Warning: unable to raise socket buffer size to "<<size<<": "<<strerror(errno)<<endl;
109}
110
111
112static void setSocketReceiveBuffer(int fd, uint32_t size)
113{
114 setSocketBuffer(fd, SO_RCVBUF, size);
115}
116
117static void setSocketSendBuffer(int fd, uint32_t size)
118{
119 setSocketBuffer(fd, SO_SNDBUF, size);
120}
121
fa5958af 122void sendPackets(const vector<Socket*>* sockets, const vector<vector<uint8_t>* >& packets, int qps, ComboAddress dest)
e9a27786 123{
c1e527e3 124 unsigned int burst=100;
e9a27786 125 struct timespec nsec;
126 nsec.tv_sec=0;
c1e527e3 127 nsec.tv_nsec=1*(unsigned long)(burst*1000000000.0/qps);
e9a27786 128 int count=0;
129
c1e527e3 130 struct Unit {
c1e527e3 131 struct msghdr msgh;
132 struct iovec iov;
133 char cbuf[256];
c1e527e3 134 };
ea550a82 135 vector<unique_ptr<Unit> > units;
136 int ret;
c1e527e3 137
fa5958af 138 for(const auto& p : packets) {
e9a27786 139 count++;
e9a27786 140
19f9117b 141 Unit u;
c1e527e3 142
fa5958af 143 fillMSGHdr(&u.msgh, &u.iov, u.cbuf, 0, (char*)&(*p)[0], p->size(), &dest);
19f9117b 144 if((ret=sendmsg((*sockets)[count % sockets->size()]->getHandle(),
145 &u.msgh, 0)))
146 if(ret < 0)
147 unixDie("sendmmsg");
148
149
150 if(!(count%burst))
e9a27786 151 nanosleep(&nsec, 0);
152 }
153}
154
c7c8bdee 155void usage(po::options_description &desc) {
1fe98901 156 cerr<<"Syntax: calidns [OPTIONS] QUERY_FILE DESTINATION INITIAL_QPS HITRATE"<<endl;
c7c8bdee 157 cerr<<desc<<endl;
32823984 158}
7d8a5de7 159
fa5958af 160/*
161 New plan. Set cache hit percentage, which we achieve on a per second basis.
162 So we start with 10000 qps for example, and for 90% cache hit ratio means
163 we take 1000 unique queries and each send them 10 times.
164
165 We then move the 1000 unique queries to the 'known' pool.
166
167 For the next second, say 20000 qps, we know we are going to need 2000 new queries,
168 so we take 2000 from the unknown pool. Then we need 18000 cache hits. We can get 1000 from
169 the known pool, leaving us down 17000. Or, we have 3000 in total now and we need 2000. We simply
170 repeat the 3000 mix we have ~7 times. The 2000 can now go to the known pool too.
171
172 For the next second, say 30000 qps, we'll need 3000 cache misses, which we get from
173 the unknown pool. To this we add 3000 queries from the known pool. Next up we repeat this batch 5
174 times.
175
176 In general the algorithm therefore is:
177
178 1) Calculate number of cache misses required, get them from the unknown pool
179 2) Move those to the known pool
180 3) Fill up to amount of queries we need with random picks from the known pool
181
182*/
7d8a5de7 183
184int main(int argc, char** argv)
185try
186{
c7c8bdee
PL
187 po::options_description desc("Options");
188 desc.add_options()
189 ("help,h", "Show this helpful message")
190 ("version", "Show the version number")
191 ("increment", po::value<float>()->default_value(1.1), "Set the factor to increase the QPS load per run")
192 ("want-recursion", "Set the Recursion Desired flag on queries");
193 po::options_description alloptions;
194 po::options_description hidden("hidden options");
195 hidden.add_options()
196 ("query-file", po::value<string>(), "File with queries")
197 ("destination", po::value<string>(), "Destination address")
d9ea45fc 198 ("initial-qps", po::value<uint32_t>(), "Initial number of queries per second")
c7c8bdee
PL
199 ("hitrate", po::value<double>(), "Aim this percent cache hitrate");
200
201 alloptions.add(desc).add(hidden);
202 po::positional_options_description p;
203 p.add("query-file", 1);
204 p.add("destination", 1);
d9ea45fc 205 p.add("initial-qps", 1);
c7c8bdee
PL
206 p.add("hitrate", 1);
207
208 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
209 po::notify(g_vm);
210
211 if (g_vm.count("help")) {
212 usage(desc);
213 return EXIT_SUCCESS;
1fe98901 214 }
32823984 215
c7c8bdee
PL
216 if (g_vm.count("version")) {
217 cerr<<"calidns "<<VERSION<<endl;
218 return EXIT_SUCCESS;
219 }
1fe98901 220
d9ea45fc 221 if (!(g_vm.count("query-file") && g_vm.count("destination") && g_vm.count("initial-qps") && g_vm.count("hitrate"))) {
c7c8bdee
PL
222 usage(desc);
223 return EXIT_FAILURE;
1fe98901
PL
224 }
225
c7c8bdee
PL
226 float increment = 1.1;
227 try {
228 increment = g_vm["increment"].as<float>();
b5955f7a 229 }
c7c8bdee
PL
230 catch(...) {
231 }
232
233 bool wantRecursion = g_vm.count("want-recursion");
32823984 234
0a76eab7
PL
235 double hitrate = g_vm["hitrate"].as<double>();
236 if (hitrate > 100 || hitrate < 0) {
237 cerr<<"hitrate must be between 0 and 100, not "<<hitrate<<endl;
238 return EXIT_FAILURE;
239 }
240 hitrate /= 100;
d9ea45fc 241 uint32_t qpsstart = g_vm["initial-qps"].as<uint32_t>();
1fe98901 242
c7c8bdee
PL
243 struct sched_param param;
244 param.sched_priority=99;
1fe98901 245
e9a27786 246 if(sched_setscheduler(0, SCHED_FIFO, &param) < 0)
fa5958af 247 cerr<<"Unable to set SCHED_FIFO: "<<strerror(errno)<<endl;
e9a27786 248
c7c8bdee 249 ifstream ifs(g_vm["query-file"].as<string>());
7d8a5de7 250 string line;
251 reportAllTypes();
fa5958af 252 vector<std::shared_ptr<vector<uint8_t> > > unknown, known;
7d8a5de7 253 while(getline(ifs, line)) {
254 vector<uint8_t> packet;
255 boost::trim(line);
2010ac95
RG
256 const auto fields = splitField(line, ' ');
257 DNSPacketWriter pw(packet, DNSName(fields.first), DNSRecordContent::TypeToNumber(fields.second));
1fe98901 258 pw.getHeader()->rd=wantRecursion;
4a6b6ee9 259 pw.getHeader()->id=random();
3df2e6c4 260 if(pw.getHeader()->id % 2) {
261 pw.addOpt(1500, 0, EDNSOpts::DNSSECOK);
262 pw.commit();
263 }
fa5958af 264 unknown.emplace_back(std::make_shared<vector<uint8_t>>(packet));
7d8a5de7 265 }
fa5958af 266 random_shuffle(unknown.begin(), unknown.end());
267 cout<<"Generated "<<unknown.size()<<" ready to use queries"<<endl;
7d8a5de7 268
e9a27786 269 vector<Socket*> sockets;
b807ae7b
PL
270 ComboAddress dest;
271 try {
272 dest = ComboAddress(g_vm["destination"].as<string>(), 53);
273 }
274 catch (PDNSException &e) {
275 cerr<<e.reason<<endl;
276 return EXIT_FAILURE;
277 }
c1e527e3 278 for(int i=0; i < 24; ++i) {
7f363f60 279 Socket *sock = new Socket(dest.sin4.sin_family, SOCK_DGRAM);
c1e527e3 280 // sock->connect(dest);
e9a27786 281 setSocketSendBuffer(sock->getHandle(), 2000000);
282 setSocketReceiveBuffer(sock->getHandle(), 2000000);
283 sockets.push_back(sock);
c1e527e3 284 }
285 new thread(recvThread, &sockets);
34c513f9 286 int qps;
7d8a5de7 287
e9a27786 288 ofstream plot("plot");
1fe98901 289 for(qps=qpsstart;;qps *= increment) {
fa5958af 290 double seconds=1;
d9ea45fc 291 cout<<"Aiming at "<<qps<< "qps (RD="<<wantRecursion<<") for "<<seconds<<" seconds at cache hitrate "<<100.0*hitrate<<"%";
fa5958af 292 unsigned int misses=(1-hitrate)*qps*seconds;
293 unsigned int total=qps*seconds;
b4f5799b
RG
294 if (misses == 0) {
295 misses = 1;
296 }
fa5958af 297 cout<<", need "<<misses<<" misses, "<<total<<" queries, have "<<unknown.size()<<" unknown left!"<<endl;
298
b4f5799b
RG
299 if (misses > unknown.size()) {
300 cerr<<"Not enough queries remaining (need at least "<<misses<<" and got "<<unknown.size()<<", please add more to the query file), exiting."<<endl;
301 exit(1);
302 }
fa5958af 303 vector<vector<uint8_t>*> toSend;
304 unsigned int n;
305 for(n=0; n < misses; ++n) {
306 auto ptr=unknown.back();
307 unknown.pop_back();
308 toSend.push_back(ptr.get());
309 known.push_back(ptr);
310 }
311 for(;n < total; ++n) {
312 toSend.push_back(known[random()%known.size()].get());
313 }
314 random_shuffle(toSend.begin(), toSend.end());
e9a27786 315 g_recvcounter.store(0);
3df2e6c4 316 g_recvbytes=0;
e9a27786 317 DTime dt;
318 dt.set();
319
fa5958af 320 sendPackets(&sockets, toSend, qps, dest);
e9a27786 321
322 auto udiff = dt.udiff();
fa5958af 323 auto realqps=toSend.size()/(udiff/1000000.0);
e9a27786 324 cout<<"Achieved "<<realqps<<"qps"<< " over "<< udiff/1000000.0<<" seconds"<<endl;
325
326 usleep(50000);
fa5958af 327 double perc=g_recvcounter.load()*100.0/toSend.size();
c1e527e3 328 cout<<"Received "<<g_recvcounter.load()<<" packets ("<<perc<<"%)"<<endl;
3df2e6c4 329 plot<<qps<<" "<<realqps<<" "<<perc<<" "<<g_recvcounter.load()/(udiff/1000000.0)<<" " << 8*g_recvbytes.load()/(udiff/1000000.0)<<endl;
330 plot.flush();
7d8a5de7 331 }
e9a27786 332 plot.flush();
333 // t1.detach();
7d8a5de7 334}
335 catch(std::exception& e)
336{
337 cerr<<"Fatal error: "<<e.what()<<endl;
338}