FIB path preference
[vpp.git] / src / vnet / fib / fib_entry_src.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 <vnet/adj/adj.h>
17 #include <vnet/dpo/load_balance.h>
18 #include <vnet/dpo/mpls_label_dpo.h>
19 #include <vnet/dpo/drop_dpo.h>
20 #include <vnet/dpo/replicate_dpo.h>
21
22 #include <vnet/fib/fib_entry_src.h>
23 #include <vnet/fib/fib_table.h>
24 #include <vnet/fib/fib_path_ext.h>
25 #include <vnet/fib/fib_urpf_list.h>
26
27 /*
28  * per-source type vft
29  */
30 static fib_entry_src_vft_t fib_entry_src_vft[FIB_SOURCE_MAX];
31
32 static fib_protocol_t
33 fib_entry_get_proto (const fib_entry_t * fib_entry)
34 {
35     return (fib_entry->fe_prefix.fp_proto);
36 }
37
38 void
39 fib_entry_src_register (fib_source_t source,
40                         const fib_entry_src_vft_t *vft)
41 {
42     fib_entry_src_vft[source] = *vft;
43 }
44
45 static int
46 fib_entry_src_cmp_for_sort (void * v1,
47                             void * v2)
48 {
49     fib_entry_src_t *esrc1 = v1, *esrc2 = v2;
50
51     return (esrc1->fes_src - esrc2->fes_src);
52 }
53
54 void
55 fib_entry_src_action_init (fib_entry_t *fib_entry,
56                            fib_source_t source)
57
58 {
59     fib_entry_src_t esrc = {
60         .fes_pl = FIB_NODE_INDEX_INVALID,
61         .fes_flags = FIB_ENTRY_SRC_FLAG_NONE,
62         .fes_src = source,
63     };
64
65     if (NULL != fib_entry_src_vft[source].fesv_init)
66     {
67         fib_entry_src_vft[source].fesv_init(&esrc);
68     }
69
70     vec_add1(fib_entry->fe_srcs, esrc);
71     vec_sort_with_function(fib_entry->fe_srcs,
72                            fib_entry_src_cmp_for_sort);
73 }
74
75 static fib_entry_src_t *
76 fib_entry_src_find (const fib_entry_t *fib_entry,
77                     fib_source_t source,
78                     u32 *index)
79
80 {
81     fib_entry_src_t *esrc;
82     int ii;
83
84     ii = 0;
85     vec_foreach(esrc, fib_entry->fe_srcs)
86     {
87         if (esrc->fes_src == source)
88         {
89             if (NULL != index)
90             {
91                 *index = ii;
92             }
93             return (esrc);
94         }
95         else
96         {
97             ii++;
98         }
99     }
100
101     return (NULL);
102 }
103
104 int
105 fib_entry_is_sourced (fib_node_index_t fib_entry_index,
106                       fib_source_t source)
107 {
108     fib_entry_t *fib_entry;
109
110     fib_entry = fib_entry_get(fib_entry_index);
111
112     return (NULL != fib_entry_src_find(fib_entry, source, NULL));
113 }
114
115 static fib_entry_src_t *
116 fib_entry_src_find_or_create (fib_entry_t *fib_entry,
117                               fib_source_t source,
118                               u32 *index)
119 {
120     fib_entry_src_t *esrc;
121
122     esrc = fib_entry_src_find(fib_entry, source, NULL);
123
124     if (NULL == esrc)
125     {
126         fib_entry_src_action_init(fib_entry, source);
127     }
128
129     return (fib_entry_src_find(fib_entry, source, NULL));
130 }
131
132 void
133 fib_entry_src_action_deinit (fib_entry_t *fib_entry,
134                              fib_source_t source)
135
136 {
137     fib_entry_src_t *esrc;
138     u32 index = ~0;
139
140     esrc = fib_entry_src_find(fib_entry, source, &index);
141
142     ASSERT(NULL != esrc);
143
144     if (NULL != fib_entry_src_vft[source].fesv_deinit)
145     {
146         fib_entry_src_vft[source].fesv_deinit(esrc);
147     }
148
149     fib_path_ext_list_flush(&esrc->fes_path_exts);
150     vec_del1(fib_entry->fe_srcs, index);
151 }
152
153 fib_entry_src_cover_res_t
154 fib_entry_src_action_cover_change (fib_entry_t *fib_entry,
155                                    fib_source_t source)
156 {
157     if (NULL != fib_entry_src_vft[source].fesv_cover_change)
158     {
159         return (fib_entry_src_vft[source].fesv_cover_change(
160                     fib_entry_src_find(fib_entry, source, NULL),
161                     fib_entry));
162     }
163
164     fib_entry_src_cover_res_t res = {
165         .install = !0,
166         .bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
167     };
168     return (res);
169 }
170
171 fib_entry_src_cover_res_t
172 fib_entry_src_action_cover_update (fib_entry_t *fib_entry,
173                                    fib_source_t source)
174 {
175     if (NULL != fib_entry_src_vft[source].fesv_cover_update)
176     {
177         return (fib_entry_src_vft[source].fesv_cover_update(
178                     fib_entry_src_find(fib_entry, source, NULL),
179                     fib_entry));
180     }
181
182     fib_entry_src_cover_res_t res = {
183         .install = !0,
184         .bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
185     };
186     return (res);
187 }
188
189 typedef struct fib_entry_src_collect_forwarding_ctx_t_
190 {
191     load_balance_path_t *next_hops;
192     const fib_entry_t *fib_entry;
193     const fib_entry_src_t *esrc;
194     fib_forward_chain_type_t fct;
195     int n_recursive_constrained;
196     u16 preference;
197 } fib_entry_src_collect_forwarding_ctx_t;
198
199 /**
200  * @brief Determine whether this FIB entry should use a load-balance MAP
201  * to support PIC edge fast convergence
202  */
203 load_balance_flags_t
204 fib_entry_calc_lb_flags (fib_entry_src_collect_forwarding_ctx_t *ctx)
205 {
206     /**
207      * We'll use a LB map if the path-list has multiple recursive paths.
208      * recursive paths implies BGP, and hence scale.
209      */
210     if (ctx->n_recursive_constrained > 1 &&
211         fib_path_list_is_popular(ctx->esrc->fes_pl))
212     {
213         return (LOAD_BALANCE_FLAG_USES_MAP);
214     }
215     return (LOAD_BALANCE_FLAG_NONE);
216 }
217
218 static int
219 fib_entry_src_valid_out_label (mpls_label_t label)
220 {
221     return ((MPLS_LABEL_IS_REAL(label) ||
222              MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL == label ||
223              MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL == label ||
224              MPLS_IETF_IMPLICIT_NULL_LABEL == label));
225 }
226
227 /**
228  * @brief Turn the chain type requested by the client into the one they
229  * really wanted
230  */
231 fib_forward_chain_type_t
232 fib_entry_chain_type_fixup (const fib_entry_t *entry,
233                             fib_forward_chain_type_t fct)
234 {
235     /*
236      * The EOS chain is a tricky since one cannot know the adjacency
237      * to link to without knowing what the packets payload protocol
238      * will be once the label is popped.
239      */
240     fib_forward_chain_type_t dfct;
241
242     if (FIB_FORW_CHAIN_TYPE_MPLS_EOS != fct)
243     {
244         return (fct);
245     }
246
247     dfct = fib_entry_get_default_chain_type(entry);
248
249     if (FIB_FORW_CHAIN_TYPE_MPLS_EOS == dfct)
250     {
251         /*
252          * If the entry being asked is a eos-MPLS label entry,
253          * then use the payload-protocol field, that we stashed there
254          * for just this purpose
255          */
256         return (fib_forw_chain_type_from_dpo_proto(
257                     entry->fe_prefix.fp_payload_proto));
258     }
259     /*
260      * else give them what this entry would be by default. i.e. if it's a v6
261      * entry, then the label its local labelled should be carrying v6 traffic.
262      * If it's a non-EOS label entry, then there are more labels and we want
263      * a non-eos chain.
264      */
265     return (dfct);
266 }
267
268 static void
269 fib_entry_src_get_path_forwarding (fib_node_index_t path_index,
270                                    fib_entry_src_collect_forwarding_ctx_t *ctx)
271 {
272     load_balance_path_t *nh;
273
274     /*
275      * no extension => no out-going label for this path. that's OK
276      * in the case of an IP or EOS chain, but not for non-EOS
277      */
278     switch (ctx->fct)
279     {
280     case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
281     case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
282     case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
283     case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
284         /*
285          * EOS traffic with no label to stack, we need the IP Adj
286          */
287         vec_add2(ctx->next_hops, nh, 1);
288
289         nh->path_index = path_index;
290         nh->path_weight = fib_path_get_weight(path_index);
291         fib_path_contribute_forwarding(path_index, ctx->fct, &nh->path_dpo);
292
293         break;
294     case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
295         if (fib_path_is_exclusive(path_index) ||
296             fib_path_is_deag(path_index))
297         {
298             vec_add2(ctx->next_hops, nh, 1);
299
300             nh->path_index = path_index;
301             nh->path_weight = fib_path_get_weight(path_index);
302             fib_path_contribute_forwarding(path_index,
303                                            FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
304                                            &nh->path_dpo);
305         }
306         break;
307     case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
308         {
309             /*
310              * no label. we need a chain based on the payload. fixup.
311              */
312             vec_add2(ctx->next_hops, nh, 1);
313
314             nh->path_index = path_index;
315             nh->path_weight = fib_path_get_weight(path_index);
316             fib_path_contribute_forwarding(path_index,
317                                            fib_entry_chain_type_fixup(ctx->fib_entry,
318                                                                       ctx->fct),
319                                            &nh->path_dpo);
320             fib_path_stack_mpls_disp(path_index,
321                                      ctx->fib_entry->fe_prefix.fp_payload_proto,
322                                      &nh->path_dpo);
323
324             break;
325         }
326     case FIB_FORW_CHAIN_TYPE_ETHERNET:
327     case FIB_FORW_CHAIN_TYPE_NSH:
328         ASSERT(0);
329         break;
330     }
331 }
332
333 static fib_path_list_walk_rc_t
334 fib_entry_src_collect_forwarding (fib_node_index_t pl_index,
335                                   fib_node_index_t path_index,
336                                   void *arg)
337 {
338     fib_entry_src_collect_forwarding_ctx_t *ctx;
339     fib_path_ext_t *path_ext;
340
341     ctx = arg;
342
343     /*
344      * if the path is not resolved, don't include it.
345      */
346     if (!fib_path_is_resolved(path_index))
347     {
348         return (FIB_PATH_LIST_WALK_CONTINUE);
349     }
350
351     if (fib_path_is_recursive_constrained(path_index))
352     {
353         ctx->n_recursive_constrained += 1;
354     }
355     if (0xffff == ctx->preference)
356     {
357         /*
358          * not set a preference yet, so the first path we encounter
359          * sets the preference we are collecting.
360          */
361         ctx->preference = fib_path_get_preference(path_index);
362     }
363     else if (ctx->preference != fib_path_get_preference(path_index))
364     {
365         /*
366          * this path does not belong to the same preference as the
367          * previous paths encountered. we are done now.
368          */
369         return (FIB_PATH_LIST_WALK_STOP);
370     }
371
372     /*
373      * get the matching path-extension for the path being visited.
374      */
375     path_ext = fib_path_ext_list_find_by_path_index(&ctx->esrc->fes_path_exts,
376                                                     path_index);
377
378     if (NULL != path_ext)
379     {
380         switch (path_ext->fpe_type)
381         {
382         case FIB_PATH_EXT_MPLS:
383             if (fib_entry_src_valid_out_label(path_ext->fpe_label_stack[0]))
384             {
385                 /*
386                  * found a matching extension. stack it to obtain the forwarding
387                  * info for this path.
388                  */
389                 ctx->next_hops =
390                     fib_path_ext_stack(path_ext,
391                                        ctx->fct,
392                                        fib_entry_chain_type_fixup(ctx->fib_entry,
393                                                                   ctx->fct),
394                                        ctx->next_hops);
395             }
396             else
397             {
398                 fib_entry_src_get_path_forwarding(path_index, ctx);
399             }
400             break;
401         case FIB_PATH_EXT_ADJ:
402             if (FIB_PATH_EXT_ADJ_FLAG_REFINES_COVER & path_ext->fpe_adj_flags)
403             {
404                 fib_entry_src_get_path_forwarding(path_index, ctx);
405             }
406             /*
407              * else
408              *  the path does not refine the cover, meaning that
409              *  the adjacency doesdoes not match the sub-net on the link.
410              *  So this path does not contribute forwarding.
411              */
412             break;
413         }
414     }
415     else
416     {
417         fib_entry_src_get_path_forwarding(path_index, ctx);
418     }
419
420     return (FIB_PATH_LIST_WALK_CONTINUE);
421 }
422
423 void
424 fib_entry_src_mk_lb (fib_entry_t *fib_entry,
425                      const fib_entry_src_t *esrc,
426                      fib_forward_chain_type_t fct,
427                      dpo_id_t *dpo_lb)
428 {
429     dpo_proto_t lb_proto;
430
431     /*
432      * If the entry has path extensions then we construct a load-balance
433      * by stacking the extensions on the forwarding chains of the paths.
434      * Otherwise we use the load-balance of the path-list
435      */
436     fib_entry_src_collect_forwarding_ctx_t ctx = {
437         .esrc = esrc,
438         .fib_entry = fib_entry,
439         .next_hops = NULL,
440         .n_recursive_constrained = 0,
441         .fct = fct,
442         .preference = 0xffff,
443     };
444
445     /*
446      * As an optimisation we allocate the vector of next-hops to be sized
447      * equal to the maximum nuber of paths we will need, which is also the
448      * most likely number we will need, since in most cases the paths are 'up'.
449      */
450     vec_validate(ctx.next_hops, fib_path_list_get_n_paths(esrc->fes_pl));
451     vec_reset_length(ctx.next_hops);
452
453     lb_proto = fib_forw_chain_type_to_dpo_proto(fct);
454
455     fib_path_list_walk(esrc->fes_pl,
456                        fib_entry_src_collect_forwarding,
457                        &ctx);
458
459     if (esrc->fes_entry_flags & FIB_ENTRY_FLAG_EXCLUSIVE)
460     {
461         /*
462          * the client provided the DPO that the entry should link to.
463          * all entries must link to a LB, so if it is an LB already
464          * then we can use it.
465          */
466         if ((1 == vec_len(ctx.next_hops)) &&
467             (DPO_LOAD_BALANCE == ctx.next_hops[0].path_dpo.dpoi_type))
468         {
469             dpo_copy(dpo_lb, &ctx.next_hops[0].path_dpo);
470             dpo_reset(&ctx.next_hops[0].path_dpo);
471             return;
472         }
473     }
474
475     if (!dpo_id_is_valid(dpo_lb))
476     {
477         /*
478          * first time create
479          */
480         if (esrc->fes_entry_flags & FIB_ENTRY_FLAG_MULTICAST)
481         {
482             dpo_set(dpo_lb,
483                     DPO_REPLICATE,
484                     lb_proto,
485                     MPLS_IS_REPLICATE | replicate_create(0, lb_proto));
486         }
487         else
488         {
489             flow_hash_config_t fhc;
490             fib_protocol_t fp;
491
492             /*
493              * if the protocol for the LB we are building does not match that
494              * of the fib_entry (i.e. we are build the [n]EOS LB for an IPv[46]
495              * then the fib_index is not an index that relates to the table
496              * type we need. So get the default flow-hash config instead.
497              */
498             fp = dpo_proto_to_fib(lb_proto);
499
500             if (fib_entry->fe_prefix.fp_proto != fp)
501             {
502                 fhc = fib_table_get_default_flow_hash_config(fp);
503             }
504             else
505             {
506                 fhc = fib_table_get_flow_hash_config(fib_entry->fe_fib_index, fp);
507             }
508             dpo_set(dpo_lb,
509                     DPO_LOAD_BALANCE,
510                     lb_proto,
511                     load_balance_create(0, lb_proto, fhc));
512         }
513     }
514
515     if (esrc->fes_entry_flags & FIB_ENTRY_FLAG_MULTICAST)
516     {
517         /*
518          * MPLS multicast
519          */
520         replicate_multipath_update(dpo_lb, ctx.next_hops);
521     }
522     else
523     {
524         load_balance_multipath_update(dpo_lb,
525                                       ctx.next_hops,
526                                       fib_entry_calc_lb_flags(&ctx));
527         vec_free(ctx.next_hops);
528
529         /*
530          * if this entry is sourced by the uRPF-exempt source then we
531          * append the always present local0 interface (index 0) to the
532          * uRPF list so it is not empty. that way packets pass the loose check.
533          */
534         index_t ui = fib_path_list_get_urpf(esrc->fes_pl);
535
536         if ((fib_entry_is_sourced(fib_entry_get_index(fib_entry),
537                                   FIB_SOURCE_URPF_EXEMPT) ||
538              (esrc->fes_entry_flags & FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT))&&
539             (0 == fib_urpf_check_size(ui)))
540         {
541             /*
542              * The uRPF list we get from the path-list is shared by all
543              * other users of the list, but the uRPF exemption applies
544              * only to this prefix. So we need our own list.
545              */
546             ui = fib_urpf_list_alloc_and_lock();
547             fib_urpf_list_append(ui, 0);
548             fib_urpf_list_bake(ui);
549             load_balance_set_urpf(dpo_lb->dpoi_index, ui);
550             fib_urpf_list_unlock(ui);
551         }
552         else
553         {
554             load_balance_set_urpf(dpo_lb->dpoi_index, ui);
555         }
556         load_balance_set_fib_entry_flags(dpo_lb->dpoi_index,
557                                          fib_entry_get_flags_i(fib_entry));
558     }
559 }
560
561 void
562 fib_entry_src_action_install (fib_entry_t *fib_entry,
563                               fib_source_t source)
564 {
565     /*
566      * Install the forwarding chain for the given source into the forwarding
567      * tables
568      */
569     fib_forward_chain_type_t fct;
570     fib_entry_src_t *esrc;
571     int insert;
572
573     fct = fib_entry_get_default_chain_type(fib_entry);
574     esrc = fib_entry_src_find(fib_entry, source, NULL);
575
576     /*
577      * Every entry has its own load-balance object. All changes to the entry's
578      * forwarding result in an inplace modify of the load-balance. This means
579      * the load-balance object only needs to be added to the forwarding
580      * DB once, when it is created.
581      */
582     insert = !dpo_id_is_valid(&fib_entry->fe_lb);
583
584     fib_entry_src_mk_lb(fib_entry, esrc, fct, &fib_entry->fe_lb);
585
586     ASSERT(dpo_id_is_valid(&fib_entry->fe_lb));
587     FIB_ENTRY_DBG(fib_entry, "install: %d", fib_entry->fe_lb);
588
589     /*
590      * insert the adj into the data-plane forwarding trie
591      */
592     if (insert)
593     {
594        fib_table_fwding_dpo_update(fib_entry->fe_fib_index,
595                                    &fib_entry->fe_prefix,
596                                    &fib_entry->fe_lb);
597     }
598
599     /*
600      * if any of the other chain types are already created they will need
601      * updating too
602      */
603     fib_entry_delegate_type_t fdt;
604     fib_entry_delegate_t *fed;
605
606     FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
607     {
608         fib_entry_src_mk_lb(fib_entry, esrc,
609                             fib_entry_delegate_type_to_chain_type(fdt),
610                             &fed->fd_dpo);
611     });
612 }
613
614 void
615 fib_entry_src_action_uninstall (fib_entry_t *fib_entry)
616 {
617     /*
618      * uninstall the forwarding chain from the forwarding tables
619      */
620     FIB_ENTRY_DBG(fib_entry, "uninstall: %d",
621                   fib_entry->fe_adj_index);
622
623     if (dpo_id_is_valid(&fib_entry->fe_lb))
624     {
625         fib_table_fwding_dpo_remove(
626             fib_entry->fe_fib_index,
627             &fib_entry->fe_prefix,
628             &fib_entry->fe_lb);
629
630         dpo_reset(&fib_entry->fe_lb);
631     }
632 }
633
634 static void
635 fib_entry_recursive_loop_detect_i (fib_node_index_t path_list_index)
636 {
637     fib_node_index_t *entries = NULL;
638
639     fib_path_list_recursive_loop_detect(path_list_index, &entries);
640
641     vec_free(entries);
642 }
643
644 void
645 fib_entry_src_action_activate (fib_entry_t *fib_entry,
646                                fib_source_t source)
647
648 {
649     int houston_we_are_go_for_install;
650     fib_entry_src_t *esrc;
651
652     esrc = fib_entry_src_find(fib_entry, source, NULL);
653
654     ASSERT(!(esrc->fes_flags & FIB_ENTRY_SRC_FLAG_ACTIVE));
655     ASSERT(esrc->fes_flags & FIB_ENTRY_SRC_FLAG_ADDED);
656
657     esrc->fes_flags |= FIB_ENTRY_SRC_FLAG_ACTIVE;
658
659     if (NULL != fib_entry_src_vft[source].fesv_activate)
660     {
661         houston_we_are_go_for_install =
662             fib_entry_src_vft[source].fesv_activate(esrc, fib_entry);
663     }
664     else
665     {
666         /*
667          * the source is not providing an activate function, we'll assume
668          * therefore it has no objection to installing the entry
669          */
670         houston_we_are_go_for_install = !0;
671     }
672
673     /*
674      * link to the path-list provided by the source, and go check
675      * if that forms any loops in the graph.
676      */
677     fib_entry->fe_parent = esrc->fes_pl;
678     fib_entry->fe_sibling =
679         fib_path_list_child_add(fib_entry->fe_parent,
680                                 FIB_NODE_TYPE_ENTRY,
681                                 fib_entry_get_index(fib_entry));
682
683     fib_entry_recursive_loop_detect_i(fib_entry->fe_parent);
684
685     FIB_ENTRY_DBG(fib_entry, "activate: %d",
686                   fib_entry->fe_parent);
687
688     if (0 != houston_we_are_go_for_install)
689     {
690         fib_entry_src_action_install(fib_entry, source);
691     }
692     else
693     {
694         fib_entry_src_action_uninstall(fib_entry);
695     }
696 }
697
698 void
699 fib_entry_src_action_deactivate (fib_entry_t *fib_entry,
700                                  fib_source_t source)
701
702 {
703     fib_node_index_t path_list_index;
704     fib_entry_src_t *esrc;
705
706     esrc = fib_entry_src_find(fib_entry, source, NULL);
707
708     ASSERT(esrc->fes_flags & FIB_ENTRY_SRC_FLAG_ACTIVE);
709
710     if (NULL != fib_entry_src_vft[source].fesv_deactivate)
711     {
712         fib_entry_src_vft[source].fesv_deactivate(esrc, fib_entry);
713     }
714
715     esrc->fes_flags &= ~FIB_ENTRY_SRC_FLAG_ACTIVE;
716
717     FIB_ENTRY_DBG(fib_entry, "deactivate: %d", fib_entry->fe_parent);
718
719     /*
720      * un-link from an old path-list. Check for any loops this will clear
721      */
722     path_list_index = fib_entry->fe_parent;
723     fib_entry->fe_parent = FIB_NODE_INDEX_INVALID;
724
725     fib_entry_recursive_loop_detect_i(path_list_index);
726
727     /*
728      * this will unlock the path-list, so it may be invalid thereafter.
729      */
730     fib_path_list_child_remove(path_list_index, fib_entry->fe_sibling);
731     fib_entry->fe_sibling = FIB_NODE_INDEX_INVALID;
732 }
733
734 static void
735 fib_entry_src_action_fwd_update (const fib_entry_t *fib_entry,
736                                  fib_source_t source)
737 {
738     fib_entry_src_t *esrc;
739
740     vec_foreach(esrc, fib_entry->fe_srcs)
741     {
742         if (NULL != fib_entry_src_vft[esrc->fes_src].fesv_fwd_update)
743         {
744             fib_entry_src_vft[esrc->fes_src].fesv_fwd_update(esrc,
745                                                              fib_entry,
746                                                              source);
747         }
748     }
749 }
750
751 void
752 fib_entry_src_action_reactivate (fib_entry_t *fib_entry,
753                                  fib_source_t source)
754 {
755     fib_node_index_t path_list_index;
756     fib_entry_src_t *esrc;
757
758     esrc = fib_entry_src_find(fib_entry, source, NULL);
759
760     ASSERT(esrc->fes_flags & FIB_ENTRY_SRC_FLAG_ACTIVE);
761
762     FIB_ENTRY_DBG(fib_entry, "reactivate: %d to %d",
763                   fib_entry->fe_parent,
764                   esrc->fes_pl);
765
766     if (fib_entry->fe_parent != esrc->fes_pl)
767     {
768         /*
769          * un-link from an old path-list. Check for any loops this will clear
770          */
771         path_list_index = fib_entry->fe_parent;
772         fib_entry->fe_parent = FIB_NODE_INDEX_INVALID;
773
774         /*
775          * temporary lock so it doesn't get deleted when this entry is no
776          * longer a child.
777          */
778         fib_path_list_lock(path_list_index);
779
780         /*
781          * this entry is no longer a child. after unlinking check if any loops
782          * were broken
783          */
784         fib_path_list_child_remove(path_list_index,
785                                    fib_entry->fe_sibling);
786
787         fib_entry_recursive_loop_detect_i(path_list_index);
788
789         /*
790          * link to the path-list provided by the source, and go check
791          * if that forms any loops in the graph.
792          */
793         fib_entry->fe_parent = esrc->fes_pl;
794         fib_entry->fe_sibling =
795             fib_path_list_child_add(fib_entry->fe_parent,
796                                     FIB_NODE_TYPE_ENTRY,
797                                     fib_entry_get_index(fib_entry));
798
799         fib_entry_recursive_loop_detect_i(fib_entry->fe_parent);
800         fib_path_list_unlock(path_list_index);
801     }
802     fib_entry_src_action_install(fib_entry, source);
803     fib_entry_src_action_fwd_update(fib_entry, source);
804 }
805
806 void
807 fib_entry_src_action_installed (const fib_entry_t *fib_entry,
808                                 fib_source_t source)
809 {
810     fib_entry_src_t *esrc;
811
812     esrc = fib_entry_src_find(fib_entry, source, NULL);
813
814     if (NULL != fib_entry_src_vft[source].fesv_installed)
815     {
816         fib_entry_src_vft[source].fesv_installed(esrc,
817                                                  fib_entry);
818     }
819
820     fib_entry_src_action_fwd_update(fib_entry, source);
821 }
822
823 /*
824  * fib_entry_src_action_add
825  *
826  * Adding a source can result in a new fib_entry being created, which
827  * can inturn mean the pool is realloc'd and thus the entry passed as
828  * an argument it also realloc'd
829  * @return the original entry
830  */
831 fib_entry_t *
832 fib_entry_src_action_add (fib_entry_t *fib_entry,
833                           fib_source_t source,
834                           fib_entry_flag_t flags,
835                           const dpo_id_t *dpo)
836 {
837     fib_node_index_t fib_entry_index;
838     fib_entry_src_t *esrc;
839
840     esrc = fib_entry_src_find_or_create(fib_entry, source, NULL);
841
842     esrc->fes_ref_count++;
843
844     if (1 != esrc->fes_ref_count)
845     {
846         /*
847          * we only want to add the source on the 0->1 transition
848          */
849         return (fib_entry);
850     }
851
852     esrc->fes_entry_flags = flags;
853
854     /*
855      * save variable so we can recover from a fib_entry realloc.
856      */
857     fib_entry_index = fib_entry_get_index(fib_entry);
858
859     if (NULL != fib_entry_src_vft[source].fesv_add)
860     {
861         fib_entry_src_vft[source].fesv_add(esrc,
862                                            fib_entry,
863                                            flags,
864                                            fib_entry_get_proto(fib_entry),
865                                            dpo);
866     }
867
868     fib_entry = fib_entry_get(fib_entry_index);
869
870     esrc->fes_flags |= FIB_ENTRY_SRC_FLAG_ADDED;
871
872     fib_path_list_lock(esrc->fes_pl);
873
874     /*
875      * the source owns a lock on the entry
876      */
877     fib_entry_lock(fib_entry_get_index(fib_entry));
878
879     return (fib_entry);
880 }
881
882 /*
883  * fib_entry_src_action_update
884  *
885  * Adding a source can result in a new fib_entry being created, which
886  * can inturn mean the pool is realloc'd and thus the entry passed as
887  * an argument it also realloc'd
888  * @return the original entry
889  */
890 fib_entry_t *
891 fib_entry_src_action_update (fib_entry_t *fib_entry,
892                              fib_source_t source,
893                              fib_entry_flag_t flags,
894                              const dpo_id_t *dpo)
895 {
896     fib_node_index_t fib_entry_index, old_path_list_index;
897     fib_entry_src_t *esrc;
898
899     esrc = fib_entry_src_find_or_create(fib_entry, source, NULL);
900
901     if (NULL == esrc)
902         return (fib_entry_src_action_add(fib_entry, source, flags, dpo));
903
904     old_path_list_index = esrc->fes_pl;
905     esrc->fes_entry_flags = flags;
906
907     /*
908      * save variable so we can recover from a fib_entry realloc.
909      */
910     fib_entry_index = fib_entry_get_index(fib_entry);
911
912     if (NULL != fib_entry_src_vft[source].fesv_add)
913     {
914         fib_entry_src_vft[source].fesv_add(esrc,
915                                            fib_entry,
916                                            flags,
917                                            fib_entry_get_proto(fib_entry),
918                                            dpo);
919     }
920
921     fib_entry = fib_entry_get(fib_entry_index);
922
923     esrc->fes_flags |= FIB_ENTRY_SRC_FLAG_ADDED;
924
925     fib_path_list_lock(esrc->fes_pl);
926     fib_path_list_unlock(old_path_list_index);
927
928     return (fib_entry);
929 }
930
931
932 fib_entry_src_flag_t
933 fib_entry_src_action_remove (fib_entry_t *fib_entry,
934                              fib_source_t source)
935
936 {
937     fib_node_index_t old_path_list;
938     fib_entry_src_flag_t sflags;
939     fib_entry_src_t *esrc;
940
941     esrc = fib_entry_src_find(fib_entry, source, NULL);
942
943     if (NULL == esrc)
944         return (FIB_ENTRY_SRC_FLAG_ACTIVE);
945
946     esrc->fes_ref_count--;
947     sflags = esrc->fes_flags;
948
949     if (0 != esrc->fes_ref_count)
950     {
951         /*
952          * only remove the source on the 1->0 transisition
953          */
954         return (sflags);
955     }
956
957     if (esrc->fes_flags & FIB_ENTRY_SRC_FLAG_ACTIVE)
958     {
959         fib_entry_src_action_deactivate(fib_entry, source);
960     }
961
962     old_path_list = esrc->fes_pl;
963
964     if (NULL != fib_entry_src_vft[source].fesv_remove)
965     {
966         fib_entry_src_vft[source].fesv_remove(esrc);
967     }
968
969     fib_path_list_unlock(old_path_list);
970     fib_entry_unlock(fib_entry_get_index(fib_entry));
971
972     sflags &= ~FIB_ENTRY_SRC_FLAG_ADDED;
973     fib_entry_src_action_deinit(fib_entry, source);
974
975     return (sflags);
976 }
977
978 /*
979  * fib_route_attached_cross_table
980  *
981  * Return true the the route is attached via an interface that
982  * is not in the same table as the route
983  */
984 static inline int
985 fib_route_attached_cross_table (const fib_entry_t *fib_entry,
986                                 const fib_route_path_t *rpath)
987 {
988     /*
989      * - All zeros next-hop
990      * - a valid interface
991      * - entry's fib index not equeal to interface's index
992      */
993     if (ip46_address_is_zero(&rpath->frp_addr) &&
994         (~0 != rpath->frp_sw_if_index) &&
995         (fib_entry->fe_fib_index != 
996          fib_table_get_index_for_sw_if_index(fib_entry_get_proto(fib_entry),
997                                              rpath->frp_sw_if_index)))
998     {
999         return (!0);
1000     }
1001     return (0);
1002 }
1003
1004 /*
1005  * fib_route_attached_cross_table
1006  *
1007  * Return true the the route is attached via an interface that
1008  * is not in the same table as the route
1009  */
1010 static inline int
1011 fib_path_is_attached (const fib_route_path_t *rpath)
1012 {
1013     /*
1014      * - All zeros next-hop
1015      * - a valid interface
1016      */
1017     if (ip46_address_is_zero(&rpath->frp_addr) &&
1018         (~0 != rpath->frp_sw_if_index))
1019     {
1020         return (!0);
1021     }
1022     else if (rpath->frp_flags & FIB_ROUTE_PATH_ATTACHED)
1023     {
1024         return (!0);
1025     }
1026     return (0);
1027 }
1028
1029 fib_path_list_flags_t
1030 fib_entry_src_flags_2_path_list_flags (fib_entry_flag_t eflags)
1031 {
1032     fib_path_list_flags_t plf = FIB_PATH_LIST_FLAG_NONE;
1033
1034     if (eflags & FIB_ENTRY_FLAG_DROP)
1035     {
1036         plf |= FIB_PATH_LIST_FLAG_DROP;
1037     }
1038     if (eflags & FIB_ENTRY_FLAG_EXCLUSIVE)
1039     {
1040         plf |= FIB_PATH_LIST_FLAG_EXCLUSIVE;
1041     }
1042     if (eflags & FIB_ENTRY_FLAG_LOCAL)
1043     {
1044         plf |= FIB_PATH_LIST_FLAG_LOCAL;
1045     }
1046
1047     return (plf);
1048 }
1049
1050 static void
1051 fib_entry_flags_update (const fib_entry_t *fib_entry,
1052                         const fib_route_path_t *rpath,
1053                         fib_path_list_flags_t *pl_flags,
1054                         fib_entry_src_t *esrc)
1055 {
1056     if ((esrc->fes_src == FIB_SOURCE_API) ||
1057         (esrc->fes_src == FIB_SOURCE_CLI))
1058     {
1059         if (fib_path_is_attached(rpath))
1060         {
1061             esrc->fes_entry_flags |= FIB_ENTRY_FLAG_ATTACHED;
1062         }
1063         else
1064         {
1065             esrc->fes_entry_flags &= ~FIB_ENTRY_FLAG_ATTACHED;
1066         }
1067     }
1068     if (fib_route_attached_cross_table(fib_entry, rpath))
1069     {
1070         esrc->fes_entry_flags |= FIB_ENTRY_FLAG_IMPORT;
1071     }
1072     else
1073     {
1074         esrc->fes_entry_flags &= ~FIB_ENTRY_FLAG_IMPORT;
1075     }
1076 }
1077
1078 /*
1079  * fib_entry_src_action_add
1080  *
1081  * Adding a source can result in a new fib_entry being created, which
1082  * can inturn mean the pool is realloc'd and thus the entry passed as
1083  * an argument it also realloc'd
1084  * @return the entry
1085  */
1086 fib_entry_t*
1087 fib_entry_src_action_path_add (fib_entry_t *fib_entry,
1088                                fib_source_t source,
1089                                fib_entry_flag_t flags,
1090                                const fib_route_path_t *rpath)
1091 {
1092     fib_node_index_t old_path_list, fib_entry_index;
1093     fib_path_list_flags_t pl_flags;
1094     fib_entry_src_t *esrc;
1095
1096     /*
1097      * save variable so we can recover from a fib_entry realloc.
1098      */
1099     fib_entry_index = fib_entry_get_index(fib_entry);
1100
1101     esrc = fib_entry_src_find(fib_entry, source, NULL);
1102     if (NULL == esrc)
1103     {
1104         fib_entry =
1105             fib_entry_src_action_add(fib_entry,
1106                                      source,
1107                                      flags,
1108                                      drop_dpo_get(
1109                                          fib_proto_to_dpo(
1110                                              fib_entry_get_proto(fib_entry))));
1111         esrc = fib_entry_src_find(fib_entry, source, NULL);
1112     }
1113
1114     /*
1115      * we are no doubt modifying a path-list. If the path-list
1116      * is shared, and hence not modifiable, then the index returned
1117      * will be for a different path-list. This FIB entry to needs
1118      * to maintain its lock appropriately.
1119      */
1120     old_path_list = esrc->fes_pl;
1121
1122     ASSERT(NULL != fib_entry_src_vft[source].fesv_path_add);
1123
1124     pl_flags = fib_entry_src_flags_2_path_list_flags(fib_entry_get_flags_i(fib_entry));
1125     fib_entry_flags_update(fib_entry, rpath, &pl_flags, esrc);
1126
1127     fib_entry_src_vft[source].fesv_path_add(esrc, fib_entry, pl_flags, rpath);
1128     fib_entry = fib_entry_get(fib_entry_index);
1129
1130     fib_path_list_lock(esrc->fes_pl);
1131     fib_path_list_unlock(old_path_list);
1132
1133     return (fib_entry);
1134 }
1135
1136 /*
1137  * fib_entry_src_action_swap
1138  *
1139  * The source is providing new paths to replace the old ones.
1140  * Adding a source can result in a new fib_entry being created, which
1141  * can inturn mean the pool is realloc'd and thus the entry passed as
1142  * an argument it also realloc'd
1143  * @return the entry
1144  */
1145 fib_entry_t*
1146 fib_entry_src_action_path_swap (fib_entry_t *fib_entry,
1147                                 fib_source_t source,
1148                                 fib_entry_flag_t flags,                         
1149                                 const fib_route_path_t *rpaths)
1150 {
1151     fib_node_index_t old_path_list, fib_entry_index;
1152     fib_path_list_flags_t pl_flags;
1153     const fib_route_path_t *rpath;
1154     fib_entry_src_t *esrc;
1155
1156     esrc = fib_entry_src_find(fib_entry, source, NULL);
1157
1158     /*
1159      * save variable so we can recover from a fib_entry realloc.
1160      */
1161     fib_entry_index = fib_entry_get_index(fib_entry);
1162
1163     if (NULL == esrc)
1164     {
1165         fib_entry = fib_entry_src_action_add(fib_entry,
1166                                              source,
1167                                              flags,
1168                                              drop_dpo_get(
1169                                                  fib_proto_to_dpo(
1170                                                      fib_entry_get_proto(fib_entry))));
1171         esrc = fib_entry_src_find(fib_entry, source, NULL);
1172     }
1173
1174     /*
1175      * swapping paths may create a new path-list (or may use an existing shared)
1176      * but we are certainly getting a different one. This FIB entry to needs
1177      * to maintain its lock appropriately.
1178      */
1179     old_path_list = esrc->fes_pl;
1180
1181     ASSERT(NULL != fib_entry_src_vft[source].fesv_path_swap);
1182
1183     pl_flags = fib_entry_src_flags_2_path_list_flags(flags);
1184
1185     vec_foreach(rpath, rpaths)
1186     {
1187         fib_entry_flags_update(fib_entry, rpath, &pl_flags, esrc);
1188     }
1189
1190     fib_entry_src_vft[source].fesv_path_swap(esrc,
1191                                              fib_entry,
1192                                              pl_flags,
1193                                              rpaths);
1194
1195     fib_entry = fib_entry_get(fib_entry_index);
1196
1197     fib_path_list_lock(esrc->fes_pl);
1198     fib_path_list_unlock(old_path_list);
1199
1200     return (fib_entry);
1201 }
1202
1203 fib_entry_src_flag_t
1204 fib_entry_src_action_path_remove (fib_entry_t *fib_entry,
1205                                   fib_source_t source,
1206                                   const fib_route_path_t *rpath)
1207 {
1208     fib_path_list_flags_t pl_flags;
1209     fib_node_index_t old_path_list;
1210     fib_entry_src_t *esrc;
1211
1212     esrc = fib_entry_src_find(fib_entry, source, NULL);
1213
1214     ASSERT(NULL != esrc);
1215     ASSERT(esrc->fes_flags & FIB_ENTRY_SRC_FLAG_ADDED);
1216
1217     /*
1218      * we no doubt modifying a path-list. If the path-list
1219      * is shared, and hence not modifiable, then the index returned
1220      * will be for a different path-list. This FIB entry to needs
1221      * to maintain its lock appropriately.
1222      */
1223     old_path_list = esrc->fes_pl;
1224
1225     ASSERT(NULL != fib_entry_src_vft[source].fesv_path_remove);
1226
1227     pl_flags = fib_entry_src_flags_2_path_list_flags(fib_entry_get_flags_i(fib_entry));
1228     fib_entry_flags_update(fib_entry, rpath, &pl_flags, esrc);
1229
1230     fib_entry_src_vft[source].fesv_path_remove(esrc, pl_flags, rpath);
1231
1232     /*
1233      * lock the new path-list, unlock the old if it had one
1234      */
1235     fib_path_list_unlock(old_path_list);
1236
1237     if (FIB_NODE_INDEX_INVALID != esrc->fes_pl) {
1238         fib_path_list_lock(esrc->fes_pl);
1239         return (FIB_ENTRY_SRC_FLAG_ADDED);
1240     }
1241     else
1242     {
1243         /*
1244          * no more paths left from this source
1245          */
1246         fib_entry_src_action_remove(fib_entry, source);
1247         return (FIB_ENTRY_SRC_FLAG_NONE);
1248     }
1249 }
1250
1251 u8*
1252 fib_entry_src_format (fib_entry_t *fib_entry,
1253                       fib_source_t source,
1254                       u8* s)
1255 {
1256     fib_entry_src_t *esrc;
1257
1258     esrc = fib_entry_src_find(fib_entry, source, NULL);
1259
1260     if (NULL != fib_entry_src_vft[source].fesv_format)
1261     {
1262         return (fib_entry_src_vft[source].fesv_format(esrc, s));
1263     }
1264     return (s);
1265 }
1266
1267 adj_index_t
1268 fib_entry_get_adj_for_source (fib_node_index_t fib_entry_index,
1269                               fib_source_t source)
1270 {
1271     fib_entry_t *fib_entry;
1272     fib_entry_src_t *esrc;
1273
1274     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
1275         return (ADJ_INDEX_INVALID);
1276
1277     fib_entry = fib_entry_get(fib_entry_index);
1278     esrc = fib_entry_src_find(fib_entry, source, NULL);
1279
1280     if (NULL != esrc)
1281     {
1282         if (FIB_NODE_INDEX_INVALID != esrc->fes_pl)
1283         {
1284             return (fib_path_list_get_adj(
1285                         esrc->fes_pl,
1286                         fib_entry_get_default_chain_type(fib_entry)));
1287         }
1288     }
1289     return (ADJ_INDEX_INVALID);
1290 }
1291
1292 const int
1293 fib_entry_get_dpo_for_source (fib_node_index_t fib_entry_index,
1294                               fib_source_t source,
1295                               dpo_id_t *dpo)
1296 {
1297     fib_entry_t *fib_entry;
1298     fib_entry_src_t *esrc;
1299
1300     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
1301         return (0);
1302
1303     fib_entry = fib_entry_get(fib_entry_index);
1304     esrc = fib_entry_src_find(fib_entry, source, NULL);
1305
1306     if (NULL != esrc)
1307     {
1308         if (FIB_NODE_INDEX_INVALID != esrc->fes_pl)
1309         {
1310             fib_path_list_contribute_forwarding(
1311                 esrc->fes_pl,
1312                 fib_entry_get_default_chain_type(fib_entry),
1313                 dpo);
1314
1315             return (dpo_id_is_valid(dpo));
1316         }
1317     }
1318     return (0);
1319 }
1320
1321 u32
1322 fib_entry_get_resolving_interface_for_source (fib_node_index_t entry_index,
1323                                               fib_source_t source)
1324 {
1325     fib_entry_t *fib_entry;
1326     fib_entry_src_t *esrc;
1327
1328     fib_entry = fib_entry_get(entry_index);
1329
1330     esrc = fib_entry_src_find(fib_entry, source, NULL);
1331
1332     if (NULL != esrc)
1333     {
1334         if (FIB_NODE_INDEX_INVALID != esrc->fes_pl)
1335         {
1336             return (fib_path_list_get_resolving_interface(esrc->fes_pl));
1337         }
1338     }
1339     return (~0);
1340 }
1341
1342 fib_entry_flag_t
1343 fib_entry_get_flags_for_source (fib_node_index_t entry_index,
1344                                 fib_source_t source)
1345 {
1346     fib_entry_t *fib_entry;
1347     fib_entry_src_t *esrc;
1348
1349     fib_entry = fib_entry_get(entry_index);
1350
1351     esrc = fib_entry_src_find(fib_entry, source, NULL);
1352
1353     if (NULL != esrc)
1354     {
1355         return (esrc->fes_entry_flags);
1356     }
1357
1358     return (FIB_ENTRY_FLAG_NONE);
1359 }
1360
1361 fib_entry_flag_t
1362 fib_entry_get_flags_i (const fib_entry_t *fib_entry)
1363 {
1364     fib_entry_flag_t flags;
1365
1366     /*
1367      * the vector of sources is deliberately arranged in priority order
1368      */
1369     if (0 == vec_len(fib_entry->fe_srcs))
1370     {
1371         flags = FIB_ENTRY_FLAG_NONE;
1372     }
1373     else
1374     {
1375         fib_entry_src_t *esrc;
1376
1377         esrc = vec_elt_at_index(fib_entry->fe_srcs, 0);
1378         flags = esrc->fes_entry_flags;
1379     }
1380
1381     return (flags);
1382 }
1383
1384 void
1385 fib_entry_set_source_data (fib_node_index_t fib_entry_index,
1386                            fib_source_t source,
1387                            const void *data)
1388 {
1389     fib_entry_t *fib_entry;
1390     fib_entry_src_t *esrc;
1391
1392     fib_entry = fib_entry_get(fib_entry_index);
1393     esrc = fib_entry_src_find(fib_entry, source, NULL);
1394
1395     if (NULL != esrc &&
1396         NULL != fib_entry_src_vft[source].fesv_set_data)
1397     {
1398         fib_entry_src_vft[source].fesv_set_data(esrc, fib_entry, data);
1399     }
1400 }
1401
1402 const void*
1403 fib_entry_get_source_data (fib_node_index_t fib_entry_index,
1404                            fib_source_t source)
1405 {
1406     fib_entry_t *fib_entry;
1407     fib_entry_src_t *esrc;
1408
1409     fib_entry = fib_entry_get(fib_entry_index);
1410     esrc = fib_entry_src_find(fib_entry, source, NULL);
1411
1412     if (NULL != esrc &&
1413         NULL != fib_entry_src_vft[source].fesv_get_data)
1414     {
1415         return (fib_entry_src_vft[source].fesv_get_data(esrc, fib_entry));
1416     }
1417     return (NULL);
1418 }
1419
1420 void
1421 fib_entry_src_module_init (void)
1422 {
1423     fib_entry_src_rr_register();
1424     fib_entry_src_interface_register();
1425     fib_entry_src_default_route_register();
1426     fib_entry_src_special_register();
1427     fib_entry_src_api_register();
1428     fib_entry_src_adj_register();
1429     fib_entry_src_mpls_register();
1430     fib_entry_src_lisp_register();
1431 }