4d695d63d51260da93014b5527e348c317a12615
[vpp.git] / vnet / 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 int
617 fib_path_list_is_looped (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     return (path_list->fpl_flags & FIB_PATH_LIST_FLAG_LOOPED);
624 }
625
626 static fib_path_cfg_flags_t 
627 fib_path_list_flags_2_path_flags (fib_path_list_flags_t plf)
628 {
629     fib_path_cfg_flags_t pf = FIB_PATH_CFG_FLAG_NONE;
630
631     if (plf & FIB_PATH_LIST_FLAG_LOCAL)
632     {
633         pf |= FIB_PATH_CFG_FLAG_LOCAL;
634     }
635     if (plf & FIB_PATH_LIST_FLAG_DROP)
636     {
637         pf |= FIB_PATH_CFG_FLAG_DROP;
638     }
639     if (plf & FIB_PATH_LIST_FLAG_EXCLUSIVE)
640     {
641         pf |= FIB_PATH_CFG_FLAG_EXCLUSIVE;
642     }
643
644     return (pf);
645 }
646
647 static fib_path_list_flags_t
648 fib_path_list_flags_fixup (fib_path_list_flags_t flags)
649 {
650     /*
651      * we do no share drop nor exclusive path-lists
652      */
653     if (flags & FIB_PATH_LIST_FLAG_DROP ||
654         flags & FIB_PATH_LIST_FLAG_EXCLUSIVE)
655     {
656         flags &= ~FIB_PATH_LIST_FLAG_SHARED;
657     }
658
659     return (flags);
660 }
661
662 fib_node_index_t
663 fib_path_list_create (fib_path_list_flags_t flags,
664                       const fib_route_path_t *rpaths)
665 {
666     fib_node_index_t path_list_index, old_path_list_index;
667     fib_path_list_t *path_list;
668     int i;
669
670     flags = fib_path_list_flags_fixup(flags);
671     path_list = fib_path_list_alloc(&path_list_index);
672     path_list->fpl_flags = flags;
673     /*
674      * we'll assume for now all paths are the same next-hop protocol
675      */
676     path_list->fpl_nh_proto = rpaths[0].frp_proto;
677
678     vec_foreach_index(i, rpaths)
679     {
680         vec_add1(path_list->fpl_paths,
681                  fib_path_create(path_list_index,
682                                  path_list->fpl_nh_proto,
683                                  fib_path_list_flags_2_path_flags(flags),
684                                  &rpaths[i]));
685     }
686
687     /*
688      * If a shared path list is requested, consult the DB for a match
689      */
690     if (flags & FIB_PATH_LIST_FLAG_SHARED)
691     {
692         /*
693          * check for a matching path-list in the DB.
694          * If we find one then we can return the existing one and destroy the
695          * new one just created.
696          */
697         old_path_list_index = fib_path_list_db_find(path_list);
698         if (FIB_NODE_INDEX_INVALID != old_path_list_index)
699         {
700             fib_path_list_destroy(path_list);
701         
702             path_list_index = old_path_list_index;
703         }
704         else
705         {
706             /*
707              * if there was not a matching path-list, then this
708              * new one will need inserting into the DB and resolving.
709              */
710             fib_path_list_db_insert(path_list_index);
711             path_list = fib_path_list_resolve(path_list);
712         }
713     }
714     else
715     {
716         /*
717          * no shared path list requested. resolve and use the one
718          * just created.
719          */
720         path_list = fib_path_list_resolve(path_list);
721     }
722
723     return (path_list_index);
724 }
725
726 fib_node_index_t
727 fib_path_list_create_special (fib_protocol_t nh_proto,
728                               fib_path_list_flags_t flags,
729                               const dpo_id_t *dpo)
730 {
731     fib_node_index_t path_index, path_list_index;
732     fib_path_list_t *path_list;
733
734     path_list = fib_path_list_alloc(&path_list_index);
735     path_list->fpl_flags = flags;
736     path_list->fpl_nh_proto = nh_proto;
737
738     path_index =
739         fib_path_create_special(path_list_index,
740                                 path_list->fpl_nh_proto,
741                                 fib_path_list_flags_2_path_flags(flags),
742                                 dpo);
743     vec_add1(path_list->fpl_paths, path_index);
744
745     /*
746      * we don't share path-lists. we can do PIC on them so why bother.
747      */
748     path_list = fib_path_list_resolve(path_list);
749
750     return (path_list_index);
751 }
752
753 /*
754  * fib_path_list_copy_and_path_add
755  *
756  * Create a copy of a path-list and append one more path to it.
757  * The path-list returned could either have been newly created, or
758  * can be a shared path-list from the data-base.
759  */
760 fib_node_index_t
761 fib_path_list_copy_and_path_add (fib_node_index_t orig_path_list_index,
762                                  fib_path_list_flags_t flags,
763                                  const fib_route_path_t *rpaths)
764 {
765     fib_node_index_t path_index, new_path_index, *orig_path_index;
766     fib_path_list_t *path_list, *orig_path_list;
767     fib_node_index_t path_list_index;
768     fib_node_index_t pi;
769
770     ASSERT(1 == vec_len(rpaths));
771
772     /*
773      * alloc the new list before we retrieve the old one, lest
774      * the alloc result in a realloc
775      */
776     path_list = fib_path_list_alloc(&path_list_index);
777
778     orig_path_list = fib_path_list_get(orig_path_list_index);
779
780     FIB_PATH_LIST_DBG(orig_path_list, "copy-add");
781
782     flags = fib_path_list_flags_fixup(flags);
783     path_list->fpl_flags = flags;
784     path_list->fpl_nh_proto = orig_path_list->fpl_nh_proto;
785     vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths));
786     pi = 0;
787
788     new_path_index = fib_path_create(path_list_index,
789                                      path_list->fpl_nh_proto,
790                                      fib_path_list_flags_2_path_flags(flags),
791                                      rpaths);
792
793     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
794     {
795         /*
796          * don't add duplicate paths
797          * In the unlikely event the path is a duplicate, then we'll
798          * find a matching path-list later and this one will be toast.
799          */
800         if (0 != fib_path_cmp(new_path_index, *orig_path_index))
801         {
802             path_index = fib_path_copy(*orig_path_index, path_list_index);
803             path_list->fpl_paths[pi++] = path_index;
804         }
805         else
806         {
807             _vec_len(path_list->fpl_paths) = vec_len(orig_path_list->fpl_paths);
808         }
809     }
810
811     path_list->fpl_paths[pi] = new_path_index;
812
813     /*
814      * we sort the paths since the key for the path-list is
815      * the description of the paths it contains. The paths need to
816      * be sorted else this description will differ.
817      */
818     vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
819
820     FIB_PATH_LIST_DBG(path_list, "path-added");
821
822     /*
823      * If a shared path list is requested, consult the DB for a match
824      */
825     if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
826     {
827         fib_node_index_t exist_path_list_index;
828         /*
829          * check for a matching path-list in the DB.
830          * If we find one then we can return the existing one and destroy the
831          * new one just created.
832          */
833         exist_path_list_index = fib_path_list_db_find(path_list);
834         if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
835         {
836             fib_path_list_destroy(path_list);
837         
838             path_list_index = exist_path_list_index;
839         }
840         else
841         {
842             /*
843              * if there was not a matching path-list, then this
844              * new one will need inserting into the DB and resolving.
845              */
846             fib_path_list_db_insert(path_list_index);
847
848             path_list = fib_path_list_resolve(path_list);
849         }
850     }
851     else
852     {
853         /*
854          * no shared path list requested. resolve and use the one
855          * just created.
856          */
857         path_list = fib_path_list_resolve(path_list);
858     }
859
860     return (path_list_index);
861 }
862
863 /*
864  * fib_path_list_copy_and_path_remove
865  *
866  * Copy the path-list excluding the path passed.
867  * If the path is the last one, then the index reurned will be invalid.
868  * i.e. the path-list is toast.
869  */
870 fib_node_index_t
871 fib_path_list_copy_and_path_remove (fib_node_index_t orig_path_list_index,
872                                     fib_path_list_flags_t flags,
873                                     const fib_route_path_t *rpaths)
874 {
875     fib_node_index_t path_index, *orig_path_index, path_list_index, tmp_path_index;
876     fib_path_list_t *path_list,  *orig_path_list;
877     fib_node_index_t pi;
878
879     ASSERT(1 == vec_len(rpaths));
880
881     path_list = fib_path_list_alloc(&path_list_index);
882
883     flags = fib_path_list_flags_fixup(flags);
884     orig_path_list = fib_path_list_get(orig_path_list_index);
885
886     FIB_PATH_LIST_DBG(orig_path_list, "copy-remove");
887
888     path_list->fpl_flags = flags;
889     path_list->fpl_nh_proto = orig_path_list->fpl_nh_proto;
890     /*
891      * allocate as many paths as we might need in one go, rather than
892      * using vec_add to do a few at a time.
893      */
894     if (vec_len(orig_path_list->fpl_paths) > 1)
895     {
896         vec_validate(path_list->fpl_paths, vec_len(orig_path_list->fpl_paths) - 2);
897     }
898     pi = 0;
899
900     /*
901      * create a representation of the path to be removed, so it
902      * can be used as a comparison object during the copy.
903      */
904     tmp_path_index = fib_path_create(path_list_index,
905                                      path_list->fpl_nh_proto,
906                                      fib_path_list_flags_2_path_flags(flags),
907                                      rpaths);
908
909     vec_foreach (orig_path_index, orig_path_list->fpl_paths)
910     {
911         if (0 != fib_path_cmp(tmp_path_index, *orig_path_index)) {
912             path_index = fib_path_copy(*orig_path_index, path_list_index);
913             if (pi < vec_len(path_list->fpl_paths))
914             {
915                 path_list->fpl_paths[pi++] = path_index;
916             }
917             else
918             {
919                 /*
920                  * this is the unlikely case that the path being
921                  * removed does not match one in the path-list, so
922                  * we end up with as many paths as we started with.
923                  * the paths vector was sized above with the expectation
924                  * that we would have 1 less.
925                  */
926                 vec_add1(path_list->fpl_paths, path_index);
927             }
928         }
929     }
930
931     /*
932      * done with the temporary now
933      */
934     fib_path_destroy(tmp_path_index);
935
936     /*
937      * if there are no paths, then the new path-list is aborted
938      */
939     if (0 == vec_len(path_list->fpl_paths)) {
940         FIB_PATH_LIST_DBG(path_list, "last-path-removed");
941
942         fib_path_list_destroy(path_list);
943
944         path_list_index = FIB_NODE_INDEX_INVALID;
945     } else {
946         /*
947          * we sort the paths since the key for the path-list is
948          * the description of the paths it contains. The paths need to
949          * be sorted else this description will differ.
950          */
951         vec_sort_with_function(path_list->fpl_paths, fib_path_cmp_for_sort);
952     
953         /*
954          * If a shared path list is requested, consult the DB for a match
955          */
956         if (path_list->fpl_flags & FIB_PATH_LIST_FLAG_SHARED)
957         {
958             fib_node_index_t exist_path_list_index;
959
960             /*
961              * check for a matching path-list in the DB.
962              * If we find one then we can return the existing one and destroy the
963              * new one just created.
964              */
965             exist_path_list_index = fib_path_list_db_find(path_list);
966             if (FIB_NODE_INDEX_INVALID != exist_path_list_index)
967             {
968                 fib_path_list_destroy(path_list);
969         
970                 path_list_index = exist_path_list_index;
971             }
972             else
973             {
974                 /*
975                  * if there was not a matching path-list, then this
976                  * new one will need inserting into the DB and resolving.
977                  */
978                 fib_path_list_db_insert(path_list_index);
979
980                 path_list = fib_path_list_resolve(path_list);
981             }
982         }
983         else
984         {
985             /*
986              * no shared path list requested. resolve and use the one
987              * just created.
988              */
989             path_list = fib_path_list_resolve(path_list);
990         }
991     }
992
993     return (path_list_index);
994 }
995
996 /*
997  * fib_path_list_contribute_forwarding
998  *
999  * Return the index of a load-balance that user of this path-list should
1000  * use for forwarding
1001  */
1002 void
1003 fib_path_list_contribute_forwarding (fib_node_index_t path_list_index,
1004                                      fib_forward_chain_type_t type,
1005                                      dpo_id_t *dpo)
1006 {
1007     fib_path_list_t *path_list;
1008
1009     path_list = fib_path_list_get(path_list_index);
1010
1011     fib_path_list_mk_lb(path_list, type, dpo);
1012 }
1013
1014 /*
1015  * fib_path_list_get_adj
1016  *
1017  * Return the index of a adjacency for the first path that user of this
1018  * path-list should use for forwarding
1019  */
1020 adj_index_t
1021 fib_path_list_get_adj (fib_node_index_t path_list_index,
1022                        fib_forward_chain_type_t type)
1023 {
1024     fib_path_list_t *path_list;
1025
1026     path_list = fib_path_list_get(path_list_index);
1027     return (fib_path_get_adj(path_list->fpl_paths[0]));
1028 }
1029
1030 int
1031 fib_path_list_recursive_loop_detect (fib_node_index_t path_list_index,
1032                                      fib_node_index_t **entry_indicies)
1033 {
1034     fib_node_index_t *path_index;
1035     int is_looped, list_looped;
1036     fib_path_list_t *path_list;
1037
1038     list_looped = 0;
1039     path_list = fib_path_list_get(path_list_index);
1040
1041     vec_foreach (path_index, path_list->fpl_paths)
1042     {
1043         fib_node_index_t *copy, **copy_ptr;
1044
1045         /*
1046          * we need a copy of the nodes visited so that when we add entries
1047          * we explore on the nth path and a looped is detected, those entries
1048          * are not again searched for n+1 path and so finding a loop that does
1049          * not exist.
1050          */
1051         copy = vec_dup(*entry_indicies);
1052         copy_ptr = &copy;
1053
1054         is_looped  = fib_path_recursive_loop_detect(*path_index, copy_ptr);
1055         list_looped += is_looped;
1056     }
1057
1058     FIB_PATH_LIST_DBG(path_list, "loop-detect: eval:%d", eval);
1059
1060     if (list_looped)
1061     {
1062         path_list->fpl_flags |= FIB_PATH_LIST_FLAG_LOOPED;
1063     }
1064     else
1065     {
1066         path_list->fpl_flags &= ~FIB_PATH_LIST_FLAG_LOOPED;
1067     }
1068
1069     return (list_looped);
1070 }
1071
1072 u32
1073 fib_path_list_child_add (fib_node_index_t path_list_index,
1074                          fib_node_type_t child_type,
1075                          fib_node_index_t child_index)
1076 {
1077     return (fib_node_child_add(FIB_NODE_TYPE_PATH_LIST,
1078                                path_list_index,
1079                                child_type,
1080                                child_index));
1081 }
1082
1083 void
1084 fib_path_list_child_remove (fib_node_index_t path_list_index,
1085                             u32 si)
1086 {
1087     fib_node_child_remove(FIB_NODE_TYPE_PATH_LIST,
1088                           path_list_index,
1089                           si);
1090 }
1091
1092 void
1093 fib_path_list_lock(fib_node_index_t path_list_index)
1094 {
1095     fib_path_list_t *path_list;
1096
1097     if (FIB_NODE_INDEX_INVALID != path_list_index)
1098     {
1099         path_list = fib_path_list_get(path_list_index);
1100
1101         fib_node_lock(&path_list->fpl_node);
1102         FIB_PATH_LIST_DBG(path_list, "lock");
1103     }
1104 }
1105
1106 void
1107 fib_path_list_unlock (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         FIB_PATH_LIST_DBG(path_list, "unlock");
1115     
1116         fib_node_unlock(&path_list->fpl_node);
1117     }
1118 }
1119
1120 u32
1121 fib_path_list_pool_size (void)
1122 {
1123     return (pool_elts(fib_path_list_pool));    
1124 }
1125
1126 u32
1127 fib_path_list_db_size (void)
1128 {
1129     return (hash_elts(fib_path_list_db));
1130 }
1131
1132 void
1133 fib_path_list_walk (fib_node_index_t path_list_index,
1134                     fib_path_list_walk_fn_t func,
1135                     void *ctx)
1136 {
1137     fib_node_index_t *path_index;
1138     fib_path_list_t *path_list;
1139
1140     path_list = fib_path_list_get(path_list_index);
1141
1142     vec_foreach(path_index, path_list->fpl_paths)
1143     {
1144         if (!func(path_list_index, *path_index, ctx))
1145             break;
1146     }
1147 }
1148
1149
1150 void
1151 fib_path_list_module_init (void)
1152 {
1153     fib_node_register_type (FIB_NODE_TYPE_PATH_LIST, &fib_path_list_vft);
1154
1155     fib_path_list_db = hash_create2 (/* elts */ 0,
1156                                      /* user */ 0,
1157                                      /* value_bytes */ sizeof (fib_node_index_t),
1158                                      fib_path_list_db_hash_key_sum,
1159                                      fib_path_list_db_hash_key_equal,
1160                                      /* format pair/arg */
1161                                      0, 0);
1162 }
1163
1164 static clib_error_t *
1165 show_fib_path_list_command (vlib_main_t * vm,
1166                             unformat_input_t * input,
1167                             vlib_cli_command_t * cmd)
1168 {
1169     fib_path_list_t *path_list;
1170     fib_node_index_t pli;
1171
1172     if (unformat (input, "%d", &pli))
1173     {
1174         /*
1175          * show one in detail
1176          */
1177         if (!pool_is_free_index(fib_path_list_pool, pli))
1178         {
1179             path_list = fib_path_list_get(pli);
1180             u8 *s = fib_path_list_format(pli, NULL);
1181             s = format(s, "children:");
1182             s = fib_node_children_format(path_list->fpl_node.fn_children, s);
1183             vlib_cli_output (vm, "%s", s);
1184             vec_free(s);
1185         }
1186         else
1187         {
1188             vlib_cli_output (vm, "path list %d invalid", pli);
1189         }
1190     }
1191     else
1192     {
1193         /*
1194          * show all
1195          */
1196         vlib_cli_output (vm, "FIB Path Lists");
1197         pool_foreach(path_list, fib_path_list_pool,
1198         ({
1199             vlib_cli_output (vm, "%U", format_fib_path_list, path_list);
1200         }));
1201     }
1202     return (NULL);
1203 }
1204
1205 VLIB_CLI_COMMAND (show_fib_path_list, static) = {
1206   .path = "show fib path-lists",
1207   .function = show_fib_path_list_command,
1208   .short_help = "show fib path-lists",
1209 };