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