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