]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/test-packetcache_cc.cc
Merge pull request #5461 from rgacogne/rec-cache-index
[thirdparty/pdns.git] / pdns / test-packetcache_cc.cc
1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
3
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
7 #include <boost/test/unit_test.hpp>
8 #include <boost/test/floating_point_comparison.hpp>
9 #include "iputils.hh"
10 #include "nameserver.hh"
11 #include "statbag.hh"
12 #include "auth-packetcache.hh"
13 #include "auth-querycache.hh"
14 #include "arguments.hh"
15 #include <utility>
16 extern StatBag S;
17
18 BOOST_AUTO_TEST_SUITE(packetcache_cc)
19
20 BOOST_AUTO_TEST_CASE(test_AuthQueryCacheSimple) {
21 AuthQueryCache QC;
22 QC.setMaxEntries(1000000);
23
24 vector<DNSZoneRecord> records;
25
26 BOOST_CHECK_EQUAL(QC.size(), 0);
27 QC.insert(DNSName("hello"), QType(QType::A), records, 3600, 1);
28 BOOST_CHECK_EQUAL(QC.size(), 1);
29 BOOST_CHECK_EQUAL(QC.purge(), 1);
30 BOOST_CHECK_EQUAL(QC.size(), 0);
31
32 uint64_t counter=0;
33 try {
34 for(counter = 0; counter < 100000; ++counter) {
35 DNSName a=DNSName("hello ")+DNSName(std::to_string(counter));
36 BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
37
38 QC.insert(a, QType(QType::A), records, 3600, 1);
39 if(!QC.purge(a.toString()))
40 BOOST_FAIL("Could not remove entry we just added to the query cache!");
41 QC.insert(a, QType(QType::A), records, 3600, 1);
42 }
43
44 BOOST_CHECK_EQUAL(QC.size(), counter);
45
46 uint64_t delcounter=0;
47 for(delcounter=0; delcounter < counter/100; ++delcounter) {
48 DNSName a=DNSName("hello ")+DNSName(std::to_string(delcounter));
49 BOOST_CHECK_EQUAL(QC.purge(a.toString()), 1);
50 }
51
52 BOOST_CHECK_EQUAL(QC.size(), counter-delcounter);
53
54 uint64_t matches=0;
55 vector<DNSZoneRecord> entry;
56 int64_t expected=counter-delcounter;
57 for(; delcounter < counter; ++delcounter) {
58 if(QC.getEntry(DNSName("hello ")+DNSName(std::to_string(delcounter)), QType(QType::A), entry, 1)) {
59 matches++;
60 }
61 }
62 BOOST_CHECK_EQUAL(matches, expected);
63 BOOST_CHECK_EQUAL(entry.size(), records.size());
64 }
65 catch(PDNSException& e) {
66 cerr<<"Had error: "<<e.reason<<endl;
67 throw;
68 }
69
70 }
71
72 static AuthQueryCache* g_QC;
73 static AtomicCounter g_QCmissing;
74
75 static void *threadQCMangler(void* a)
76 try
77 {
78 vector<DNSZoneRecord> records;
79 unsigned int offset=(unsigned int)(unsigned long)a;
80 for(unsigned int counter=0; counter < 100000; ++counter)
81 g_QC->insert(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), records, 3600, 1);
82 return 0;
83 }
84 catch(PDNSException& e) {
85 cerr<<"Had error: "<<e.reason<<endl;
86 throw;
87 }
88
89 static void *threadQCReader(void* a)
90 try
91 {
92 unsigned int offset=(unsigned int)(unsigned long)a;
93 vector<DNSZoneRecord> entry;
94 for(unsigned int counter=0; counter < 100000; ++counter)
95 if(!g_QC->getEntry(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), entry, 1)) {
96 g_QCmissing++;
97 }
98 return 0;
99 }
100 catch(PDNSException& e) {
101 cerr<<"Had error in threadQCReader: "<<e.reason<<endl;
102 throw;
103 }
104
105 BOOST_AUTO_TEST_CASE(test_QueryCacheThreaded) {
106 try {
107 AuthQueryCache QC;
108 QC.setMaxEntries(1000000);
109 g_QC=&QC;
110 pthread_t tid[4];
111 for(int i=0; i < 4; ++i)
112 pthread_create(&tid[i], 0, threadQCMangler, (void*)(i*1000000UL));
113 void* res;
114 for(int i=0; i < 4 ; ++i)
115 pthread_join(tid[i], &res);
116
117 BOOST_CHECK_EQUAL(QC.size() + S.read("deferred-cache-inserts"), 400000);
118 BOOST_CHECK_SMALL(1.0*S.read("deferred-cache-inserts"), 10000.0);
119
120 for(int i=0; i < 4; ++i)
121 pthread_create(&tid[i], 0, threadQCReader, (void*)(i*1000000UL));
122 for(int i=0; i < 4 ; ++i)
123 pthread_join(tid[i], &res);
124
125 BOOST_CHECK(S.read("deferred-cache-inserts") + S.read("deferred-cache-lookup") >= g_QCmissing);
126 // BOOST_CHECK_EQUAL(S.read("deferred-cache-lookup"), 0); // cache cleaning invalidates this
127 }
128 catch(PDNSException& e) {
129 cerr<<"Had error: "<<e.reason<<endl;
130 throw;
131 }
132
133 }
134
135 static AuthPacketCache* g_PC;
136 static AtomicCounter g_PCmissing;
137
138 static void *threadPCMangler(void* a)
139 try
140 {
141 unsigned int offset=(unsigned int)(unsigned long)a;
142 for(unsigned int counter=0; counter < 100000; ++counter) {
143 vector<uint8_t> pak;
144 DNSName qname = DNSName("hello ")+DNSName(std::to_string(counter+offset));
145
146 DNSPacketWriter pw(pak, qname, QType::A);
147 DNSPacket q(true);
148 q.parse((char*)&pak[0], pak.size());
149
150 pak.clear();
151 DNSPacketWriter pw2(pak, qname, QType::A);
152 pw2.startRecord(qname, QType::A, 16, 1, DNSResourceRecord::ANSWER);
153 pw2.xfrIP(htonl(0x7f000001));
154 pw2.commit();
155
156 DNSPacket r(false);
157 r.parse((char*)&pak[0], pak.size());
158
159 /* this step is necessary to get a valid hash */
160 DNSPacket cached(false);
161 g_PC->get(&q, &cached);
162
163 g_PC->insert(&q, &r, 10);
164 }
165
166 return 0;
167 }
168 catch(PDNSException& e) {
169 cerr<<"Had error: "<<e.reason<<endl;
170 throw;
171 }
172
173 static void *threadPCReader(void* a)
174 try
175 {
176 unsigned int offset=(unsigned int)(unsigned long)a;
177 vector<DNSZoneRecord> entry;
178 for(unsigned int counter=0; counter < 100000; ++counter) {
179 vector<uint8_t> pak;
180 DNSName qname = DNSName("hello ")+DNSName(std::to_string(counter+offset));
181
182 DNSPacketWriter pw(pak, qname, QType::A);
183 DNSPacket q(true);
184 q.parse((char*)&pak[0], pak.size());
185 DNSPacket r(false);
186
187 if(!g_PC->get(&q, &r)) {
188 g_PCmissing++;
189 }
190 }
191
192 return 0;
193 }
194 catch(PDNSException& e) {
195 cerr<<"Had error in threadPCReader: "<<e.reason<<endl;
196 throw;
197 }
198
199 BOOST_AUTO_TEST_CASE(test_PacketCacheThreaded) {
200 try {
201 AuthPacketCache PC;
202 PC.setMaxEntries(1000000);
203 PC.setTTL(20);
204
205 g_PC=&PC;
206 pthread_t tid[4];
207 for(int i=0; i < 4; ++i)
208 pthread_create(&tid[i], 0, threadPCMangler, (void*)(i*1000000UL));
209 void* res;
210 for(int i=0; i < 4 ; ++i)
211 pthread_join(tid[i], &res);
212
213 BOOST_CHECK_EQUAL(PC.size() + S.read("deferred-packetcache-inserts"), 400000);
214 BOOST_CHECK_SMALL(1.0*S.read("deferred-packetcache-inserts"), 10000.0);
215
216 for(int i=0; i < 4; ++i)
217 pthread_create(&tid[i], 0, threadPCReader, (void*)(i*1000000UL));
218 for(int i=0; i < 4 ; ++i)
219 pthread_join(tid[i], &res);
220
221 /*
222 cerr<<"Misses: "<<S.read("packetcache-miss")<<endl;
223 cerr<<"Hits: "<<S.read("packetcache-hit")<<endl;
224 cerr<<"Deferred inserts: "<<S.read("deferred-packetcache-inserts")<<endl;
225 cerr<<"Deferred lookups: "<<S.read("deferred-packetcache-lookup")<<endl;
226 */
227 BOOST_CHECK_EQUAL(g_PCmissing + S.read("packetcache-hit"), 400000);
228 BOOST_CHECK_GT(S.read("deferred-packetcache-inserts") + S.read("deferred-packetcache-lookup"), g_PCmissing);
229 }
230 catch(PDNSException& e) {
231 cerr<<"Had error: "<<e.reason<<endl;
232 throw;
233 }
234
235 }
236
237 bool g_stopCleaning;
238 static void *cacheCleaner(void*)
239 try
240 {
241 while(!g_stopCleaning) {
242 g_QC->cleanup();
243 }
244
245 return 0;
246 }
247 catch(PDNSException& e) {
248 cerr<<"Had error in cacheCleaner: "<<e.reason<<endl;
249 throw;
250 }
251
252 BOOST_AUTO_TEST_CASE(test_QueryCacheClean) {
253 try {
254 AuthQueryCache QC;
255 QC.setMaxEntries(10000);
256 vector<DNSZoneRecord> records;
257
258 for(unsigned int counter = 0; counter < 1000000; ++counter) {
259 QC.insert(DNSName("hello ")+DNSName(std::to_string(counter)), QType(QType::A), records, 1, 1);
260 }
261
262 sleep(1);
263
264 g_QC=&QC;
265 pthread_t tid[4];
266
267 pthread_create(&tid[0], 0, threadQCReader, (void*)(0*1000000UL));
268 pthread_create(&tid[1], 0, threadQCReader, (void*)(1*1000000UL));
269 pthread_create(&tid[2], 0, threadQCReader, (void*)(2*1000000UL));
270 // pthread_create(&tid[2], 0, threadMangler, (void*)(0*1000000UL));
271 pthread_create(&tid[3], 0, cacheCleaner, 0);
272
273 void *res;
274 for(int i=0; i < 3 ; ++i)
275 pthread_join(tid[i], &res);
276 g_stopCleaning=true;
277 pthread_join(tid[3], &res);
278 }
279 catch(PDNSException& e) {
280 cerr<<"Had error in test_QueryCacheClean: "<<e.reason<<endl;
281 throw;
282 }
283 }
284
285 BOOST_AUTO_TEST_CASE(test_AuthPacketCache) {
286 try {
287 ::arg().setSwitch("no-shuffle","Set this to prevent random shuffling of answers - for regression testing")="off";
288
289 AuthPacketCache PC;
290 PC.setTTL(20);
291 PC.setMaxEntries(100000);
292
293 vector<uint8_t> pak;
294 DNSPacket q(true), differentIDQ(true), ednsQ(true), ednsVersion42(true), ednsDO(true), ecs1(true), ecs2(true), ecs3(true);
295 DNSPacket r(false), r2(false);
296
297 {
298 DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
299 q.parse((char*)&pak[0], pak.size());
300
301 differentIDQ.parse((char*)&pak[0], pak.size());
302 differentIDQ.setID(4242);
303
304 pw.addOpt(512, 0, 0);
305 pw.commit();
306 ednsQ.parse((char*)&pak[0], pak.size());
307
308 pak.clear();
309 }
310
311 DNSPacketWriter::optvect_t opts;
312 EDNSSubnetOpts ecsOpts;
313 {
314 DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
315 pw.addOpt(512, 0, 0, DNSPacketWriter::optvect_t(), 42);
316 pw.commit();
317 ednsVersion42.parse((char*)&pak[0], pak.size());
318 pak.clear();
319 }
320
321 {
322 DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
323 pw.addOpt(512, 0, EDNSOpts::DNSSECOK);
324 pw.commit();
325 ednsDO.parse((char*)&pak[0], pak.size());
326 pak.clear();
327 }
328
329 {
330 ecsOpts.source = Netmask(ComboAddress("192.0.2.1"), 32);
331 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(ecsOpts)));
332 DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
333 pw.addOpt(512, 0, 0, opts);
334 pw.commit();
335 ecs1.parse((char*)&pak[0], pak.size());
336 pak.clear();
337 opts.clear();
338 }
339
340 {
341 DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
342 ecsOpts.source = Netmask(ComboAddress("192.0.2.2"), 32);
343 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(ecsOpts)));
344 pw.addOpt(512, 0, 0, opts);
345 pw.commit();
346 ecs2.parse((char*)&pak[0], pak.size());
347 pak.clear();
348 opts.clear();
349 }
350
351 {
352 DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
353 ecsOpts.source = Netmask(ComboAddress("192.0.2.3"), 16);
354 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(ecsOpts)));
355 pw.addOpt(512, 0, 0, opts);
356 pw.commit();
357 ecs3.parse((char*)&pak[0], pak.size());
358 pak.clear();
359 opts.clear();
360 }
361
362 {
363 DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
364 pw.startRecord(DNSName("www.powerdns.com"), QType::A, 16, 1, DNSResourceRecord::ANSWER);
365 pw.xfrIP(htonl(0x7f000001));
366 pw.commit();
367
368 r.parse((char*)&pak[0], pak.size());
369 }
370
371 /* this call is required so the correct hash is set into q->d_hash */
372 BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
373
374 PC.insert(&q, &r, 3600);
375 BOOST_CHECK_EQUAL(PC.size(), 1);
376
377 BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
378 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
379
380 /* different QID, still should match */
381 BOOST_CHECK_EQUAL(PC.get(&differentIDQ, &r2), true);
382 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
383
384 /* with EDNS, should not match */
385 BOOST_CHECK_EQUAL(PC.get(&ednsQ, &r2), false);
386 /* inserting the EDNS-enabled one too */
387 PC.insert(&ednsQ, &r, 3600);
388 BOOST_CHECK_EQUAL(PC.size(), 2);
389
390 /* different EDNS versions, should not match */
391 BOOST_CHECK_EQUAL(PC.get(&ednsVersion42, &r2), false);
392
393 /* EDNS DO set, should not match */
394 BOOST_CHECK_EQUAL(PC.get(&ednsDO, &r2), false);
395
396 /* EDNS Client Subnet set, should not match
397 since not only we don't skip the actual option, but the
398 total EDNS opt RR is still different. */
399 BOOST_CHECK_EQUAL(PC.get(&ecs1, &r2), false);
400
401 /* inserting the version with ECS Client Subnet set,
402 it should NOT replace the existing EDNS one. */
403 PC.insert(&ecs1, &r, 3600);
404 BOOST_CHECK_EQUAL(PC.size(), 3);
405
406 /* different subnet of same size, should NOT match
407 since we don't skip the option */
408 BOOST_CHECK_EQUAL(PC.get(&ecs2, &r2), false);
409 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
410
411 /* different subnet of different size, should NOT match. */
412 BOOST_CHECK_EQUAL(PC.get(&ecs3, &r2), false);
413
414 BOOST_CHECK_EQUAL(PC.purge("www.powerdns.com"), 3);
415 BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
416 BOOST_CHECK_EQUAL(PC.size(), 0);
417
418 PC.insert(&q, &r, 3600);
419 BOOST_CHECK_EQUAL(PC.size(), 1);
420 BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
421 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
422 BOOST_CHECK_EQUAL(PC.purge("com$"), 1);
423 BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
424 BOOST_CHECK_EQUAL(PC.size(), 0);
425
426 PC.insert(&q, &r, 3600);
427 BOOST_CHECK_EQUAL(PC.size(), 1);
428 BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
429 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
430 BOOST_CHECK_EQUAL(PC.purge("powerdns.com$"), 1);
431 BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
432 BOOST_CHECK_EQUAL(PC.size(), 0);
433
434 PC.insert(&q, &r, 3600);
435 BOOST_CHECK_EQUAL(PC.size(), 1);
436 BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
437 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
438 BOOST_CHECK_EQUAL(PC.purge("www.powerdns.com$"), 1);
439 BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
440 BOOST_CHECK_EQUAL(PC.size(), 0);
441
442 PC.insert(&q, &r, 3600);
443 BOOST_CHECK_EQUAL(PC.size(), 1);
444 BOOST_CHECK_EQUAL(PC.purge("www.powerdns.net"), 0);
445 BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
446 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
447 BOOST_CHECK_EQUAL(PC.size(), 1);
448
449 BOOST_CHECK_EQUAL(PC.purge("net$"), 0);
450 BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
451 BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
452 BOOST_CHECK_EQUAL(PC.size(), 1);
453
454 BOOST_CHECK_EQUAL(PC.purge("www.powerdns.com$"), 1);
455 BOOST_CHECK_EQUAL(PC.size(), 0);
456 }
457 catch(PDNSException& e) {
458 cerr<<"Had error in AuthPacketCache: "<<e.reason<<endl;
459 throw;
460 }
461 }
462
463 BOOST_AUTO_TEST_SUITE_END()