app: various tests update
[dpdk.git] / app / test / test_lpm.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2012 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         next_hop_add = 100;
729
730         status = rte_lpm_delete(lpm, ip, depth);
731         TEST_LPM_ASSERT(status < 0);
732
733         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
734         TEST_LPM_ASSERT(status == -ENOENT);
735
736         rte_lpm_delete_all(lpm);
737
738         /* Delete a rule that is not present in the TBL8 & lookup */
739
740         ip = IPv4(128, 0, 0, 0);
741         depth = 32;
742         next_hop_add = 100;
743
744         status = rte_lpm_delete(lpm, ip, depth);
745         TEST_LPM_ASSERT(status < 0);
746
747         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
748         TEST_LPM_ASSERT(status == -ENOENT);
749
750         rte_lpm_free(lpm);
751
752         return PASS;
753 }
754
755 /*
756  * Add two rules, lookup to hit the more specific one, lookup to hit the less
757  * specific one delete the less specific rule and lookup previous values again;
758  * add a more specific rule than the existing rule, lookup again
759  *
760  * */
761 int32_t
762 test11(void)
763 {
764
765         struct rte_lpm *lpm = NULL;
766         uint32_t ip;
767         uint8_t depth, next_hop_add, next_hop_return;
768         int32_t status = 0;
769
770         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
771         TEST_LPM_ASSERT(lpm != NULL);
772
773         ip = IPv4(128, 0, 0, 0);
774         depth = 24;
775         next_hop_add = 100;
776
777         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
778         TEST_LPM_ASSERT(status == 0);
779
780         ip = IPv4(128, 0, 0, 10);
781         depth = 32;
782         next_hop_add = 101;
783
784         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
785         TEST_LPM_ASSERT(status == 0);
786
787         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
788         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
789
790         ip = IPv4(128, 0, 0, 0);
791         next_hop_add = 100;
792
793         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
794         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
795
796         ip = IPv4(128, 0, 0, 0);
797         depth = 24;
798
799         status = rte_lpm_delete(lpm, ip, depth);
800         TEST_LPM_ASSERT(status == 0);
801
802         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
803         TEST_LPM_ASSERT(status == -ENOENT);
804
805         ip = IPv4(128, 0, 0, 10);
806         depth = 32;
807
808         status = rte_lpm_delete(lpm, ip, depth);
809         TEST_LPM_ASSERT(status == 0);
810
811         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
812         TEST_LPM_ASSERT(status == -ENOENT);
813
814         rte_lpm_free(lpm);
815
816         return PASS;
817 }
818
819 /*
820  * Add an extended rule (i.e. depth greater than 24, lookup (hit), delete,
821  * lookup (miss) in a for loop of 1000 times. This will check tbl8 extension
822  * and contraction.
823  *
824  * */
825
826 int32_t
827 test12(void)
828 {
829         struct rte_lpm *lpm = NULL;
830         uint32_t ip, i;
831         uint8_t depth, next_hop_add, next_hop_return;
832         int32_t status = 0;
833
834         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
835         TEST_LPM_ASSERT(lpm != NULL);
836
837         ip = IPv4(128, 0, 0, 0);
838         depth = 32;
839         next_hop_add = 100;
840
841         for (i = 0; i < 1000; i++) {
842                 status = rte_lpm_add(lpm, ip, depth, next_hop_add);
843                 TEST_LPM_ASSERT(status == 0);
844
845                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
846                 TEST_LPM_ASSERT((status == 0) &&
847                                 (next_hop_return == next_hop_add));
848
849                 status = rte_lpm_delete(lpm, ip, depth);
850                 TEST_LPM_ASSERT(status == 0);
851
852                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
853                 TEST_LPM_ASSERT(status == -ENOENT);
854         }
855
856         rte_lpm_free(lpm);
857
858         return PASS;
859 }
860
861 /*
862  * Add a rule to tbl24, lookup (hit), then add a rule that will extend this
863  * tbl24 entry, lookup (hit). delete the rule that caused the tbl24 extension,
864  * lookup (miss) and repeat for loop of 1000 times. This will check tbl8
865  * extension and contraction.
866  *
867  * */
868
869 int32_t
870 test13(void)
871 {
872         struct rte_lpm *lpm = NULL;
873         uint32_t ip, i;
874         uint8_t depth, next_hop_add_1, next_hop_add_2, next_hop_return;
875         int32_t status = 0;
876
877         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
878         TEST_LPM_ASSERT(lpm != NULL);
879
880         ip = IPv4(128, 0, 0, 0);
881         depth = 24;
882         next_hop_add_1 = 100;
883
884         status = rte_lpm_add(lpm, ip, depth, next_hop_add_1);
885         TEST_LPM_ASSERT(status == 0);
886
887         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
888         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_1));
889
890         depth = 32;
891         next_hop_add_2 = 101;
892
893         for (i = 0; i < 1000; i++) {
894                 status = rte_lpm_add(lpm, ip, depth, next_hop_add_2);
895                 TEST_LPM_ASSERT(status == 0);
896
897                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
898                 TEST_LPM_ASSERT((status == 0) &&
899                                 (next_hop_return == next_hop_add_2));
900
901                 status = rte_lpm_delete(lpm, ip, depth);
902                 TEST_LPM_ASSERT(status == 0);
903
904                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
905                 TEST_LPM_ASSERT((status == 0) &&
906                                 (next_hop_return == next_hop_add_1));
907         }
908
909         depth = 24;
910
911         status = rte_lpm_delete(lpm, ip, depth);
912         TEST_LPM_ASSERT(status == 0);
913
914         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
915         TEST_LPM_ASSERT(status == -ENOENT);
916
917         rte_lpm_free(lpm);
918
919         return PASS;
920 }
921
922 /*
923  * Fore TBL8 extension exhaustion. Add 256 rules that require a tbl8 extension.
924  * No more tbl8 extensions will be allowed. Now add one more rule that required
925  * a tbl8 extension and get fail.
926  * */
927 int32_t
928 test14(void)
929 {
930
931         /* We only use depth = 32 in the loop below so we must make sure
932          * that we have enough storage for all rules at that depth*/
933
934         struct rte_lpm *lpm = NULL;
935         uint32_t ip;
936         uint8_t depth, next_hop_add, next_hop_return;
937         int32_t status = 0;
938
939         /* Add enough space for 256 rules for every depth */
940         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, 256 * 32, 0);
941         TEST_LPM_ASSERT(lpm != NULL);
942
943         ip = IPv4(0, 0, 0, 0);
944         depth = 32;
945         next_hop_add = 100;
946
947         /* Add 256 rules that require a tbl8 extension */
948         for (ip = 0; ip <= IPv4(0, 0, 255, 0); ip += 256) {
949                 status = rte_lpm_add(lpm, ip, depth, next_hop_add);
950                 TEST_LPM_ASSERT(status == 0);
951
952                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
953                 TEST_LPM_ASSERT((status == 0) &&
954                                 (next_hop_return == next_hop_add));
955         }
956
957         /* All tbl8 extensions have been used above. Try to add one more and
958          * we get a fail */
959         ip = IPv4(1, 0, 0, 0);
960         depth = 32;
961
962         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
963         TEST_LPM_ASSERT(status < 0);
964
965         rte_lpm_free(lpm);
966
967         return PASS;
968 }
969
970 /*
971  * Sequence of operations for find existing lpm table
972  *
973  *  - create table
974  *  - find existing table: hit
975  *  - find non-existing table: miss
976  *
977  */
978 int32_t
979 test15(void)
980 {
981         struct rte_lpm *lpm = NULL, *result = NULL;
982
983         /* Create lpm  */
984         lpm = rte_lpm_create("lpm_find_existing", SOCKET_ID_ANY, 256 * 32, 0);
985         TEST_LPM_ASSERT(lpm != NULL);
986
987         /* Try to find existing lpm */
988         result = rte_lpm_find_existing("lpm_find_existing");
989         TEST_LPM_ASSERT(result == lpm);
990
991         /* Try to find non-existing lpm */
992         result = rte_lpm_find_existing("lpm_find_non_existing");
993         TEST_LPM_ASSERT(result == NULL);
994
995         /* Cleanup. */
996         rte_lpm_delete_all(lpm);
997         rte_lpm_free(lpm);
998
999         return PASS;
1000 }
1001
1002 /*
1003  * test failure condition of overloading the tbl8 so no more will fit
1004  * Check we get an error return value in that case
1005  */
1006 int32_t
1007 test16(void)
1008 {
1009         uint32_t ip;
1010         struct rte_lpm *lpm = rte_lpm_create(__func__, SOCKET_ID_ANY,
1011                         256 * 32, 0);
1012
1013         /* ip loops through all possibilities for top 24 bits of address */
1014         for (ip = 0; ip < 0xFFFFFF; ip++){
1015                 /* add an entry within a different tbl8 each time, since
1016                  * depth >24 and the top 24 bits are different */
1017                 if (rte_lpm_add(lpm, (ip << 8) + 0xF0, 30, 0) < 0)
1018                         break;
1019         }
1020
1021         if (ip != RTE_LPM_TBL8_NUM_GROUPS) {
1022                 printf("Error, unexpected failure with filling tbl8 groups\n");
1023                 printf("Failed after %u additions, expected after %u\n",
1024                                 (unsigned)ip, (unsigned)RTE_LPM_TBL8_NUM_GROUPS);
1025         }
1026
1027         rte_lpm_free(lpm);
1028         return 0;
1029 }
1030
1031 /*
1032  * Test for overwriting of tbl8:
1033  *  - add rule /32 and lookup
1034  *  - add new rule /24 and lookup
1035  *      - add third rule /25 and lookup
1036  *      - lookup /32 and /24 rule to ensure the table has not been overwritten.
1037  */
1038 int32_t
1039 test17(void)
1040 {
1041         struct rte_lpm *lpm = NULL;
1042         const uint32_t ip_10_32 = IPv4(10, 10, 10, 2);
1043         const uint32_t ip_10_24 = IPv4(10, 10, 10, 0);
1044         const uint32_t ip_20_25 = IPv4(10, 10, 20, 2);
1045         const uint8_t d_ip_10_32 = 32,
1046                         d_ip_10_24 = 24,
1047                         d_ip_20_25 = 25;
1048         const uint8_t next_hop_ip_10_32 = 100,
1049                         next_hop_ip_10_24 = 105,
1050                         next_hop_ip_20_25 = 111;
1051         uint8_t next_hop_return = 0;
1052         int32_t status = 0;
1053
1054         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, MAX_RULES, 0);
1055         TEST_LPM_ASSERT(lpm != NULL);
1056
1057         if ((status = rte_lpm_add(lpm, ip_10_32, d_ip_10_32,
1058                         next_hop_ip_10_32)) < 0)
1059                 return -1;
1060
1061         status = rte_lpm_lookup(lpm, ip_10_32, &next_hop_return);
1062         uint8_t test_hop_10_32 = next_hop_return;
1063         TEST_LPM_ASSERT(status == 0);
1064         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_32);
1065
1066         if ((status = rte_lpm_add(lpm, ip_10_24, d_ip_10_24,
1067                         next_hop_ip_10_24)) < 0)
1068                         return -1;
1069
1070         status = rte_lpm_lookup(lpm, ip_10_24, &next_hop_return);
1071         uint8_t test_hop_10_24 = next_hop_return;
1072         TEST_LPM_ASSERT(status == 0);
1073         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_24);
1074
1075         if ((status = rte_lpm_add(lpm, ip_20_25, d_ip_20_25,
1076                         next_hop_ip_20_25)) < 0)
1077                 return -1;
1078
1079         status = rte_lpm_lookup(lpm, ip_20_25, &next_hop_return);
1080         uint8_t test_hop_20_25 = next_hop_return;
1081         TEST_LPM_ASSERT(status == 0);
1082         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_20_25);
1083
1084         if (test_hop_10_32 == test_hop_10_24) {
1085                 printf("Next hop return equal\n");
1086                 return -1;
1087         }
1088
1089         if (test_hop_10_24 == test_hop_20_25){
1090                 printf("Next hop return equal\n");
1091                 return -1;
1092         }
1093
1094         status = rte_lpm_lookup(lpm, ip_10_32, &next_hop_return);
1095         TEST_LPM_ASSERT(status == 0);
1096         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_32);
1097
1098         status = rte_lpm_lookup(lpm, ip_10_24, &next_hop_return);
1099         TEST_LPM_ASSERT(status == 0);
1100         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_24);
1101
1102         rte_lpm_free(lpm);
1103
1104         return PASS;
1105 }
1106
1107 /*
1108  * Lookup performance test
1109  */
1110
1111 #define ITERATIONS (1 << 10)
1112 #define BATCH_SIZE (1 << 12)
1113 #define BULK_SIZE 32
1114
1115 static void
1116 print_route_distribution(const struct route_rule *table, uint32_t n)
1117 {
1118         unsigned i, j;
1119
1120         printf("Route distribution per prefix width: \n");
1121         printf("DEPTH    QUANTITY (PERCENT)\n");
1122         printf("--------------------------- \n");
1123
1124         /* Count depths. */
1125         for(i = 1; i <= 32; i++) {
1126                 unsigned depth_counter = 0;
1127                 double percent_hits;
1128
1129                 for (j = 0; j < n; j++)
1130                         if (table[j].depth == (uint8_t) i)
1131                                 depth_counter++;
1132
1133                 percent_hits = ((double)depth_counter)/((double)n) * 100;
1134                 printf("%.2u%15u (%.2f)\n", i, depth_counter, percent_hits);
1135         }
1136         printf("\n");
1137 }
1138
1139 int32_t
1140 perf_test(void)
1141 {
1142         struct rte_lpm *lpm = NULL;
1143         uint64_t begin, total_time, lpm_used_entries = 0;
1144         unsigned i, j;
1145         uint8_t next_hop_add = 0xAA, next_hop_return = 0;
1146         int status = 0;
1147         uint64_t cache_line_counter = 0;
1148         int64_t count = 0;
1149
1150         rte_srand(rte_rdtsc());
1151
1152         printf("No. routes = %u\n", (unsigned) NUM_ROUTE_ENTRIES);
1153
1154         print_route_distribution(large_route_table, (uint32_t) NUM_ROUTE_ENTRIES);
1155
1156         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, 1000000, 0);
1157         TEST_LPM_ASSERT(lpm != NULL);
1158
1159         /* Measue add. */
1160         begin = rte_rdtsc();
1161
1162         for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
1163                 if (rte_lpm_add(lpm, large_route_table[i].ip,
1164                                 large_route_table[i].depth, next_hop_add) == 0)
1165                         status++;
1166         }
1167         /* End Timer. */
1168         total_time = rte_rdtsc() - begin;
1169
1170         printf("Unique added entries = %d\n", status);
1171         /* Obtain add statistics. */
1172         for (i = 0; i < RTE_LPM_TBL24_NUM_ENTRIES; i++) {
1173                 if (lpm->tbl24[i].valid)
1174                         lpm_used_entries++;
1175
1176                 if (i % 32 == 0){
1177                         if ((uint64_t)count < lpm_used_entries) {
1178                                 cache_line_counter++;
1179                                 count = lpm_used_entries;
1180                         }
1181                 }
1182         }
1183
1184         printf("Used table 24 entries = %u (%g%%)\n",
1185                         (unsigned) lpm_used_entries,
1186                         (lpm_used_entries * 100.0) / RTE_LPM_TBL24_NUM_ENTRIES);
1187         printf("64 byte Cache entries used = %u (%u bytes)\n",
1188                         (unsigned) cache_line_counter, (unsigned) cache_line_counter * 64);
1189
1190         printf("Average LPM Add: %g cycles\n", (double)total_time / NUM_ROUTE_ENTRIES);
1191
1192         /* Measure single Lookup */
1193         total_time = 0;
1194         count = 0;
1195
1196         for (i = 0; i < ITERATIONS; i ++) {
1197                 static uint32_t ip_batch[BATCH_SIZE];
1198
1199                 for (j = 0; j < BATCH_SIZE; j ++)
1200                         ip_batch[j] = rte_rand();
1201
1202                 /* Lookup per batch */
1203                 begin = rte_rdtsc();
1204
1205                 for (j = 0; j < BATCH_SIZE; j ++) {
1206                         if (rte_lpm_lookup(lpm, ip_batch[j], &next_hop_return) != 0)
1207                                 count++;
1208                 }
1209
1210                 total_time += rte_rdtsc() - begin;
1211
1212         }
1213         printf("Average LPM Lookup: %.1f cycles (fails = %.1f%%)\n",
1214                         (double)total_time / ((double)ITERATIONS * BATCH_SIZE),
1215                         (count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
1216
1217         /* Measure bulk Lookup */
1218         total_time = 0;
1219         count = 0;
1220         for (i = 0; i < ITERATIONS; i ++) {
1221                 static uint32_t ip_batch[BATCH_SIZE];
1222                 uint16_t next_hops[BULK_SIZE];
1223
1224                 /* Create array of random IP addresses */
1225                 for (j = 0; j < BATCH_SIZE; j ++)
1226                         ip_batch[j] = rte_rand();
1227
1228                 /* Lookup per batch */
1229                 begin = rte_rdtsc();
1230                 for (j = 0; j < BATCH_SIZE; j += BULK_SIZE) {
1231                         unsigned k;
1232                         rte_lpm_lookup_bulk(lpm, &ip_batch[j], next_hops, BULK_SIZE);
1233                         for (k = 0; k < BULK_SIZE; k++)
1234                                 if (unlikely(!(next_hops[k] & RTE_LPM_LOOKUP_SUCCESS)))
1235                                         count++;
1236                 }
1237
1238                 total_time += rte_rdtsc() - begin;
1239         }
1240         printf("BULK LPM Lookup: %.1f cycles (fails = %.1f%%)\n",
1241                         (double)total_time / ((double)ITERATIONS * BATCH_SIZE),
1242                         (count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
1243
1244         /* Delete */
1245         status = 0;
1246         begin = rte_rdtsc();
1247
1248         for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
1249                 /* rte_lpm_delete(lpm, ip, depth) */
1250                 status += rte_lpm_delete(lpm, large_route_table[i].ip,
1251                                 large_route_table[i].depth);
1252         }
1253
1254         total_time += rte_rdtsc() - begin;
1255
1256         printf("Average LPM Delete: %g cycles\n",
1257                         (double)total_time / NUM_ROUTE_ENTRIES);
1258
1259         rte_lpm_delete_all(lpm);
1260         rte_lpm_free(lpm);
1261
1262         return PASS;
1263 }
1264
1265 /*
1266  * Do all unit and performance tests.
1267  */
1268
1269 int
1270 test_lpm(void)
1271 {
1272         unsigned i;
1273         int status, global_status = 0;
1274
1275         for (i = 0; i < NUM_LPM_TESTS; i++) {
1276                 status = tests[i]();
1277                 if (status < 0) {
1278                         printf("ERROR: LPM Test %s: FAIL\n", RTE_STR(tests[i]));
1279                         global_status = status;
1280                 }
1281         }
1282
1283         return global_status;
1284 }
1285
1286 #else
1287
1288 int
1289 test_lpm(void)
1290 {
1291         printf("The LPM library is not included in this build\n");
1292         return 0;
1293 }
1294
1295 #endif