Merge changes from citrix branch into master.
[sliver-openvswitch.git] / tests / test-classifier.c
1 /*
2  * Copyright (c) 2009 Nicira Networks.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* "White box" tests for classifier.
18  *
19  * With very few exceptions, these tests obtain complete coverage of every
20  * basic block and every branch in the classifier implementation, e.g. a clean
21  * report from "gcov -b".  (Covering the exceptions would require finding
22  * collisions in the hash function used for flow data, etc.)
23  *
24  * This test should receive a clean report from "valgrind --leak-check=full":
25  * it frees every heap block that it allocates.
26  */
27
28 #include <config.h>
29 #include <limits.h>
30 #include "classifier.h"
31 #include <errno.h>
32 #include <limits.h>
33 #include "flow.h"
34 #include <limits.h>
35 #include "packets.h"
36
37 #undef NDEBUG
38 #include <assert.h>
39
40 struct test_rule {
41     int aux;                    /* Auxiliary data. */
42     struct cls_rule cls_rule;   /* Classifier rule data. */
43 };
44
45 static struct test_rule *
46 test_rule_from_cls_rule(const struct cls_rule *rule)
47 {
48     return rule ? CONTAINER_OF(rule, struct test_rule, cls_rule) : NULL;
49 }
50
51 /* Trivial (linear) classifier. */
52 struct tcls {
53     size_t n_rules;
54     size_t allocated_rules;
55     struct test_rule **rules;
56 };
57
58 static void
59 tcls_init(struct tcls *tcls)
60 {
61     tcls->n_rules = 0;
62     tcls->allocated_rules = 0;
63     tcls->rules = NULL;
64 }
65
66 static void
67 tcls_destroy(struct tcls *tcls)
68 {
69     if (tcls) {
70         size_t i;
71
72         for (i = 0; i < tcls->n_rules; i++) {
73             free(tcls->rules[i]);
74         }
75         free(tcls->rules);
76     }
77 }
78
79 static int
80 tcls_count_exact(const struct tcls *tcls)
81 {
82     int n_exact;
83     size_t i;
84
85     n_exact = 0;
86     for (i = 0; i < tcls->n_rules; i++) {
87         n_exact += tcls->rules[i]->cls_rule.wc.wildcards == 0;
88     }
89     return n_exact;
90 }
91
92 static bool
93 tcls_is_empty(const struct tcls *tcls)
94 {
95     return tcls->n_rules == 0;
96 }
97
98 static struct test_rule *
99 tcls_insert(struct tcls *tcls, const struct test_rule *rule)
100 {
101     size_t i;
102
103     assert(rule->cls_rule.wc.wildcards || rule->cls_rule.priority == UINT_MAX);
104     for (i = 0; i < tcls->n_rules; i++) {
105         const struct cls_rule *pos = &tcls->rules[i]->cls_rule;
106         if (pos->priority == rule->cls_rule.priority
107             && pos->wc.wildcards == rule->cls_rule.wc.wildcards
108             && flow_equal(&pos->flow, &rule->cls_rule.flow)) {
109             /* Exact match.
110              * XXX flow_equal should ignore wildcarded fields */
111             free(tcls->rules[i]);
112             tcls->rules[i] = xmemdup(rule, sizeof *rule);
113             return tcls->rules[i];
114         } else if (pos->priority <= rule->cls_rule.priority) {
115             break;
116         }
117     }
118
119     if (tcls->n_rules >= tcls->allocated_rules) {
120         tcls->rules = x2nrealloc(tcls->rules, &tcls->allocated_rules,
121                                  sizeof *tcls->rules);
122     }
123     if (i != tcls->n_rules) {
124         memmove(&tcls->rules[i + 1], &tcls->rules[i],
125                 sizeof *tcls->rules * (tcls->n_rules - i));
126     }
127     tcls->rules[i] = xmemdup(rule, sizeof *rule);
128     tcls->n_rules++;
129     return tcls->rules[i];
130 }
131
132 static void
133 tcls_remove(struct tcls *cls, const struct test_rule *rule)
134 {
135     size_t i;
136
137     for (i = 0; i < cls->n_rules; i++) {
138         struct test_rule *pos = cls->rules[i];
139         if (pos == rule) {
140             free(pos);
141             memmove(&cls->rules[i], &cls->rules[i + 1],
142                     sizeof *cls->rules * (cls->n_rules - i - 1));
143             cls->n_rules--;
144             return;
145         }
146     }
147     NOT_REACHED();
148 }
149
150 static uint32_t
151 read_uint32(const void *p)
152 {
153     uint32_t x;
154     memcpy(&x, p, sizeof x);
155     return x;
156 }
157
158 static bool
159 match(const struct cls_rule *wild, const flow_t *fixed)
160 {
161     int f_idx;
162
163     for (f_idx = 0; f_idx < CLS_N_FIELDS; f_idx++) {
164         const struct cls_field *f = &cls_fields[f_idx];
165         void *wild_field = (char *) &wild->flow + f->ofs;
166         void *fixed_field = (char *) fixed + f->ofs;
167
168         if ((wild->wc.wildcards & f->wildcards) == f->wildcards ||
169             !memcmp(wild_field, fixed_field, f->len)) {
170             /* Definite match. */
171             continue;
172         }
173
174         if (wild->wc.wildcards & f->wildcards) {
175             uint32_t test = read_uint32(wild_field);
176             uint32_t ip = read_uint32(fixed_field);
177             int shift = (f_idx == CLS_F_IDX_NW_SRC
178                          ? OFPFW_NW_SRC_SHIFT : OFPFW_NW_DST_SHIFT);
179             uint32_t mask = flow_nw_bits_to_mask(wild->wc.wildcards, shift);
180             if (!((test ^ ip) & mask)) {
181                 continue;
182             }
183         }
184
185         return false;
186     }
187     return true;
188 }
189
190 static struct cls_rule *
191 tcls_lookup(const struct tcls *cls, const flow_t *flow, int include)
192 {
193     size_t i;
194
195     for (i = 0; i < cls->n_rules; i++) {
196         struct test_rule *pos = cls->rules[i];
197         uint32_t wildcards = pos->cls_rule.wc.wildcards;
198         if (include & (wildcards ? CLS_INC_WILD : CLS_INC_EXACT)
199             && match(&pos->cls_rule, flow)) {
200             return &pos->cls_rule;
201         }
202     }
203     return NULL;
204 }
205
206 static void
207 tcls_delete_matches(struct tcls *cls,
208                     const struct cls_rule *target,
209                     int include)
210 {
211     size_t i;
212
213     for (i = 0; i < cls->n_rules; ) {
214         struct test_rule *pos = cls->rules[i];
215         uint32_t wildcards = pos->cls_rule.wc.wildcards;
216         if (include & (wildcards ? CLS_INC_WILD : CLS_INC_EXACT)
217             && match(target, &pos->cls_rule.flow)) {
218             tcls_remove(cls, pos);
219         } else {
220             i++;
221         }
222     }
223 }
224 \f
225 #ifdef WORDS_BIGENDIAN
226 #define HTONL(VALUE) ((uint32_t) (VALUE))
227 #define HTONS(VALUE) ((uint32_t) (VALUE))
228 #else
229 #define HTONL(VALUE) (((((uint32_t) (VALUE)) & 0x000000ff) << 24) | \
230                       ((((uint32_t) (VALUE)) & 0x0000ff00) <<  8) | \
231                       ((((uint32_t) (VALUE)) & 0x00ff0000) >>  8) | \
232                       ((((uint32_t) (VALUE)) & 0xff000000) >> 24))
233 #define HTONS(VALUE) (((((uint16_t) (VALUE)) & 0xff00) >> 8) |  \
234                       ((((uint16_t) (VALUE)) & 0x00ff) << 8))
235 #endif
236
237 static uint32_t nw_src_values[] = { HTONL(0xc0a80001),
238                                     HTONL(0xc0a04455) };
239 static uint32_t nw_dst_values[] = { HTONL(0xc0a80002),
240                                     HTONL(0xc0a04455) };
241 static uint16_t in_port_values[] = { HTONS(1), HTONS(OFPP_LOCAL) };
242 static uint16_t dl_vlan_values[] = { HTONS(101), HTONS(0) };
243 static uint16_t dl_type_values[] = { HTONS(ETH_TYPE_IP), HTONS(ETH_TYPE_ARP) };
244 static uint16_t tp_src_values[] = { HTONS(49362), HTONS(80) };
245 static uint16_t tp_dst_values[] = { HTONS(6667), HTONS(22) };
246 static uint8_t dl_src_values[][6] = { { 0x00, 0x02, 0xe3, 0x0f, 0x80, 0xa4 },
247                                       { 0x5e, 0x33, 0x7f, 0x5f, 0x1e, 0x99 } };
248 static uint8_t dl_dst_values[][6] = { { 0x4a, 0x27, 0x71, 0xae, 0x64, 0xc1 },
249                                       { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
250 static uint8_t nw_proto_values[] = { IP_TYPE_TCP, IP_TYPE_ICMP };
251
252 static void *values[CLS_N_FIELDS][2];
253
254 static void
255 init_values(void)
256 {
257     values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0];
258     values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1];
259
260     values[CLS_F_IDX_DL_VLAN][0] = &dl_vlan_values[0];
261     values[CLS_F_IDX_DL_VLAN][1] = &dl_vlan_values[1];
262
263     values[CLS_F_IDX_DL_SRC][0] = dl_src_values[0];
264     values[CLS_F_IDX_DL_SRC][1] = dl_src_values[1];
265
266     values[CLS_F_IDX_DL_DST][0] = dl_dst_values[0];
267     values[CLS_F_IDX_DL_DST][1] = dl_dst_values[1];
268
269     values[CLS_F_IDX_DL_TYPE][0] = &dl_type_values[0];
270     values[CLS_F_IDX_DL_TYPE][1] = &dl_type_values[1];
271
272     values[CLS_F_IDX_NW_SRC][0] = &nw_src_values[0];
273     values[CLS_F_IDX_NW_SRC][1] = &nw_src_values[1];
274
275     values[CLS_F_IDX_NW_DST][0] = &nw_dst_values[0];
276     values[CLS_F_IDX_NW_DST][1] = &nw_dst_values[1];
277
278     values[CLS_F_IDX_NW_PROTO][0] = &nw_proto_values[0];
279     values[CLS_F_IDX_NW_PROTO][1] = &nw_proto_values[1];
280
281     values[CLS_F_IDX_TP_SRC][0] = &tp_src_values[0];
282     values[CLS_F_IDX_TP_SRC][1] = &tp_src_values[1];
283
284     values[CLS_F_IDX_TP_DST][0] = &tp_dst_values[0];
285     values[CLS_F_IDX_TP_DST][1] = &tp_dst_values[1];
286 }
287
288 #define N_NW_SRC_VALUES ARRAY_SIZE(nw_src_values)
289 #define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values)
290 #define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values)
291 #define N_DL_VLAN_VALUES ARRAY_SIZE(dl_vlan_values)
292 #define N_DL_TYPE_VALUES ARRAY_SIZE(dl_type_values)
293 #define N_TP_SRC_VALUES ARRAY_SIZE(tp_src_values)
294 #define N_TP_DST_VALUES ARRAY_SIZE(tp_dst_values)
295 #define N_DL_SRC_VALUES ARRAY_SIZE(dl_src_values)
296 #define N_DL_DST_VALUES ARRAY_SIZE(dl_dst_values)
297 #define N_NW_PROTO_VALUES ARRAY_SIZE(nw_proto_values)
298
299 #define N_FLOW_VALUES (N_NW_SRC_VALUES *        \
300                        N_NW_DST_VALUES *        \
301                        N_IN_PORT_VALUES *       \
302                        N_DL_VLAN_VALUES *       \
303                        N_DL_TYPE_VALUES *       \
304                        N_TP_SRC_VALUES *        \
305                        N_TP_DST_VALUES *        \
306                        N_DL_SRC_VALUES *        \
307                        N_DL_DST_VALUES *        \
308                        N_NW_PROTO_VALUES)
309
310 static unsigned int
311 get_value(unsigned int *x, unsigned n_values)
312 {
313     unsigned int rem = *x % n_values;
314     *x /= n_values;
315     return rem;
316 }
317
318 static struct cls_rule *
319 lookup_with_include_bits(const struct classifier *cls,
320                          const flow_t *flow, int include)
321 {
322     switch (include) {
323     case CLS_INC_WILD:
324         return classifier_lookup_wild(cls, flow);
325     case CLS_INC_EXACT:
326         return classifier_lookup_exact(cls, flow);
327     case CLS_INC_WILD | CLS_INC_EXACT:
328         return classifier_lookup(cls, flow);
329     default:
330         abort();
331     }
332 }
333
334 static void
335 compare_classifiers(struct classifier *cls, struct tcls *tcls)
336 {
337     unsigned int i;
338
339     assert(classifier_count(cls) == tcls->n_rules);
340     assert(classifier_count_exact(cls) == tcls_count_exact(tcls));
341     for (i = 0; i < N_FLOW_VALUES; i++) {
342         struct cls_rule *cr0, *cr1;
343         flow_t flow;
344         unsigned int x;
345         int include;
346
347         x = i;
348         flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
349         flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
350         flow.in_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)];
351         flow.dl_vlan = dl_vlan_values[get_value(&x, N_DL_VLAN_VALUES)];
352         flow.dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)];
353         flow.tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)];
354         flow.tp_dst = tp_dst_values[get_value(&x, N_TP_DST_VALUES)];
355         memcpy(flow.dl_src, dl_src_values[get_value(&x, N_DL_SRC_VALUES)],
356                ETH_ADDR_LEN);
357         memcpy(flow.dl_dst, dl_dst_values[get_value(&x, N_DL_DST_VALUES)],
358                ETH_ADDR_LEN);
359         flow.nw_proto = nw_proto_values[get_value(&x, N_NW_PROTO_VALUES)];
360         flow.reserved = 0;
361
362         for (include = 1; include <= 3; include++) {
363             cr0 = lookup_with_include_bits(cls, &flow, include);
364             cr1 = tcls_lookup(tcls, &flow, include);
365             assert((cr0 == NULL) == (cr1 == NULL));
366             if (cr0 != NULL) {
367                 const struct test_rule *tr0 = test_rule_from_cls_rule(cr0);
368                 const struct test_rule *tr1 = test_rule_from_cls_rule(cr1);
369
370                 assert(flow_equal(&cr0->flow, &cr1->flow));
371                 assert(cr0->wc.wildcards == cr1->wc.wildcards);
372                 assert(cr0->priority == cr1->priority);
373                 /* Skip nw_src_mask and nw_dst_mask, because they are derived
374                  * members whose values are used only for optimization. */
375                 assert(tr0->aux == tr1->aux);
376             }
377         }
378     }
379 }
380
381 static void
382 free_rule(struct cls_rule *cls_rule, void *cls)
383 {
384     classifier_remove(cls, cls_rule);
385     free(test_rule_from_cls_rule(cls_rule));
386 }
387
388 static void
389 destroy_classifier(struct classifier *cls)
390 {
391     classifier_for_each(cls, CLS_INC_ALL, free_rule, cls);
392     classifier_destroy(cls);
393 }
394
395 static void
396 check_tables(const struct classifier *cls,
397              int n_tables, int n_buckets, int n_rules)
398 {
399     int found_tables = 0;
400     int found_buckets = 0;
401     int found_rules = 0;
402     int i;
403
404     BUILD_ASSERT(CLS_N_FIELDS == ARRAY_SIZE(cls->tables));
405     for (i = 0; i < CLS_N_FIELDS; i++) {
406         const struct cls_bucket *bucket;
407         if (!hmap_is_empty(&cls->tables[i])) {
408             found_tables++;
409         }
410         HMAP_FOR_EACH (bucket, struct cls_bucket, hmap_node, &cls->tables[i]) {
411             found_buckets++;
412             assert(!list_is_empty(&bucket->rules));
413             found_rules += list_size(&bucket->rules);
414         }
415     }
416
417     if (!hmap_is_empty(&cls->exact_table)) {
418         found_tables++;
419         found_buckets++;
420         found_rules += hmap_count(&cls->exact_table);
421     }
422
423     assert(n_tables == -1 || found_tables == n_tables);
424     assert(n_rules == -1 || found_rules == n_rules);
425     assert(n_buckets == -1 || found_buckets == n_buckets);
426 }
427
428 static struct test_rule *
429 make_rule(int wc_fields, unsigned int priority, int value_pat)
430 {
431     const struct cls_field *f;
432     struct test_rule *rule;
433     uint32_t wildcards;
434     flow_t flow;
435
436     wildcards = 0;
437     memset(&flow, 0, sizeof flow);
438     for (f = &cls_fields[0]; f < &cls_fields[CLS_N_FIELDS]; f++) {
439         int f_idx = f - cls_fields;
440         if (wc_fields & (1u << f_idx)) {
441             wildcards |= f->wildcards;
442         } else {
443             int value_idx = (value_pat & (1u << f_idx)) != 0;
444             memcpy((char *) &flow + f->ofs, values[f_idx][value_idx], f->len);
445         }
446     }
447
448     rule = xcalloc(1, sizeof *rule);
449     cls_rule_from_flow(&rule->cls_rule, &flow, wildcards,
450                        !wildcards ? UINT_MAX : priority);
451     return rule;
452 }
453
454 static void
455 shuffle(unsigned int *p, size_t n)
456 {
457     for (; n > 1; n--, p++) {
458         unsigned int *q = &p[rand() % n];
459         unsigned int tmp = *p;
460         *p = *q;
461         *q = tmp;
462     }
463 }
464 \f
465 /* Tests an empty classifier. */
466 static void
467 test_empty(void)
468 {
469     struct classifier cls;
470     struct tcls tcls;
471
472     classifier_init(&cls);
473     tcls_init(&tcls);
474     assert(classifier_is_empty(&cls));
475     assert(tcls_is_empty(&tcls));
476     compare_classifiers(&cls, &tcls);
477     classifier_destroy(&cls);
478     tcls_destroy(&tcls);
479 }
480
481 /* Destroys a null classifier. */
482 static void
483 test_destroy_null(void)
484 {
485     classifier_destroy(NULL);
486 }
487
488 /* Tests classification with one rule at a time. */
489 static void
490 test_single_rule(void)
491 {
492     unsigned int wc_fields;     /* Hilarious. */
493
494     for (wc_fields = 0; wc_fields < (1u << CLS_N_FIELDS); wc_fields++) {
495         struct classifier cls;
496         struct test_rule *rule, *tcls_rule;
497         struct tcls tcls;
498
499         rule = make_rule(wc_fields,
500                          hash_bytes(&wc_fields, sizeof wc_fields, 0), 0);
501
502         classifier_init(&cls);
503         tcls_init(&tcls);
504
505         tcls_rule = tcls_insert(&tcls, rule);
506         if (wc_fields) {
507             assert(!classifier_insert(&cls, &rule->cls_rule));
508         } else {
509             classifier_insert_exact(&cls, &rule->cls_rule);
510         }
511         check_tables(&cls, 1, 1, 1);
512         compare_classifiers(&cls, &tcls);
513
514         classifier_remove(&cls, &rule->cls_rule);
515         tcls_remove(&tcls, tcls_rule);
516         assert(classifier_is_empty(&cls));
517         assert(tcls_is_empty(&tcls));
518         compare_classifiers(&cls, &tcls);
519
520         free(rule);
521         classifier_destroy(&cls);
522         tcls_destroy(&tcls);
523     }
524 }
525
526 /* Tests replacing one rule by another. */
527 static void
528 test_rule_replacement(void)
529 {
530     unsigned int wc_fields;
531
532     for (wc_fields = 0; wc_fields < (1u << CLS_N_FIELDS); wc_fields++) {
533         struct classifier cls;
534         struct test_rule *rule1, *tcls_rule1;
535         struct test_rule *rule2, *tcls_rule2;
536         struct tcls tcls;
537
538         rule1 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX);
539         rule2 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX);
540         rule2->aux += 5;
541         rule2->aux += 5;
542
543         classifier_init(&cls);
544         tcls_init(&tcls);
545         tcls_rule1 = tcls_insert(&tcls, rule1);
546         assert(!classifier_insert(&cls, &rule1->cls_rule));
547         check_tables(&cls, 1, 1, 1);
548         compare_classifiers(&cls, &tcls);
549         tcls_destroy(&tcls);
550
551         tcls_init(&tcls);
552         tcls_rule2 = tcls_insert(&tcls, rule2);
553         assert(test_rule_from_cls_rule(
554                    classifier_insert(&cls, &rule2->cls_rule)) == rule1);
555         free(rule1);
556         check_tables(&cls, 1, 1, 1);
557         compare_classifiers(&cls, &tcls);
558         tcls_destroy(&tcls);
559         destroy_classifier(&cls);
560     }
561 }
562
563 static int
564 table_mask(int table)
565 {
566     return ((1u << CLS_N_FIELDS) - 1) & ~((1u << table) - 1);
567 }
568
569 static int
570 random_wcf_in_table(int table, int seed)
571 {
572     int wc_fields = (1u << table) | hash_int(seed, 0);
573     return wc_fields & table_mask(table);
574 }
575
576 /* Tests classification with two rules at a time that fall into the same
577  * bucket. */
578 static void
579 test_two_rules_in_one_bucket(void)
580 {
581     int table, rel_pri, wcf_pat, value_pat;
582
583     for (table = 0; table <= CLS_N_FIELDS; table++) {
584         for (rel_pri = -1; rel_pri <= +1; rel_pri++) {
585             for (wcf_pat = 0; wcf_pat < 4; wcf_pat++) {
586                 int n_value_pats = table == CLS_N_FIELDS - 1 ? 1 : 2;
587                 for (value_pat = 0; value_pat < n_value_pats; value_pat++) {
588                     struct test_rule *rule1, *tcls_rule1;
589                     struct test_rule *rule2, *tcls_rule2;
590                     struct test_rule *displaced_rule;
591                     struct classifier cls;
592                     struct tcls tcls;
593                     unsigned int pri1, pri2;
594                     int wcf1, wcf2;
595
596                     if (table != CLS_F_IDX_EXACT) {
597                         /* We can use identical priorities in this test because
598                          * the classifier always chooses the rule added later
599                          * for equal-priority rules that fall into the same
600                          * bucket.  */
601                         pri1 = table * 257 + 50;
602                         pri2 = pri1 + rel_pri;
603
604                         wcf1 = (wcf_pat & 1
605                                 ? random_wcf_in_table(table, pri1)
606                                 : 1u << table);
607                         wcf2 = (wcf_pat & 2
608                                 ? random_wcf_in_table(table, pri2)
609                                 : 1u << table);
610                         if (value_pat) {
611                             wcf1 &= ~(1u << (CLS_N_FIELDS - 1));
612                             wcf2 &= ~(1u << (CLS_N_FIELDS - 1));
613                         }
614                     } else {
615                         /* This classifier always puts exact-match rules at
616                          * maximum priority.  */
617                         pri1 = pri2 = UINT_MAX;
618
619                         /* No wildcard fields. */
620                         wcf1 = wcf2 = 0;
621                     }
622
623                     rule1 = make_rule(wcf1, pri1, 0);
624                     rule2 = make_rule(wcf2, pri2,
625                                       value_pat << (CLS_N_FIELDS - 1));
626
627                     classifier_init(&cls);
628                     tcls_init(&tcls);
629
630                     tcls_rule1 = tcls_insert(&tcls, rule1);
631                     tcls_rule2 = tcls_insert(&tcls, rule2);
632                     assert(!classifier_insert(&cls, &rule1->cls_rule));
633                     displaced_rule = test_rule_from_cls_rule(
634                         classifier_insert(&cls, &rule2->cls_rule));
635                     if (wcf1 != wcf2 || pri1 != pri2 || value_pat) {
636                         assert(!displaced_rule);
637
638                         check_tables(&cls, 1, 1, 2);
639                         compare_classifiers(&cls, &tcls);
640
641                         classifier_remove(&cls, &rule1->cls_rule);
642                         tcls_remove(&tcls, tcls_rule1);
643                         check_tables(&cls, 1, 1, 1);
644                         compare_classifiers(&cls, &tcls);
645                     } else {
646                         assert(displaced_rule == rule1);
647                         check_tables(&cls, 1, 1, 1);
648                         compare_classifiers(&cls, &tcls);
649                     }
650                     free(rule1);
651
652                     classifier_remove(&cls, &rule2->cls_rule);
653                     tcls_remove(&tcls, tcls_rule2);
654                     compare_classifiers(&cls, &tcls);
655                     free(rule2);
656
657                     destroy_classifier(&cls);
658                     tcls_destroy(&tcls);
659                 }
660             }
661         }
662     }
663 }
664
665 /* Tests classification with two rules at a time that fall into the same
666  * table but different buckets. */
667 static void
668 test_two_rules_in_one_table(void)
669 {
670     int table, rel_pri, wcf_pat;
671
672     /* Skip tables 0 and CLS_F_IDX_EXACT because they have one bucket. */
673     for (table = 1; table < CLS_N_FIELDS; table++) {
674         for (rel_pri = -1; rel_pri <= +1; rel_pri++) {
675             for (wcf_pat = 0; wcf_pat < 5; wcf_pat++) {
676                 struct test_rule *rule1, *tcls_rule1;
677                 struct test_rule *rule2, *tcls_rule2;
678                 struct classifier cls;
679                 struct tcls tcls;
680                 unsigned int pri1, pri2;
681                 int wcf1, wcf2;
682                 int value_mask, value_pat1, value_pat2;
683                 int i;
684
685                 /* We can use identical priorities in this test because the
686                  * classifier always chooses the rule added later for
687                  * equal-priority rules that fall into the same table.  */
688                 pri1 = table * 257 + 50;
689                 pri2 = pri1 + rel_pri;
690
691                 if (wcf_pat & 4) {
692                     wcf1 = wcf2 = random_wcf_in_table(table, pri1);
693                 } else {
694                     wcf1 = (wcf_pat & 1
695                             ? random_wcf_in_table(table, pri1)
696                             : 1u << table);
697                     wcf2 = (wcf_pat & 2
698                             ? random_wcf_in_table(table, pri2)
699                             : 1u << table);
700                 }
701
702                 /* Generate value patterns that will put the two rules into
703                  * different buckets. */
704                 value_mask = ((1u << table) - 1);
705                 value_pat1 = hash_int(pri1, 1) & value_mask;
706                 i = 0;
707                 do {
708                     value_pat2 = (hash_int(pri2, i++) & value_mask);
709                 } while (value_pat1 == value_pat2);
710                 rule1 = make_rule(wcf1, pri1, value_pat1);
711                 rule2 = make_rule(wcf2, pri2, value_pat2);
712
713                 classifier_init(&cls);
714                 tcls_init(&tcls);
715
716                 tcls_rule1 = tcls_insert(&tcls, rule1);
717                 tcls_rule2 = tcls_insert(&tcls, rule2);
718                 assert(!classifier_insert(&cls, &rule1->cls_rule));
719                 assert(!classifier_insert(&cls, &rule2->cls_rule));
720                 check_tables(&cls, 1, 2, 2);
721                 compare_classifiers(&cls, &tcls);
722
723                 classifier_remove(&cls, &rule1->cls_rule);
724                 tcls_remove(&tcls, tcls_rule1);
725                 check_tables(&cls, 1, 1, 1);
726                 compare_classifiers(&cls, &tcls);
727                 free(rule1);
728
729                 classifier_remove(&cls, &rule2->cls_rule);
730                 tcls_remove(&tcls, tcls_rule2);
731                 compare_classifiers(&cls, &tcls);
732                 free(rule2);
733
734                 classifier_destroy(&cls);
735                 tcls_destroy(&tcls);
736             }
737         }
738     }
739 }
740
741 /* Tests classification with two rules at a time that fall into different
742  * tables. */
743 static void
744 test_two_rules_in_different_tables(void)
745 {
746     int table1, table2, rel_pri, wcf_pat;
747
748     for (table1 = 0; table1 < CLS_N_FIELDS; table1++) {
749         for (table2 = table1 + 1; table2 <= CLS_N_FIELDS; table2++) {
750             for (rel_pri = 0; rel_pri < 2; rel_pri++) {
751                 for (wcf_pat = 0; wcf_pat < 4; wcf_pat++) {
752                     struct test_rule *rule1, *tcls_rule1;
753                     struct test_rule *rule2, *tcls_rule2;
754                     struct classifier cls;
755                     struct tcls tcls;
756                     unsigned int pri1, pri2;
757                     int wcf1, wcf2;
758
759                     /* We must use unique priorities in this test because the
760                      * classifier makes the rule choice undefined for rules of
761                      * equal priority that fall into different tables.  (In
762                      * practice, lower-numbered tables win.)  */
763                     pri1 = table1 * 257 + 50;
764                     pri2 = rel_pri ? pri1 - 1 : pri1 + 1;
765
766                     wcf1 = (wcf_pat & 1
767                             ? random_wcf_in_table(table1, pri1)
768                             : 1u << table1);
769                     wcf2 = (wcf_pat & 2
770                             ? random_wcf_in_table(table2, pri2)
771                             : 1u << table2);
772
773                     if (table2 == CLS_F_IDX_EXACT) {
774                         pri2 = UINT16_MAX;
775                         wcf2 = 0;
776                     }
777
778                     rule1 = make_rule(wcf1, pri1, 0);
779                     rule2 = make_rule(wcf2, pri2, 0);
780
781                     classifier_init(&cls);
782                     tcls_init(&tcls);
783
784                     tcls_rule1 = tcls_insert(&tcls, rule1);
785                     tcls_rule2 = tcls_insert(&tcls, rule2);
786                     assert(!classifier_insert(&cls, &rule1->cls_rule));
787                     assert(!classifier_insert(&cls, &rule2->cls_rule));
788                     check_tables(&cls, 2, 2, 2);
789                     compare_classifiers(&cls, &tcls);
790
791                     classifier_remove(&cls, &rule1->cls_rule);
792                     tcls_remove(&tcls, tcls_rule1);
793                     check_tables(&cls, 1, 1, 1);
794                     compare_classifiers(&cls, &tcls);
795                     free(rule1);
796
797                     classifier_remove(&cls, &rule2->cls_rule);
798                     tcls_remove(&tcls, tcls_rule2);
799                     compare_classifiers(&cls, &tcls);
800                     free(rule2);
801
802                     classifier_destroy(&cls);
803                     tcls_destroy(&tcls);
804                 }
805             }
806         }
807     }
808 }
809
810 /* Tests classification with many rules at a time that fall into the same
811  * bucket but have unique priorities (and various wildcards). */
812 static void
813 test_many_rules_in_one_bucket(void)
814 {
815     enum { MAX_RULES = 50 };
816     int iteration, table;
817
818     for (iteration = 0; iteration < 3; iteration++) {
819         for (table = 0; table <= CLS_N_FIELDS; table++) {
820             unsigned int priorities[MAX_RULES];
821             struct classifier cls;
822             struct tcls tcls;
823             int i;
824
825             srand(hash_int(table, iteration));
826             for (i = 0; i < MAX_RULES; i++) {
827                 priorities[i] = i * 129;
828             }
829             shuffle(priorities, ARRAY_SIZE(priorities));
830
831             classifier_init(&cls);
832             tcls_init(&tcls);
833
834             for (i = 0; i < MAX_RULES; i++) {
835                 struct test_rule *rule;
836                 unsigned int priority = priorities[i];
837                 int wcf;
838
839                 wcf = random_wcf_in_table(table, priority);
840                 rule = make_rule(wcf, priority,
841                                  table == CLS_F_IDX_EXACT ? i : 1234);
842                 tcls_insert(&tcls, rule);
843                 assert(!classifier_insert(&cls, &rule->cls_rule));
844                 check_tables(&cls, 1, 1, i + 1);
845                 compare_classifiers(&cls, &tcls);
846             }
847
848             destroy_classifier(&cls);
849             tcls_destroy(&tcls);
850         }
851     }
852 }
853
854 /* Tests classification with many rules at a time that fall into the same
855  * table but random buckets. */
856 static void
857 test_many_rules_in_one_table(void)
858 {
859     enum { MAX_RULES = 50 };
860     int iteration, table;
861
862     for (iteration = 0; iteration < 3; iteration++) {
863         for (table = 0; table < CLS_N_FIELDS; table++) {
864             unsigned int priorities[MAX_RULES];
865             struct classifier cls;
866             struct tcls tcls;
867             int i;
868
869             srand(hash_int(table, iteration));
870             for (i = 0; i < MAX_RULES; i++) {
871                 priorities[i] = i * 129;
872             }
873             shuffle(priorities, ARRAY_SIZE(priorities));
874
875             classifier_init(&cls);
876             tcls_init(&tcls);
877
878             for (i = 0; i < MAX_RULES; i++) {
879                 struct test_rule *rule;
880                 unsigned int priority = priorities[i];
881                 int wcf;
882
883                 wcf = random_wcf_in_table(table, priority);
884                 rule = make_rule(wcf, priority, hash_int(priority, 1));
885                 tcls_insert(&tcls, rule);
886                 assert(!classifier_insert(&cls, &rule->cls_rule));
887                 check_tables(&cls, 1, -1, i + 1);
888                 compare_classifiers(&cls, &tcls);
889             }
890
891             destroy_classifier(&cls);
892             tcls_destroy(&tcls);
893         }
894     }
895 }
896
897 /* Tests classification with many rules at a time that fall into random buckets
898  * in random tables. */
899 static void
900 test_many_rules_in_different_tables(void)
901 {
902     enum { MAX_RULES = 50 };
903     int iteration;
904
905     for (iteration = 0; iteration < 30; iteration++) {
906         unsigned int priorities[MAX_RULES];
907         struct classifier cls;
908         struct tcls tcls;
909         int i;
910
911         srand(iteration);
912         for (i = 0; i < MAX_RULES; i++) {
913             priorities[i] = i * 129;
914         }
915         shuffle(priorities, ARRAY_SIZE(priorities));
916
917         classifier_init(&cls);
918         tcls_init(&tcls);
919
920         for (i = 0; i < MAX_RULES; i++) {
921             struct test_rule *rule;
922             unsigned int priority = priorities[i];
923             int table = rand() % (CLS_N_FIELDS + 1);
924             int wcf = random_wcf_in_table(table, rand());
925             int value_pat = rand() & ((1u << CLS_N_FIELDS) - 1);
926             rule = make_rule(wcf, priority, value_pat);
927             tcls_insert(&tcls, rule);
928             assert(!classifier_insert(&cls, &rule->cls_rule));
929             check_tables(&cls, -1, -1, i + 1);
930             compare_classifiers(&cls, &tcls);
931         }
932
933         while (!classifier_is_empty(&cls)) {
934             struct test_rule *rule = xmemdup(tcls.rules[rand() % tcls.n_rules],
935                                              sizeof(struct test_rule));
936             int include = rand() % 2 ? CLS_INC_WILD : CLS_INC_EXACT;
937             include |= (rule->cls_rule.wc.wildcards
938                         ? CLS_INC_WILD : CLS_INC_EXACT);
939             classifier_for_each_match(&cls, &rule->cls_rule, include,
940                                       free_rule, &cls);
941             tcls_delete_matches(&tcls, &rule->cls_rule, include);
942             compare_classifiers(&cls, &tcls);
943             free(rule);
944         }
945         putchar('.');
946         fflush(stdout);
947
948         destroy_classifier(&cls);
949         tcls_destroy(&tcls);
950     }
951 }
952 \f
953 static void
954 run_test(void (*function)(void))
955 {
956     function();
957     putchar('.');
958     fflush(stdout);
959 }
960
961 int
962 main(void)
963 {
964     init_values();
965     run_test(test_empty);
966     run_test(test_destroy_null);
967     run_test(test_single_rule);
968     run_test(test_rule_replacement);
969     run_test(test_two_rules_in_one_bucket);
970     run_test(test_two_rules_in_one_table);
971     run_test(test_two_rules_in_different_tables);
972     run_test(test_many_rules_in_one_bucket);
973     run_test(test_many_rules_in_one_table);
974     run_test(test_many_rules_in_different_tables);
975     putchar('\n');
976     return 0;
977 }