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