a75d5c9cf8cb6e336991074f2cc2740b113e63ee
[vpp.git] / vnet / vnet / fib / fib_entry.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 <vlib/vlib.h>
17 #include <vnet/ip/format.h>
18 #include <vnet/ip/lookup.h>
19 #include <vnet/adj/adj.h>
20 #include <vnet/dpo/load_balance.h>
21 #include <vnet/dpo/drop_dpo.h>
22
23 #include <vnet/fib/fib_entry.h>
24 #include <vnet/fib/fib_walk.h>
25 #include <vnet/fib/fib_entry_src.h>
26 #include <vnet/fib/fib_entry_cover.h>
27 #include <vnet/fib/fib_table.h>
28 #include <vnet/fib/fib_internal.h>
29 #include <vnet/fib/fib_attached_export.h>
30 #include <vnet/fib/fib_path_ext.h>
31
32 /*
33  * Array of strings/names for the FIB sources
34  */
35 static const char *fib_source_names[] = FIB_SOURCES;
36 static const char *fib_attribute_names[] = FIB_ENTRY_ATTRIBUTES;
37
38 /*
39  * Pool for all fib_entries
40  */
41 static fib_entry_t *fib_entry_pool;
42
43 fib_entry_t *
44 fib_entry_get (fib_node_index_t index)
45 {
46     return (pool_elt_at_index(fib_entry_pool, index));
47 }
48
49 static fib_node_t *
50 fib_entry_get_node (fib_node_index_t index)
51 {
52     return ((fib_node_t*)fib_entry_get(index));
53 }
54
55 fib_node_index_t
56 fib_entry_get_index (const fib_entry_t * fib_entry)
57 {
58     return (fib_entry - fib_entry_pool);
59 }
60
61 static fib_protocol_t
62 fib_entry_get_proto (const fib_entry_t * fib_entry)
63 {
64     return (fib_entry->fe_prefix.fp_proto);
65 }
66
67 /**
68  * @brief Turn the chain type requested by the client into the one they
69  * really wanted
70  */
71 static fib_forward_chain_type_t
72 fib_entry_chain_type_fixup (const fib_entry_t *entry,
73                             fib_forward_chain_type_t fct)
74 {
75     if (FIB_FORW_CHAIN_TYPE_MPLS_EOS == fct)
76     {
77         /*
78          * The EOS chain is a tricky since one cannot know the adjacency
79          * to link to without knowing what the packets payload protocol
80          * will be once the label is popped.
81          */
82         fib_forward_chain_type_t dfct;
83
84         dfct = fib_entry_get_default_chain_type(entry);
85
86         if (FIB_FORW_CHAIN_TYPE_MPLS_EOS == dfct)
87         {
88             /*
89              * If the entry being asked is a eos-MPLS label entry,
90              * then use the payload-protocol field, that we stashed there
91              * for just this purpose
92              */
93             return (fib_forw_chain_type_from_dpo_proto(
94                         entry->fe_prefix.fp_payload_proto));
95         }
96         /*
97          * else give them what this entry would be by default. i.e. if it's a v6
98          * entry, then the label its local labelled should be carrying v6 traffic.
99          * If it's a non-EOS label entry, then there are more labels and we want
100          * a non-eos chain.
101          */
102         return (dfct);
103     }
104
105     return (fct);
106 }
107
108 fib_forward_chain_type_t
109 fib_entry_get_default_chain_type (const fib_entry_t *fib_entry)
110 {
111     switch (fib_entry->fe_prefix.fp_proto)
112     {
113     case FIB_PROTOCOL_IP4:
114         return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
115     case FIB_PROTOCOL_IP6:
116         return (FIB_FORW_CHAIN_TYPE_UNICAST_IP6);
117     case FIB_PROTOCOL_MPLS:
118         if (MPLS_EOS == fib_entry->fe_prefix.fp_eos)
119             /*
120              * If the entry being asked is a eos-MPLS label entry,
121              * then use the payload-protocol field, that we stashed there
122              * for just this purpose
123              */
124             return (fib_forw_chain_type_from_dpo_proto(
125                         fib_entry->fe_prefix.fp_payload_proto));
126         else
127             return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS);
128     }
129
130     return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
131 }
132
133 u8 *
134 format_fib_entry (u8 * s, va_list * args)
135 {
136     fib_forward_chain_type_t fct;
137     fib_entry_attribute_t attr;
138     fib_path_ext_t *path_ext;
139     fib_entry_t *fib_entry;
140     fib_entry_src_t *src;
141     fib_node_index_t fei;
142     fib_source_t source;
143     u32 n_covered;
144     int level;
145
146     fei = va_arg (*args, fib_node_index_t);
147     level = va_arg (*args, int);
148     fib_entry = fib_entry_get(fei);
149
150     s = format (s, "%U", format_fib_prefix, &fib_entry->fe_prefix);
151
152     if (level >= FIB_ENTRY_FORMAT_DETAIL)
153     {
154         s = format (s, " fib:%d", fib_entry->fe_fib_index);
155         s = format (s, " index:%d", fib_entry_get_index(fib_entry));
156         s = format (s, " locks:%d", fib_entry->fe_node.fn_locks);
157
158         FOR_EACH_SRC_ADDED(fib_entry, src, source,
159         ({
160             s = format (s, "\n  src:%s ",
161                         fib_source_names[source]);
162             s = fib_entry_src_format(fib_entry, source, s);
163             s = format (s, " refs:%d ", src->fes_ref_count);
164             if (FIB_ENTRY_FLAG_NONE != src->fes_entry_flags) {
165                 s = format(s, "flags:");
166                 FOR_EACH_FIB_ATTRIBUTE(attr) {
167                     if ((1<<attr) & src->fes_entry_flags) {
168                         s = format (s, "%s,", fib_attribute_names[attr]);
169                     }
170                 }
171             }
172             s = format (s, "\n");
173             if (FIB_NODE_INDEX_INVALID != src->fes_pl)
174             {
175                 s = fib_path_list_format(src->fes_pl, s);
176             }
177             if (NULL != src->fes_path_exts)
178             {
179                 s = format(s, "    Extensions:");
180                 vec_foreach(path_ext, src->fes_path_exts)
181                 {
182                     s = format(s, "\n     %U", format_fib_path_ext, path_ext);
183                 }
184             }
185         }));
186     
187         n_covered = fib_entry_cover_get_size(fib_entry);
188         if (n_covered > 0) {
189             s = format(s, "\n tracking %d covered: ", n_covered);
190             s = fib_entry_cover_list_format(fib_entry, s);
191         }
192         s = fib_ae_import_format(fib_entry->fe_import, s);
193         s = fib_ae_export_format(fib_entry->fe_export, s);
194
195         s = format (s, "\n forwarding: ");
196     }
197     else
198     {
199         s = format (s, "\n");
200     }
201
202     fct = fib_entry_get_default_chain_type(fib_entry);
203
204     if (!dpo_id_is_valid(&fib_entry->fe_lb[fct]))
205     {
206         s = format (s, "  UNRESOLVED\n");
207         return (s);
208     }
209     else
210     {
211         if (level >= FIB_ENTRY_FORMAT_DETAIL2)
212         {
213
214             FOR_EACH_FIB_FORW_CHAIN(fct)
215             {
216                 s = format(s, "  %U-chain\n  %U",
217                            format_fib_forw_chain_type, fct,
218                            format_dpo_id,
219                            &fib_entry->fe_lb[fct],
220                            2);
221                 s = format(s, "\n");
222             }
223         }
224         else
225         {
226             s = format(s, "  %U-chain\n  %U",
227                        format_fib_forw_chain_type, fct,
228                        format_dpo_id,
229                        &fib_entry->fe_lb[fct],
230                        2);
231             s = format(s, "\n");
232         }
233     }
234
235     if (level >= FIB_ENTRY_FORMAT_DETAIL2)
236     {
237         s = format(s, "\nchildren:");
238         s = fib_node_children_format(fib_entry->fe_node.fn_children, s);
239     }
240
241     /* adj = adj_get(fib_entry->fe_prefix.fp_proto, fib_entry->fe_adj_index); */
242
243     /* ip_multipath_next_hop_t * nhs, tmp_nhs[1]; */
244     /* u32 i, j, n_left, n_nhs; */
245     /* vlib_counter_t c, sum; */
246     /* ip_lookup_main_t *lm = fib_get_lookup_main(fib_entry->fe_prefix.fp_proto); */
247
248     /* if (adj->n_adj == 1) */
249     /* { */
250     /*  nhs = &tmp_nhs[0]; */
251     /*  nhs[0].next_hop_adj_index = ~0; /\* not used *\/ */
252     /*  nhs[0].weight = 1; */
253     /*  n_nhs = 1; */
254     /* } */
255     /* else */
256     /* { */
257     /*  ip_multipath_adjacency_t * madj; */
258     /*  madj = vec_elt_at_index (lm->multipath_adjacencies, adj->heap_handle); */
259     /*  nhs = heap_elt_at_index (lm->next_hop_heap, madj->normalized_next_hops.heap_offset); */
260     /*  n_nhs = madj->normalized_next_hops.count; */
261     /* } */
262
263     /* n_left = nhs[0].weight; */
264     /* vlib_counter_zero (&sum); */
265     /* for (i = j = 0; i < adj->n_adj; i++) */
266     /* { */
267     /*  n_left -= 1; */
268     /*  vlib_get_combined_counter(&lm->adjacency_counters,  */
269     /*                            fib_entry->fe_adj_index + i, */
270     /*                            &c); */
271     /*  /\* if (clear) *\/ */
272     /*  /\*     vlib_zero_combined_counter (&lm->adjacency_counters,  *\/ */
273     /*  /\*                             fib_entry->fe_adj_index + i); *\/ */
274
275     /*  vlib_counter_add (&sum, &c); */
276     /*  if (n_left == 0) */
277     /*  { */
278     /*      s = format (s, "%16Ld%16Ld ", sum.packets, sum.bytes); */
279     /*      s = format (s, "weight %d, index %d", */
280     /*                    nhs[j].weight, fib_entry->fe_adj_index + i); */
281
282     /*      if (adj->n_adj > 1) */
283     /*          s = format (s, ", multipath"); */
284
285     /*      s = format (s, "\n%U", */
286     /*                  format_ip_adjacency, */
287     /*                  vnet_get_main(), lm, fib_entry->fe_adj_index + i); */
288
289     /*      //   vlib_cli_output (vm, "%v", msg); */
290     /*      //vec_free (msg); */
291     /*  } */
292     /*  else */
293     /*  { */
294     /*      j++; */
295     /*      if (j < n_nhs) */
296     /*      { */
297     /*          n_left = nhs[j].weight; */
298     /*          vlib_counter_zero (&sum); */
299     /*      } */
300     /*  } */
301     /* } */
302
303     return (s);
304 }
305
306 static fib_entry_t*
307 fib_entry_from_fib_node (fib_node_t *node)
308 {
309 #if CLIB_DEBUG > 0
310     ASSERT(FIB_NODE_TYPE_ENTRY == node->fn_type);
311 #endif
312     return ((fib_entry_t*)node);
313 }
314
315 static void
316 fib_entry_last_lock_gone (fib_node_t *node)
317 {
318     fib_forward_chain_type_t fct;
319     fib_entry_t *fib_entry;
320
321     fib_entry = fib_entry_from_fib_node(node);
322
323     FOR_EACH_FIB_FORW_CHAIN(fct)
324     {
325         dpo_reset(&fib_entry->fe_lb[fct]);
326     }
327
328     FIB_ENTRY_DBG(fib_entry, "last-lock");
329
330     fib_node_deinit(&fib_entry->fe_node);
331     // FIXME -RR Backwalk
332     pool_put(fib_entry_pool, fib_entry);
333 }
334
335 static fib_entry_src_t*
336 fib_entry_get_best_src_i (const fib_entry_t *fib_entry)
337 {
338     fib_entry_src_t *bsrc;
339
340     /*
341      * the enum of sources is deliberately arranged in priority order
342      */
343     if (0 == vec_len(fib_entry->fe_srcs))
344     {
345         bsrc = NULL;
346     }
347     else
348     {
349         bsrc = vec_elt_at_index(fib_entry->fe_srcs, 0);
350     }
351
352     return (bsrc);
353 }
354
355 static fib_source_t
356 fib_entry_src_get_source (const fib_entry_src_t *esrc)
357 {
358     if (NULL != esrc)
359     {
360         return (esrc->fes_src);
361     }
362     return (FIB_SOURCE_MAX);
363 }
364
365 static fib_entry_flag_t
366 fib_entry_src_get_flags (const fib_entry_src_t *esrc)
367 {
368     if (NULL != esrc)
369     {
370         return (esrc->fes_entry_flags);
371     }
372     return (FIB_ENTRY_FLAG_NONE);
373 }
374
375 fib_entry_flag_t
376 fib_entry_get_flags (fib_node_index_t fib_entry_index)
377 {
378     return (fib_entry_get_flags_i(fib_entry_get(fib_entry_index)));
379 }
380
381 /*
382  * fib_entry_back_walk_notify
383  *
384  * A back walk has reach this entry.
385  */
386 static fib_node_back_walk_rc_t
387 fib_entry_back_walk_notify (fib_node_t *node,
388                             fib_node_back_walk_ctx_t *ctx)
389 {
390     fib_entry_t *fib_entry;
391
392     fib_entry = fib_entry_from_fib_node(node);
393
394     if (FIB_NODE_BW_REASON_FLAG_EVALUATE & ctx->fnbw_reason        ||
395         FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE & ctx->fnbw_reason      ||
396         FIB_NODE_BW_REASON_FLAG_INTERFACE_UP & ctx->fnbw_reason    ||
397         FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN & ctx->fnbw_reason  ||
398         FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE & ctx->fnbw_reason)
399     {
400         fib_entry_src_action_reactivate(fib_entry,
401                                         fib_entry_get_best_source(
402                                             fib_entry_get_index(fib_entry)));
403     }
404
405     if (FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE & ctx->fnbw_reason)
406     {
407         /*
408          * ADJ updates (complete<->incomplete) do not need to propagate to
409          * recursive entries.
410          * The only reason its needed as far back as here, is that the adj
411          * and the incomplete adj are a different DPO type, so the LBs need
412          * to re-stack.
413          */
414         return (FIB_NODE_BACK_WALK_CONTINUE);
415     }
416     else
417     {
418         /*
419          * all other walk types can be reclassifed to a re-evaluate to
420          * all recursive dependents.
421          * By reclassifying we ensure that should any of these walk types meet
422          * they can be merged.
423          */
424         ctx->fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE;
425
426         /*
427          * propagate the backwalk further if we haven't already reached the
428          * maximum depth.
429          */
430         fib_walk_sync(FIB_NODE_TYPE_ENTRY,
431                       fib_entry_get_index(fib_entry),
432                       ctx);
433     }
434
435     return (FIB_NODE_BACK_WALK_CONTINUE);
436 }
437
438 /*
439  * The FIB path-list's graph node virtual function table
440  */
441 static const fib_node_vft_t fib_entry_vft = {
442     .fnv_get = fib_entry_get_node,
443     .fnv_last_lock = fib_entry_last_lock_gone,
444     .fnv_back_walk = fib_entry_back_walk_notify,
445 };
446
447 /*
448  * fib_entry_contribute_forwarding
449  *
450  * Get an lock the forwarding information (DPO) contributed by the FIB entry.
451  */
452 void
453 fib_entry_contribute_forwarding (fib_node_index_t fib_entry_index,
454                                  fib_forward_chain_type_t type,
455                                  dpo_id_t *dpo)
456 {
457     fib_entry_t *fib_entry;
458
459     fib_entry = fib_entry_get(fib_entry_index);
460
461     /*
462      * these are not the droids you are looking for...
463      */
464     type = fib_entry_chain_type_fixup(fib_entry, type);
465
466     if (!dpo_id_is_valid(&fib_entry->fe_lb[type]))
467     {
468         /*
469          * on-demand create eos/non-eos.
470          * There is no on-demand delete because:
471          *   - memory versus complexity & reliability:
472          *      leaving unrequired [n]eos LB arounds wastes memory, cleaning
473          *      then up on the right trigger is more code. i favour the latter.
474          */
475         fib_entry_src_mk_lb(fib_entry,
476                             fib_entry_get_best_src_i(fib_entry),
477                             type,
478                             &fib_entry->fe_lb[type]);
479     }
480
481     dpo_copy(dpo, &fib_entry->fe_lb[type]);
482 }
483
484 const dpo_id_t *
485 fib_entry_contribute_ip_forwarding (fib_node_index_t fib_entry_index)
486 {
487     fib_entry_t *fib_entry;
488
489     fib_entry = fib_entry_get(fib_entry_index);
490
491     return (&fib_entry->fe_lb[fib_entry_get_default_chain_type(fib_entry)]);
492 }
493
494 adj_index_t
495 fib_entry_get_adj (fib_node_index_t fib_entry_index)
496 {
497     const dpo_id_t *dpo;
498
499     dpo = fib_entry_contribute_ip_forwarding(fib_entry_index);
500     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
501
502     if (dpo_is_adj(dpo))
503     {
504         return (dpo->dpoi_index);
505     }
506     return (ADJ_INDEX_INVALID);
507 }
508
509 fib_node_index_t
510 fib_entry_get_path_list (fib_node_index_t fib_entry_index)
511 {
512     fib_entry_t *fib_entry;
513
514     fib_entry = fib_entry_get(fib_entry_index);
515
516     return (fib_entry->fe_parent);
517 }
518
519 u32
520 fib_entry_get_fib_table_id(fib_node_index_t fib_entry_index)
521 {
522     
523
524     return (0);
525 }
526
527 u32
528 fib_entry_child_add (fib_node_index_t fib_entry_index,
529                      fib_node_type_t child_type,
530                      fib_node_index_t child_index)
531 {
532     return (fib_node_child_add(FIB_NODE_TYPE_ENTRY,
533                                fib_entry_index,
534                                child_type,
535                                child_index));
536 };
537
538 void
539 fib_entry_child_remove (fib_node_index_t fib_entry_index,
540                         u32 sibling_index)
541 {
542     fib_node_child_remove(FIB_NODE_TYPE_ENTRY,
543                           fib_entry_index,
544                           sibling_index);
545 }
546
547 static fib_entry_t *
548 fib_entry_alloc (u32 fib_index,
549                  const fib_prefix_t *prefix,
550                  fib_node_index_t *fib_entry_index)
551 {
552     fib_forward_chain_type_t fct;
553     fib_entry_t *fib_entry;
554
555     pool_get(fib_entry_pool, fib_entry);
556     memset(fib_entry, 0, sizeof(*fib_entry));
557
558     fib_node_init(&fib_entry->fe_node,
559                   FIB_NODE_TYPE_ENTRY);
560
561     fib_entry->fe_fib_index = fib_index;
562     fib_entry->fe_prefix = *prefix;
563     if (FIB_PROTOCOL_MPLS == fib_entry->fe_prefix.fp_proto)
564     {
565         fib_entry->fe_prefix.fp_len = 21;
566         ASSERT(DPO_PROTO_NONE != fib_entry->fe_prefix.fp_payload_proto);
567     }
568
569     fib_entry->fe_export = FIB_NODE_INDEX_INVALID;
570     fib_entry->fe_import = FIB_NODE_INDEX_INVALID;
571     fib_entry->fe_covered = FIB_NODE_INDEX_INVALID;
572     FOR_EACH_FIB_FORW_CHAIN(fct)
573     {
574         dpo_reset(&fib_entry->fe_lb[fct]);
575     }
576
577     *fib_entry_index = fib_entry_get_index(fib_entry);
578
579     FIB_ENTRY_DBG(fib_entry, "alloc");
580
581     return (fib_entry);
582 }
583
584 static void
585 fib_entry_post_flag_update_actions (fib_entry_t *fib_entry,
586                                     fib_source_t source,
587                                     fib_entry_flag_t old_flags)
588 {
589     /*
590      * handle changes to attached export for import entries
591      */
592     int is_import  = (FIB_ENTRY_FLAG_IMPORT & fib_entry_get_flags_i(fib_entry));
593     int was_import = (FIB_ENTRY_FLAG_IMPORT & old_flags);
594
595     if (!was_import && is_import)
596     {
597         /*
598          * transition from not exported to exported
599          */
600
601         /*
602          * there is an assumption here that the entry resolves via only
603          * one interface and that it is the cross VRF interface.
604          */
605         u32 sw_if_index = fib_path_list_get_resolving_interface(fib_entry->fe_parent);
606
607         fib_attached_export_import(fib_entry,
608                                    fib_table_get_index_for_sw_if_index(
609                                        fib_entry_get_proto(fib_entry),
610                                        sw_if_index));
611     }
612     else if (was_import && !is_import)
613     {
614         /*
615          * transition from exported to not exported
616          */
617         fib_attached_export_purge(fib_entry);
618     }
619     /*
620      * else
621      *   no change. nothing to do.
622      */
623
624     /*
625      * handle changes to attached export for export entries
626      */
627     int is_attached  = (FIB_ENTRY_FLAG_ATTACHED & fib_entry_get_flags_i(fib_entry));
628     int was_attached = (FIB_ENTRY_FLAG_ATTACHED & old_flags);
629
630     if (!was_attached && is_attached)
631     {
632         /*
633          * transition to attached. time to export
634          */
635         // FIXME
636     }
637     // else FIXME
638 }
639
640 static void
641 fib_entry_post_install_actions (fib_entry_t *fib_entry,
642                                 fib_source_t source,
643                                 fib_entry_flag_t old_flags)
644 {
645     fib_entry_post_flag_update_actions(fib_entry, source, old_flags);
646     fib_entry_src_action_installed(fib_entry, source);
647 }
648
649 fib_node_index_t
650 fib_entry_create (u32 fib_index,
651                   const fib_prefix_t *prefix,
652                   fib_source_t source,
653                   fib_entry_flag_t flags,
654                   const fib_route_path_t *paths)
655 {
656     fib_node_index_t fib_entry_index;
657     fib_entry_t *fib_entry;
658
659     ASSERT(0 < vec_len(paths));
660
661     fib_entry = fib_entry_alloc(fib_index, prefix, &fib_entry_index);
662
663     /*
664      * since this is a new entry create, we don't need to check for winning
665      * sources - there is only one.
666      */
667     fib_entry = fib_entry_src_action_add(fib_entry, source, flags,
668                                          drop_dpo_get(
669                                              fib_proto_to_dpo(
670                                                  fib_entry_get_proto(fib_entry))));
671     fib_entry_src_action_path_swap(fib_entry,
672                                    source,
673                                    flags,
674                                    paths);
675     /*
676      * handle possible realloc's by refetching the pointer
677      */
678     fib_entry = fib_entry_get(fib_entry_index);
679     fib_entry_src_action_activate(fib_entry, source);
680
681     fib_entry_post_install_actions(fib_entry, source, FIB_ENTRY_FLAG_NONE);
682
683     return (fib_entry_index);
684 }
685
686 fib_node_index_t
687 fib_entry_create_special (u32 fib_index,
688                           const fib_prefix_t *prefix,
689                           fib_source_t source,
690                           fib_entry_flag_t flags,
691                           const dpo_id_t *dpo)
692 {
693     fib_node_index_t fib_entry_index;
694     fib_entry_t *fib_entry;
695
696     /*
697      * create and initiliase the new enty
698      */
699     fib_entry = fib_entry_alloc(fib_index, prefix, &fib_entry_index);
700
701     /*
702      * create the path-list
703      */
704     fib_entry = fib_entry_src_action_add(fib_entry, source, flags, dpo);
705     fib_entry_src_action_activate(fib_entry, source);
706
707     fib_entry_post_install_actions(fib_entry, source, FIB_ENTRY_FLAG_NONE);
708
709     return (fib_entry_index);
710 }
711
712 static void
713 fib_entry_post_update_actions (fib_entry_t *fib_entry,
714                                fib_source_t source,
715                                fib_entry_flag_t old_flags)
716 {
717     /*
718      * backwalk to children to inform then of the change to forwarding.
719      */
720     fib_node_back_walk_ctx_t bw_ctx = {
721         .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
722     };
723
724     fib_walk_sync(FIB_NODE_TYPE_ENTRY, fib_entry_get_index(fib_entry), &bw_ctx);
725
726     /*
727      * then inform any covered prefixes
728      */
729     fib_entry_cover_update_notify(fib_entry);
730
731     fib_entry_post_install_actions(fib_entry, source, old_flags);
732 }
733
734 void
735 fib_entry_special_add (fib_node_index_t fib_entry_index,
736                        fib_source_t source,
737                        fib_entry_flag_t flags,
738                        const dpo_id_t *dpo)
739 {
740     fib_source_t best_source;
741     fib_entry_flag_t bflags;
742     fib_entry_t *fib_entry;
743     fib_entry_src_t *bsrc;
744
745     fib_entry = fib_entry_get(fib_entry_index);
746
747     bsrc = fib_entry_get_best_src_i(fib_entry);
748     best_source = fib_entry_src_get_source(bsrc);
749     bflags = fib_entry_src_get_flags(bsrc);
750
751     fib_entry = fib_entry_src_action_add(fib_entry, source, flags, dpo);
752
753     /*
754      * if the path list for the source passed is invalid,
755      * then we need to create a new one. else we are updating
756      * an existing.
757      */
758     if (source < best_source)
759     {
760         /*
761          * we have a new winning source.
762          */
763         fib_entry_src_action_deactivate(fib_entry, best_source);
764         fib_entry_src_action_activate(fib_entry, source);
765     }
766     else if (source > best_source)
767     {
768         /*
769          * the new source loses. nothing to do here.
770          * the data from the source is saved in the path-list created
771          */
772         return;
773     }
774     else
775     {
776         /*
777          * the new source is one this entry already has.
778          * But the path-list was updated, which will contribute new forwarding,
779          * so install it.
780          */
781         fib_entry_src_action_deactivate(fib_entry, source);
782         fib_entry_src_action_activate(fib_entry, source);
783     }
784
785     fib_entry_post_update_actions(fib_entry, source, bflags);
786 }
787
788 void
789 fib_entry_path_add (fib_node_index_t fib_entry_index,
790                     fib_source_t source,
791                     fib_entry_flag_t flags,
792                     const fib_route_path_t *rpath)
793 {
794     fib_source_t best_source;
795     fib_entry_flag_t bflags;
796     fib_entry_t *fib_entry;
797     fib_entry_src_t *bsrc;
798
799     ASSERT(1 == vec_len(rpath));
800
801     fib_entry = fib_entry_get(fib_entry_index);
802     ASSERT(NULL != fib_entry);
803
804     bsrc = fib_entry_get_best_src_i(fib_entry);
805     best_source = fib_entry_src_get_source(bsrc);
806     bflags = fib_entry_src_get_flags(bsrc);
807     
808     fib_entry = fib_entry_src_action_path_add(fib_entry, source, flags, rpath);
809
810     /*
811      * if the path list for the source passed is invalid,
812      * then we need to create a new one. else we are updating
813      * an existing.
814      */
815     if (source < best_source)
816     {
817         /*
818          * we have a new winning source.
819          */
820         fib_entry_src_action_deactivate(fib_entry, best_source);
821         fib_entry_src_action_activate(fib_entry, source);
822     }
823     else if (source > best_source)
824     {
825         /*
826          * the new source loses. nothing to do here.
827          * the data from the source is saved in the path-list created
828          */
829         return;
830     }
831     else
832     {
833         /*
834          * the new source is one this entry already has.
835          * But the path-list was updated, which will contribute new forwarding,
836          * so install it.
837          */
838         fib_entry_src_action_deactivate(fib_entry, source);
839         fib_entry_src_action_activate(fib_entry, source);
840     }
841
842     fib_entry_post_update_actions(fib_entry, source, bflags);
843 }
844
845 /*
846  * fib_entry_path_remove
847  *
848  * remove a path from the entry.
849  * return the fib_entry's index if it is still present, INVALID otherwise.
850  */
851 fib_entry_src_flag_t
852 fib_entry_path_remove (fib_node_index_t fib_entry_index,
853                        fib_source_t source,
854                        const fib_route_path_t *rpath)
855 {
856     fib_entry_src_flag_t sflag;
857     fib_source_t best_source;
858     fib_entry_flag_t bflags;
859     fib_entry_t *fib_entry;
860     fib_entry_src_t *bsrc;
861
862     ASSERT(1 == vec_len(rpath));
863
864     fib_entry = fib_entry_get(fib_entry_index);
865     ASSERT(NULL != fib_entry);
866
867     bsrc = fib_entry_get_best_src_i(fib_entry);
868     best_source = fib_entry_src_get_source(bsrc);
869     bflags = fib_entry_src_get_flags(bsrc);
870
871     sflag = fib_entry_src_action_path_remove(fib_entry, source, rpath);
872
873     /*
874      * if the path list for the source passed is invalid,
875      * then we need to create a new one. else we are updating
876      * an existing.
877      */
878     if (source < best_source )
879     {
880         /*
881          * Que! removing a path from a source that is better than the
882          * one this entry is using.
883          */
884         ASSERT(0);
885     }
886     else if (source > best_source )
887     {
888         /*
889          * the source is not the best. nothing to do.
890          */
891         return (FIB_ENTRY_SRC_FLAG_ADDED);
892     }
893     else
894     {
895         /*
896          * removing a path from the path-list we were using.
897          */
898         if (!(FIB_ENTRY_SRC_FLAG_ADDED & sflag))
899         {
900             /*
901              * the last path from the source was removed.
902              * fallback to lower source
903              */
904             bsrc = fib_entry_get_best_src_i(fib_entry);
905             best_source = fib_entry_src_get_source(bsrc);
906
907             if (FIB_SOURCE_MAX == best_source) {
908                 /*
909                  * no more sources left. this entry is toast.
910                  */
911                 fib_entry_src_action_uninstall(fib_entry);
912                 fib_entry_post_flag_update_actions(fib_entry, source, bflags);
913
914                 return (FIB_ENTRY_SRC_FLAG_NONE);
915             }
916             else
917             {
918                 fib_entry_src_action_activate(fib_entry, best_source);
919                 source = best_source;
920             }
921         }
922         else
923         {
924             /*
925              * re-install the new forwarding information
926              */
927             fib_entry_src_action_deactivate(fib_entry, source);
928             fib_entry_src_action_activate(fib_entry, source);
929         }
930     }
931
932     fib_entry_post_update_actions(fib_entry, source, bflags);
933
934     /*
935      * still have sources
936      */
937     return (FIB_ENTRY_SRC_FLAG_ADDED);
938 }
939
940 /*
941  * fib_entry_special_remove
942  *
943  * remove a special source from the entry.
944  * return the fib_entry's index if it is still present, INVALID otherwise.
945  */
946 fib_entry_src_flag_t
947 fib_entry_special_remove (fib_node_index_t fib_entry_index,
948                           fib_source_t source)
949 {
950     fib_entry_src_flag_t sflag;
951     fib_source_t best_source;
952     fib_entry_flag_t bflags;
953     fib_entry_t *fib_entry;
954     fib_entry_src_t *bsrc;
955
956     fib_entry = fib_entry_get(fib_entry_index);
957     ASSERT(NULL != fib_entry);
958
959     bsrc = fib_entry_get_best_src_i(fib_entry);
960     best_source = fib_entry_src_get_source(bsrc);
961     bflags = fib_entry_src_get_flags(bsrc);
962
963     sflag = fib_entry_src_action_remove(fib_entry, source);
964
965     /*
966      * if the path list for the source passed is invalid,
967      * then we need to create a new one. else we are updating
968      * an existing.
969      */
970     if (source < best_source )
971     {
972         /*
973          * Que! removing a path from a source that is better than the
974          * one this entry is using. This can only mean it is a source
975          * this prefix does not have.
976          */
977         return (FIB_ENTRY_SRC_FLAG_ADDED);
978     }
979     else if (source > best_source ) {
980         /*
981          * the source is not the best. nothing to do.
982          */
983         return (FIB_ENTRY_SRC_FLAG_ADDED);
984     }
985     else
986     {
987         if (!(FIB_ENTRY_SRC_FLAG_ADDED & sflag))
988         {
989             /*
990              * the source was removed. use the next best.
991              */
992             bsrc = fib_entry_get_best_src_i(fib_entry);
993             best_source = fib_entry_src_get_source(bsrc);
994
995             if (FIB_SOURCE_MAX == best_source) {
996                 /*
997                  * no more sources left. this entry is toast.
998                  */
999                 fib_entry_src_action_uninstall(fib_entry);
1000                 fib_entry_post_flag_update_actions(fib_entry, source, bflags);
1001
1002                 return (FIB_ENTRY_SRC_FLAG_NONE);
1003             }
1004             else
1005             {
1006                 fib_entry_src_action_activate(fib_entry, best_source);
1007                 source = best_source;
1008             }
1009         }
1010         else
1011         {
1012             /*
1013              * re-install the new forwarding information
1014              */
1015             fib_entry_src_action_reactivate(fib_entry, source);
1016         }
1017     }
1018
1019     fib_entry_post_update_actions(fib_entry, source, bflags);
1020
1021     /*
1022      * still have sources
1023      */
1024     return (FIB_ENTRY_SRC_FLAG_ADDED);
1025 }
1026
1027 /**
1028  * fib_entry_delete
1029  *
1030  * The source is withdrawing all the paths it provided
1031  */
1032 fib_entry_src_flag_t
1033 fib_entry_delete (fib_node_index_t fib_entry_index,
1034                   fib_source_t source)
1035 {
1036     return (fib_entry_special_remove(fib_entry_index, source));
1037 }
1038
1039 /**
1040  * fib_entry_update
1041  *
1042  * The source has provided a new set of paths that will replace the old.
1043  */
1044 void
1045 fib_entry_update (fib_node_index_t fib_entry_index,
1046                   fib_source_t source,
1047                   fib_entry_flag_t flags,
1048                   const fib_route_path_t *paths)
1049 {
1050     fib_source_t best_source;
1051     fib_entry_flag_t bflags;
1052     fib_entry_t *fib_entry;
1053     fib_entry_src_t *bsrc;
1054
1055     fib_entry = fib_entry_get(fib_entry_index);
1056     ASSERT(NULL != fib_entry);
1057
1058     bsrc = fib_entry_get_best_src_i(fib_entry);
1059     best_source = fib_entry_src_get_source(bsrc);
1060     bflags = fib_entry_src_get_flags(bsrc);
1061
1062     fib_entry_src_action_path_swap(fib_entry,
1063                                    source,
1064                                    flags,
1065                                    paths);
1066     /*
1067      * handle possible realloc's by refetching the pointer
1068      */
1069     fib_entry = fib_entry_get(fib_entry_index);
1070
1071     /*
1072      * if the path list for the source passed is invalid,
1073      * then we need to create a new one. else we are updating
1074      * an existing.
1075      */
1076     if (source < best_source)
1077     {
1078         /*
1079          * we have a new winning source.
1080          */
1081         fib_entry_src_action_deactivate(fib_entry, best_source);
1082         fib_entry_src_action_activate(fib_entry, source);
1083     }
1084     else if (source > best_source) {
1085         /*
1086          * the new source loses. nothing to do here.
1087          * the data from the source is saved in the path-list created
1088          */
1089         return;
1090     }
1091     else
1092     {
1093         /*
1094          * the new source is one this entry already has.
1095          * But the path-list was updated, which will contribute new forwarding,
1096          * so install it.
1097          */
1098         fib_entry_src_action_deactivate(fib_entry, source);
1099         fib_entry_src_action_activate(fib_entry, source);
1100     }
1101
1102     fib_entry_post_update_actions(fib_entry, source, bflags);
1103 }
1104
1105
1106 /*
1107  * fib_entry_cover_changed
1108  *
1109  * this entry is tracking its cover and that cover has changed.
1110  */
1111 void
1112 fib_entry_cover_changed (fib_node_index_t fib_entry_index)
1113 {
1114     fib_entry_src_cover_res_t res = {
1115         .install = !0,
1116         .bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
1117     };
1118     fib_source_t source, best_source;
1119     fib_entry_flag_t bflags;
1120     fib_entry_t *fib_entry;
1121     fib_entry_src_t *esrc;
1122     u32 index;
1123
1124     bflags = FIB_ENTRY_FLAG_NONE;
1125     best_source = FIB_SOURCE_FIRST;
1126     fib_entry = fib_entry_get(fib_entry_index);
1127
1128     fib_attached_export_cover_change(fib_entry);
1129
1130     /*
1131      * propagate the notificuation to each of the added sources
1132      */
1133     index = 0;
1134     FOR_EACH_SRC_ADDED(fib_entry, esrc, source,
1135     ({
1136         if (0 == index)
1137         {
1138             /*
1139              * only the best source gets to set the back walk flags
1140              */
1141             res = fib_entry_src_action_cover_change(fib_entry, source);
1142             bflags = fib_entry_src_get_flags(esrc);
1143             best_source = fib_entry_src_get_source(esrc);
1144         }
1145         else
1146         {
1147             fib_entry_src_action_cover_change(fib_entry, source);
1148         }
1149         index++;
1150     }));
1151
1152     if (res.install)
1153     {
1154         fib_entry_src_action_reactivate(fib_entry,
1155                                         fib_entry_src_get_source(
1156                                             fib_entry_get_best_src_i(fib_entry)));
1157         fib_entry_post_install_actions(fib_entry, best_source, bflags);
1158     }
1159     else
1160     {
1161         fib_entry_src_action_uninstall(fib_entry);
1162     }
1163
1164     if (FIB_NODE_BW_REASON_FLAG_NONE != res.bw_reason)
1165     {
1166         /*
1167          * time for walkies fido.
1168          */
1169         fib_node_back_walk_ctx_t bw_ctx = {
1170             .fnbw_reason = res.bw_reason,
1171         };
1172
1173         fib_walk_sync(FIB_NODE_TYPE_ENTRY, fib_entry_index, &bw_ctx);
1174     }
1175 }
1176
1177 /*
1178  * fib_entry_cover_updated
1179  *
1180  * this entry is tracking its cover and that cover has been updated
1181  * (i.e. its forwarding information has changed).
1182  */
1183 void
1184 fib_entry_cover_updated (fib_node_index_t fib_entry_index)
1185 {
1186     fib_entry_src_cover_res_t res = {
1187         .install = !0,
1188         .bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
1189     };
1190     fib_source_t source, best_source;
1191     fib_entry_flag_t bflags;
1192     fib_entry_t *fib_entry;
1193     fib_entry_src_t *esrc;
1194     u32 index;
1195
1196     bflags = FIB_ENTRY_FLAG_NONE;
1197     best_source = FIB_SOURCE_FIRST;
1198     fib_entry = fib_entry_get(fib_entry_index);
1199
1200     fib_attached_export_cover_update(fib_entry);
1201
1202     /*
1203      * propagate the notificuation to each of the added sources
1204      */
1205     index = 0;
1206     FOR_EACH_SRC_ADDED(fib_entry, esrc, source,
1207     ({
1208         if (0 == index)
1209         {
1210             /*
1211              * only the best source gets to set the back walk flags
1212              */
1213             res = fib_entry_src_action_cover_update(fib_entry, source);
1214             bflags = fib_entry_src_get_flags(esrc);
1215             best_source = fib_entry_src_get_source(esrc);
1216         }
1217         else
1218         {
1219             fib_entry_src_action_cover_update(fib_entry, source);
1220         }
1221         index++;
1222     }));
1223
1224     if (res.install)
1225     {
1226         fib_entry_src_action_reactivate(fib_entry,
1227                                         fib_entry_src_get_source(
1228                                             fib_entry_get_best_src_i(fib_entry)));
1229         fib_entry_post_install_actions(fib_entry, best_source, bflags);
1230     }
1231     else
1232     {
1233         fib_entry_src_action_uninstall(fib_entry);
1234     }
1235
1236     if (FIB_NODE_BW_REASON_FLAG_NONE != res.bw_reason)
1237     {
1238         /*
1239          * time for walkies fido.
1240          */
1241         fib_node_back_walk_ctx_t bw_ctx = {
1242             .fnbw_reason = res.bw_reason,
1243         };
1244
1245         fib_walk_sync(FIB_NODE_TYPE_ENTRY, fib_entry_index, &bw_ctx);
1246     }
1247 }
1248
1249 int
1250 fib_entry_recursive_loop_detect (fib_node_index_t entry_index,
1251                                  fib_node_index_t **entry_indicies)
1252 {
1253     fib_entry_t *fib_entry;
1254     int was_looped, is_looped;
1255
1256     fib_entry = fib_entry_get(entry_index);
1257
1258     if (FIB_NODE_INDEX_INVALID != fib_entry->fe_parent)
1259     {
1260         fib_node_index_t *entries = *entry_indicies;
1261         fib_forward_chain_type_t fct;
1262
1263         vec_add1(entries, entry_index);
1264         was_looped = fib_path_list_is_looped(fib_entry->fe_parent);
1265         is_looped = fib_path_list_recursive_loop_detect(fib_entry->fe_parent,
1266                                                         &entries);
1267
1268         *entry_indicies = entries;
1269
1270         if (!!was_looped != !!is_looped)
1271         {
1272             /*
1273              * re-evaluate all the entry's forwarding
1274              * NOTE: this is an inplace modify
1275              */
1276             FOR_EACH_FIB_FORW_CHAIN(fct)
1277             {
1278                 if (dpo_id_is_valid(&fib_entry->fe_lb[fct]))
1279                 {
1280                     fib_entry_src_mk_lb(fib_entry,
1281                                         fib_entry_get_best_src_i(fib_entry),
1282                                         fct,
1283                                         &fib_entry->fe_lb[fct]);
1284                 }
1285             }
1286         }
1287     }
1288     else
1289     {
1290         /*
1291          * the entry is currently not linked to a path-list. this happens
1292          * when it is this entry that is re-linking path-lists and has thus
1293          * broken the loop
1294          */
1295         is_looped = 0;
1296     }
1297
1298     return (is_looped);
1299 }
1300
1301 u32
1302 fib_entry_get_resolving_interface (fib_node_index_t entry_index)
1303 {
1304     fib_entry_t *fib_entry;
1305
1306     fib_entry = fib_entry_get(entry_index);
1307
1308     return (fib_path_list_get_resolving_interface(fib_entry->fe_parent));
1309 }
1310
1311 fib_source_t
1312 fib_entry_get_best_source (fib_node_index_t entry_index)
1313 {
1314     fib_entry_t *fib_entry;
1315     fib_entry_src_t *bsrc;
1316
1317     fib_entry = fib_entry_get(entry_index);
1318
1319     bsrc = fib_entry_get_best_src_i(fib_entry);
1320     return (fib_entry_src_get_source(bsrc));
1321 }
1322
1323 static int
1324 fib_ip4_address_compare (ip4_address_t * a1,
1325                          ip4_address_t * a2)
1326 {
1327     /*
1328      * IP addresses are unsiged ints. the return value here needs to be signed
1329      * a simple subtraction won't cut it.
1330      * If the addresses are the same, the sort order is undefiend, so phoey.
1331      */
1332     return ((clib_net_to_host_u32(a1->data_u32) >
1333              clib_net_to_host_u32(a2->data_u32) ) ?
1334             1 : -1);
1335 }
1336
1337 static int
1338 fib_ip6_address_compare (ip6_address_t * a1,
1339                          ip6_address_t * a2)
1340 {
1341   int i;
1342   for (i = 0; i < ARRAY_LEN (a1->as_u16); i++)
1343   {
1344       int cmp = (clib_net_to_host_u16 (a1->as_u16[i]) -
1345                  clib_net_to_host_u16 (a2->as_u16[i]));
1346       if (cmp != 0)
1347           return cmp;
1348   }
1349   return 0;
1350 }
1351
1352 static int
1353 fib_entry_cmp (fib_node_index_t fib_entry_index1,
1354                fib_node_index_t fib_entry_index2)
1355 {
1356     fib_entry_t *fib_entry1, *fib_entry2;
1357     int cmp = 0;
1358
1359     fib_entry1 = fib_entry_get(fib_entry_index1);
1360     fib_entry2 = fib_entry_get(fib_entry_index2);
1361
1362     switch (fib_entry1->fe_prefix.fp_proto)
1363     {
1364     case FIB_PROTOCOL_IP4:
1365         cmp = fib_ip4_address_compare(&fib_entry1->fe_prefix.fp_addr.ip4,
1366                                       &fib_entry2->fe_prefix.fp_addr.ip4);
1367         break;
1368     case FIB_PROTOCOL_IP6:
1369         cmp = fib_ip6_address_compare(&fib_entry1->fe_prefix.fp_addr.ip6,
1370                                       &fib_entry2->fe_prefix.fp_addr.ip6);
1371         break;
1372     case FIB_PROTOCOL_MPLS:
1373         cmp = (fib_entry1->fe_prefix.fp_label - fib_entry2->fe_prefix.fp_label);
1374
1375         if (0 == cmp)
1376         {
1377             cmp = (fib_entry1->fe_prefix.fp_eos - fib_entry2->fe_prefix.fp_eos);
1378         }
1379         break;
1380     }
1381
1382     if (0 == cmp) {
1383         cmp = (fib_entry1->fe_prefix.fp_len - fib_entry2->fe_prefix.fp_len);
1384     }
1385     return (cmp);   
1386 }
1387
1388 int
1389 fib_entry_cmp_for_sort (void *i1, void *i2)
1390 {
1391     fib_node_index_t *fib_entry_index1 = i1, *fib_entry_index2 = i2;
1392
1393     return (fib_entry_cmp(*fib_entry_index1,
1394                           *fib_entry_index2));
1395 }
1396
1397 void
1398 fib_entry_lock (fib_node_index_t fib_entry_index)
1399 {
1400     fib_entry_t *fib_entry;
1401
1402     fib_entry = fib_entry_get(fib_entry_index);
1403
1404     fib_node_lock(&fib_entry->fe_node);
1405 }
1406
1407 void
1408 fib_entry_unlock (fib_node_index_t fib_entry_index)
1409 {
1410     fib_entry_t *fib_entry;
1411
1412     fib_entry = fib_entry_get(fib_entry_index);
1413
1414     fib_node_unlock(&fib_entry->fe_node);
1415 }
1416
1417 void
1418 fib_entry_module_init (void)
1419 {
1420     fib_node_register_type (FIB_NODE_TYPE_ENTRY, &fib_entry_vft);
1421 }
1422
1423 void
1424 fib_entry_get_prefix (fib_node_index_t fib_entry_index,
1425                       fib_prefix_t *pfx)
1426 {
1427     fib_entry_t *fib_entry;
1428
1429     fib_entry = fib_entry_get(fib_entry_index);
1430     *pfx = fib_entry->fe_prefix;
1431 }
1432
1433 u32
1434 fib_entry_get_fib_index (fib_node_index_t fib_entry_index)
1435 {
1436     fib_entry_t *fib_entry;
1437
1438     fib_entry = fib_entry_get(fib_entry_index);
1439
1440     return (fib_entry->fe_fib_index);
1441 }
1442
1443 u32
1444 fib_entry_pool_size (void)
1445 {
1446     return (pool_elts(fib_entry_pool));
1447 }
1448
1449 static clib_error_t *
1450 show_fib_entry_command (vlib_main_t * vm,
1451                         unformat_input_t * input,
1452                         vlib_cli_command_t * cmd)
1453 {
1454     fib_node_index_t fei;
1455
1456     if (unformat (input, "%d", &fei))
1457     {
1458         /*
1459          * show one in detail
1460          */
1461         if (!pool_is_free_index(fib_entry_pool, fei))
1462         {
1463             vlib_cli_output (vm, "%d@%U",
1464                              fei,
1465                              format_fib_entry, fei,
1466                              FIB_ENTRY_FORMAT_DETAIL2);
1467         }
1468         else
1469         {
1470             vlib_cli_output (vm, "entry %d invalid", fei);
1471         }
1472     }
1473     else
1474     {
1475         /*
1476          * show all
1477          */
1478         vlib_cli_output (vm, "FIB Entries:");
1479         pool_foreach_index(fei, fib_entry_pool,
1480         ({
1481             vlib_cli_output (vm, "%d@%U",
1482                              fei,
1483                              format_fib_entry, fei,
1484                              FIB_ENTRY_FORMAT_BRIEF);
1485         }));
1486     }
1487
1488     return (NULL);
1489 }
1490
1491 VLIB_CLI_COMMAND (show_fib_entry, static) = {
1492   .path = "show fib entry",
1493   .function = show_fib_entry_command,
1494   .short_help = "show fib entry",
1495 };