1712b2f30Ssthen /*
2712b2f30Ssthen * testcode/unitecs.c - unit test for ecs routines.
3712b2f30Ssthen *
4712b2f30Ssthen * Copyright (c) 2013, NLnet Labs. All rights reserved.
5712b2f30Ssthen *
6712b2f30Ssthen * This software is open source.
7712b2f30Ssthen *
8712b2f30Ssthen * Redistribution and use in source and binary forms, with or without
9712b2f30Ssthen * modification, are permitted provided that the following conditions
10712b2f30Ssthen * are met:
11712b2f30Ssthen *
12712b2f30Ssthen * Redistributions of source code must retain the above copyright notice,
13712b2f30Ssthen * this list of conditions and the following disclaimer.
14712b2f30Ssthen *
15712b2f30Ssthen * Redistributions in binary form must reproduce the above copyright notice,
16712b2f30Ssthen * this list of conditions and the following disclaimer in the documentation
17712b2f30Ssthen * and/or other materials provided with the distribution.
18712b2f30Ssthen *
19712b2f30Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may
20712b2f30Ssthen * be used to endorse or promote products derived from this software without
21712b2f30Ssthen * specific prior written permission.
22712b2f30Ssthen *
23712b2f30Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24712b2f30Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25712b2f30Ssthen * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26712b2f30Ssthen * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27712b2f30Ssthen * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28712b2f30Ssthen * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29712b2f30Ssthen * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30712b2f30Ssthen * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31712b2f30Ssthen * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32712b2f30Ssthen * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33712b2f30Ssthen * POSSIBILITY OF SUCH DAMAGE.
34712b2f30Ssthen *
35712b2f30Ssthen */
36712b2f30Ssthen
37712b2f30Ssthen /**
38712b2f30Ssthen * \file
39712b2f30Ssthen * Calls ecs related unit tests. Exits with code 1 on a failure.
40712b2f30Ssthen */
41712b2f30Ssthen
42712b2f30Ssthen #include "config.h"
43712b2f30Ssthen
44712b2f30Ssthen #ifdef CLIENT_SUBNET
45712b2f30Ssthen
46712b2f30Ssthen #include "util/log.h"
47712b2f30Ssthen #include "util/module.h"
48712b2f30Ssthen #include "testcode/unitmain.h"
49712b2f30Ssthen #include "edns-subnet/addrtree.h"
50712b2f30Ssthen #include "edns-subnet/subnetmod.h"
51712b2f30Ssthen
52712b2f30Ssthen /*
53712b2f30Ssthen void printkey(addrkey_t *k, addrlen_t bits)
54712b2f30Ssthen {
55712b2f30Ssthen int byte;
56712b2f30Ssthen int bytes = bits/8 + ((bits%8)>0);
57712b2f30Ssthen char msk = 0xFF;
58712b2f30Ssthen for (byte = 0; byte < bytes; byte++) {
59712b2f30Ssthen //~ if (byte+1 == bytes)
60712b2f30Ssthen //~ msk = 0xFF<<(8-bits%8);
61712b2f30Ssthen printf("%02x ", k[byte]&msk);
62712b2f30Ssthen }
63712b2f30Ssthen }
64712b2f30Ssthen
65712b2f30Ssthen void print_tree(struct addrnode* node, int indent, int maxdepth)
66712b2f30Ssthen {
67712b2f30Ssthen struct addredge* edge;
68712b2f30Ssthen int i, s, byte;
69712b2f30Ssthen if (indent == 0) printf("-----Tree-----\n");
70712b2f30Ssthen if (indent > maxdepth) {
71712b2f30Ssthen printf("\n");
72712b2f30Ssthen return;
73712b2f30Ssthen }
74712b2f30Ssthen printf("[node elem:%d] (%d)\n", node->elem != NULL, node);
75712b2f30Ssthen for (i = 0; i<2; i++) {
76712b2f30Ssthen if (node->edge[i]) {
77712b2f30Ssthen for (s = 0; s < indent; s++) printf(" ");
78712b2f30Ssthen printkey(node->edge[i]->str, node->edge[i]->len);
79712b2f30Ssthen printf("(len %d bits, %d bytes) ", node->edge[i]->len,
80712b2f30Ssthen node->edge[i]->len/8 + ((node->edge[i]->len%8)>0));
81712b2f30Ssthen print_tree(node->edge[i]->node, indent+1, maxdepth);
82712b2f30Ssthen }
83712b2f30Ssthen }
84712b2f30Ssthen if (indent == 0) printf("-----Tree-----");
85712b2f30Ssthen }
86712b2f30Ssthen */
87712b2f30Ssthen
88712b2f30Ssthen /* what should we check?
89712b2f30Ssthen * X - is it balanced? (a node with 1 child should not have
90712b2f30Ssthen * a node with 1 child MUST have elem
91712b2f30Ssthen * child must be sub of parent
92712b2f30Ssthen * edge must be longer than parent edge
93712b2f30Ssthen * */
addrtree_inconsistent_subtree(struct addrtree * tree,struct addredge * parent_edge,addrlen_t depth)94712b2f30Ssthen static int addrtree_inconsistent_subtree(struct addrtree* tree,
95712b2f30Ssthen struct addredge* parent_edge, addrlen_t depth)
96712b2f30Ssthen {
97712b2f30Ssthen struct addredge* edge;
98712b2f30Ssthen struct addrnode* node = parent_edge->node;
99712b2f30Ssthen int childcount, i, r;
100712b2f30Ssthen if (depth > tree->max_depth) return 15;
101712b2f30Ssthen childcount = (node->edge[0] != NULL) + (node->edge[1] != NULL);
102712b2f30Ssthen /* Only nodes with 2 children should possibly have no element. */
103712b2f30Ssthen if (childcount < 2 && !node->elem) return 10;
104712b2f30Ssthen for (i = 0; i<2; i++) {
105712b2f30Ssthen edge = node->edge[i];
106712b2f30Ssthen if (!edge) continue;
107712b2f30Ssthen if (!edge->node) return 11;
108712b2f30Ssthen if (!edge->str) return 12;
109712b2f30Ssthen if (edge->len <= parent_edge->len) return 13;
110712b2f30Ssthen if (!unittest_wrapper_addrtree_issub(parent_edge->str,
111712b2f30Ssthen parent_edge->len, edge->str, edge->len, 0))
112712b2f30Ssthen return 14;
113712b2f30Ssthen if ((r = addrtree_inconsistent_subtree(tree, edge, depth+1)) != 0)
114712b2f30Ssthen return 100+r;
115712b2f30Ssthen }
116712b2f30Ssthen return 0;
117712b2f30Ssthen }
118712b2f30Ssthen
addrtree_inconsistent(struct addrtree * tree)119712b2f30Ssthen static int addrtree_inconsistent(struct addrtree* tree)
120712b2f30Ssthen {
121712b2f30Ssthen struct addredge* edge;
122712b2f30Ssthen int i, r;
123712b2f30Ssthen
124712b2f30Ssthen if (!tree) return 0;
125712b2f30Ssthen if (!tree->root) return 1;
126712b2f30Ssthen
127712b2f30Ssthen for (i = 0; i<2; i++) {
128712b2f30Ssthen edge = tree->root->edge[i];
129712b2f30Ssthen if (!edge) continue;
130712b2f30Ssthen if (!edge->node) return 3;
131712b2f30Ssthen if (!edge->str) return 4;
132712b2f30Ssthen if ((r = addrtree_inconsistent_subtree(tree, edge, 1)) != 0)
133712b2f30Ssthen return r;
134712b2f30Ssthen }
135712b2f30Ssthen return 0;
136712b2f30Ssthen }
137712b2f30Ssthen
randomkey(addrkey_t ** k,int maxlen)138712b2f30Ssthen static addrlen_t randomkey(addrkey_t **k, int maxlen)
139712b2f30Ssthen {
140712b2f30Ssthen int byte;
141712b2f30Ssthen int bits = rand() % maxlen;
142712b2f30Ssthen int bytes = bits/8 + (bits%8>0); /*ceil*/
143712b2f30Ssthen *k = (addrkey_t *) malloc(bytes * sizeof(addrkey_t));
144712b2f30Ssthen for (byte = 0; byte < bytes; byte++) {
145712b2f30Ssthen (*k)[byte] = (addrkey_t)(rand() & 0xFF);
146712b2f30Ssthen }
147712b2f30Ssthen return (addrlen_t)bits;
148712b2f30Ssthen }
149712b2f30Ssthen
elemfree(void * envptr,void * elemptr)150712b2f30Ssthen static void elemfree(void *envptr, void *elemptr)
151712b2f30Ssthen {
152712b2f30Ssthen struct reply_info *elem = (struct reply_info *)elemptr;
153712b2f30Ssthen (void)envptr;
154712b2f30Ssthen free(elem);
155712b2f30Ssthen }
156712b2f30Ssthen
consistency_test(void)157712b2f30Ssthen static void consistency_test(void)
158712b2f30Ssthen {
159712b2f30Ssthen addrlen_t l;
160712b2f30Ssthen time_t i;
161dc90232dSsthen uint32_t count;
162712b2f30Ssthen addrkey_t *k;
163712b2f30Ssthen struct addrtree* t;
164712b2f30Ssthen struct module_env env;
165712b2f30Ssthen struct reply_info *elem;
166712b2f30Ssthen time_t timenow = 0;
167712b2f30Ssthen unit_show_func("edns-subnet/addrtree.h", "Tree consistency check");
168712b2f30Ssthen srand(9195); /* just some value for reproducibility */
169712b2f30Ssthen
170712b2f30Ssthen t = addrtree_create(100, &elemfree, &unittest_wrapper_subnetmod_sizefunc, &env, 0);
171712b2f30Ssthen count = t->node_count;
172712b2f30Ssthen unit_assert(count == 0);
173712b2f30Ssthen for (i = 0; i < 1000; i++) {
174712b2f30Ssthen l = randomkey(&k, 128);
175712b2f30Ssthen elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
176*0e9b6f9fSsthen addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow, 0);
177712b2f30Ssthen /* This should always hold because no items ever expire. They
178712b2f30Ssthen * could be overwritten, though. */
179712b2f30Ssthen unit_assert( count <= t->node_count );
180712b2f30Ssthen count = t->node_count;
181712b2f30Ssthen free(k);
182712b2f30Ssthen unit_assert( !addrtree_inconsistent(t) );
183712b2f30Ssthen }
184712b2f30Ssthen addrtree_delete(t);
185712b2f30Ssthen
186712b2f30Ssthen unit_show_func("edns-subnet/addrtree.h", "Tree consistency with purge");
187712b2f30Ssthen t = addrtree_create(8, &elemfree, &unittest_wrapper_subnetmod_sizefunc, &env, 0);
188712b2f30Ssthen unit_assert(t->node_count == 0);
189712b2f30Ssthen for (i = 0; i < 1000; i++) {
190712b2f30Ssthen l = randomkey(&k, 128);
191712b2f30Ssthen elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
192*0e9b6f9fSsthen addrtree_insert(t, k, l, 64, elem, i + 10, i, 0);
193712b2f30Ssthen free(k);
194712b2f30Ssthen unit_assert( !addrtree_inconsistent(t) );
195712b2f30Ssthen }
196712b2f30Ssthen addrtree_delete(t);
197712b2f30Ssthen
198712b2f30Ssthen unit_show_func("edns-subnet/addrtree.h", "Tree consistency with limit");
199712b2f30Ssthen t = addrtree_create(8, &elemfree, &unittest_wrapper_subnetmod_sizefunc, &env, 27);
200712b2f30Ssthen unit_assert(t->node_count == 0);
201712b2f30Ssthen for (i = 0; i < 1000; i++) {
202712b2f30Ssthen l = randomkey(&k, 128);
203712b2f30Ssthen elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
204*0e9b6f9fSsthen addrtree_insert(t, k, l, 64, elem, i + 10, i, 0);
205712b2f30Ssthen unit_assert( t->node_count <= 27);
206712b2f30Ssthen free(k);
207712b2f30Ssthen unit_assert( !addrtree_inconsistent(t) );
208712b2f30Ssthen }
209712b2f30Ssthen addrtree_delete(t);
210712b2f30Ssthen }
211712b2f30Ssthen
issub_test(void)212712b2f30Ssthen static void issub_test(void)
213712b2f30Ssthen {
214712b2f30Ssthen addrkey_t k1[] = {0x55, 0x55, 0x5A};
215712b2f30Ssthen addrkey_t k2[] = {0x55, 0x5D, 0x5A};
216712b2f30Ssthen unit_show_func("edns-subnet/addrtree.h", "issub");
217712b2f30Ssthen unit_assert( !unittest_wrapper_addrtree_issub(k1, 24, k2, 24, 0) );
218712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_issub(k1, 8, k2, 16, 0) );
219712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_issub(k2, 12, k1, 13, 0) );
220712b2f30Ssthen unit_assert( !unittest_wrapper_addrtree_issub(k1, 16, k2, 12, 0) );
221712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_issub(k1, 12, k2, 12, 0) );
222712b2f30Ssthen unit_assert( !unittest_wrapper_addrtree_issub(k1, 13, k2, 13, 0) );
223712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_issub(k1, 24, k2, 24, 13) );
224712b2f30Ssthen unit_assert( !unittest_wrapper_addrtree_issub(k1, 24, k2, 20, 13) );
225712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_issub(k1, 20, k2, 24, 13) );
226712b2f30Ssthen }
227712b2f30Ssthen
getbit_test(void)228712b2f30Ssthen static void getbit_test(void)
229712b2f30Ssthen {
230712b2f30Ssthen addrkey_t k1[] = {0x55, 0x55, 0x5A};
231712b2f30Ssthen int i;
232712b2f30Ssthen unit_show_func("edns-subnet/addrtree.h", "getbit");
233712b2f30Ssthen for(i = 0; i<20; i++) {
234712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_getbit(k1, 20, (addrlen_t)i) == (i&1) );
235712b2f30Ssthen }
236712b2f30Ssthen }
237712b2f30Ssthen
bits_common_test(void)238712b2f30Ssthen static void bits_common_test(void)
239712b2f30Ssthen {
240712b2f30Ssthen addrkey_t k1[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
241712b2f30Ssthen addrkey_t k2[] = {0,0,0,0,0,0,0,0};
242712b2f30Ssthen addrlen_t i;
243712b2f30Ssthen
244712b2f30Ssthen unit_show_func("edns-subnet/addrtree.h", "bits_common");
245712b2f30Ssthen for(i = 0; i<64; i++) {
246712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k1, 64, i) == 64 );
247712b2f30Ssthen }
248712b2f30Ssthen for(i = 0; i<8; i++) {
249712b2f30Ssthen k2[i] = k1[i]^(1<<i);
250712b2f30Ssthen }
251712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 0) == 0*8+7 );
252712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 8) == 1*8+6 );
253712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 16) == 2*8+5 );
254712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 24) == 3*8+4 );
255712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 32) == 4*8+3 );
256712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 40) == 5*8+2 );
257712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 48) == 6*8+1 );
258712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 56) == 7*8+0 );
259712b2f30Ssthen }
260712b2f30Ssthen
cmpbit_test(void)261712b2f30Ssthen static void cmpbit_test(void)
262712b2f30Ssthen {
263712b2f30Ssthen addrkey_t k1[] = {0xA5, 0x0F};
264712b2f30Ssthen addrkey_t k2[] = {0x5A, 0xF0};
265712b2f30Ssthen addrlen_t i;
266712b2f30Ssthen
267712b2f30Ssthen unit_show_func("edns-subnet/addrtree.h", "cmpbit");
268712b2f30Ssthen for(i = 0; i<16; i++) {
269712b2f30Ssthen unit_assert( !unittest_wrapper_addrtree_cmpbit(k1,k1,i) );
270712b2f30Ssthen unit_assert( unittest_wrapper_addrtree_cmpbit(k1,k2,i) );
271712b2f30Ssthen }
272712b2f30Ssthen }
273712b2f30Ssthen
ecs_test(void)274712b2f30Ssthen void ecs_test(void)
275712b2f30Ssthen {
276712b2f30Ssthen unit_show_feature("ecs");
277712b2f30Ssthen cmpbit_test();
278712b2f30Ssthen bits_common_test();
279712b2f30Ssthen getbit_test();
280712b2f30Ssthen issub_test();
281712b2f30Ssthen consistency_test();
282712b2f30Ssthen }
283712b2f30Ssthen #endif /* CLIENT_SUBNET */
284712b2f30Ssthen
285