MFIB: changes to improve route add/delete performance
[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
28 /**
29  * FIB path-list
30  * A representation of the list/set of path trough which a prefix is reachable
31  */
32 typedef struct fib_path_list_t_ {
33     /**
34      * A path-list is a node in the FIB graph.
35      */
36     fib_node_t fpl_node;
37
38     /**
39      * Flags on the path-list
40      */
41     fib_path_list_flags_t fpl_flags;
42
43     /**
44      * The next-hop protocol for the paths in this path list.
45      * Note that fixing the proto here means we don't support a mix of
46      * v4 and v6 paths. ho hum.
47      */
48     fib_protocol_t fpl_nh_proto;
49
50     /**
51      * Vector of paths indicies for all configured paths.
52      * For shareable path-lists this list MUST not change.
53      */
54     fib_node_index_t *fpl_paths;
55
56     /**
57      * the RPF list calculated for this path list
58      */
59     fib_node_index_t fpl_urpf;
60 } fib_path_list_t;
61
62 /*
63  * Array of strings/names for the FIB sources
64  */
65 static const char *fib_path_list_attr_names[] = FIB_PATH_LIST_ATTRIBUTES;
66
67 /*
68  * The memory pool from which we allocate all the path-lists
69  */
70 static fib_path_list_t * fib_path_list_pool;
71
72 /*
73  * The data-base of shared path-lists
74  */
75 static uword *fib_path_list_db;
76
77 /*
78  * Debug macro
79  */
80 #ifdef FIB_DEBUG
81 #define FIB_PATH_LIST_DBG(_pl, _fmt, _args...)            \
82 {                                                         \
83     u8 *_tmp = 0;                                         \
84     _tmp = fib_path_list_format(                          \
85         fib_path_list_get_index(_pl), _tmp);              \
86     clib_warning("pl:[%d:%p:%p:%s]:" _fmt,                \
87                  fib_path_list_get_index(_pl),            \
88                  _pl, _pl->fpl_paths, _tmp,               \
89                  ##_args);                                \
90     vec_free(_tmp);                                       \
91 }
92 #else
93 #define FIB_PATH_LIST_DBG(_pl, _fmt, _args...)
94 #endif
95
96 static fib_path_list_t *
97 fib_path_list_get (fib_node_index_t index)
98 {
99     return (pool_elt_at_index(fib_path_list_pool, index));
100 }
101
102 static fib_node_t *
103 fib_path_list_get_node (fib_node_index_t index)
104 {
105     return ((fib_node_t*)fib_path_list_get(index));
106 }
107
108 static fib_path_list_t*
109 fib_path_list_from_fib_node (fib_node_t *node)
110 {
111 #if CLIB_DEBUG > 0
112     ASSERT(FIB_NODE_TYPE_PATH_LIST == node->fn_type);
113 #endif
114     return ((fib_path_list_t*)node);
115 }
116
117 static fib_node_index_t
118 fib_path_list_get_index (fib_path_list_t *path_list)
119 {
120     return (path_list - fib_path_list_pool);
121 }
122
123 static u8 *
124 format_fib_path_list (u8 * s, va_list * args)
125 {
126     fib_path_list_attribute_t attr;
127     fib_node_index_t *path_index;
128     fib_path_list_t *path_list;
129
130     path_list = va_arg (*args, fib_path_list_t *);
131     
132     s = format (s, "    index:%u", fib_path_list_get_index(path_list));
133     s = format (s, " locks:%u", path_list->fpl_node.fn_locks);
134     s = format (s, " proto:%U", format_fib_protocol, path_list->fpl_nh_proto);
135
136     if (FIB_PATH_LIST_FLAG_NONE != path_list->fpl_flags)
137     {
138         s = format (s, " flags:");
139         FOR_EACH_PATH_LIST_ATTRIBUTE(attr)
140         {
141             if ((1<<attr) & path_list->fpl_flags)
142             {
143                 s = format (s, "%s,", fib_path_list_attr_names[attr]);
144             }
145         }
146     }
147     s = format (s, " %U\n", format_fib_urpf_list, path_list->fpl_urpf);
148
149     vec_foreach (path_index, path_list->fpl_paths)
150     {
151         s = fib_path_format(*path_index, s);
152         s = format(s, "\n");
153     }
154
155     return (s);
156 }
157
158 u8 *
159 fib_path_list_adjs_format (fib_node_index_t path_list_index,
160                            u32 indent,
161                            u8 * s)
162 {
163     fib_path_list_t *path_list;
164     u32 i;
165
166     path_list = fib_path_list_get(path_list_index);
167
168     vec_foreach_index (i, path_list->fpl_paths)
169     {
170         s = fib_path_adj_format(path_list->fpl_paths[i],
171                                 indent, s);
172     }
173
174     return (s);
175 }
176
177
178 u8 *
179 fib_path_list_format (fib_node_index_t path_list_index,
180                       u8 * s)
181 {
182     fib_path_list_t *path_list;
183
184     path_list = fib_path_list_get(path_list_index);
185
186     return (format(s, "%U", format_fib_path_list, path_list));
187 }
188
189 static uword
190 fib_path_list_hash (fib_path_list_t *path_list)
191 {
192     uword old_path_list_hash, new_path_list_hash, path_hash;
193     fib_node_index_t *path_index;
194
195     ASSERT(path_list);
196
197     new_path_list_hash = old_path_list_hash = vec_len(path_list->fpl_paths);
198
199     vec_foreach (path_index, path_list->fpl_paths)
200     {
201         path_hash = fib_path_hash(*path_index);
202 #if uword_bits == 64
203         hash_mix64(path_hash, old_path_list_hash, new_path_list_hash);
204 #else
205         hash_mix32(path_hash, old_path_list_hash, new_path_list_hash);
206 #endif
207     }
208
209     return (new_path_list_hash);
210 }
211
212 always_inline uword
213 fib_path_list_db_hash_key_from_index (uword index)
214 {
215     return 1 + 2*index;
216 }
217
218 always_inline uword
219 fib_path_list_db_hash_key_is_index (uword key)
220 {
221     return key & 1;
222 }
223
224 always_inline uword
225 fib_path_list_db_hash_key_2_index (uword key)
226 {
227     ASSERT (fib_path_list_db_hash_key_is_index (key));
228     return key / 2;
229 }
230
231 static fib_path_list_t*
232 fib_path_list_db_get_from_hash_key (uword key)
233 {
234     fib_path_list_t *path_list;
235
236     if (fib_path_list_db_hash_key_is_index (key))
237     {
238         fib_node_index_t path_list_index;
239
240         path_list_index = fib_path_list_db_hash_key_2_index(key);
241         path_list = fib_path_list_get(path_list_index);
242     }
243     else
244     {       
245         path_list = uword_to_pointer (key, fib_path_list_t *);
246     }
247
248     return (path_list);
249 }
250
251 static uword
252 fib_path_list_db_hash_key_sum (hash_t * h,
253                                uword key)
254 {
255     fib_path_list_t *path_list;
256
257     path_list = fib_path_list_db_get_from_hash_key(key);
258
259     return (fib_path_list_hash(path_list));
260 }
261
262 static uword
263 fib_path_list_db_hash_key_equal (hash_t * h,
264                                  uword key1,
265                                  uword key2)
266 {
267     fib_path_list_t *path_list1, *path_list2;
268
269     path_list1 = fib_path_list_db_get_from_hash_key(key1);
270     path_list2 = fib_path_list_db_get_from_hash_key(key2);
271
272     return (fib_path_list_hash(path_list1) ==
273             fib_path_list_hash(path_list2));
274 }
275
276 static fib_node_index_t
277 fib_path_list_db_find (fib_path_list_t *path_list)
278 {
279     uword *p;
280
281     p = hash_get(fib_path_list_db, path_list);
282
283     if (NULL != p)
284     {
285         return p[0];
286     }
287
288     return (FIB_NODE_INDEX_INVALID);
289 }
290
291 static void
292 fib_path_list_db_insert (fib_node_index_t path_list_index)
293 {
294     fib_path_list_t *path_list;
295
296     path_list = fib_path_list_get(path_list_index);
297
298     ASSERT(FIB_NODE_INDEX_INVALID == fib_path_list_db_find(path_list));
299
300     hash_set (fib_path_list_db,
301               fib_path_list_db_hash_key_from_index(path_list_index),
302               path_list_index);
303
304     FIB_PATH_LIST_DBG(path_list, "DB-inserted");
305 }
306
307 static void
308 fib_path_list_db_remove (fib_node_index_t path_list_index)
309 {
310     fib_path_list_t *path_list;
311
312     path_list = fib_path_list_get(path_list_index);
313
314     ASSERT(FIB_NODE_INDEX_INVALID != fib_path_list_db_find(path_list));
315
316     hash_unset(fib_path_list_db,
317                fib_path_list_db_hash_key_from_index(path_list_index));
318
319     FIB_PATH_LIST_DBG(path_list, "DB-removed");
320 }
321
322 static void
323 fib_path_list_destroy (fib_path_list_t *path_list)
324 {
325     fib_node_index_t *path_index;
326
327     FIB_PATH_LIST_DBG(path_list, "destroy");
328
329     vec_foreach (path_index, path_list->fpl_paths)
330     {
331         fib_path_destroy(*path_index);
332     }
333
334     vec_free(path_list->fpl_paths);
335     fib_urpf_list_unlock(path_list->fpl_urpf);
336
337     fib_node_deinit(&path_list->fpl_node);
338     pool_put(fib_path_list_pool, path_list);
339 }
340
341 static void
342 fib_path_list_last_lock_gone (fib_node_t *node)
343 {
344     fib_path_list_t *path_list;
345
346     path_list = fib_path_list_from_fib_node(node);
347
348     FIB_PATH_LIST_DBG(path_list, "last-lock");
349
350     if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
351     {
352         fib_path_list_db_remove(fib_path_list_get_index(path_list));
353     }
354     fib_path_list_destroy(path_list);
355 }
356
357 /*
358  * fib_path_mk_lb
359  *
360  * update the multipath adj this path-list will contribute to its
361  * children's forwarding.
362  */
363 static void
364 fib_path_list_mk_lb (fib_path_list_t *path_list,
365                      fib_forward_chain_type_t fct,
366                      dpo_id_t *dpo)
367 {
368     load_balance_path_t *nhs;
369     fib_node_index_t *path_index;
370
371     nhs  = NULL;
372
373     if (!dpo_id_is_valid(dpo))
374     {
375         /*
376          * first time create
377          */
378         dpo_set(dpo,
379                 DPO_LOAD_BALANCE,
380                 fib_forw_chain_type_to_dpo_proto(fct),
381                 load_balance_create(0,
382                                     fib_forw_chain_type_to_dpo_proto(fct),
383                                     0 /* FIXME FLOW HASH */));
384     }
385
386     /*
387      * We gather the DPOs from resolved paths.
388      */
389     vec_foreach (path_index, path_list->fpl_paths)
390     {
391         nhs = fib_path_append_nh_for_multipath_hash(*path_index,
392                                                     fct,
393                                                     nhs);
394     }
395
396     /*
397      * Path-list load-balances, which if used, would be shared and hence
398      * never need a load-balance map.
399      */
400     load_balance_multipath_update(dpo, nhs, LOAD_BALANCE_FLAG_NONE);
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     /*
478      * propagate the backwalk further
479      */
480     if (32 >= fib_node_list_get_size(path_list->fpl_node.fn_children))
481     {
482         /*
483          * only a few children. continue the walk synchronously
484          */
485         fib_walk_sync(FIB_NODE_TYPE_PATH_LIST, path_list_index, ctx);
486     }
487     else
488     {
489         /*
490          * many children. schedule a async walk
491          */
492         fib_walk_async(FIB_NODE_TYPE_PATH_LIST,
493                        path_list_index,
494                        FIB_WALK_PRIORITY_LOW,
495                        ctx);
496     }
497 }
498
499 /*
500  * fib_path_list_back_walk_notify
501  *
502  * A back walk has reach this path-list.
503  */
504 static fib_node_back_walk_rc_t
505 fib_path_list_back_walk_notify (fib_node_t *node,
506                                 fib_node_back_walk_ctx_t *ctx)
507 {
508     /*
509      * the path-list is not a direct child of any other node type
510      * paths, which do not change thier to-list-mapping, save the
511      * list they are a member of, and invoke the BW function directly.
512      */
513     ASSERT(0);
514
515     return (FIB_NODE_BACK_WALK_CONTINUE);
516 }
517
518 /*
519  * Display the path-list memory usage
520  */
521 static void
522 fib_path_list_memory_show (void)
523 {
524     fib_show_memory_usage("Path-list",
525                           pool_elts(fib_path_list_pool),
526                           pool_len(fib_path_list_pool),
527                           sizeof(fib_path_list_t));
528     fib_urpf_list_show_mem();
529 }
530
531 /*
532  * The FIB path-list's graph node virtual function table
533  */
534 static const fib_node_vft_t fib_path_list_vft = {
535     .fnv_get = fib_path_list_get_node,
536     .fnv_last_lock = fib_path_list_last_lock_gone,
537     .fnv_back_walk = fib_path_list_back_walk_notify,
538     .fnv_mem_show = fib_path_list_memory_show,
539 };
540
541 static inline fib_path_list_t *
542 fib_path_list_alloc (fib_node_index_t *path_list_index)
543 {
544     fib_path_list_t *path_list;
545
546     pool_get(fib_path_list_pool, path_list);
547     memset(path_list, 0, sizeof(*path_list));
548
549     fib_node_init(&path_list->fpl_node,
550                   FIB_NODE_TYPE_PATH_LIST);
551     path_list->fpl_urpf = INDEX_INVALID;
552     path_list->fpl_paths = NULL;
553
554     *path_list_index = fib_path_list_get_index(path_list);
555
556     FIB_PATH_LIST_DBG(path_list, "alloc");
557
558     return (path_list);
559 }
560
561 static fib_path_list_t *
562 fib_path_list_resolve (fib_path_list_t *path_list)
563 {
564     fib_node_index_t *path_index, *paths, path_list_index;
565
566     ASSERT(!(path_list->fpl_flags & FIB_PATH_LIST_FLAG_RESOLVED));
567
568     /*
569      * resolving a path-list is a recursive action. this means more path
570      * lists can be created during this call, and hence this path-list
571      * can be realloc'd. so we work with copies.
572      * this function is called only once per-path list, so its no great overhead.
573      */
574     path_list_index = fib_path_list_get_index(path_list);
575     paths = vec_dup(path_list->fpl_paths);
576
577     vec_foreach (path_index, paths)
578     {
579         fib_path_resolve(*path_index);
580     }
581
582     vec_free(paths);
583     path_list = fib_path_list_get(path_list_index);
584
585     FIB_PATH_LIST_DBG(path_list, "resovled");
586
587     if (!(path_list->fpl_flags & FIB_PATH_LIST_FLAG_NO_URPF))
588     {
589         fib_path_list_mk_urpf(path_list);
590     }
591     return (path_list);
592 }
593
594 u32
595 fib_path_list_get_n_paths (fib_node_index_t path_list_index)
596 {
597     fib_path_list_t *path_list;
598
599     path_list = fib_path_list_get(path_list_index);
600
601     return (vec_len(path_list->fpl_paths));
602 }
603
604
605 u32
606 fib_path_list_get_resolving_interface (fib_node_index_t path_list_index)
607 {
608     fib_node_index_t *path_index;
609     fib_path_list_t *path_list;
610     u32 sw_if_index;
611
612     path_list = fib_path_list_get(path_list_index);
613
614     sw_if_index = ~0;
615     vec_foreach (path_index, path_list->fpl_paths)
616     {
617         sw_if_index = fib_path_get_resolving_interface(*path_index);
618         if (~0 != sw_if_index)
619         {
620             return (sw_if_index);
621         }
622     }
623
624     return (sw_if_index);
625 }
626
627 fib_protocol_t
628 fib_path_list_get_proto (fib_node_index_t path_list_index)
629 {
630     fib_path_list_t *path_list;
631
632     path_list = fib_path_list_get(path_list_index);
633
634     /*
635      * we don't support a mix of path protocols, so we can return the proto
636      * of the first
637      */
638     return (fib_path_get_proto(path_list->fpl_paths[0]));
639 }
640
641 int
642 fib_path_list_is_looped (fib_node_index_t path_list_index)
643 {
644     fib_path_list_t *path_list;
645
646     path_list = fib_path_list_get(path_list_index);
647
648     return (path_list->fpl_flags & FIB_PATH_LIST_FLAG_LOOPED);
649 }
650
651 static fib_path_cfg_flags_t 
652 fib_path_list_flags_2_path_flags (fib_path_list_flags_t plf)
653 {
654     fib_path_cfg_flags_t pf = FIB_PATH_CFG_FLAG_NONE;
655
656     if (plf & FIB_PATH_LIST_FLAG_LOCAL)
657     {
658         pf |= FIB_PATH_CFG_FLAG_LOCAL;
659     }
660     if (plf & FIB_PATH_LIST_FLAG_DROP)
661     {
662         pf |= FIB_PATH_CFG_FLAG_DROP;
663     }
664     if (plf & FIB_PATH_LIST_FLAG_EXCLUSIVE)
665     {
666         pf |= FIB_PATH_CFG_FLAG_EXCLUSIVE;
667     }
668
669     return (pf);
670 }
671
672 static fib_path_list_flags_t
673 fib_path_list_flags_fixup (fib_path_list_flags_t flags)
674 {
675     /*
676      * we do no share drop nor exclusive path-lists
677      */
678     if (flags & FIB_PATH_LIST_FLAG_DROP ||
679         flags & FIB_PATH_LIST_FLAG_EXCLUSIVE)
680     {
681         flags &= ~FIB_PATH_LIST_FLAG_SHARED;
682     }
683
684     return (flags);
685 }
686
687 fib_node_index_t
688 fib_path_list_create (fib_path_list_flags_t flags,
689                       const fib_route_path_t *rpaths)
690 {
691     fib_node_index_t path_list_index, old_path_list_index;
692     fib_path_list_t *path_list;
693     int i;
694
695     flags = fib_path_list_flags_fixup(flags);
696     path_list = fib_path_list_alloc(&path_list_index);
697     path_list->fpl_flags = flags;
698     /*
699      * we'll assume for now all paths are the same next-hop protocol
700      */
701     path_list->fpl_nh_proto = rpaths[0].frp_proto;
702
703     vec_foreach_index(i, rpaths)
704     {
705         vec_add1(path_list->fpl_paths,
706                  fib_path_create(path_list_index,
707                                  path_list->fpl_nh_proto,
708                                  fib_path_list_flags_2_path_flags(flags),
709                                  &rpaths[i]));
710     }
711
712     /*
713      * If a shared path list is requested, consult the DB for a match
714      */
715     if (flags & FIB_PATH_LIST_FLAG_SHARED)
716     {
717         /*
718          * check for a matching path-list in the DB.
719          * If we find one then we can return the existing one and destroy the
720          * new one just created.
721          */
722         old_path_list_index = fib_path_list_db_find(path_list);
723         if (FIB_NODE_INDEX_INVALID != old_path_list_index)
724         {
725             fib_path_list_destroy(path_list);
726         
727             path_list_index = old_path_list_index;
728         }
729         else
730         {
731             /*
732              * if there was not a matching path-list, then this
733              * new one will need inserting into the DB and resolving.
734              */
735             fib_path_list_db_insert(path_list_index);
736             path_list = fib_path_list_resolve(path_list);
737         }
738     }
739     else
740     {
741         /*
742          * no shared path list requested. resolve and use the one
743          * just created.
744          */
745         path_list = fib_path_list_resolve(path_list);
746     }
747
748     return (path_list_index);
749 }
750
751 fib_node_index_t
752 fib_path_list_create_special (fib_protocol_t nh_proto,
753                               fib_path_list_flags_t flags,
754                               const dpo_id_t *dpo)
755 {
756     fib_node_index_t path_index, path_list_index;
757     fib_path_list_t *path_list;
758
759     path_list = fib_path_list_alloc(&path_list_index);
760     path_list->fpl_flags = flags;
761     path_list->fpl_nh_proto = nh_proto;
762
763     path_index =
764         fib_path_create_special(path_list_index,
765                                 path_list->fpl_nh_proto,
766                                 fib_path_list_flags_2_path_flags(flags),
767                                 dpo);
768     vec_add1(path_list->fpl_paths, path_index);
769
770     /*
771      * we don't share path-lists. we can do PIC on them so why bother.
772      */
773     path_list = fib_path_list_resolve(path_list);
774
775     return (path_list_index);
776 }
777
778 /*
779  * fib_path_list_copy_and_path_add
780  *
781  * Create a copy of a path-list and append one more path to it.
782  * The path-list returned could either have been newly created, or
783  * can be a shared path-list from the data-base.
784  */
785 fib_node_index_t
786 fib_path_list_copy_and_path_add (fib_node_index_t orig_path_list_index,
787                                  fib_path_list_flags_t flags,
788                                  const fib_route_path_t *rpaths)
789 {
790     fib_node_index_t path_index, new_path_index, *orig_path_index;
791     fib_path_list_t *path_list, *orig_path_list;
792     fib_node_index_t path_list_index;
793     fib_node_index_t pi;
794
795     ASSERT(1 == vec_len(rpaths));
796
797     /*
798      * alloc the new list before we retrieve the old one, lest
799      * the alloc result in a realloc
800      */
801     path_list = fib_path_list_alloc(&path_list_index);
802
803     orig_path_list = fib_path_list_get(orig_path_list_index);
804
805     FIB_PATH_LIST_DBG(orig_path_list, "copy-add");
806
807     flags = fib_path_list_flags_fixup(flags);
808     path_list->fpl_flags = flags;
809     path_list->fpl_nh_proto = orig_path_list->fpl_nh_proto;
810     vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths));
811     pi = 0;
812
813     new_path_index = fib_path_create(path_list_index,
814                                      path_list->fpl_nh_proto,
815                                      fib_path_list_flags_2_path_flags(flags),
816                                      rpaths);
817
818     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
819     {
820         /*
821          * don't add duplicate paths
822          * In the unlikely event the path is a duplicate, then we'll
823          * find a matching path-list later and this one will be toast.
824          */
825         if (0 != fib_path_cmp(new_path_index, *orig_path_index))
826         {
827             path_index = fib_path_copy(*orig_path_index, path_list_index);
828             path_list->fpl_paths[pi++] = path_index;
829         }
830         else
831         {
832             _vec_len(path_list->fpl_paths) = vec_len(orig_path_list->fpl_paths);
833         }
834     }
835
836     path_list->fpl_paths[pi] = new_path_index;
837
838     /*
839      * we sort the paths since the key for the path-list is
840      * the description of the paths it contains. The paths need to
841      * be sorted else this description will differ.
842      */
843     vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
844
845     FIB_PATH_LIST_DBG(path_list, "path-added");
846
847     /*
848      * If a shared path list is requested, consult the DB for a match
849      */
850     if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
851     {
852         fib_node_index_t exist_path_list_index;
853         /*
854          * check for a matching path-list in the DB.
855          * If we find one then we can return the existing one and destroy the
856          * new one just created.
857          */
858         exist_path_list_index = fib_path_list_db_find(path_list);
859         if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
860         {
861             fib_path_list_destroy(path_list);
862         
863             path_list_index = exist_path_list_index;
864         }
865         else
866         {
867             /*
868              * if there was not a matching path-list, then this
869              * new one will need inserting into the DB and resolving.
870              */
871             fib_path_list_db_insert(path_list_index);
872
873             path_list = fib_path_list_resolve(path_list);
874         }
875     }
876     else
877     {
878         /*
879          * no shared path list requested. resolve and use the one
880          * just created.
881          */
882         path_list = fib_path_list_resolve(path_list);
883     }
884
885     return (path_list_index);
886 }
887
888 /*
889  * fib_path_list_copy_and_path_remove
890  *
891  * Copy the path-list excluding the path passed.
892  * If the path is the last one, then the index reurned will be invalid.
893  * i.e. the path-list is toast.
894  */
895 fib_node_index_t
896 fib_path_list_copy_and_path_remove (fib_node_index_t orig_path_list_index,
897                                     fib_path_list_flags_t flags,
898                                     const fib_route_path_t *rpaths)
899 {
900     fib_node_index_t path_index, *orig_path_index, path_list_index, tmp_path_index;
901     fib_path_list_t *path_list,  *orig_path_list;
902     fib_node_index_t pi;
903
904     ASSERT(1 == vec_len(rpaths));
905
906     path_list = fib_path_list_alloc(&path_list_index);
907
908     flags = fib_path_list_flags_fixup(flags);
909     orig_path_list = fib_path_list_get(orig_path_list_index);
910
911     FIB_PATH_LIST_DBG(orig_path_list, "copy-remove");
912
913     path_list->fpl_flags = flags;
914     path_list->fpl_nh_proto = orig_path_list->fpl_nh_proto;
915     /*
916      * allocate as many paths as we might need in one go, rather than
917      * using vec_add to do a few at a time.
918      */
919     if (vec_len(orig_path_list->fpl_paths) > 1)
920     {
921         vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths) - 2);
922     }
923     pi = 0;
924
925     /*
926      * create a representation of the path to be removed, so it
927      * can be used as a comparison object during the copy.
928      */
929     tmp_path_index = fib_path_create(path_list_index,
930                                      path_list->fpl_nh_proto,
931                                      fib_path_list_flags_2_path_flags(flags),
932                                      rpaths);
933
934     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
935     {
936         if (0 != fib_path_cmp(tmp_path_index, *orig_path_index)) {
937             path_index = fib_path_copy(*orig_path_index, path_list_index);
938             if (pi < vec_len(path_list->fpl_paths))
939             {
940                 path_list->fpl_paths[pi++] = path_index;
941             }
942             else
943             {
944                 /*
945                  * this is the unlikely case that the path being
946                  * removed does not match one in the path-list, so
947                  * we end up with as many paths as we started with.
948                  * the paths vector was sized above with the expectation
949                  * that we would have 1 less.
950                  */
951                 vec_add1(path_list->fpl_paths, path_index);
952             }
953         }
954     }
955
956     /*
957      * done with the temporary now
958      */
959     fib_path_destroy(tmp_path_index);
960
961     /*
962      * if there are no paths, then the new path-list is aborted
963      */
964     if (0 == vec_len(path_list->fpl_paths)) {
965         FIB_PATH_LIST_DBG(path_list, "last-path-removed");
966
967         fib_path_list_destroy(path_list);
968
969         path_list_index = FIB_NODE_INDEX_INVALID;
970     } else {
971         /*
972          * we sort the paths since the key for the path-list is
973          * the description of the paths it contains. The paths need to
974          * be sorted else this description will differ.
975          */
976         vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
977     
978         /*
979          * If a shared path list is requested, consult the DB for a match
980          */
981         if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
982         {
983             fib_node_index_t exist_path_list_index;
984
985             /*
986              * check for a matching path-list in the DB.
987              * If we find one then we can return the existing one and destroy the
988              * new one just created.
989              */
990             exist_path_list_index = fib_path_list_db_find(path_list);
991             if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
992             {
993                 fib_path_list_destroy(path_list);
994         
995                 path_list_index = exist_path_list_index;
996             }
997             else
998             {
999                 /*
1000                  * if there was not a matching path-list, then this
1001                  * new one will need inserting into the DB and resolving.
1002                  */
1003                 fib_path_list_db_insert(path_list_index);
1004
1005                 path_list = fib_path_list_resolve(path_list);
1006             }
1007         }
1008         else
1009         {
1010             /*
1011              * no shared path list requested. resolve and use the one
1012              * just created.
1013              */
1014             path_list = fib_path_list_resolve(path_list);
1015         }
1016     }
1017
1018     return (path_list_index);
1019 }
1020
1021 /*
1022  * fib_path_list_contribute_forwarding
1023  *
1024  * Return the index of a load-balance that user of this path-list should
1025  * use for forwarding
1026  */
1027 void
1028 fib_path_list_contribute_forwarding (fib_node_index_t path_list_index,
1029                                      fib_forward_chain_type_t fct,
1030                                      dpo_id_t *dpo)
1031 {
1032     fib_path_list_t *path_list;
1033
1034     path_list = fib_path_list_get(path_list_index);
1035
1036     fib_path_list_mk_lb(path_list, fct, dpo);
1037 }
1038
1039 /*
1040  * fib_path_list_get_adj
1041  *
1042  * Return the index of a adjacency for the first path that user of this
1043  * path-list should use for forwarding
1044  */
1045 adj_index_t
1046 fib_path_list_get_adj (fib_node_index_t path_list_index,
1047                        fib_forward_chain_type_t type)
1048 {
1049     fib_path_list_t *path_list;
1050
1051     path_list = fib_path_list_get(path_list_index);
1052     return (fib_path_get_adj(path_list->fpl_paths[0]));
1053 }
1054
1055 int
1056 fib_path_list_recursive_loop_detect (fib_node_index_t path_list_index,
1057                                      fib_node_index_t **entry_indicies)
1058 {
1059     fib_node_index_t *path_index;
1060     int is_looped, list_looped;
1061     fib_path_list_t *path_list;
1062
1063     list_looped = 0;
1064     path_list = fib_path_list_get(path_list_index);
1065
1066     vec_foreach (path_index, path_list->fpl_paths)
1067     {
1068         fib_node_index_t *copy, **copy_ptr;
1069
1070         /*
1071          * we need a copy of the nodes visited so that when we add entries
1072          * we explore on the nth path and a looped is detected, those entries
1073          * are not again searched for n+1 path and so finding a loop that does
1074          * not exist.
1075          */
1076         copy = vec_dup(*entry_indicies);
1077         copy_ptr = &copy;
1078
1079         is_looped  = fib_path_recursive_loop_detect(*path_index, copy_ptr);
1080         list_looped += is_looped;
1081     }
1082
1083     FIB_PATH_LIST_DBG(path_list, "loop-detect: eval:%d", eval);
1084
1085     if (list_looped)
1086     {
1087         path_list->fpl_flags |= FIB_PATH_LIST_FLAG_LOOPED;
1088     }
1089     else
1090     {
1091         path_list->fpl_flags &= ~FIB_PATH_LIST_FLAG_LOOPED;
1092     }
1093
1094     return (list_looped);
1095 }
1096
1097 u32
1098 fib_path_list_child_add (fib_node_index_t path_list_index,
1099                          fib_node_type_t child_type,
1100                          fib_node_index_t child_index)
1101 {
1102     return (fib_node_child_add(FIB_NODE_TYPE_PATH_LIST,
1103                                path_list_index,
1104                                child_type,
1105                                child_index));
1106 }
1107
1108 void
1109 fib_path_list_child_remove (fib_node_index_t path_list_index,
1110                             u32 si)
1111 {
1112     fib_node_child_remove(FIB_NODE_TYPE_PATH_LIST,
1113                           path_list_index,
1114                           si);
1115 }
1116
1117 void
1118 fib_path_list_lock(fib_node_index_t path_list_index)
1119 {
1120     fib_path_list_t *path_list;
1121
1122     if (FIB_NODE_INDEX_INVALID != path_list_index)
1123     {
1124         path_list = fib_path_list_get(path_list_index);
1125
1126         fib_node_lock(&path_list->fpl_node);
1127         FIB_PATH_LIST_DBG(path_list, "lock");
1128     }
1129 }
1130
1131 void
1132 fib_path_list_unlock (fib_node_index_t path_list_index)
1133 {
1134     fib_path_list_t *path_list;
1135
1136     if (FIB_NODE_INDEX_INVALID != path_list_index)
1137     {
1138         path_list = fib_path_list_get(path_list_index);
1139         FIB_PATH_LIST_DBG(path_list, "unlock");
1140     
1141         fib_node_unlock(&path_list->fpl_node);
1142     }
1143 }
1144
1145 u32
1146 fib_path_list_pool_size (void)
1147 {
1148     return (pool_elts(fib_path_list_pool));    
1149 }
1150
1151 u32
1152 fib_path_list_db_size (void)
1153 {
1154     return (hash_elts(fib_path_list_db));
1155 }
1156
1157 void
1158 fib_path_list_walk (fib_node_index_t path_list_index,
1159                     fib_path_list_walk_fn_t func,
1160                     void *ctx)
1161 {
1162     fib_node_index_t *path_index;
1163     fib_path_list_t *path_list;
1164
1165     path_list = fib_path_list_get(path_list_index);
1166
1167     vec_foreach(path_index, path_list->fpl_paths)
1168     {
1169         if (!func(path_list_index, *path_index, ctx))
1170             break;
1171     }
1172 }
1173
1174
1175 void
1176 fib_path_list_module_init (void)
1177 {
1178     fib_node_register_type (FIB_NODE_TYPE_PATH_LIST, &fib_path_list_vft);
1179
1180     fib_path_list_db = hash_create2 (/* elts */ 0,
1181                                      /* user */ 0,
1182                                      /* value_bytes */ sizeof (fib_node_index_t),
1183                                      fib_path_list_db_hash_key_sum,
1184                                      fib_path_list_db_hash_key_equal,
1185                                      /* format pair/arg */
1186                                      0, 0);
1187 }
1188
1189 static clib_error_t *
1190 show_fib_path_list_command (vlib_main_t * vm,
1191                             unformat_input_t * input,
1192                             vlib_cli_command_t * cmd)
1193 {
1194     fib_path_list_t *path_list;
1195     fib_node_index_t pli;
1196
1197     if (unformat (input, "%d", &pli))
1198     {
1199         /*
1200          * show one in detail
1201          */
1202         if (!pool_is_free_index(fib_path_list_pool, pli))
1203         {
1204             path_list = fib_path_list_get(pli);
1205             u8 *s = fib_path_list_format(pli, NULL);
1206             s = format(s, "children:");
1207             s = fib_node_children_format(path_list->fpl_node.fn_children, s);
1208             vlib_cli_output (vm, "%s", s);
1209             vec_free(s);
1210         }
1211         else
1212         {
1213             vlib_cli_output (vm, "path list %d invalid", pli);
1214         }
1215     }
1216     else
1217     {
1218         /*
1219          * show all
1220          */
1221         vlib_cli_output (vm, "FIB Path Lists");
1222         pool_foreach(path_list, fib_path_list_pool,
1223         ({
1224             vlib_cli_output (vm, "%U", format_fib_path_list, path_list);
1225         }));
1226     }
1227     return (NULL);
1228 }
1229
1230 VLIB_CLI_COMMAND (show_fib_path_list, static) = {
1231   .path = "show fib path-lists",
1232   .function = show_fib_path_list_command,
1233   .short_help = "show fib path-lists",
1234 };