2 * testcode/unitneg.c - unit test for negative cache routines.
4 * Copyright (c) 2008, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
38 * Calls negative cache unit tests. Exits with code 1 on a failure.
43 #include "util/net_help.h"
44 #include "util/data/packed_rrset.h"
45 #include "util/data/dname.h"
46 #include "testcode/unitmain.h"
47 #include "validator/val_neg.h"
49 /** verbose unit test for negative cache */
50 static int negverbose = 0;
52 /** debug printout of neg cache */
53 static void print_neg_cache(struct val_neg_cache* neg)
56 struct val_neg_zone* z;
57 struct val_neg_data* d;
58 printf("neg_cache print\n");
59 printf("memuse %d of %d\n", (int)neg->use, (int)neg->max);
60 printf("maxiter %d\n", (int)neg->nsec3_max_iter);
61 printf("%d zones\n", (int)neg->tree.count);
62 RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
63 dname_str(z->name, buf);
65 printf(" len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
66 (int)z->len, z->labs, (int)z->in_use, z->count,
69 RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
71 dname_print(stdout, NULL, z->name);
72 printf(" zone details\n");
73 printf("len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
74 (int)z->len, z->labs, (int)z->in_use, z->count,
78 dname_print(stdout, NULL, z->parent->name);
81 printf("parent=NULL\n");
84 RBTREE_FOR(d, struct val_neg_data*, &z->tree) {
85 dname_str(d->name, buf);
87 printf(" len=%2.2d labs=%d inuse=%d count=%d\n",
88 (int)d->len, d->labs, (int)d->in_use, d->count);
93 /** get static pointer to random zone name */
94 static char* get_random_zone(void)
96 static char zname[256];
97 int labels = random() % 3;
102 for(i=0; i<labels; i++) {
103 labnum = random()%10;
104 snprintf(p, 256-(p-zname), "\003%3.3d", labnum);
107 snprintf(p, 256-(p-zname), "\007example\003com");
111 /** get static pointer to random data names from and to */
112 static void get_random_data(char** fromp, char** top, char* zname)
114 static char buf1[256], buf2[256];
117 int labnum1[10], labnum2[10];
127 lab1 = random() %3 + 1;
128 lab2 = lab1 + random()%3 + 1;
129 for(i=0; i<lab1; i++) {
130 labnum1[i] = random()%100;
131 labnum2[i] = labnum1[i];
133 for(i=lab1; i<lab2; i++) {
134 labnum2[i] = random()%100;
136 } else if(type == 1) {
139 lab1 = random()%3 + 1;
140 for(i=0; i<lab1; i++) {
141 labnum1[i] = random()%100;
143 } else if(type == 2) {
146 lab2 = random()%3 + 1;
147 for(i=0; i<lab2; i++) {
148 labnum2[i] = random()%100;
152 int common = random()%3;
153 lab1 = random() %3 + 1;
154 lab2 = random() %3 + 1;
155 for(i=0; i<common; i++) {
156 labnum1[i] = random()%100;
157 labnum2[i] = labnum1[i];
159 labnum1[common] = random()%100;
160 labnum2[common] = labnum1[common] + random()%20;
161 for(i=common; i<lab1; i++)
162 labnum1[i] = random()%100;
163 for(i=common; i<lab2; i++)
164 labnum2[i] = random()%100;
167 /* construct first */
169 for(i=0; i<lab1; i++) {
170 snprintf(p, 256-(p-buf1), "\003%3.3d", labnum1[i]);
173 snprintf(p, 256-(p-buf1), "%s", zname);
177 for(i=0; i<lab2; i++) {
178 snprintf(p, 256-(p-buf2)-3, "\003%3.3d", labnum2[i]);
181 snprintf(p, 256-(p-buf2)-3, "%s", zname);
182 buf2[0] = (char)(strlen(buf2+2)+1);
186 log_nametypeclass(0, "add from", (uint8_t*)buf1, 0, 0);
187 log_nametypeclass(0, "add to ", (uint8_t*)buf2+2, 0, 0);
191 /** add a random item */
192 static void add_item(struct val_neg_cache* neg)
194 struct val_neg_zone* z;
195 struct packed_rrset_data rd;
196 struct ub_packed_rrset_key nsec;
200 char* zname = get_random_zone();
203 lock_basic_lock(&neg->lock);
205 log_nametypeclass(0, "add to zone", (uint8_t*)zname, 0, 0);
206 z = neg_find_zone(neg, (uint8_t*)zname, strlen(zname)+1,
209 z = neg_create_zone(neg, (uint8_t*)zname, strlen(zname)+1,
213 val_neg_zone_take_inuse(z);
215 /* construct random NSEC item */
216 get_random_data(&from, &to, zname);
218 /* create nsec and insert it */
219 memset(&rd, 0, sizeof(rd));
220 memset(&nsec, 0, sizeof(nsec));
221 nsec.rk.dname = (uint8_t*)from;
222 nsec.rk.dname_len = strlen(from)+1;
223 nsec.rk.type = htons(LDNS_RR_TYPE_NSEC);
224 nsec.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
225 nsec.entry.data = &rd;
226 rd.security = sec_status_secure;
232 rd.rr_data = &rr_data;
233 rr_data = (uint8_t*)to;
235 neg_insert_data(neg, z, &nsec);
236 lock_basic_unlock(&neg->lock);
239 /** remove a random item */
240 static void remove_item(struct val_neg_cache* neg)
243 struct val_neg_data* d;
245 struct val_neg_zone* z;
247 lock_basic_lock(&neg->lock);
248 if(neg->tree.count == 0) {
249 lock_basic_unlock(&neg->lock);
250 return; /* nothing to delete */
253 /* pick a random zone */
254 walk = rbtree_first(&neg->tree); /* first highest parent, big count */
255 z = (struct val_neg_zone*)walk;
256 n = random() % (int)(z->count);
258 printf("neg stress delete zone %d\n", n);
260 walk = rbtree_first(&neg->tree);
261 z = (struct val_neg_zone*)walk;
262 while(i!=n+1 && walk && walk != RBTREE_NULL && !z->in_use) {
263 walk = rbtree_next(walk);
264 z = (struct val_neg_zone*)walk;
268 if(!walk || walk == RBTREE_NULL) {
269 lock_basic_unlock(&neg->lock);
273 lock_basic_unlock(&neg->lock);
277 log_nametypeclass(0, "delete zone", z->name, 0, 0);
279 /* pick a random nsec item. - that is in use */
280 walk = rbtree_first(&z->tree); /* first is highest parent */
281 d = (struct val_neg_data*)walk;
282 n = random() % (int)(d->count);
284 printf("neg stress delete item %d\n", n);
286 walk = rbtree_first(&z->tree);
287 d = (struct val_neg_data*)walk;
288 while(i!=n+1 && walk && walk != RBTREE_NULL && !d->in_use) {
289 walk = rbtree_next(walk);
290 d = (struct val_neg_data*)walk;
294 if(!walk || walk == RBTREE_NULL) {
295 lock_basic_unlock(&neg->lock);
300 log_nametypeclass(0, "neg delete item:", d->name, 0, 0);
301 neg_delete_data(neg, d);
303 lock_basic_unlock(&neg->lock);
306 /** sum up the zone trees */
307 static size_t sumtrees_all(struct val_neg_cache* neg)
310 struct val_neg_zone* z;
311 RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
312 res += z->tree.count;
317 /** sum up the zone trees, in_use only */
318 static size_t sumtrees_inuse(struct val_neg_cache* neg)
321 struct val_neg_zone* z;
322 struct val_neg_data* d;
323 RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
324 /* get count of highest parent for num in use */
325 d = (struct val_neg_data*)rbtree_first(&z->tree);
326 if(d && (rbnode_t*)d!=RBTREE_NULL)
332 /** check if lru is still valid */
333 static void check_lru(struct val_neg_cache* neg)
335 struct val_neg_data* p, *np;
341 unit_assert(neg->first == p);
345 unit_assert(np->prev == p);
347 unit_assert(neg->last == p);
352 inuse = sumtrees_inuse(neg);
354 printf("num lru %d, inuse %d, all %d\n",
355 (int)num, (int)sumtrees_inuse(neg),
356 (int)sumtrees_all(neg));
357 unit_assert( num == inuse);
358 unit_assert( inuse <= sumtrees_all(neg));
361 /** sum up number of items inuse in subtree */
362 static int sum_subtree_inuse(struct val_neg_zone* zone,
363 struct val_neg_data* data)
365 struct val_neg_data* d;
367 RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
368 if(dname_subdomain_c(d->name, data->name)) {
376 /** sum up number of items inuse in subtree */
377 static int sum_zone_subtree_inuse(struct val_neg_cache* neg,
378 struct val_neg_zone* zone)
380 struct val_neg_zone* z;
382 RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
383 if(dname_subdomain_c(z->name, zone->name)) {
391 /** check point in data tree */
392 static void check_data(struct val_neg_zone* zone, struct val_neg_data* data)
394 unit_assert(data->count > 0);
396 unit_assert(data->parent->count >= data->count);
397 if(data->parent->in_use) {
398 unit_assert(data->parent->count > data->count);
400 unit_assert(data->parent->labs == data->labs-1);
401 /* and parent must be one label shorter */
402 unit_assert(data->name[0] == (data->len-data->parent->len-1));
403 unit_assert(query_dname_compare(data->name + data->name[0]+1,
404 data->parent->name) == 0);
407 unit_assert(dname_is_root(data->name));
410 unit_assert(data->count == sum_subtree_inuse(zone, data));
413 /** check if tree of data in zone is valid */
414 static void checkzonetree(struct val_neg_zone* zone)
416 struct val_neg_data* d;
418 /* check all data in tree */
419 RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
424 /** check if negative cache is still valid */
425 static void check_zone_invariants(struct val_neg_cache* neg,
426 struct val_neg_zone* zone)
428 unit_assert(zone->nsec3_hash == 0);
429 unit_assert(zone->tree.cmp == &val_neg_data_compare);
430 unit_assert(zone->count != 0);
432 if(zone->tree.count == 0)
433 unit_assert(!zone->in_use);
436 /* details on error */
437 log_nametypeclass(0, "zone", zone->name, 0, 0);
438 log_err("inuse %d count=%d tree.count=%d",
439 zone->in_use, zone->count,
440 (int)zone->tree.count);
442 print_neg_cache(neg);
444 unit_assert(zone->in_use);
448 unit_assert(zone->parent->count >= zone->count);
449 if(zone->parent->in_use) {
450 unit_assert(zone->parent->count > zone->count);
452 unit_assert(zone->parent->labs == zone->labs-1);
453 /* and parent must be one label shorter */
454 unit_assert(zone->name[0] == (zone->len-zone->parent->len-1));
455 unit_assert(query_dname_compare(zone->name + zone->name[0]+1,
456 zone->parent->name) == 0);
459 unit_assert(dname_is_root(zone->name));
462 unit_assert(zone->count == sum_zone_subtree_inuse(neg, zone));
464 /* check structure of zone data tree */
468 /** check if negative cache is still valid */
469 static void check_neg_invariants(struct val_neg_cache* neg)
471 struct val_neg_zone* z;
472 /* check structure of LRU list */
473 lock_basic_lock(&neg->lock);
475 unit_assert(neg->max == 1024*1024);
476 unit_assert(neg->nsec3_max_iter == 1500);
477 unit_assert(neg->tree.cmp == &val_neg_zone_compare);
479 if(neg->tree.count == 0) {
481 unit_assert(neg->tree.count == 0);
482 unit_assert(neg->first == NULL);
483 unit_assert(neg->last == NULL);
484 unit_assert(neg->use == 0);
485 lock_basic_unlock(&neg->lock);
489 unit_assert(neg->first != NULL);
490 unit_assert(neg->last != NULL);
492 RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
493 check_zone_invariants(neg, z);
495 lock_basic_unlock(&neg->lock);
498 /** perform stress test on insert and delete in neg cache */
499 static void stress_test(struct val_neg_cache* neg)
503 printf("negcache test\n");
504 for(i=0; i<100; i++) {
505 if(random() % 10 < 8)
507 else remove_item(neg);
508 check_neg_invariants(neg);
512 printf("neg stress empty\n");
515 check_neg_invariants(neg);
518 printf("neg stress emptied\n");
519 unit_assert(neg->first == NULL);
521 for(i=0; i<100; i++) {
522 if(random() % 10 < 8)
524 else remove_item(neg);
525 check_neg_invariants(neg);
531 struct val_neg_cache* neg;
533 unit_show_feature("negative cache");
535 /* create with defaults */
536 neg = val_neg_create(NULL, 1500);
541 neg_cache_delete(neg);