L3 cross connect
[vpp.git] / src / vnet / fib / fib_path_list.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vppinfra/mhash.h>
17 #include <vnet/ip/ip.h>
18 #include <vnet/adj/adj.h>
19 #include <vnet/dpo/load_balance.h>
20 #include <vnet/dpo/load_balance_map.h>
21
22 #include <vnet/fib/fib_path_list.h>
23 #include <vnet/fib/fib_internal.h>
24 #include <vnet/fib/fib_node_list.h>
25 #include <vnet/fib/fib_walk.h>
26 #include <vnet/fib/fib_urpf_list.h>
27 #include <vnet/fib/fib_path_ext.h>
28 #include <vnet/fib/fib_table.h>
29
30 /**
31  * The magic number of child entries that make a path-list popular.
32  * There's a trade-off here between convergnece and forwarding speed.
33  * Popular path-lists generate load-balance maps for the entries that
34  * use them. If the map is present there is a switch path cost to indirect
35  * through the map - this indirection provides the fast convergence - so
36  * without the map convergence is slower.
37  */
38 #define FIB_PATH_LIST_POPULAR 64
39
40 /**
41  * FIB path-list
42  * A representation of the list/set of path trough which a prefix is reachable
43  */
44 typedef struct fib_path_list_t_ {
45     /**
46      * A path-list is a node in the FIB graph.
47      */
48     fib_node_t fpl_node;
49
50     /**
51      * Flags on the path-list
52      */
53     fib_path_list_flags_t fpl_flags;
54
55     /**
56      * Vector of paths indicies for all configured paths.
57      * For shareable path-lists this list MUST not change.
58      */
59     fib_node_index_t *fpl_paths;
60
61     /**
62      * the RPF list calculated for this path list
63      */
64     fib_node_index_t fpl_urpf;
65
66     /**
67      * Hash table of paths. valid only with INDEXED flag
68      */
69     uword *fpl_db;
70 } fib_path_list_t;
71
72 /*
73  * Array of strings/names for the FIB sources
74  */
75 static const char *fib_path_list_attr_names[] = FIB_PATH_LIST_ATTRIBUTES;
76
77 /*
78  * The memory pool from which we allocate all the path-lists
79  */
80 static fib_path_list_t * fib_path_list_pool;
81
82 /*
83  * The data-base of shared path-lists
84  */
85 static uword *fib_path_list_db;
86
87 /**
88  * the logger
89  */
90 vlib_log_class_t fib_path_list_logger;
91
92 /*
93  * Debug macro
94  */
95 #define FIB_PATH_LIST_DBG(_pl, _fmt, _args...)                  \
96 {                                                               \
97     vlib_log_debug(fib_path_list_logger,                        \
98                    "[%U]:" _fmt,                                \
99                    format_fib_path_list,                        \
100                    fib_path_list_get_index(_pl), 0,             \
101                    ##_args);                                    \
102 }
103
104 static fib_path_list_t *
105 fib_path_list_get (fib_node_index_t index)
106 {
107     return (pool_elt_at_index(fib_path_list_pool, index));
108 }
109
110 static fib_node_t *
111 fib_path_list_get_node (fib_node_index_t index)
112 {
113     return ((fib_node_t*)fib_path_list_get(index));
114 }
115
116 static fib_path_list_t*
117 fib_path_list_from_fib_node (fib_node_t *node)
118 {
119     ASSERT(FIB_NODE_TYPE_PATH_LIST == node->fn_type);
120     return ((fib_path_list_t*)node);
121 }
122
123 static fib_node_index_t
124 fib_path_list_get_index (fib_path_list_t *path_list)
125 {
126     return (path_list - fib_path_list_pool);
127 }
128
129 u8 *
130 format_fib_path_list (u8 * s, va_list * args)
131 {
132     fib_node_index_t *path_index, path_list_index;
133     fib_path_list_attribute_t attr;
134     fib_path_list_t *path_list;
135     u32 indent;
136
137     path_list_index = va_arg (*args, fib_node_index_t);
138     indent = va_arg (*args, u32);
139     path_list = fib_path_list_get(path_list_index);
140
141     s = format (s, "%Upath-list:[%d]",
142                 format_white_space, indent,
143                 fib_path_list_get_index(path_list));
144     s = format (s, " locks:%u", path_list->fpl_node.fn_locks);
145
146     if (FIB_PATH_LIST_FLAG_NONE != path_list->fpl_flags)
147     {
148         s = format (s, " flags:");
149         FOR_EACH_PATH_LIST_ATTRIBUTE(attr)
150         {
151             if ((1<<attr) & path_list->fpl_flags)
152             {
153                 s = format (s, "%s,", fib_path_list_attr_names[attr]);
154             }
155         }
156     }
157     s = format (s, " %U\n", format_fib_urpf_list, path_list->fpl_urpf);
158
159     vec_foreach (path_index, path_list->fpl_paths)
160     {
161         s = format(s, "%U", format_fib_path, *path_index, indent+2,
162                    FIB_PATH_FORMAT_FLAGS_NONE);
163         s = format(s, "\n");
164     }
165
166     return (s);
167 }
168
169 u8 *
170 fib_path_list_format (fib_node_index_t path_list_index,
171                       u8 * s)
172 {
173     return (format(s, "%U", format_fib_path_list, path_list_index, 4));
174 }
175
176 static uword
177 fib_path_list_hash (fib_path_list_t *path_list)
178 {
179     uword old_path_list_hash, new_path_list_hash, path_hash;
180     fib_node_index_t *path_index;
181
182     ASSERT(path_list);
183
184     new_path_list_hash = old_path_list_hash = vec_len(path_list->fpl_paths);
185
186     vec_foreach (path_index, path_list->fpl_paths)
187     {
188         path_hash = fib_path_hash(*path_index);
189 #if uword_bits == 64
190         hash_mix64(path_hash, old_path_list_hash, new_path_list_hash);
191 #else
192         hash_mix32(path_hash, old_path_list_hash, new_path_list_hash);
193 #endif
194     }
195
196     return (new_path_list_hash);
197 }
198
199 always_inline uword
200 fib_path_list_db_hash_key_from_index (uword index)
201 {
202     return 1 + 2*index;
203 }
204
205 always_inline uword
206 fib_path_list_db_hash_key_is_index (uword key)
207 {
208     return key & 1;
209 }
210
211 always_inline uword
212 fib_path_list_db_hash_key_2_index (uword key)
213 {
214     ASSERT (fib_path_list_db_hash_key_is_index (key));
215     return key / 2;
216 }
217
218 static fib_path_list_t*
219 fib_path_list_db_get_from_hash_key (uword key)
220 {
221     fib_path_list_t *path_list;
222
223     if (fib_path_list_db_hash_key_is_index (key))
224     {
225         fib_node_index_t path_list_index;
226
227         path_list_index = fib_path_list_db_hash_key_2_index(key);
228         path_list = fib_path_list_get(path_list_index);
229     }
230     else
231     {       
232         path_list = uword_to_pointer (key, fib_path_list_t *);
233     }
234
235     return (path_list);
236 }
237
238 static uword
239 fib_path_list_db_hash_key_sum (hash_t * h,
240                                uword key)
241 {
242     fib_path_list_t *path_list;
243
244     path_list = fib_path_list_db_get_from_hash_key(key);
245
246     return (fib_path_list_hash(path_list));
247 }
248
249 static uword
250 fib_path_list_db_hash_key_equal (hash_t * h,
251                                  uword key1,
252                                  uword key2)
253 {
254     fib_path_list_t *path_list1, *path_list2;
255
256     path_list1 = fib_path_list_db_get_from_hash_key(key1);
257     path_list2 = fib_path_list_db_get_from_hash_key(key2);
258
259     return (fib_path_list_hash(path_list1) ==
260             fib_path_list_hash(path_list2));
261 }
262
263 static fib_node_index_t
264 fib_path_list_db_find (fib_path_list_t *path_list)
265 {
266     uword *p;
267
268     p = hash_get(fib_path_list_db, path_list);
269
270     if (NULL != p)
271     {
272         return p[0];
273     }
274
275     return (FIB_NODE_INDEX_INVALID);
276 }
277
278 static void
279 fib_path_list_db_insert (fib_node_index_t path_list_index)
280 {
281     fib_path_list_t *path_list;
282
283     path_list = fib_path_list_get(path_list_index);
284
285     ASSERT(FIB_NODE_INDEX_INVALID == fib_path_list_db_find(path_list));
286
287     hash_set (fib_path_list_db,
288               fib_path_list_db_hash_key_from_index(path_list_index),
289               path_list_index);
290
291     FIB_PATH_LIST_DBG(path_list, "DB-inserted");
292 }
293
294 static void
295 fib_path_list_db_remove (fib_node_index_t path_list_index)
296 {
297     fib_path_list_t *path_list;
298
299     path_list = fib_path_list_get(path_list_index);
300
301     ASSERT(FIB_NODE_INDEX_INVALID != fib_path_list_db_find(path_list));
302
303     hash_unset(fib_path_list_db,
304                fib_path_list_db_hash_key_from_index(path_list_index));
305
306     FIB_PATH_LIST_DBG(path_list, "DB-removed");
307 }
308
309 static void
310 fib_path_list_destroy (fib_path_list_t *path_list)
311 {
312     fib_node_index_t *path_index;
313
314     FIB_PATH_LIST_DBG(path_list, "destroy");
315
316     vec_foreach (path_index, path_list->fpl_paths)
317     {
318         fib_path_destroy(*path_index);
319     }
320
321     vec_free(path_list->fpl_paths);
322     fib_urpf_list_unlock(path_list->fpl_urpf);
323
324     fib_node_deinit(&path_list->fpl_node);
325     pool_put(fib_path_list_pool, path_list);
326 }
327
328 static void
329 fib_path_list_last_lock_gone (fib_node_t *node)
330 {
331     fib_path_list_t *path_list;
332
333     path_list = fib_path_list_from_fib_node(node);
334
335     FIB_PATH_LIST_DBG(path_list, "last-lock");
336
337     if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
338     {
339         fib_path_list_db_remove(fib_path_list_get_index(path_list));
340     }
341     fib_path_list_destroy(path_list);
342 }
343
344 static load_balance_flags_t
345 fib_path_list_fwd_flags_2_load_balance (fib_path_list_fwd_flags_t pl_flags)
346 {
347     load_balance_flags_t lb_flags = LOAD_BALANCE_FLAG_NONE;
348
349     if (pl_flags & FIB_PATH_LIST_FWD_FLAG_STICKY)
350     {
351         lb_flags |= LOAD_BALANCE_ATTR_STICKY;
352     }
353     return (lb_flags);
354 }
355
356 /*
357  * fib_path_mk_lb
358  *
359  * update the multipath adj this path-list will contribute to its
360  * children's forwarding.
361  */
362 static void
363 fib_path_list_mk_lb (fib_path_list_t *path_list,
364                      fib_forward_chain_type_t fct,
365                      dpo_id_t *dpo,
366                      fib_path_list_fwd_flags_t flags)
367 {
368     fib_node_index_t *path_index;
369     load_balance_path_t *nhs;
370     dpo_proto_t dproto;
371
372     nhs = NULL;
373     dproto = fib_forw_chain_type_to_dpo_proto(fct);
374
375     /*
376      * We gather the DPOs from resolved paths.
377      */
378     vec_foreach (path_index, path_list->fpl_paths)
379     {
380         if ((flags & FIB_PATH_LIST_FWD_FLAG_STICKY) ||
381             fib_path_is_resolved(*path_index))
382         {
383             nhs = fib_path_append_nh_for_multipath_hash(*path_index,
384                                                         fct, nhs);
385         }
386     }
387
388     /*
389      * Path-list load-balances, which if used, would be shared and hence
390      * never need a load-balance map.
391      */
392     dpo_set(dpo,
393             DPO_LOAD_BALANCE,
394             dproto,
395             load_balance_create(vec_len(nhs),
396                                 dproto,
397                                 fib_table_get_default_flow_hash_config(
398                                     dpo_proto_to_fib(dproto))));
399     load_balance_multipath_update(dpo, nhs,
400                                   fib_path_list_fwd_flags_2_load_balance(flags));
401
402     FIB_PATH_LIST_DBG(path_list, "mk lb: %d", dpo->dpoi_index);
403
404     vec_free(nhs);
405 }
406
407 /**
408  * @brief [re]build the path list's uRPF list
409  */
410 static void
411 fib_path_list_mk_urpf (fib_path_list_t *path_list)
412 {
413     fib_node_index_t *path_index;
414
415     /*
416      * ditch the old one. by iterating through all paths we are going
417      * to re-find all the adjs that were in the old one anyway. If we
418      * keep the old one, then the |sort|uniq requires more work.
419      * All users of the RPF list have their own lock, so we can release
420      * immediately.
421      */
422     fib_urpf_list_unlock(path_list->fpl_urpf);
423     path_list->fpl_urpf = fib_urpf_list_alloc_and_lock();
424
425     vec_foreach (path_index, path_list->fpl_paths)
426     {
427         fib_path_contribute_urpf(*path_index, path_list->fpl_urpf);
428     }
429
430     fib_urpf_list_bake(path_list->fpl_urpf);
431 }
432
433 /**
434  * @brief Contribute (add) this path list's uRPF list. This allows the child
435  * to construct an aggregate list.
436  */
437 void
438 fib_path_list_contribute_urpf (fib_node_index_t path_list_index,
439                                index_t urpf)
440 {
441     fib_path_list_t *path_list;
442
443     path_list = fib_path_list_get(path_list_index);
444
445     fib_urpf_list_combine(urpf, path_list->fpl_urpf);
446 }
447
448 /**
449  * @brief Return the the child the RPF list pre-built for this path list
450  */
451 index_t
452 fib_path_list_get_urpf (fib_node_index_t path_list_index)
453 {
454     fib_path_list_t *path_list;
455
456     path_list = fib_path_list_get(path_list_index);
457
458     return (path_list->fpl_urpf);
459 }
460
461 /*
462  * fib_path_list_back_walk
463  *
464  * Called from one of this path-list's paths to progate
465  * a back walk
466  */
467 void
468 fib_path_list_back_walk (fib_node_index_t path_list_index,
469                          fib_node_back_walk_ctx_t *ctx)
470 {
471     fib_path_list_t *path_list;
472
473     path_list = fib_path_list_get(path_list_index);
474
475     fib_path_list_mk_urpf(path_list);
476
477     FIB_PATH_LIST_DBG(path_list, "bw:%U",
478                       format_fib_node_bw_reason, ctx->fnbw_reason);
479
480     /*
481      * propagate the backwalk further
482      */
483     if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_POPULAR)
484     {
485         /*
486          * many children. schedule a async walk
487          */
488         fib_walk_async(FIB_NODE_TYPE_PATH_LIST,
489                        path_list_index,
490                        FIB_WALK_PRIORITY_LOW,
491                        ctx);
492     }
493     else
494     {
495         /*
496          * only a few children. continue the walk synchronously
497          */
498         fib_walk_sync(FIB_NODE_TYPE_PATH_LIST, path_list_index, ctx);
499     }
500 }
501
502 /*
503  * fib_path_list_back_walk_notify
504  *
505  * A back walk has reach this path-list.
506  */
507 static fib_node_back_walk_rc_t
508 fib_path_list_back_walk_notify (fib_node_t *node,
509                                 fib_node_back_walk_ctx_t *ctx)
510 {
511     /*
512      * the path-list is not a direct child of any other node type
513      * paths, which do not change thier to-list-mapping, save the
514      * list they are a member of, and invoke the BW function directly.
515      */
516     ASSERT(0);
517
518     return (FIB_NODE_BACK_WALK_CONTINUE);
519 }
520
521 /*
522  * Display the path-list memory usage
523  */
524 static void
525 fib_path_list_memory_show (void)
526 {
527     fib_show_memory_usage("Path-list",
528                           pool_elts(fib_path_list_pool),
529                           pool_len(fib_path_list_pool),
530                           sizeof(fib_path_list_t));
531     fib_urpf_list_show_mem();
532 }
533
534 /*
535  * The FIB path-list's graph node virtual function table
536  */
537 static const fib_node_vft_t fib_path_list_vft = {
538     .fnv_get = fib_path_list_get_node,
539     .fnv_last_lock = fib_path_list_last_lock_gone,
540     .fnv_back_walk = fib_path_list_back_walk_notify,
541     .fnv_mem_show = fib_path_list_memory_show,
542 };
543
544 static inline fib_path_list_t *
545 fib_path_list_alloc (fib_node_index_t *path_list_index)
546 {
547     fib_path_list_t *path_list;
548
549     pool_get(fib_path_list_pool, path_list);
550     clib_memset(path_list, 0, sizeof(*path_list));
551
552     fib_node_init(&path_list->fpl_node,
553                   FIB_NODE_TYPE_PATH_LIST);
554     path_list->fpl_urpf = INDEX_INVALID;
555     path_list->fpl_paths = NULL;
556
557     *path_list_index = fib_path_list_get_index(path_list);
558
559     FIB_PATH_LIST_DBG(path_list, "alloc");
560
561     return (path_list);
562 }
563
564 static fib_path_list_t *
565 fib_path_list_resolve (fib_path_list_t *path_list)
566 {
567     fib_node_index_t *path_index, *paths, path_list_index;
568
569     ASSERT(!(path_list->fpl_flags & FIB_PATH_LIST_FLAG_RESOLVED));
570
571     /*
572      * resolving a path-list is a recursive action. this means more path
573      * lists can be created during this call, and hence this path-list
574      * can be realloc'd. so we work with copies.
575      * this function is called only once per-path list, so its no great overhead.
576      */
577     path_list_index = fib_path_list_get_index(path_list);
578     paths = vec_dup(path_list->fpl_paths);
579
580     vec_foreach (path_index, paths)
581     {
582         fib_path_resolve(*path_index);
583     }
584
585     vec_free(paths);
586     path_list = fib_path_list_get(path_list_index);
587
588     FIB_PATH_LIST_DBG(path_list, "resovled");
589
590     if (!(path_list->fpl_flags & FIB_PATH_LIST_FLAG_NO_URPF))
591     {
592         fib_path_list_mk_urpf(path_list);
593     }
594     return (path_list);
595 }
596
597 u32
598 fib_path_list_get_n_paths (fib_node_index_t path_list_index)
599 {
600     fib_path_list_t *path_list;
601
602     if (FIB_NODE_INDEX_INVALID == path_list_index)
603     {
604         return (0);
605     }
606
607     path_list = fib_path_list_get(path_list_index);
608
609     return (vec_len(path_list->fpl_paths));
610 }
611
612
613 u32
614 fib_path_list_get_resolving_interface (fib_node_index_t path_list_index)
615 {
616     fib_node_index_t *path_index;
617     fib_path_list_t *path_list;
618     u32 sw_if_index;
619
620     path_list = fib_path_list_get(path_list_index);
621
622     sw_if_index = ~0;
623     vec_foreach (path_index, path_list->fpl_paths)
624     {
625         sw_if_index = fib_path_get_resolving_interface(*path_index);
626         if (~0 != sw_if_index)
627         {
628             return (sw_if_index);
629         }
630     }
631
632     return (sw_if_index);
633 }
634
635 dpo_proto_t
636 fib_path_list_get_proto (fib_node_index_t path_list_index)
637 {
638     fib_path_list_t *path_list;
639
640     path_list = fib_path_list_get(path_list_index);
641
642     /*
643      * we don't support a mix of path protocols, so we can return the proto
644      * of the first
645      */
646     return (fib_path_get_proto(path_list->fpl_paths[0]));
647 }
648
649 int
650 fib_path_list_is_looped (fib_node_index_t path_list_index)
651 {
652     fib_path_list_t *path_list;
653
654     path_list = fib_path_list_get(path_list_index);
655
656     return (path_list->fpl_flags & FIB_PATH_LIST_FLAG_LOOPED);
657 }
658
659 int
660 fib_path_list_is_popular (fib_node_index_t path_list_index)
661 {
662     fib_path_list_t *path_list;
663
664     path_list = fib_path_list_get(path_list_index);
665
666     return (path_list->fpl_flags & FIB_PATH_LIST_FLAG_POPULAR);
667 }
668
669 static fib_path_list_flags_t
670 fib_path_list_flags_fixup (fib_path_list_flags_t flags)
671 {
672     /*
673      * we do no share drop nor exclusive path-lists
674      */
675     if (flags & FIB_PATH_LIST_FLAG_DROP ||
676         flags & FIB_PATH_LIST_FLAG_EXCLUSIVE)
677     {
678         flags &= ~FIB_PATH_LIST_FLAG_SHARED;
679     }
680
681     return (flags);
682 }
683
684 fib_node_index_t
685 fib_path_list_create (fib_path_list_flags_t flags,
686                       const fib_route_path_t *rpaths)
687 {
688     fib_node_index_t path_list_index, old_path_list_index;
689     fib_path_list_t *path_list;
690     int i;
691
692     flags = fib_path_list_flags_fixup(flags);
693     path_list = fib_path_list_alloc(&path_list_index);
694     path_list->fpl_flags = flags;
695
696     if (NULL != rpaths)
697     {
698         vec_foreach_index(i, rpaths)
699         {
700             vec_add1(path_list->fpl_paths,
701                      fib_path_create(path_list_index,
702                                      &rpaths[i]));
703         }
704         /*
705          * we sort the paths since the key for the path-list is
706          * the description of the paths it contains. The paths need to
707          * be sorted else this description will differ.
708          */
709         if (vec_len(path_list->fpl_paths) > 1)
710         {
711             vec_sort_with_function(path_list->fpl_paths,
712                                    fib_path_cmp_for_sort);
713         }
714     }
715
716     /*
717      * If a shared path list is requested, consult the DB for a match
718      */
719     if (flags & FIB_PATH_LIST_FLAG_SHARED)
720     {
721         /*
722          * check for a matching path-list in the DB.
723          * If we find one then we can return the existing one and destroy the
724          * new one just created.
725          */
726         old_path_list_index = fib_path_list_db_find(path_list);
727         if (FIB_NODE_INDEX_INVALID != old_path_list_index)
728         {
729             fib_path_list_destroy(path_list);
730
731             path_list_index = old_path_list_index;
732         }
733         else
734         {
735             /*
736              * if there was not a matching path-list, then this
737              * new one will need inserting into the DB and resolving.
738              */
739             fib_path_list_db_insert(path_list_index);
740             path_list = fib_path_list_resolve(path_list);
741         }
742     }
743     else
744     {
745         /*
746          * no shared path list requested. resolve and use the one
747          * just created.
748          */
749         path_list = fib_path_list_resolve(path_list);
750     }
751
752     return (path_list_index);
753 }
754
755 static fib_path_cfg_flags_t
756 fib_path_list_flags_2_path_flags (fib_path_list_flags_t plf)
757 {
758     fib_path_cfg_flags_t pf = FIB_PATH_CFG_FLAG_NONE;
759
760     if (plf & FIB_PATH_LIST_FLAG_DROP)
761     {
762         pf |= FIB_PATH_CFG_FLAG_DROP;
763     }
764     if (plf & FIB_PATH_LIST_FLAG_EXCLUSIVE)
765     {
766         pf |= FIB_PATH_CFG_FLAG_EXCLUSIVE;
767     }
768     if (plf & FIB_PATH_LIST_FLAG_LOCAL)
769     {
770         pf |= FIB_PATH_CFG_FLAG_LOCAL;
771     }
772
773     return (pf);
774 }
775
776 fib_node_index_t
777 fib_path_list_create_special (dpo_proto_t nh_proto,
778                               fib_path_list_flags_t flags,
779                               const dpo_id_t *dpo)
780 {
781     fib_node_index_t path_index, path_list_index;
782     fib_path_list_t *path_list;
783
784     path_list = fib_path_list_alloc(&path_list_index);
785     path_list->fpl_flags = flags;
786
787     path_index =
788         fib_path_create_special(path_list_index,
789                                 nh_proto,
790                                 fib_path_list_flags_2_path_flags(flags),
791                                 dpo);
792     vec_add1(path_list->fpl_paths, path_index);
793
794     /*
795      * we don't share path-lists. we can do PIC on them so why bother.
796      */
797     path_list = fib_path_list_resolve(path_list);
798
799     return (path_list_index);
800 }
801
802 /*
803  * return the index info the path-lists's vector of paths, of the matching path.
804  * ~0 if not found
805  */
806 u32
807 fib_path_list_find_rpath (fib_node_index_t path_list_index,
808                           const fib_route_path_t *rpath)
809 {
810     fib_path_list_t *path_list;
811     u32 ii;
812
813     path_list = fib_path_list_get(path_list_index);
814
815     vec_foreach_index (ii, path_list->fpl_paths)
816     {
817         if (!fib_path_cmp_w_route_path(path_list->fpl_paths[ii], rpath))
818         {
819             return (ii);
820         }
821     }
822     return (~0);
823 }
824
825
826 /*
827  * fib_path_list_copy_and_path_add
828  *
829  * Create a copy of a path-list and append one more path to it.
830  * The path-list returned could either have been newly created, or
831  * can be a shared path-list from the data-base.
832  */
833 fib_node_index_t
834 fib_path_list_path_add (fib_node_index_t path_list_index,
835                         const fib_route_path_t *rpaths)
836 {
837     fib_node_index_t new_path_index, *orig_path_index;
838     fib_path_list_t *path_list;
839
840     /*
841      * alloc the new list before we retrieve the old one, lest
842      * the alloc result in a realloc
843      */
844     path_list = fib_path_list_get(path_list_index);
845
846     ASSERT(1 == vec_len(rpaths));
847     ASSERT(!(path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED));
848
849     FIB_PATH_LIST_DBG(path_list, "path-add");
850
851     new_path_index = fib_path_create(path_list_index,
852                                      rpaths);
853
854     vec_foreach (orig_path_index, path_list->fpl_paths)
855     {
856         /*
857          * don't add duplicate paths
858          */
859         if (0 == fib_path_cmp(new_path_index, *orig_path_index))
860         {
861             fib_path_destroy(new_path_index);
862             return (*orig_path_index);
863         }
864     }
865
866     /*
867      * Add the new path - no sort, no sharing, no key..
868      */
869     vec_add1(path_list->fpl_paths, new_path_index);
870
871     FIB_PATH_LIST_DBG(path_list, "path-added");
872
873     /*
874      * no shared path list requested. resolve and use the one
875      * just created.
876      */
877     fib_path_resolve(new_path_index);
878
879     return (new_path_index);
880 }
881
882 fib_node_index_t
883 fib_path_list_copy_and_path_add (fib_node_index_t orig_path_list_index,
884                                  fib_path_list_flags_t flags,
885                                  const fib_route_path_t *rpaths)
886 {
887     fib_node_index_t path_index, new_path_index, *orig_path_index;
888     fib_path_list_t *path_list, *orig_path_list;
889     fib_node_index_t exist_path_list_index;
890     fib_node_index_t path_list_index;
891     fib_node_index_t pi;
892
893     ASSERT(1 == vec_len(rpaths));
894
895     /*
896      * alloc the new list before we retrieve the old one, lest
897      * the alloc result in a realloc
898      */
899     path_list = fib_path_list_alloc(&path_list_index);
900
901     orig_path_list = fib_path_list_get(orig_path_list_index);
902
903     FIB_PATH_LIST_DBG(orig_path_list, "copy-add");
904
905     flags = fib_path_list_flags_fixup(flags);
906     path_list->fpl_flags = flags;
907
908     vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths));
909     pi = 0;
910
911     new_path_index = fib_path_create(path_list_index,
912                                      rpaths);
913
914     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
915     {
916         /*
917          * don't add duplicate paths
918          * In the unlikely event the path is a duplicate, then we'll
919          * find a matching path-list later and this one will be toast.
920          */
921         if (0 != fib_path_cmp(new_path_index, *orig_path_index))
922         {
923             path_index = fib_path_copy(*orig_path_index, path_list_index);
924             path_list->fpl_paths[pi++] = path_index;
925         }
926         else
927         {
928             _vec_len(path_list->fpl_paths) = vec_len(orig_path_list->fpl_paths);
929         }
930     }
931
932     path_list->fpl_paths[pi] = new_path_index;
933
934     /*
935      * we sort the paths since the key for the path-list is
936      * the description of the paths it contains. The paths need to
937      * be sorted else this description will differ.
938      */
939     vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
940
941     FIB_PATH_LIST_DBG(path_list, "path-added");
942
943     /*
944      * check for a matching path-list in the DB.
945      * If we find one then we can return the existing one and destroy the
946      * new one just created.
947      */
948     if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
949     {
950         exist_path_list_index = fib_path_list_db_find(path_list);
951         if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
952         {
953             fib_path_list_destroy(path_list);
954         
955             path_list_index = exist_path_list_index;
956         }
957         else
958         {
959             /*
960              * if there was not a matching path-list, then this
961              * new one will need inserting into the DB and resolving.
962              */
963             fib_path_list_db_insert(path_list_index);
964
965             path_list = fib_path_list_resolve(path_list);
966         }
967     }
968     else
969     {
970         /*
971          * no shared path list requested. resolve and use the one
972          * just created.
973          */
974         path_list = fib_path_list_resolve(path_list);
975     }
976
977     return (path_list_index);
978 }
979
980 /*
981  * fib_path_list_path_remove
982  */
983 fib_node_index_t
984 fib_path_list_path_remove (fib_node_index_t path_list_index,
985                            const fib_route_path_t *rpaths)
986 {
987     fib_node_index_t match_path_index, tmp_path_index;
988     fib_path_list_t *path_list;
989     fib_node_index_t pi;
990
991     path_list = fib_path_list_get(path_list_index);
992
993     ASSERT(1 == vec_len(rpaths));
994     ASSERT(!(path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED));
995
996     FIB_PATH_LIST_DBG(path_list, "path-remove");
997
998     /*
999      * create a representation of the path to be removed, so it
1000      * can be used as a comparison object during the copy.
1001      */
1002     tmp_path_index = fib_path_create(path_list_index,
1003                                      rpaths);
1004     match_path_index = FIB_NODE_INDEX_INVALID;
1005
1006     vec_foreach_index (pi, path_list->fpl_paths)
1007     {
1008         if (0 == fib_path_cmp(tmp_path_index,
1009                               path_list->fpl_paths[pi]))
1010         {
1011             /*
1012              * match - remove it
1013              */
1014             match_path_index = path_list->fpl_paths[pi];
1015             fib_path_destroy(match_path_index);
1016             vec_del1(path_list->fpl_paths, pi);
1017         }
1018     }
1019
1020     /*
1021      * done with the temporary now
1022      */
1023     fib_path_destroy(tmp_path_index);
1024
1025     return (match_path_index);
1026 }
1027
1028 /*
1029  * fib_path_list_copy_and_path_remove
1030  *
1031  * Copy the path-list excluding the path passed.
1032  * If the path is the last one, then the index reurned will be invalid.
1033  * i.e. the path-list is toast.
1034  */
1035 fib_node_index_t
1036 fib_path_list_copy_and_path_remove (fib_node_index_t orig_path_list_index,
1037                                     fib_path_list_flags_t flags,
1038                                     const fib_route_path_t *rpath)
1039 {
1040     fib_node_index_t path_index, *orig_path_index, path_list_index, tmp_path_index;
1041     fib_path_list_t *path_list,  *orig_path_list;
1042     fib_node_index_t pi;
1043
1044     path_list = fib_path_list_alloc(&path_list_index);
1045
1046     flags = fib_path_list_flags_fixup(flags);
1047     orig_path_list = fib_path_list_get(orig_path_list_index);
1048
1049     FIB_PATH_LIST_DBG(orig_path_list, "copy-remove");
1050
1051     path_list->fpl_flags = flags;
1052     /*
1053      * allocate as many paths as we might need in one go, rather than
1054      * using vec_add to do a few at a time.
1055      */
1056     if (vec_len(orig_path_list->fpl_paths) > 1)
1057     {
1058         vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths) - 2);
1059     }
1060     pi = 0;
1061
1062     /*
1063      * create a representation of the path to be removed, so it
1064      * can be used as a comparison object during the copy.
1065      */
1066     tmp_path_index = fib_path_create(path_list_index, rpath);
1067
1068     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
1069     {
1070         if (0 != fib_path_cmp(tmp_path_index, *orig_path_index)) {
1071             path_index = fib_path_copy(*orig_path_index, path_list_index);
1072             if (pi < vec_len(path_list->fpl_paths))
1073             {
1074                 path_list->fpl_paths[pi++] = path_index;
1075             }
1076             else
1077             {
1078                 /*
1079                  * this is the unlikely case that the path being
1080                  * removed does not match one in the path-list, so
1081                  * we end up with as many paths as we started with.
1082                  * the paths vector was sized above with the expectation
1083                  * that we would have 1 less.
1084                  */
1085                 vec_add1(path_list->fpl_paths, path_index);
1086             }
1087         }
1088     }
1089
1090     /*
1091      * done with the temporary now
1092      */
1093     fib_path_destroy(tmp_path_index);
1094
1095     /*
1096      * if there are no paths, then the new path-list is aborted
1097      */
1098     if (0 == vec_len(path_list->fpl_paths)) {
1099         FIB_PATH_LIST_DBG(path_list, "last-path-removed");
1100
1101         fib_path_list_destroy(path_list);
1102
1103         path_list_index = FIB_NODE_INDEX_INVALID;
1104     } else {
1105         /*
1106          * we sort the paths since the key for the path-list is
1107          * the description of the paths it contains. The paths need to
1108          * be sorted else this description will differ.
1109          */
1110         vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
1111     
1112         /*
1113          * If a shared path list is requested, consult the DB for a match
1114          */
1115         if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
1116         {
1117             fib_node_index_t exist_path_list_index;
1118
1119             /*
1120              * check for a matching path-list in the DB.
1121              * If we find one then we can return the existing one and destroy the
1122              * new one just created.
1123              */
1124             exist_path_list_index = fib_path_list_db_find(path_list);
1125             if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
1126             {
1127                 fib_path_list_destroy(path_list);
1128         
1129                 path_list_index = exist_path_list_index;
1130             }
1131             else
1132             {
1133                 /*
1134                  * if there was not a matching path-list, then this
1135                  * new one will need inserting into the DB and resolving.
1136                  */
1137                 fib_path_list_db_insert(path_list_index);
1138
1139                 path_list = fib_path_list_resolve(path_list);
1140             }
1141         }
1142         else
1143         {
1144             /*
1145              * no shared path list requested. resolve and use the one
1146              * just created.
1147              */
1148             path_list = fib_path_list_resolve(path_list);
1149         }
1150     }
1151
1152     return (path_list_index);
1153 }
1154
1155 /*
1156  * fib_path_list_contribute_forwarding
1157  *
1158  * Return the index of a load-balance that user of this path-list should
1159  * use for forwarding
1160  */
1161 void
1162 fib_path_list_contribute_forwarding (fib_node_index_t path_list_index,
1163                                      fib_forward_chain_type_t fct,
1164                                      fib_path_list_fwd_flags_t flags,
1165                                      dpo_id_t *dpo)
1166 {
1167     fib_path_list_t *path_list;
1168
1169     path_list = fib_path_list_get(path_list_index);
1170
1171     fib_path_list_mk_lb(path_list, fct, dpo, flags);
1172
1173     ASSERT(DPO_LOAD_BALANCE == dpo->dpoi_type);
1174
1175     /*
1176      * If there's only one bucket in the load-balance then we can
1177      * squash it out.
1178      */
1179     if ((1 == load_balance_n_buckets(dpo->dpoi_index)) &&
1180         (FIB_PATH_LIST_FWD_FLAG_COLLAPSE & flags))
1181     {
1182         dpo_copy(dpo, load_balance_get_bucket(dpo->dpoi_index, 0));
1183     }
1184 }
1185
1186 /*
1187  * fib_path_list_get_adj
1188  *
1189  * Return the index of a adjacency for the first path that user of this
1190  * path-list should use for forwarding
1191  */
1192 adj_index_t
1193 fib_path_list_get_adj (fib_node_index_t path_list_index,
1194                        fib_forward_chain_type_t type)
1195 {
1196     fib_path_list_t *path_list;
1197
1198     path_list = fib_path_list_get(path_list_index);
1199     return (fib_path_get_adj(path_list->fpl_paths[0]));
1200 }
1201
1202 int
1203 fib_path_list_recursive_loop_detect (fib_node_index_t path_list_index,
1204                                      fib_node_index_t **entry_indicies)
1205 {
1206     fib_node_index_t *path_index;
1207     int is_looped, list_looped;
1208     fib_path_list_t *path_list;
1209
1210     list_looped = 0;
1211     path_list = fib_path_list_get(path_list_index);
1212
1213     vec_foreach (path_index, path_list->fpl_paths)
1214     {
1215         fib_node_index_t *copy, **copy_ptr;
1216
1217         /*
1218          * we need a copy of the nodes visited so that when we add entries
1219          * we explore on the nth path and a looped is detected, those entries
1220          * are not again searched for n+1 path and so finding a loop that does
1221          * not exist.
1222          */
1223         copy = vec_dup(*entry_indicies);
1224         copy_ptr = &copy;
1225
1226         is_looped  = fib_path_recursive_loop_detect(*path_index, copy_ptr);
1227         list_looped += is_looped;
1228
1229         vec_free(copy);
1230     }
1231
1232     FIB_PATH_LIST_DBG(path_list, "loop-detect: eval:%d", list_looped);
1233
1234     if (list_looped)
1235     {
1236         path_list->fpl_flags |= FIB_PATH_LIST_FLAG_LOOPED;
1237     }
1238     else
1239     {
1240         path_list->fpl_flags &= ~FIB_PATH_LIST_FLAG_LOOPED;
1241     }
1242
1243     return (list_looped);
1244 }
1245
1246 u32
1247 fib_path_list_child_add (fib_node_index_t path_list_index,
1248                          fib_node_type_t child_type,
1249                          fib_node_index_t child_index)
1250 {
1251     u32 sibling;
1252
1253     sibling = fib_node_child_add(FIB_NODE_TYPE_PATH_LIST,
1254                                  path_list_index,
1255                                  child_type,
1256                                  child_index);
1257
1258     if (FIB_PATH_LIST_POPULAR == fib_node_get_n_children(FIB_NODE_TYPE_PATH_LIST,
1259                                                          path_list_index))
1260     {
1261         /*
1262          * Set the popular flag on the path-list once we pass the magic
1263          * threshold. then walk children to update.
1264          * We don't undo this action. The rational being that the number
1265          * of entries using this prefix is large enough such that it is a
1266          * non-trival amount of effort to converge them. If we get into the
1267          * situation where we are adding and removing entries such that we
1268          * flip-flop over the threshold, then this non-trivial work is added
1269          * to each of those routes adds/deletes - not a situation we want.
1270          */
1271         fib_node_back_walk_ctx_t ctx = {
1272             .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
1273         };
1274         fib_path_list_t *path_list;
1275
1276         path_list = fib_path_list_get(path_list_index);
1277         path_list->fpl_flags |= FIB_PATH_LIST_FLAG_POPULAR;
1278
1279         fib_walk_sync(FIB_NODE_TYPE_PATH_LIST, path_list_index, &ctx);
1280     }
1281
1282     return (sibling);
1283 }
1284
1285 void
1286 fib_path_list_child_remove (fib_node_index_t path_list_index,
1287                             u32 si)
1288 {
1289     fib_node_child_remove(FIB_NODE_TYPE_PATH_LIST,
1290                           path_list_index,
1291                           si);
1292 }
1293
1294 void
1295 fib_path_list_lock(fib_node_index_t path_list_index)
1296 {
1297     fib_path_list_t *path_list;
1298
1299     if (FIB_NODE_INDEX_INVALID != path_list_index)
1300     {
1301         path_list = fib_path_list_get(path_list_index);
1302
1303         fib_node_lock(&path_list->fpl_node);
1304     }
1305 }
1306
1307 void
1308 fib_path_list_unlock (fib_node_index_t path_list_index)
1309 {
1310     fib_path_list_t *path_list;
1311
1312     if (FIB_NODE_INDEX_INVALID != path_list_index)
1313     {
1314         path_list = fib_path_list_get(path_list_index);
1315     
1316         fib_node_unlock(&path_list->fpl_node);
1317     }
1318 }
1319
1320 u32
1321 fib_path_list_pool_size (void)
1322 {
1323     return (pool_elts(fib_path_list_pool));    
1324 }
1325
1326 u32
1327 fib_path_list_db_size (void)
1328 {
1329     return (hash_elts(fib_path_list_db));
1330 }
1331
1332 void
1333 fib_path_list_walk (fib_node_index_t path_list_index,
1334                     fib_path_list_walk_fn_t func,
1335                     void *ctx)
1336 {
1337     fib_node_index_t *path_index;
1338     fib_path_list_t *path_list;
1339
1340     path_list = fib_path_list_get(path_list_index);
1341
1342     vec_foreach(path_index, path_list->fpl_paths)
1343     {
1344         if (FIB_PATH_LIST_WALK_STOP == func(path_list_index,
1345                                             *path_index,
1346                                             ctx))
1347             break;
1348     }
1349 }
1350
1351 void
1352 fib_path_list_walk_w_ext (fib_node_index_t path_list_index,
1353                           const fib_path_ext_list_t *ext_list,
1354                           fib_path_list_walk_w_ext_fn_t func,
1355                           void *ctx)
1356 {
1357     fib_node_index_t *path_index;
1358     fib_path_list_t *path_list;
1359     fib_path_ext_t *path_ext;
1360
1361     path_list = fib_path_list_get(path_list_index);
1362
1363     vec_foreach(path_index, path_list->fpl_paths)
1364     {
1365         path_ext = fib_path_ext_list_find_by_path_index(ext_list, *path_index);
1366
1367         if (FIB_PATH_LIST_WALK_STOP == func(path_list_index,
1368                                             *path_index,
1369                                             path_ext,
1370                                             ctx))
1371             break;
1372     }
1373 }
1374
1375 void
1376 fib_path_list_module_init (void)
1377 {
1378     fib_node_register_type (FIB_NODE_TYPE_PATH_LIST, &fib_path_list_vft);
1379
1380     fib_path_list_db = hash_create2 (/* elts */ 0,
1381                                      /* user */ 0,
1382                                      /* value_bytes */ sizeof (fib_node_index_t),
1383                                      fib_path_list_db_hash_key_sum,
1384                                      fib_path_list_db_hash_key_equal,
1385                                      /* format pair/arg */
1386                                      0, 0);
1387     fib_path_list_logger = vlib_log_register_class("fib", "path-list");
1388 }
1389
1390 static clib_error_t *
1391 show_fib_path_list_command (vlib_main_t * vm,
1392                             unformat_input_t * input,
1393                             vlib_cli_command_t * cmd)
1394 {
1395     fib_path_list_t *path_list;
1396     fib_node_index_t pli;
1397
1398     if (unformat (input, "%d", &pli))
1399     {
1400         /*
1401          * show one in detail
1402          */
1403         if (!pool_is_free_index(fib_path_list_pool, pli))
1404         {
1405             path_list = fib_path_list_get(pli);
1406             u8 *s = fib_path_list_format(pli, NULL);
1407             s = format(s, "children:");
1408             s = fib_node_children_format(path_list->fpl_node.fn_children, s);
1409             vlib_cli_output (vm, "%s", s);
1410             vec_free(s);
1411         }
1412         else
1413         {
1414             vlib_cli_output (vm, "path list %d invalid", pli);
1415         }
1416     }
1417     else
1418     {
1419         /*
1420          * show all
1421          */
1422         vlib_cli_output (vm, "FIB Path Lists");
1423         pool_foreach_index (pli, fib_path_list_pool,
1424         ({
1425             vlib_cli_output (vm, "%U", format_fib_path_list, pli, 0);
1426         }));
1427     }
1428     return (NULL);
1429 }
1430
1431 VLIB_CLI_COMMAND (show_fib_path_list, static) = {
1432   .path = "show fib path-lists",
1433   .function = show_fib_path_list_command,
1434   .short_help = "show fib path-lists",
1435 };