Reorganize source tree to use single autotools instance
[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 *hash_key;
369     fib_node_index_t *path_index;
370
371     hash_key  = 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         hash_key = fib_path_append_nh_for_multipath_hash(
392                        *path_index,
393                        fct,
394                        hash_key);
395     }
396
397     /*
398      * Path-list load-balances, which if used, would be shared and hence
399      * never need a load-balance map.
400      */
401     load_balance_multipath_update(dpo, hash_key, LOAD_BALANCE_FLAG_NONE);
402
403     FIB_PATH_LIST_DBG(path_list, "mk lb: %d", dpo->dpoi_index);
404
405     vec_free(hash_key);
406 }
407
408 /**
409  * @brief [re]build the path list's uRPF list
410  */
411 static void
412 fib_path_list_mk_urpf (fib_path_list_t *path_list)
413 {
414     fib_node_index_t *path_index;
415
416     /*
417      * ditch the old one. by iterating through all paths we are going
418      * to re-find all the adjs that were in the old one anyway. If we
419      * keep the old one, then the |sort|uniq requires more work.
420      * All users of the RPF list have their own lock, so we can release
421      * immediately.
422      */
423     fib_urpf_list_unlock(path_list->fpl_urpf);
424     path_list->fpl_urpf = fib_urpf_list_alloc_and_lock();
425
426     vec_foreach (path_index, path_list->fpl_paths)
427     {
428         fib_path_contribute_urpf(*path_index, path_list->fpl_urpf);
429     }
430
431     fib_urpf_list_bake(path_list->fpl_urpf);
432 }
433
434 /**
435  * @brief Contribute (add) this path list's uRPF list. This allows the child
436  * to construct an aggregate list.
437  */
438 void
439 fib_path_list_contribute_urpf (fib_node_index_t path_list_index,
440                                index_t urpf)
441 {
442     fib_path_list_t *path_list;
443
444     path_list = fib_path_list_get(path_list_index);
445
446     fib_urpf_list_combine(urpf, path_list->fpl_urpf);
447 }
448
449 /**
450  * @brief Return the the child the RPF list pre-built for this path list
451  */
452 index_t
453 fib_path_list_get_urpf (fib_node_index_t path_list_index)
454 {
455     fib_path_list_t *path_list;
456
457     path_list = fib_path_list_get(path_list_index);
458
459     return (path_list->fpl_urpf);
460 }
461
462 /*
463  * fib_path_list_back_walk
464  *
465  * Called from one of this path-list's paths to progate
466  * a back walk
467  */
468 void
469 fib_path_list_back_walk (fib_node_index_t path_list_index,
470                          fib_node_back_walk_ctx_t *ctx)
471 {
472     fib_path_list_t *path_list;
473
474     path_list = fib_path_list_get(path_list_index);
475
476     fib_path_list_mk_urpf(path_list);
477
478     /*
479      * propagate the backwalk further
480      */
481     if (32 >= fib_node_list_get_size(path_list->fpl_node.fn_children))
482     {
483         /*
484          * only a few children. continue the walk synchronously
485          */
486         fib_walk_sync(FIB_NODE_TYPE_PATH_LIST, path_list_index, ctx);
487     }
488     else
489     {
490         /*
491          * many children. schedule a async walk
492          */
493         fib_walk_async(FIB_NODE_TYPE_PATH_LIST,
494                        path_list_index,
495                        FIB_WALK_PRIORITY_LOW,
496                        ctx);
497     }
498 }
499
500 /*
501  * fib_path_list_back_walk_notify
502  *
503  * A back walk has reach this path-list.
504  */
505 static fib_node_back_walk_rc_t
506 fib_path_list_back_walk_notify (fib_node_t *node,
507                                 fib_node_back_walk_ctx_t *ctx)
508 {
509     /*
510      * the path-list is not a direct child of any other node type
511      * paths, which do not change thier to-list-mapping, save the
512      * list they are a member of, and invoke the BW function directly.
513      */
514     ASSERT(0);
515
516     return (FIB_NODE_BACK_WALK_CONTINUE);
517 }
518
519 /*
520  * Display the path-list memory usage
521  */
522 static void
523 fib_path_list_memory_show (void)
524 {
525     fib_show_memory_usage("Path-list",
526                           pool_elts(fib_path_list_pool),
527                           pool_len(fib_path_list_pool),
528                           sizeof(fib_path_list_t));
529     fib_urpf_list_show_mem();
530 }
531
532 /*
533  * The FIB path-list's graph node virtual function table
534  */
535 static const fib_node_vft_t fib_path_list_vft = {
536     .fnv_get = fib_path_list_get_node,
537     .fnv_last_lock = fib_path_list_last_lock_gone,
538     .fnv_back_walk = fib_path_list_back_walk_notify,
539     .fnv_mem_show = fib_path_list_memory_show,
540 };
541
542 static fib_path_list_t *
543 fib_path_list_alloc (fib_node_index_t *path_list_index)
544 {
545     fib_path_list_t *path_list;
546
547     pool_get(fib_path_list_pool, path_list);
548     memset(path_list, 0, sizeof(*path_list));
549
550     fib_node_init(&path_list->fpl_node,
551                   FIB_NODE_TYPE_PATH_LIST);
552     path_list->fpl_urpf = INDEX_INVALID;
553
554     if (NULL != path_list_index)
555     {
556         *path_list_index = fib_path_list_get_index(path_list);
557     }
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     fib_path_list_mk_urpf(path_list);
590
591     return (path_list);
592 }
593
594 u32
595 fib_path_list_get_resolving_interface (fib_node_index_t path_list_index)
596 {
597     fib_node_index_t *path_index;
598     fib_path_list_t *path_list;
599     u32 sw_if_index;
600
601     path_list = fib_path_list_get(path_list_index);
602
603     sw_if_index = ~0;
604     vec_foreach (path_index, path_list->fpl_paths)
605     {
606         sw_if_index = fib_path_get_resolving_interface(*path_index);
607         if (~0 != sw_if_index)
608         {
609             return (sw_if_index);
610         }
611     }
612
613     return (sw_if_index);
614 }
615
616 fib_protocol_t
617 fib_path_list_get_proto (fib_node_index_t path_list_index)
618 {
619     fib_path_list_t *path_list;
620
621     path_list = fib_path_list_get(path_list_index);
622
623     /*
624      * we don't support a mix of path protocols, so we can return the proto
625      * of the first
626      */
627     return (fib_path_get_proto(path_list->fpl_paths[0]));
628 }
629
630 int
631 fib_path_list_is_looped (fib_node_index_t path_list_index)
632 {
633     fib_path_list_t *path_list;
634
635     path_list = fib_path_list_get(path_list_index);
636
637     return (path_list->fpl_flags & FIB_PATH_LIST_FLAG_LOOPED);
638 }
639
640 static fib_path_cfg_flags_t 
641 fib_path_list_flags_2_path_flags (fib_path_list_flags_t plf)
642 {
643     fib_path_cfg_flags_t pf = FIB_PATH_CFG_FLAG_NONE;
644
645     if (plf & FIB_PATH_LIST_FLAG_LOCAL)
646     {
647         pf |= FIB_PATH_CFG_FLAG_LOCAL;
648     }
649     if (plf & FIB_PATH_LIST_FLAG_DROP)
650     {
651         pf |= FIB_PATH_CFG_FLAG_DROP;
652     }
653     if (plf & FIB_PATH_LIST_FLAG_EXCLUSIVE)
654     {
655         pf |= FIB_PATH_CFG_FLAG_EXCLUSIVE;
656     }
657
658     return (pf);
659 }
660
661 static fib_path_list_flags_t
662 fib_path_list_flags_fixup (fib_path_list_flags_t flags)
663 {
664     /*
665      * we do no share drop nor exclusive path-lists
666      */
667     if (flags & FIB_PATH_LIST_FLAG_DROP ||
668         flags & FIB_PATH_LIST_FLAG_EXCLUSIVE)
669     {
670         flags &= ~FIB_PATH_LIST_FLAG_SHARED;
671     }
672
673     return (flags);
674 }
675
676 fib_node_index_t
677 fib_path_list_create (fib_path_list_flags_t flags,
678                       const fib_route_path_t *rpaths)
679 {
680     fib_node_index_t path_list_index, old_path_list_index;
681     fib_path_list_t *path_list;
682     int i;
683
684     flags = fib_path_list_flags_fixup(flags);
685     path_list = fib_path_list_alloc(&path_list_index);
686     path_list->fpl_flags = flags;
687     /*
688      * we'll assume for now all paths are the same next-hop protocol
689      */
690     path_list->fpl_nh_proto = rpaths[0].frp_proto;
691
692     vec_foreach_index(i, rpaths)
693     {
694         vec_add1(path_list->fpl_paths,
695                  fib_path_create(path_list_index,
696                                  path_list->fpl_nh_proto,
697                                  fib_path_list_flags_2_path_flags(flags),
698                                  &rpaths[i]));
699     }
700
701     /*
702      * If a shared path list is requested, consult the DB for a match
703      */
704     if (flags & FIB_PATH_LIST_FLAG_SHARED)
705     {
706         /*
707          * check for a matching path-list in the DB.
708          * If we find one then we can return the existing one and destroy the
709          * new one just created.
710          */
711         old_path_list_index = fib_path_list_db_find(path_list);
712         if (FIB_NODE_INDEX_INVALID != old_path_list_index)
713         {
714             fib_path_list_destroy(path_list);
715         
716             path_list_index = old_path_list_index;
717         }
718         else
719         {
720             /*
721              * if there was not a matching path-list, then this
722              * new one will need inserting into the DB and resolving.
723              */
724             fib_path_list_db_insert(path_list_index);
725             path_list = fib_path_list_resolve(path_list);
726         }
727     }
728     else
729     {
730         /*
731          * no shared path list requested. resolve and use the one
732          * just created.
733          */
734         path_list = fib_path_list_resolve(path_list);
735     }
736
737     return (path_list_index);
738 }
739
740 fib_node_index_t
741 fib_path_list_create_special (fib_protocol_t nh_proto,
742                               fib_path_list_flags_t flags,
743                               const dpo_id_t *dpo)
744 {
745     fib_node_index_t path_index, path_list_index;
746     fib_path_list_t *path_list;
747
748     path_list = fib_path_list_alloc(&path_list_index);
749     path_list->fpl_flags = flags;
750     path_list->fpl_nh_proto = nh_proto;
751
752     path_index =
753         fib_path_create_special(path_list_index,
754                                 path_list->fpl_nh_proto,
755                                 fib_path_list_flags_2_path_flags(flags),
756                                 dpo);
757     vec_add1(path_list->fpl_paths, path_index);
758
759     /*
760      * we don't share path-lists. we can do PIC on them so why bother.
761      */
762     path_list = fib_path_list_resolve(path_list);
763
764     return (path_list_index);
765 }
766
767 /*
768  * fib_path_list_copy_and_path_add
769  *
770  * Create a copy of a path-list and append one more path to it.
771  * The path-list returned could either have been newly created, or
772  * can be a shared path-list from the data-base.
773  */
774 fib_node_index_t
775 fib_path_list_copy_and_path_add (fib_node_index_t orig_path_list_index,
776                                  fib_path_list_flags_t flags,
777                                  const fib_route_path_t *rpaths)
778 {
779     fib_node_index_t path_index, new_path_index, *orig_path_index;
780     fib_path_list_t *path_list, *orig_path_list;
781     fib_node_index_t path_list_index;
782     fib_node_index_t pi;
783
784     ASSERT(1 == vec_len(rpaths));
785
786     /*
787      * alloc the new list before we retrieve the old one, lest
788      * the alloc result in a realloc
789      */
790     path_list = fib_path_list_alloc(&path_list_index);
791
792     orig_path_list = fib_path_list_get(orig_path_list_index);
793
794     FIB_PATH_LIST_DBG(orig_path_list, "copy-add");
795
796     flags = fib_path_list_flags_fixup(flags);
797     path_list->fpl_flags = flags;
798     path_list->fpl_nh_proto = orig_path_list->fpl_nh_proto;
799     vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths));
800     pi = 0;
801
802     new_path_index = fib_path_create(path_list_index,
803                                      path_list->fpl_nh_proto,
804                                      fib_path_list_flags_2_path_flags(flags),
805                                      rpaths);
806
807     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
808     {
809         /*
810          * don't add duplicate paths
811          * In the unlikely event the path is a duplicate, then we'll
812          * find a matching path-list later and this one will be toast.
813          */
814         if (0 != fib_path_cmp(new_path_index, *orig_path_index))
815         {
816             path_index = fib_path_copy(*orig_path_index, path_list_index);
817             path_list->fpl_paths[pi++] = path_index;
818         }
819         else
820         {
821             _vec_len(path_list->fpl_paths) = vec_len(orig_path_list->fpl_paths);
822         }
823     }
824
825     path_list->fpl_paths[pi] = new_path_index;
826
827     /*
828      * we sort the paths since the key for the path-list is
829      * the description of the paths it contains. The paths need to
830      * be sorted else this description will differ.
831      */
832     vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
833
834     FIB_PATH_LIST_DBG(path_list, "path-added");
835
836     /*
837      * If a shared path list is requested, consult the DB for a match
838      */
839     if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
840     {
841         fib_node_index_t exist_path_list_index;
842         /*
843          * check for a matching path-list in the DB.
844          * If we find one then we can return the existing one and destroy the
845          * new one just created.
846          */
847         exist_path_list_index = fib_path_list_db_find(path_list);
848         if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
849         {
850             fib_path_list_destroy(path_list);
851         
852             path_list_index = exist_path_list_index;
853         }
854         else
855         {
856             /*
857              * if there was not a matching path-list, then this
858              * new one will need inserting into the DB and resolving.
859              */
860             fib_path_list_db_insert(path_list_index);
861
862             path_list = fib_path_list_resolve(path_list);
863         }
864     }
865     else
866     {
867         /*
868          * no shared path list requested. resolve and use the one
869          * just created.
870          */
871         path_list = fib_path_list_resolve(path_list);
872     }
873
874     return (path_list_index);
875 }
876
877 /*
878  * fib_path_list_copy_and_path_remove
879  *
880  * Copy the path-list excluding the path passed.
881  * If the path is the last one, then the index reurned will be invalid.
882  * i.e. the path-list is toast.
883  */
884 fib_node_index_t
885 fib_path_list_copy_and_path_remove (fib_node_index_t orig_path_list_index,
886                                     fib_path_list_flags_t flags,
887                                     const fib_route_path_t *rpaths)
888 {
889     fib_node_index_t path_index, *orig_path_index, path_list_index, tmp_path_index;
890     fib_path_list_t *path_list,  *orig_path_list;
891     fib_node_index_t pi;
892
893     ASSERT(1 == vec_len(rpaths));
894
895     path_list = fib_path_list_alloc(&path_list_index);
896
897     flags = fib_path_list_flags_fixup(flags);
898     orig_path_list = fib_path_list_get(orig_path_list_index);
899
900     FIB_PATH_LIST_DBG(orig_path_list, "copy-remove");
901
902     path_list->fpl_flags = flags;
903     path_list->fpl_nh_proto = orig_path_list->fpl_nh_proto;
904     /*
905      * allocate as many paths as we might need in one go, rather than
906      * using vec_add to do a few at a time.
907      */
908     if (vec_len(orig_path_list->fpl_paths) > 1)
909     {
910         vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths) - 2);
911     }
912     pi = 0;
913
914     /*
915      * create a representation of the path to be removed, so it
916      * can be used as a comparison object during the copy.
917      */
918     tmp_path_index = fib_path_create(path_list_index,
919                                      path_list->fpl_nh_proto,
920                                      fib_path_list_flags_2_path_flags(flags),
921                                      rpaths);
922
923     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
924     {
925         if (0 != fib_path_cmp(tmp_path_index, *orig_path_index)) {
926             path_index = fib_path_copy(*orig_path_index, path_list_index);
927             if (pi < vec_len(path_list->fpl_paths))
928             {
929                 path_list->fpl_paths[pi++] = path_index;
930             }
931             else
932             {
933                 /*
934                  * this is the unlikely case that the path being
935                  * removed does not match one in the path-list, so
936                  * we end up with as many paths as we started with.
937                  * the paths vector was sized above with the expectation
938                  * that we would have 1 less.
939                  */
940                 vec_add1(path_list->fpl_paths, path_index);
941             }
942         }
943     }
944
945     /*
946      * done with the temporary now
947      */
948     fib_path_destroy(tmp_path_index);
949
950     /*
951      * if there are no paths, then the new path-list is aborted
952      */
953     if (0 == vec_len(path_list->fpl_paths)) {
954         FIB_PATH_LIST_DBG(path_list, "last-path-removed");
955
956         fib_path_list_destroy(path_list);
957
958         path_list_index = FIB_NODE_INDEX_INVALID;
959     } else {
960         /*
961          * we sort the paths since the key for the path-list is
962          * the description of the paths it contains. The paths need to
963          * be sorted else this description will differ.
964          */
965         vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
966     
967         /*
968          * If a shared path list is requested, consult the DB for a match
969          */
970         if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
971         {
972             fib_node_index_t exist_path_list_index;
973
974             /*
975              * check for a matching path-list in the DB.
976              * If we find one then we can return the existing one and destroy the
977              * new one just created.
978              */
979             exist_path_list_index = fib_path_list_db_find(path_list);
980             if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
981             {
982                 fib_path_list_destroy(path_list);
983         
984                 path_list_index = exist_path_list_index;
985             }
986             else
987             {
988                 /*
989                  * if there was not a matching path-list, then this
990                  * new one will need inserting into the DB and resolving.
991                  */
992                 fib_path_list_db_insert(path_list_index);
993
994                 path_list = fib_path_list_resolve(path_list);
995             }
996         }
997         else
998         {
999             /*
1000              * no shared path list requested. resolve and use the one
1001              * just created.
1002              */
1003             path_list = fib_path_list_resolve(path_list);
1004         }
1005     }
1006
1007     return (path_list_index);
1008 }
1009
1010 /*
1011  * fib_path_list_contribute_forwarding
1012  *
1013  * Return the index of a load-balance that user of this path-list should
1014  * use for forwarding
1015  */
1016 void
1017 fib_path_list_contribute_forwarding (fib_node_index_t path_list_index,
1018                                      fib_forward_chain_type_t type,
1019                                      dpo_id_t *dpo)
1020 {
1021     fib_path_list_t *path_list;
1022
1023     path_list = fib_path_list_get(path_list_index);
1024
1025     fib_path_list_mk_lb(path_list, type, dpo);
1026 }
1027
1028 /*
1029  * fib_path_list_get_adj
1030  *
1031  * Return the index of a adjacency for the first path that user of this
1032  * path-list should use for forwarding
1033  */
1034 adj_index_t
1035 fib_path_list_get_adj (fib_node_index_t path_list_index,
1036                        fib_forward_chain_type_t type)
1037 {
1038     fib_path_list_t *path_list;
1039
1040     path_list = fib_path_list_get(path_list_index);
1041     return (fib_path_get_adj(path_list->fpl_paths[0]));
1042 }
1043
1044 int
1045 fib_path_list_recursive_loop_detect (fib_node_index_t path_list_index,
1046                                      fib_node_index_t **entry_indicies)
1047 {
1048     fib_node_index_t *path_index;
1049     int is_looped, list_looped;
1050     fib_path_list_t *path_list;
1051
1052     list_looped = 0;
1053     path_list = fib_path_list_get(path_list_index);
1054
1055     vec_foreach (path_index, path_list->fpl_paths)
1056     {
1057         fib_node_index_t *copy, **copy_ptr;
1058
1059         /*
1060          * we need a copy of the nodes visited so that when we add entries
1061          * we explore on the nth path and a looped is detected, those entries
1062          * are not again searched for n+1 path and so finding a loop that does
1063          * not exist.
1064          */
1065         copy = vec_dup(*entry_indicies);
1066         copy_ptr = &copy;
1067
1068         is_looped  = fib_path_recursive_loop_detect(*path_index, copy_ptr);
1069         list_looped += is_looped;
1070     }
1071
1072     FIB_PATH_LIST_DBG(path_list, "loop-detect: eval:%d", eval);
1073
1074     if (list_looped)
1075     {
1076         path_list->fpl_flags |= FIB_PATH_LIST_FLAG_LOOPED;
1077     }
1078     else
1079     {
1080         path_list->fpl_flags &= ~FIB_PATH_LIST_FLAG_LOOPED;
1081     }
1082
1083     return (list_looped);
1084 }
1085
1086 u32
1087 fib_path_list_child_add (fib_node_index_t path_list_index,
1088                          fib_node_type_t child_type,
1089                          fib_node_index_t child_index)
1090 {
1091     return (fib_node_child_add(FIB_NODE_TYPE_PATH_LIST,
1092                                path_list_index,
1093                                child_type,
1094                                child_index));
1095 }
1096
1097 void
1098 fib_path_list_child_remove (fib_node_index_t path_list_index,
1099                             u32 si)
1100 {
1101     fib_node_child_remove(FIB_NODE_TYPE_PATH_LIST,
1102                           path_list_index,
1103                           si);
1104 }
1105
1106 void
1107 fib_path_list_lock(fib_node_index_t path_list_index)
1108 {
1109     fib_path_list_t *path_list;
1110
1111     if (FIB_NODE_INDEX_INVALID != path_list_index)
1112     {
1113         path_list = fib_path_list_get(path_list_index);
1114
1115         fib_node_lock(&path_list->fpl_node);
1116         FIB_PATH_LIST_DBG(path_list, "lock");
1117     }
1118 }
1119
1120 void
1121 fib_path_list_unlock (fib_node_index_t path_list_index)
1122 {
1123     fib_path_list_t *path_list;
1124
1125     if (FIB_NODE_INDEX_INVALID != path_list_index)
1126     {
1127         path_list = fib_path_list_get(path_list_index);
1128         FIB_PATH_LIST_DBG(path_list, "unlock");
1129     
1130         fib_node_unlock(&path_list->fpl_node);
1131     }
1132 }
1133
1134 u32
1135 fib_path_list_pool_size (void)
1136 {
1137     return (pool_elts(fib_path_list_pool));    
1138 }
1139
1140 u32
1141 fib_path_list_db_size (void)
1142 {
1143     return (hash_elts(fib_path_list_db));
1144 }
1145
1146 void
1147 fib_path_list_walk (fib_node_index_t path_list_index,
1148                     fib_path_list_walk_fn_t func,
1149                     void *ctx)
1150 {
1151     fib_node_index_t *path_index;
1152     fib_path_list_t *path_list;
1153
1154     path_list = fib_path_list_get(path_list_index);
1155
1156     vec_foreach(path_index, path_list->fpl_paths)
1157     {
1158         if (!func(path_list_index, *path_index, ctx))
1159             break;
1160     }
1161 }
1162
1163
1164 void
1165 fib_path_list_module_init (void)
1166 {
1167     fib_node_register_type (FIB_NODE_TYPE_PATH_LIST, &fib_path_list_vft);
1168
1169     fib_path_list_db = hash_create2 (/* elts */ 0,
1170                                      /* user */ 0,
1171                                      /* value_bytes */ sizeof (fib_node_index_t),
1172                                      fib_path_list_db_hash_key_sum,
1173                                      fib_path_list_db_hash_key_equal,
1174                                      /* format pair/arg */
1175                                      0, 0);
1176 }
1177
1178 static clib_error_t *
1179 show_fib_path_list_command (vlib_main_t * vm,
1180                             unformat_input_t * input,
1181                             vlib_cli_command_t * cmd)
1182 {
1183     fib_path_list_t *path_list;
1184     fib_node_index_t pli;
1185
1186     if (unformat (input, "%d", &pli))
1187     {
1188         /*
1189          * show one in detail
1190          */
1191         if (!pool_is_free_index(fib_path_list_pool, pli))
1192         {
1193             path_list = fib_path_list_get(pli);
1194             u8 *s = fib_path_list_format(pli, NULL);
1195             s = format(s, "children:");
1196             s = fib_node_children_format(path_list->fpl_node.fn_children, s);
1197             vlib_cli_output (vm, "%s", s);
1198             vec_free(s);
1199         }
1200         else
1201         {
1202             vlib_cli_output (vm, "path list %d invalid", pli);
1203         }
1204     }
1205     else
1206     {
1207         /*
1208          * show all
1209          */
1210         vlib_cli_output (vm, "FIB Path Lists");
1211         pool_foreach(path_list, fib_path_list_pool,
1212         ({
1213             vlib_cli_output (vm, "%U", format_fib_path_list, path_list);
1214         }));
1215     }
1216     return (NULL);
1217 }
1218
1219 VLIB_CLI_COMMAND (show_fib_path_list, static) = {
1220   .path = "show fib path-lists",
1221   .function = show_fib_path_list_command,
1222   .short_help = "show fib path-lists",
1223 };