remove trailing whitespaces
[dpdk.git] / app / test / test_lpm.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stdio.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <sys/queue.h>
39
40 #include <rte_common.h>
41 #include <rte_cycles.h>
42 #include <rte_memory.h>
43 #include <rte_random.h>
44 #include <rte_branch_prediction.h>
45 #include <rte_ip.h>
46 #include <time.h>
47
48 #include "test.h"
49
50 #ifdef RTE_LIBRTE_LPM
51
52 #include "rte_lpm.h"
53 #include "test_lpm_routes.h"
54
55 #define TEST_LPM_ASSERT(cond) do {                                            \
56         if (!(cond)) {                                                        \
57                 printf("Error at line %d: \n", __LINE__);                     \
58                 return -1;                                                    \
59         }                                                                     \
60 } while(0)
61
62 typedef int32_t (* rte_lpm_test)(void);
63
64 static int32_t test0(void);
65 static int32_t test1(void);
66 static int32_t test2(void);
67 static int32_t test3(void);
68 static int32_t test4(void);
69 static int32_t test5(void);
70 static int32_t test6(void);
71 static int32_t test7(void);
72 static int32_t test8(void);
73 static int32_t test9(void);
74 static int32_t test10(void);
75 static int32_t test11(void);
76 static int32_t test12(void);
77 static int32_t test13(void);
78 static int32_t test14(void);
79 static int32_t test15(void);
80 static int32_t test16(void);
81 static int32_t test17(void);
82 static int32_t perf_test(void);
83
84 rte_lpm_test tests[] = {
85 /* Test Cases */
86         test0,
87         test1,
88         test2,
89         test3,
90         test4,
91         test5,
92         test6,
93         test7,
94         test8,
95         test9,
96         test10,
97         test11,
98         test12,
99         test13,
100         test14,
101         test15,
102         test16,
103         test17,
104         perf_test,
105 };
106
107 #define NUM_LPM_TESTS (sizeof(tests)/sizeof(tests[0]))
108 #define MAX_DEPTH 32
109 #define MAX_RULES 256
110 #define PASS 0
111
112 /*
113  * Check that rte_lpm_create fails gracefully for incorrect user input
114  * arguments
115  */
116 int32_t
117 test0(void)
118 {
119         struct rte_lpm *lpm = NULL;
120
121         /* rte_lpm_create: lpm name == NULL */
122         lpm = rte_lpm_create(NULL, SOCKET_ID_ANY, MAX_RULES, 0);
123         TEST_LPM_ASSERT(lpm == NULL);
124
125         /* rte_lpm_create: max_rules = 0 */
126         /* Note: __func__ inserts the function name, in this case "test0". */
127         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, 0, 0);
128         TEST_LPM_ASSERT(lpm == NULL);
129
130         /* socket_id < -1 is invalid */
131         lpm = rte_lpm_create(__func__, -2, MAX_RULES, 0);
132         TEST_LPM_ASSERT(lpm == NULL);
133
134         return PASS;
135 }
136
137 /*
138  * Create lpm table then delete lpm table 100 times
139  * Use a slightly different rules size each time
140  * */
141 int32_t
142 test1(void)
143 {
144         struct rte_lpm *lpm = NULL;
145         int32_t i;
146
147         /* rte_lpm_free: Free NULL */
148         for (i = 0; i < 100; i++) {
149                 lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES - i, 0);
150                 TEST_LPM_ASSERT(lpm != NULL);
151
152                 rte_lpm_free(lpm);
153         }
154
155         /* Can not test free so return success */
156         return PASS;
157 }
158
159 /*
160  * Call rte_lpm_free for NULL pointer user input. Note: free has no return and
161  * therefore it is impossible to check for failure but this test is added to
162  * increase function coverage metrics and to validate that freeing null does
163  * not crash.
164  */
165 int32_t
166 test2(void)
167 {
168         struct rte_lpm *lpm = NULL;
169
170         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, RTE_LPM_HEAP);
171         TEST_LPM_ASSERT(lpm != NULL);
172
173         rte_lpm_free(lpm);
174         rte_lpm_free(NULL);
175         return PASS;
176 }
177
178 /*
179  * Check that rte_lpm_add fails gracefully for incorrect user input arguments
180  */
181 int32_t
182 test3(void)
183 {
184         struct rte_lpm *lpm = NULL;
185         uint32_t ip = IPv4(0, 0, 0, 0);
186         uint8_t depth = 24, next_hop = 100;
187         int32_t status = 0;
188
189         /* rte_lpm_add: lpm == NULL */
190         status = rte_lpm_add(NULL, ip, depth, next_hop);
191         TEST_LPM_ASSERT(status < 0);
192
193         /*Create vaild lpm to use in rest of test. */
194         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
195         TEST_LPM_ASSERT(lpm != NULL);
196
197         /* rte_lpm_add: depth < 1 */
198         status = rte_lpm_add(lpm, ip, 0, next_hop);
199         TEST_LPM_ASSERT(status < 0);
200
201         /* rte_lpm_add: depth > MAX_DEPTH */
202         status = rte_lpm_add(lpm, ip, (MAX_DEPTH + 1), next_hop);
203         TEST_LPM_ASSERT(status < 0);
204
205         rte_lpm_free(lpm);
206
207         return PASS;
208 }
209
210 /*
211  * Check that rte_lpm_delete fails gracefully for incorrect user input
212  * arguments
213  */
214 int32_t
215 test4(void)
216 {
217         struct rte_lpm *lpm = NULL;
218         uint32_t ip = IPv4(0, 0, 0, 0);
219         uint8_t depth = 24;
220         int32_t status = 0;
221
222         /* rte_lpm_delete: lpm == NULL */
223         status = rte_lpm_delete(NULL, ip, depth);
224         TEST_LPM_ASSERT(status < 0);
225
226         /*Create vaild lpm to use in rest of test. */
227         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
228         TEST_LPM_ASSERT(lpm != NULL);
229
230         /* rte_lpm_delete: depth < 1 */
231         status = rte_lpm_delete(lpm, ip, 0);
232         TEST_LPM_ASSERT(status < 0);
233
234         /* rte_lpm_delete: depth > MAX_DEPTH */
235         status = rte_lpm_delete(lpm, ip, (MAX_DEPTH + 1));
236         TEST_LPM_ASSERT(status < 0);
237
238         rte_lpm_free(lpm);
239
240         return PASS;
241 }
242
243 /*
244  * Check that rte_lpm_lookup fails gracefully for incorrect user input
245  * arguments
246  */
247 int32_t
248 test5(void)
249 {
250 #if defined(RTE_LIBRTE_LPM_DEBUG)
251         struct rte_lpm *lpm = NULL;
252         uint32_t ip = IPv4(0, 0, 0, 0);
253         uint8_t next_hop_return = 0;
254         int32_t status = 0;
255
256         /* rte_lpm_lookup: lpm == NULL */
257         status = rte_lpm_lookup(NULL, ip, &next_hop_return);
258         TEST_LPM_ASSERT(status < 0);
259
260         /*Create vaild lpm to use in rest of test. */
261         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
262         TEST_LPM_ASSERT(lpm != NULL);
263
264         /* rte_lpm_lookup: depth < 1 */
265         status = rte_lpm_lookup(lpm, ip, NULL);
266         TEST_LPM_ASSERT(status < 0);
267
268         rte_lpm_free(lpm);
269 #endif
270         return PASS;
271 }
272
273
274
275 /*
276  * Call add, lookup and delete for a single rule with depth <= 24
277  */
278 int32_t
279 test6(void)
280 {
281         struct rte_lpm *lpm = NULL;
282         uint32_t ip = IPv4(0, 0, 0, 0);
283         uint8_t depth = 24, next_hop_add = 100, next_hop_return = 0;
284         int32_t status = 0;
285
286         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
287         TEST_LPM_ASSERT(lpm != NULL);
288
289         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
290         TEST_LPM_ASSERT(status == 0);
291
292         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
293         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
294
295         status = rte_lpm_delete(lpm, ip, depth);
296         TEST_LPM_ASSERT(status == 0);
297
298         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
299         TEST_LPM_ASSERT(status == -ENOENT);
300
301         rte_lpm_free(lpm);
302
303         return PASS;
304 }
305
306 /*
307  * Call add, lookup and delete for a single rule with depth > 24
308  */
309
310 int32_t
311 test7(void)
312 {
313         struct rte_lpm *lpm = NULL;
314         uint32_t ip = IPv4(0, 0, 0, 0);
315         uint8_t depth = 32, next_hop_add = 100, next_hop_return = 0;
316         int32_t status = 0;
317
318         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
319         TEST_LPM_ASSERT(lpm != NULL);
320
321         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
322         TEST_LPM_ASSERT(status == 0);
323
324         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
325         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
326
327         status = rte_lpm_delete(lpm, ip, depth);
328         TEST_LPM_ASSERT(status == 0);
329
330         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
331         TEST_LPM_ASSERT(status == -ENOENT);
332
333         rte_lpm_free(lpm);
334
335         return PASS;
336 }
337
338 /*
339  * Use rte_lpm_add to add rules which effect only the second half of the lpm
340  * table. Use all possible depths ranging from 1..32. Set the next hop = to the
341  * depth. Check lookup hit for on every add and check for lookup miss on the
342  * first half of the lpm table after each add. Finally delete all rules going
343  * backwards (i.e. from depth = 32 ..1) and carry out a lookup after each
344  * delete. The lookup should return the next_hop_add value related to the
345  * previous depth value (i.e. depth -1).
346  */
347 int32_t
348 test8(void)
349 {
350         struct rte_lpm *lpm = NULL;
351         uint32_t ip1 = IPv4(127, 255, 255, 255), ip2 = IPv4(128, 0, 0, 0);
352         uint8_t depth, next_hop_add, next_hop_return;
353         int32_t status = 0;
354
355         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
356         TEST_LPM_ASSERT(lpm != NULL);
357
358         /* Loop with rte_lpm_add. */
359         for (depth = 1; depth <= 32; depth++) {
360                 /* Let the next_hop_add value = depth. Just for change. */
361                 next_hop_add = depth;
362
363                 status = rte_lpm_add(lpm, ip2, depth, next_hop_add);
364                 TEST_LPM_ASSERT(status == 0);
365
366                 /* Check IP in first half of tbl24 which should be empty. */
367                 status = rte_lpm_lookup(lpm, ip1, &next_hop_return);
368                 TEST_LPM_ASSERT(status == -ENOENT);
369
370                 status = rte_lpm_lookup(lpm, ip2, &next_hop_return);
371                 TEST_LPM_ASSERT((status == 0) &&
372                         (next_hop_return == next_hop_add));
373         }
374
375         /* Loop with rte_lpm_delete. */
376         for (depth = 32; depth >= 1; depth--) {
377                 next_hop_add = (uint8_t) (depth - 1);
378
379                 status = rte_lpm_delete(lpm, ip2, depth);
380                 TEST_LPM_ASSERT(status == 0);
381
382                 status = rte_lpm_lookup(lpm, ip2, &next_hop_return);
383
384                 if (depth != 1) {
385                         TEST_LPM_ASSERT((status == 0) &&
386                                 (next_hop_return == next_hop_add));
387                 }
388                 else {
389                         TEST_LPM_ASSERT(status == -ENOENT);
390                 }
391
392                 status = rte_lpm_lookup(lpm, ip1, &next_hop_return);
393                 TEST_LPM_ASSERT(status == -ENOENT);
394         }
395
396         rte_lpm_free(lpm);
397
398         return PASS;
399 }
400
401 /*
402  * - Add & lookup to hit invalid TBL24 entry
403  * - Add & lookup to hit valid TBL24 entry not extended
404  * - Add & lookup to hit valid extended TBL24 entry with invalid TBL8 entry
405  * - Add & lookup to hit valid extended TBL24 entry with valid TBL8 entry
406  *
407  */
408 int32_t
409 test9(void)
410 {
411         struct rte_lpm *lpm = NULL;
412         uint32_t ip, ip_1, ip_2;
413         uint8_t depth, depth_1, depth_2, next_hop_add, next_hop_add_1,
414                 next_hop_add_2, next_hop_return;
415         int32_t status = 0;
416
417         /* Add & lookup to hit invalid TBL24 entry */
418         ip = IPv4(128, 0, 0, 0);
419         depth = 24;
420         next_hop_add = 100;
421
422         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
423         TEST_LPM_ASSERT(lpm != NULL);
424
425         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
426         TEST_LPM_ASSERT(status == 0);
427
428         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
429         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
430
431         status = rte_lpm_delete(lpm, ip, depth);
432         TEST_LPM_ASSERT(status == 0);
433
434         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
435         TEST_LPM_ASSERT(status == -ENOENT);
436
437         rte_lpm_delete_all(lpm);
438
439         /* Add & lookup to hit valid TBL24 entry not extended */
440         ip = IPv4(128, 0, 0, 0);
441         depth = 23;
442         next_hop_add = 100;
443
444         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
445         TEST_LPM_ASSERT(status == 0);
446
447         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
448         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
449
450         depth = 24;
451         next_hop_add = 101;
452
453         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
454         TEST_LPM_ASSERT(status == 0);
455
456         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
457         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
458
459         depth = 24;
460
461         status = rte_lpm_delete(lpm, ip, depth);
462         TEST_LPM_ASSERT(status == 0);
463
464         depth = 23;
465
466         status = rte_lpm_delete(lpm, ip, depth);
467         TEST_LPM_ASSERT(status == 0);
468
469         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
470         TEST_LPM_ASSERT(status == -ENOENT);
471
472         rte_lpm_delete_all(lpm);
473
474         /* Add & lookup to hit valid extended TBL24 entry with invalid TBL8
475          * entry */
476         ip = IPv4(128, 0, 0, 0);
477         depth = 32;
478         next_hop_add = 100;
479
480         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
481         TEST_LPM_ASSERT(status == 0);
482
483         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
484         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
485
486         ip = IPv4(128, 0, 0, 5);
487         depth = 32;
488         next_hop_add = 101;
489
490         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
491         TEST_LPM_ASSERT(status == 0);
492
493         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
494         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
495
496         status = rte_lpm_delete(lpm, ip, depth);
497         TEST_LPM_ASSERT(status == 0);
498
499         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
500         TEST_LPM_ASSERT(status == -ENOENT);
501
502         ip = IPv4(128, 0, 0, 0);
503         depth = 32;
504         next_hop_add = 100;
505
506         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
507         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
508
509         status = rte_lpm_delete(lpm, ip, depth);
510         TEST_LPM_ASSERT(status == 0);
511
512         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
513         TEST_LPM_ASSERT(status == -ENOENT);
514
515         rte_lpm_delete_all(lpm);
516
517         /* Add & lookup to hit valid extended TBL24 entry with valid TBL8
518          * entry */
519         ip_1 = IPv4(128, 0, 0, 0);
520         depth_1 = 25;
521         next_hop_add_1 = 101;
522
523         ip_2 = IPv4(128, 0, 0, 5);
524         depth_2 = 32;
525         next_hop_add_2 = 102;
526
527         next_hop_return = 0;
528
529         status = rte_lpm_add(lpm, ip_1, depth_1, next_hop_add_1);
530         TEST_LPM_ASSERT(status == 0);
531
532         status = rte_lpm_lookup(lpm, ip_1, &next_hop_return);
533         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_1));
534
535         status = rte_lpm_add(lpm, ip_2, depth_2, next_hop_add_2);
536         TEST_LPM_ASSERT(status == 0);
537
538         status = rte_lpm_lookup(lpm, ip_2, &next_hop_return);
539         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_2));
540
541         status = rte_lpm_delete(lpm, ip_2, depth_2);
542         TEST_LPM_ASSERT(status == 0);
543
544         status = rte_lpm_lookup(lpm, ip_2, &next_hop_return);
545         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_1));
546
547         status = rte_lpm_delete(lpm, ip_1, depth_1);
548         TEST_LPM_ASSERT(status == 0);
549
550         status = rte_lpm_lookup(lpm, ip_1, &next_hop_return);
551         TEST_LPM_ASSERT(status == -ENOENT);
552
553         rte_lpm_free(lpm);
554
555         return PASS;
556 }
557
558
559 /*
560  * - Add rule that covers a TBL24 range previously invalid & lookup (& delete &
561  *   lookup)
562  * - Add rule that extends a TBL24 invalid entry & lookup (& delete & lookup)
563  * - Add rule that extends a TBL24 valid entry & lookup for both rules (&
564  *   delete & lookup)
565  * - Add rule that updates the next hop in TBL24 & lookup (& delete & lookup)
566  * - Add rule that updates the next hop in TBL8 & lookup (& delete & lookup)
567  * - Delete a rule that is not present in the TBL24 & lookup
568  * - Delete a rule that is not present in the TBL8 & lookup
569  *
570  */
571 int32_t
572 test10(void)
573 {
574
575         struct rte_lpm *lpm = NULL;
576         uint32_t ip;
577         uint8_t depth, next_hop_add, next_hop_return;
578         int32_t status = 0;
579
580         /* Add rule that covers a TBL24 range previously invalid & lookup
581          * (& delete & lookup) */
582         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, RTE_LPM_HEAP);
583         TEST_LPM_ASSERT(lpm != NULL);
584
585         ip = IPv4(128, 0, 0, 0);
586         depth = 16;
587         next_hop_add = 100;
588
589         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
590         TEST_LPM_ASSERT(status == 0);
591
592         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
593         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
594
595         status = rte_lpm_delete(lpm, ip, depth);
596         TEST_LPM_ASSERT(status == 0);
597
598         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
599         TEST_LPM_ASSERT(status == -ENOENT);
600
601         rte_lpm_delete_all(lpm);
602
603         ip = IPv4(128, 0, 0, 0);
604         depth = 25;
605         next_hop_add = 100;
606
607         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
608         TEST_LPM_ASSERT(status == 0);
609
610         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
611         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
612
613         status = rte_lpm_delete(lpm, ip, depth);
614         TEST_LPM_ASSERT(status == 0);
615
616         rte_lpm_delete_all(lpm);
617
618         /* Add rule that extends a TBL24 valid entry & lookup for both rules
619          * (& delete & lookup) */
620
621         ip = IPv4(128, 0, 0, 0);
622         depth = 24;
623         next_hop_add = 100;
624
625         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
626         TEST_LPM_ASSERT(status == 0);
627
628         ip = IPv4(128, 0, 0, 10);
629         depth = 32;
630         next_hop_add = 101;
631
632         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
633         TEST_LPM_ASSERT(status == 0);
634
635         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
636         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
637
638         ip = IPv4(128, 0, 0, 0);
639         next_hop_add = 100;
640
641         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
642         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
643
644         ip = IPv4(128, 0, 0, 0);
645         depth = 24;
646
647         status = rte_lpm_delete(lpm, ip, depth);
648         TEST_LPM_ASSERT(status == 0);
649
650         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
651         TEST_LPM_ASSERT(status == -ENOENT);
652
653         ip = IPv4(128, 0, 0, 10);
654         depth = 32;
655
656         status = rte_lpm_delete(lpm, ip, depth);
657         TEST_LPM_ASSERT(status == 0);
658
659         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
660         TEST_LPM_ASSERT(status == -ENOENT);
661
662         rte_lpm_delete_all(lpm);
663
664         /* Add rule that updates the next hop in TBL24 & lookup
665          * (& delete & lookup) */
666
667         ip = IPv4(128, 0, 0, 0);
668         depth = 24;
669         next_hop_add = 100;
670
671         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
672         TEST_LPM_ASSERT(status == 0);
673
674         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
675         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
676
677         next_hop_add = 101;
678
679         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
680         TEST_LPM_ASSERT(status == 0);
681
682         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
683         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
684
685         status = rte_lpm_delete(lpm, ip, depth);
686         TEST_LPM_ASSERT(status == 0);
687
688         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
689         TEST_LPM_ASSERT(status == -ENOENT);
690
691         rte_lpm_delete_all(lpm);
692
693         /* Add rule that updates the next hop in TBL8 & lookup
694          * (& delete & lookup) */
695
696         ip = IPv4(128, 0, 0, 0);
697         depth = 32;
698         next_hop_add = 100;
699
700         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
701         TEST_LPM_ASSERT(status == 0);
702
703         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
704         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
705
706         next_hop_add = 101;
707
708         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
709         TEST_LPM_ASSERT(status == 0);
710
711         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
712         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
713
714         status = rte_lpm_delete(lpm, ip, depth);
715         TEST_LPM_ASSERT(status == 0);
716
717         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
718         TEST_LPM_ASSERT(status == -ENOENT);
719
720         rte_lpm_delete_all(lpm);
721
722         /* Delete a rule that is not present in the TBL24 & lookup */
723
724         ip = IPv4(128, 0, 0, 0);
725         depth = 24;
726
727         status = rte_lpm_delete(lpm, ip, depth);
728         TEST_LPM_ASSERT(status < 0);
729
730         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
731         TEST_LPM_ASSERT(status == -ENOENT);
732
733         rte_lpm_delete_all(lpm);
734
735         /* Delete a rule that is not present in the TBL8 & lookup */
736
737         ip = IPv4(128, 0, 0, 0);
738         depth = 32;
739
740         status = rte_lpm_delete(lpm, ip, depth);
741         TEST_LPM_ASSERT(status < 0);
742
743         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
744         TEST_LPM_ASSERT(status == -ENOENT);
745
746         rte_lpm_free(lpm);
747
748         return PASS;
749 }
750
751 /*
752  * Add two rules, lookup to hit the more specific one, lookup to hit the less
753  * specific one delete the less specific rule and lookup previous values again;
754  * add a more specific rule than the existing rule, lookup again
755  *
756  * */
757 int32_t
758 test11(void)
759 {
760
761         struct rte_lpm *lpm = NULL;
762         uint32_t ip;
763         uint8_t depth, next_hop_add, next_hop_return;
764         int32_t status = 0;
765
766         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
767         TEST_LPM_ASSERT(lpm != NULL);
768
769         ip = IPv4(128, 0, 0, 0);
770         depth = 24;
771         next_hop_add = 100;
772
773         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
774         TEST_LPM_ASSERT(status == 0);
775
776         ip = IPv4(128, 0, 0, 10);
777         depth = 32;
778         next_hop_add = 101;
779
780         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
781         TEST_LPM_ASSERT(status == 0);
782
783         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
784         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
785
786         ip = IPv4(128, 0, 0, 0);
787         next_hop_add = 100;
788
789         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
790         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
791
792         ip = IPv4(128, 0, 0, 0);
793         depth = 24;
794
795         status = rte_lpm_delete(lpm, ip, depth);
796         TEST_LPM_ASSERT(status == 0);
797
798         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
799         TEST_LPM_ASSERT(status == -ENOENT);
800
801         ip = IPv4(128, 0, 0, 10);
802         depth = 32;
803
804         status = rte_lpm_delete(lpm, ip, depth);
805         TEST_LPM_ASSERT(status == 0);
806
807         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
808         TEST_LPM_ASSERT(status == -ENOENT);
809
810         rte_lpm_free(lpm);
811
812         return PASS;
813 }
814
815 /*
816  * Add an extended rule (i.e. depth greater than 24, lookup (hit), delete,
817  * lookup (miss) in a for loop of 1000 times. This will check tbl8 extension
818  * and contraction.
819  *
820  * */
821
822 int32_t
823 test12(void)
824 {
825         struct rte_lpm *lpm = NULL;
826         uint32_t ip, i;
827         uint8_t depth, next_hop_add, next_hop_return;
828         int32_t status = 0;
829
830         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
831         TEST_LPM_ASSERT(lpm != NULL);
832
833         ip = IPv4(128, 0, 0, 0);
834         depth = 32;
835         next_hop_add = 100;
836
837         for (i = 0; i < 1000; i++) {
838                 status = rte_lpm_add(lpm, ip, depth, next_hop_add);
839                 TEST_LPM_ASSERT(status == 0);
840
841                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
842                 TEST_LPM_ASSERT((status == 0) &&
843                                 (next_hop_return == next_hop_add));
844
845                 status = rte_lpm_delete(lpm, ip, depth);
846                 TEST_LPM_ASSERT(status == 0);
847
848                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
849                 TEST_LPM_ASSERT(status == -ENOENT);
850         }
851
852         rte_lpm_free(lpm);
853
854         return PASS;
855 }
856
857 /*
858  * Add a rule to tbl24, lookup (hit), then add a rule that will extend this
859  * tbl24 entry, lookup (hit). delete the rule that caused the tbl24 extension,
860  * lookup (miss) and repeat for loop of 1000 times. This will check tbl8
861  * extension and contraction.
862  *
863  * */
864
865 int32_t
866 test13(void)
867 {
868         struct rte_lpm *lpm = NULL;
869         uint32_t ip, i;
870         uint8_t depth, next_hop_add_1, next_hop_add_2, next_hop_return;
871         int32_t status = 0;
872
873         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
874         TEST_LPM_ASSERT(lpm != NULL);
875
876         ip = IPv4(128, 0, 0, 0);
877         depth = 24;
878         next_hop_add_1 = 100;
879
880         status = rte_lpm_add(lpm, ip, depth, next_hop_add_1);
881         TEST_LPM_ASSERT(status == 0);
882
883         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
884         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_1));
885
886         depth = 32;
887         next_hop_add_2 = 101;
888
889         for (i = 0; i < 1000; i++) {
890                 status = rte_lpm_add(lpm, ip, depth, next_hop_add_2);
891                 TEST_LPM_ASSERT(status == 0);
892
893                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
894                 TEST_LPM_ASSERT((status == 0) &&
895                                 (next_hop_return == next_hop_add_2));
896
897                 status = rte_lpm_delete(lpm, ip, depth);
898                 TEST_LPM_ASSERT(status == 0);
899
900                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
901                 TEST_LPM_ASSERT((status == 0) &&
902                                 (next_hop_return == next_hop_add_1));
903         }
904
905         depth = 24;
906
907         status = rte_lpm_delete(lpm, ip, depth);
908         TEST_LPM_ASSERT(status == 0);
909
910         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
911         TEST_LPM_ASSERT(status == -ENOENT);
912
913         rte_lpm_free(lpm);
914
915         return PASS;
916 }
917
918 /*
919  * Fore TBL8 extension exhaustion. Add 256 rules that require a tbl8 extension.
920  * No more tbl8 extensions will be allowed. Now add one more rule that required
921  * a tbl8 extension and get fail.
922  * */
923 int32_t
924 test14(void)
925 {
926
927         /* We only use depth = 32 in the loop below so we must make sure
928          * that we have enough storage for all rules at that depth*/
929
930         struct rte_lpm *lpm = NULL;
931         uint32_t ip;
932         uint8_t depth, next_hop_add, next_hop_return;
933         int32_t status = 0;
934
935         /* Add enough space for 256 rules for every depth */
936         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, 256 * 32, 0);
937         TEST_LPM_ASSERT(lpm != NULL);
938
939         depth = 32;
940         next_hop_add = 100;
941         ip = IPv4(0, 0, 0, 0);
942
943         /* Add 256 rules that require a tbl8 extension */
944         for (; ip <= IPv4(0, 0, 255, 0); ip += 256) {
945                 status = rte_lpm_add(lpm, ip, depth, next_hop_add);
946                 TEST_LPM_ASSERT(status == 0);
947
948                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
949                 TEST_LPM_ASSERT((status == 0) &&
950                                 (next_hop_return == next_hop_add));
951         }
952
953         /* All tbl8 extensions have been used above. Try to add one more and
954          * we get a fail */
955         ip = IPv4(1, 0, 0, 0);
956         depth = 32;
957
958         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
959         TEST_LPM_ASSERT(status < 0);
960
961         rte_lpm_free(lpm);
962
963         return PASS;
964 }
965
966 /*
967  * Sequence of operations for find existing lpm table
968  *
969  *  - create table
970  *  - find existing table: hit
971  *  - find non-existing table: miss
972  *
973  */
974 int32_t
975 test15(void)
976 {
977         struct rte_lpm *lpm = NULL, *result = NULL;
978
979         /* Create lpm  */
980         lpm = rte_lpm_create("lpm_find_existing", SOCKET_ID_ANY, 256 * 32, 0);
981         TEST_LPM_ASSERT(lpm != NULL);
982
983         /* Try to find existing lpm */
984         result = rte_lpm_find_existing("lpm_find_existing");
985         TEST_LPM_ASSERT(result == lpm);
986
987         /* Try to find non-existing lpm */
988         result = rte_lpm_find_existing("lpm_find_non_existing");
989         TEST_LPM_ASSERT(result == NULL);
990
991         /* Cleanup. */
992         rte_lpm_delete_all(lpm);
993         rte_lpm_free(lpm);
994
995         return PASS;
996 }
997
998 /*
999  * test failure condition of overloading the tbl8 so no more will fit
1000  * Check we get an error return value in that case
1001  */
1002 int32_t
1003 test16(void)
1004 {
1005         uint32_t ip;
1006         struct rte_lpm *lpm = rte_lpm_create(__func__, SOCKET_ID_ANY,
1007                         256 * 32, 0);
1008
1009         /* ip loops through all possibilities for top 24 bits of address */
1010         for (ip = 0; ip < 0xFFFFFF; ip++){
1011                 /* add an entry within a different tbl8 each time, since
1012                  * depth >24 and the top 24 bits are different */
1013                 if (rte_lpm_add(lpm, (ip << 8) + 0xF0, 30, 0) < 0)
1014                         break;
1015         }
1016
1017         if (ip != RTE_LPM_TBL8_NUM_GROUPS) {
1018                 printf("Error, unexpected failure with filling tbl8 groups\n");
1019                 printf("Failed after %u additions, expected after %u\n",
1020                                 (unsigned)ip, (unsigned)RTE_LPM_TBL8_NUM_GROUPS);
1021         }
1022
1023         rte_lpm_free(lpm);
1024         return 0;
1025 }
1026
1027 /*
1028  * Test for overwriting of tbl8:
1029  *  - add rule /32 and lookup
1030  *  - add new rule /24 and lookup
1031  *      - add third rule /25 and lookup
1032  *      - lookup /32 and /24 rule to ensure the table has not been overwritten.
1033  */
1034 int32_t
1035 test17(void)
1036 {
1037         struct rte_lpm *lpm = NULL;
1038         const uint32_t ip_10_32 = IPv4(10, 10, 10, 2);
1039         const uint32_t ip_10_24 = IPv4(10, 10, 10, 0);
1040         const uint32_t ip_20_25 = IPv4(10, 10, 20, 2);
1041         const uint8_t d_ip_10_32 = 32,
1042                         d_ip_10_24 = 24,
1043                         d_ip_20_25 = 25;
1044         const uint8_t next_hop_ip_10_32 = 100,
1045                         next_hop_ip_10_24 = 105,
1046                         next_hop_ip_20_25 = 111;
1047         uint8_t next_hop_return = 0;
1048         int32_t status = 0;
1049
1050         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
1051         TEST_LPM_ASSERT(lpm != NULL);
1052
1053         if ((status = rte_lpm_add(lpm, ip_10_32, d_ip_10_32,
1054                         next_hop_ip_10_32)) < 0)
1055                 return -1;
1056
1057         status = rte_lpm_lookup(lpm, ip_10_32, &next_hop_return);
1058         uint8_t test_hop_10_32 = next_hop_return;
1059         TEST_LPM_ASSERT(status == 0);
1060         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_32);
1061
1062         if ((status = rte_lpm_add(lpm, ip_10_24, d_ip_10_24,
1063                         next_hop_ip_10_24)) < 0)
1064                         return -1;
1065
1066         status = rte_lpm_lookup(lpm, ip_10_24, &next_hop_return);
1067         uint8_t test_hop_10_24 = next_hop_return;
1068         TEST_LPM_ASSERT(status == 0);
1069         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_24);
1070
1071         if ((status = rte_lpm_add(lpm, ip_20_25, d_ip_20_25,
1072                         next_hop_ip_20_25)) < 0)
1073                 return -1;
1074
1075         status = rte_lpm_lookup(lpm, ip_20_25, &next_hop_return);
1076         uint8_t test_hop_20_25 = next_hop_return;
1077         TEST_LPM_ASSERT(status == 0);
1078         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_20_25);
1079
1080         if (test_hop_10_32 == test_hop_10_24) {
1081                 printf("Next hop return equal\n");
1082                 return -1;
1083         }
1084
1085         if (test_hop_10_24 == test_hop_20_25){
1086                 printf("Next hop return equal\n");
1087                 return -1;
1088         }
1089
1090         status = rte_lpm_lookup(lpm, ip_10_32, &next_hop_return);
1091         TEST_LPM_ASSERT(status == 0);
1092         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_32);
1093
1094         status = rte_lpm_lookup(lpm, ip_10_24, &next_hop_return);
1095         TEST_LPM_ASSERT(status == 0);
1096         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_24);
1097
1098         rte_lpm_free(lpm);
1099
1100         return PASS;
1101 }
1102
1103 /*
1104  * Lookup performance test
1105  */
1106
1107 #define ITERATIONS (1 << 10)
1108 #define BATCH_SIZE (1 << 12)
1109 #define BULK_SIZE 32
1110
1111 static void
1112 print_route_distribution(const struct route_rule *table, uint32_t n)
1113 {
1114         unsigned i, j;
1115
1116         printf("Route distribution per prefix width: \n");
1117         printf("DEPTH    QUANTITY (PERCENT)\n");
1118         printf("--------------------------- \n");
1119
1120         /* Count depths. */
1121         for(i = 1; i <= 32; i++) {
1122                 unsigned depth_counter = 0;
1123                 double percent_hits;
1124
1125                 for (j = 0; j < n; j++)
1126                         if (table[j].depth == (uint8_t) i)
1127                                 depth_counter++;
1128
1129                 percent_hits = ((double)depth_counter)/((double)n) * 100;
1130                 printf("%.2u%15u (%.2f)\n", i, depth_counter, percent_hits);
1131         }
1132         printf("\n");
1133 }
1134
1135 int32_t
1136 perf_test(void)
1137 {
1138         struct rte_lpm *lpm = NULL;
1139         uint64_t begin, total_time, lpm_used_entries = 0;
1140         unsigned i, j;
1141         uint8_t next_hop_add = 0xAA, next_hop_return = 0;
1142         int status = 0;
1143         uint64_t cache_line_counter = 0;
1144         int64_t count = 0;
1145
1146         rte_srand(rte_rdtsc());
1147
1148         printf("No. routes = %u\n", (unsigned) NUM_ROUTE_ENTRIES);
1149
1150         print_route_distribution(large_route_table, (uint32_t) NUM_ROUTE_ENTRIES);
1151
1152         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, 1000000, 0);
1153         TEST_LPM_ASSERT(lpm != NULL);
1154
1155         /* Measue add. */
1156         begin = rte_rdtsc();
1157
1158         for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
1159                 if (rte_lpm_add(lpm, large_route_table[i].ip,
1160                                 large_route_table[i].depth, next_hop_add) == 0)
1161                         status++;
1162         }
1163         /* End Timer. */
1164         total_time = rte_rdtsc() - begin;
1165
1166         printf("Unique added entries = %d\n", status);
1167         /* Obtain add statistics. */
1168         for (i = 0; i < RTE_LPM_TBL24_NUM_ENTRIES; i++) {
1169                 if (lpm->tbl24[i].valid)
1170                         lpm_used_entries++;
1171
1172                 if (i % 32 == 0){
1173                         if ((uint64_t)count < lpm_used_entries) {
1174                                 cache_line_counter++;
1175                                 count = lpm_used_entries;
1176                         }
1177                 }
1178         }
1179
1180         printf("Used table 24 entries = %u (%g%%)\n",
1181                         (unsigned) lpm_used_entries,
1182                         (lpm_used_entries * 100.0) / RTE_LPM_TBL24_NUM_ENTRIES);
1183         printf("64 byte Cache entries used = %u (%u bytes)\n",
1184                         (unsigned) cache_line_counter, (unsigned) cache_line_counter * 64);
1185
1186         printf("Average LPM Add: %g cycles\n", (double)total_time / NUM_ROUTE_ENTRIES);
1187
1188         /* Measure single Lookup */
1189         total_time = 0;
1190         count = 0;
1191
1192         for (i = 0; i < ITERATIONS; i ++) {
1193                 static uint32_t ip_batch[BATCH_SIZE];
1194
1195                 for (j = 0; j < BATCH_SIZE; j ++)
1196                         ip_batch[j] = rte_rand();
1197
1198                 /* Lookup per batch */
1199                 begin = rte_rdtsc();
1200
1201                 for (j = 0; j < BATCH_SIZE; j ++) {
1202                         if (rte_lpm_lookup(lpm, ip_batch[j], &next_hop_return) != 0)
1203                                 count++;
1204                 }
1205
1206                 total_time += rte_rdtsc() - begin;
1207
1208         }
1209         printf("Average LPM Lookup: %.1f cycles (fails = %.1f%%)\n",
1210                         (double)total_time / ((double)ITERATIONS * BATCH_SIZE),
1211                         (count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
1212
1213         /* Measure bulk Lookup */
1214         total_time = 0;
1215         count = 0;
1216         for (i = 0; i < ITERATIONS; i ++) {
1217                 static uint32_t ip_batch[BATCH_SIZE];
1218                 uint16_t next_hops[BULK_SIZE];
1219
1220                 /* Create array of random IP addresses */
1221                 for (j = 0; j < BATCH_SIZE; j ++)
1222                         ip_batch[j] = rte_rand();
1223
1224                 /* Lookup per batch */
1225                 begin = rte_rdtsc();
1226                 for (j = 0; j < BATCH_SIZE; j += BULK_SIZE) {
1227                         unsigned k;
1228                         rte_lpm_lookup_bulk(lpm, &ip_batch[j], next_hops, BULK_SIZE);
1229                         for (k = 0; k < BULK_SIZE; k++)
1230                                 if (unlikely(!(next_hops[k] & RTE_LPM_LOOKUP_SUCCESS)))
1231                                         count++;
1232                 }
1233
1234                 total_time += rte_rdtsc() - begin;
1235         }
1236         printf("BULK LPM Lookup: %.1f cycles (fails = %.1f%%)\n",
1237                         (double)total_time / ((double)ITERATIONS * BATCH_SIZE),
1238                         (count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
1239
1240         /* Delete */
1241         status = 0;
1242         begin = rte_rdtsc();
1243
1244         for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
1245                 /* rte_lpm_delete(lpm, ip, depth) */
1246                 status += rte_lpm_delete(lpm, large_route_table[i].ip,
1247                                 large_route_table[i].depth);
1248         }
1249
1250         total_time += rte_rdtsc() - begin;
1251
1252         printf("Average LPM Delete: %g cycles\n",
1253                         (double)total_time / NUM_ROUTE_ENTRIES);
1254
1255         rte_lpm_delete_all(lpm);
1256         rte_lpm_free(lpm);
1257
1258         return PASS;
1259 }
1260
1261 /*
1262  * Do all unit and performance tests.
1263  */
1264
1265 int
1266 test_lpm(void)
1267 {
1268         unsigned i;
1269         int status, global_status = 0;
1270
1271         for (i = 0; i < NUM_LPM_TESTS; i++) {
1272                 status = tests[i]();
1273                 if (status < 0) {
1274                         printf("ERROR: LPM Test %s: FAIL\n", RTE_STR(tests[i]));
1275                         global_status = status;
1276                 }
1277         }
1278
1279         return global_status;
1280 }
1281
1282 #else /* RTE_LIBRTE_LPM */
1283
1284 int
1285 test_lpm(void)
1286 {
1287         printf("The LPM library is not included in this build\n");
1288         return 0;
1289 }
1290
1291 #endif /* RTE_LIBRTE_LPM */