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