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