FIB memory leaks (VPP-578)
[vpp.git] / src / 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 fib_forward_chain_type_t
68 fib_entry_get_default_chain_type (const fib_entry_t *fib_entry)
69 {
70     switch (fib_entry->fe_prefix.fp_proto)
71     {
72     case FIB_PROTOCOL_IP4:
73         return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
74     case FIB_PROTOCOL_IP6:
75         return (FIB_FORW_CHAIN_TYPE_UNICAST_IP6);
76     case FIB_PROTOCOL_MPLS:
77         if (MPLS_EOS == fib_entry->fe_prefix.fp_eos)
78             /*
79              * If the entry being asked is a eos-MPLS label entry,
80              * then use the payload-protocol field, that we stashed there
81              * for just this purpose
82              */
83             return (fib_forw_chain_type_from_dpo_proto(
84                         fib_entry->fe_prefix.fp_payload_proto));
85         else
86             return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS);
87     }
88
89     return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
90 }
91
92 u8 *
93 format_fib_entry (u8 * s, va_list * args)
94 {
95     fib_forward_chain_type_t fct;
96     fib_entry_attribute_t attr;
97     fib_path_ext_t *path_ext;
98     fib_entry_t *fib_entry;
99     fib_entry_src_t *src;
100     fib_node_index_t fei;
101     fib_source_t source;
102     u32 n_covered;
103     int level;
104
105     fei = va_arg (*args, fib_node_index_t);
106     level = va_arg (*args, int);
107     fib_entry = fib_entry_get(fei);
108
109     s = format (s, "%U", format_fib_prefix, &fib_entry->fe_prefix);
110
111     if (level >= FIB_ENTRY_FORMAT_DETAIL)
112     {
113         s = format (s, " fib:%d", fib_entry->fe_fib_index);
114         s = format (s, " index:%d", fib_entry_get_index(fib_entry));
115         s = format (s, " locks:%d", fib_entry->fe_node.fn_locks);
116
117         FOR_EACH_SRC_ADDED(fib_entry, src, source,
118         ({
119             s = format (s, "\n  src:%s ",
120                         fib_source_names[source]);
121             s = fib_entry_src_format(fib_entry, source, s);
122             s = format (s, " refs:%d ", src->fes_ref_count);
123             if (FIB_ENTRY_FLAG_NONE != src->fes_entry_flags) {
124                 s = format(s, "flags:");
125                 FOR_EACH_FIB_ATTRIBUTE(attr) {
126                     if ((1<<attr) & src->fes_entry_flags) {
127                         s = format (s, "%s,", fib_attribute_names[attr]);
128                     }
129                 }
130             }
131             s = format (s, "\n");
132             if (FIB_NODE_INDEX_INVALID != src->fes_pl)
133             {
134                 s = fib_path_list_format(src->fes_pl, s);
135             }
136             if (NULL != src->fes_path_exts)
137             {
138                 s = format(s, "    Extensions:");
139                 vec_foreach(path_ext, src->fes_path_exts)
140                 {
141                     s = format(s, "\n     %U", format_fib_path_ext, path_ext);
142                 }
143             }
144         }));
145     
146         n_covered = fib_entry_cover_get_size(fib_entry);
147         if (n_covered > 0) {
148             s = format(s, "\n tracking %d covered: ", n_covered);
149             s = fib_entry_cover_list_format(fib_entry, s);
150         }
151         s = fib_ae_import_format(fib_entry, s);
152         s = fib_ae_export_format(fib_entry, s);
153
154         s = format (s, "\n forwarding: ");
155     }
156     else
157     {
158         s = format (s, "\n");
159     }
160
161     fct = fib_entry_get_default_chain_type(fib_entry);
162
163     if (!dpo_id_is_valid(&fib_entry->fe_lb))
164     {
165         s = format (s, "  UNRESOLVED\n");
166         return (s);
167     }
168     else
169     {
170         s = format(s, "  %U-chain\n  %U",
171                    format_fib_forw_chain_type, fct,
172                    format_dpo_id,
173                    &fib_entry->fe_lb,
174                    2);
175         s = format(s, "\n");
176
177         if (level >= FIB_ENTRY_FORMAT_DETAIL2)
178         {
179             fib_entry_delegate_type_t fdt;
180             fib_entry_delegate_t *fed;
181
182             FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
183             {
184                 s = format(s, "  %U-chain\n  %U",
185                            format_fib_forw_chain_type,
186                            fib_entry_delegate_type_to_chain_type(fdt),
187                            format_dpo_id, &fed->fd_dpo, 2);
188                 s = format(s, "\n");
189             });
190         }
191     }
192
193     if (level >= FIB_ENTRY_FORMAT_DETAIL2)
194     {
195         s = format(s, "\nchildren:");
196         s = fib_node_children_format(fib_entry->fe_node.fn_children, s);
197     }
198
199     return (s);
200 }
201
202 static fib_entry_t*
203 fib_entry_from_fib_node (fib_node_t *node)
204 {
205 #if CLIB_DEBUG > 0
206     ASSERT(FIB_NODE_TYPE_ENTRY == node->fn_type);
207 #endif
208     return ((fib_entry_t*)node);
209 }
210
211 static void
212 fib_entry_last_lock_gone (fib_node_t *node)
213 {
214     fib_entry_delegate_type_t fdt;
215     fib_entry_delegate_t *fed;
216     fib_entry_t *fib_entry;
217
218     fib_entry = fib_entry_from_fib_node(node);
219
220     FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
221     {
222         dpo_reset(&fed->fd_dpo);
223         fib_entry_delegate_remove(fib_entry, fdt);
224     });
225
226     FIB_ENTRY_DBG(fib_entry, "last-lock");
227
228     fib_node_deinit(&fib_entry->fe_node);
229     // FIXME -RR Backwalk
230
231     ASSERT(0 == vec_len(fib_entry->fe_delegates));
232     vec_free(fib_entry->fe_delegates);
233     vec_free(fib_entry->fe_srcs);
234     pool_put(fib_entry_pool, fib_entry);
235 }
236
237 static fib_entry_src_t*
238 fib_entry_get_best_src_i (const fib_entry_t *fib_entry)
239 {
240     fib_entry_src_t *bsrc;
241
242     /*
243      * the enum of sources is deliberately arranged in priority order
244      */
245     if (0 == vec_len(fib_entry->fe_srcs))
246     {
247         bsrc = NULL;
248     }
249     else
250     {
251         bsrc = vec_elt_at_index(fib_entry->fe_srcs, 0);
252     }
253
254     return (bsrc);
255 }
256
257 static fib_source_t
258 fib_entry_src_get_source (const fib_entry_src_t *esrc)
259 {
260     if (NULL != esrc)
261     {
262         return (esrc->fes_src);
263     }
264     return (FIB_SOURCE_MAX);
265 }
266
267 static fib_entry_flag_t
268 fib_entry_src_get_flags (const fib_entry_src_t *esrc)
269 {
270     if (NULL != esrc)
271     {
272         return (esrc->fes_entry_flags);
273     }
274     return (FIB_ENTRY_FLAG_NONE);
275 }
276
277 fib_entry_flag_t
278 fib_entry_get_flags (fib_node_index_t fib_entry_index)
279 {
280     return (fib_entry_get_flags_i(fib_entry_get(fib_entry_index)));
281 }
282
283 /*
284  * fib_entry_back_walk_notify
285  *
286  * A back walk has reach this entry.
287  */
288 static fib_node_back_walk_rc_t
289 fib_entry_back_walk_notify (fib_node_t *node,
290                             fib_node_back_walk_ctx_t *ctx)
291 {
292     fib_entry_t *fib_entry;
293
294     fib_entry = fib_entry_from_fib_node(node);
295
296     if (FIB_NODE_BW_REASON_FLAG_EVALUATE & ctx->fnbw_reason        ||
297         FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE & ctx->fnbw_reason      ||
298         FIB_NODE_BW_REASON_FLAG_ADJ_DOWN & ctx->fnbw_reason        ||
299         FIB_NODE_BW_REASON_FLAG_INTERFACE_UP & ctx->fnbw_reason    ||
300         FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN & ctx->fnbw_reason  ||
301         FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE & ctx->fnbw_reason)
302     {
303         fib_entry_src_action_reactivate(fib_entry,
304                                         fib_entry_get_best_source(
305                                             fib_entry_get_index(fib_entry)));
306     }
307
308     /*
309      * all other walk types can be reclassifed to a re-evaluate to
310      * all recursive dependents.
311      * By reclassifying we ensure that should any of these walk types meet
312      * they can be merged.
313      */
314     ctx->fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE;
315
316     /*
317      * ... and nothing is forced sync from now on.
318      */
319     ctx->fnbw_flags &= ~FIB_NODE_BW_FLAG_FORCE_SYNC;
320
321     /*
322      * propagate the backwalk further if we haven't already reached the
323      * maximum depth.
324      */
325     fib_walk_sync(FIB_NODE_TYPE_ENTRY,
326                   fib_entry_get_index(fib_entry),
327                   ctx);
328
329     return (FIB_NODE_BACK_WALK_CONTINUE);
330 }
331
332 static void
333 fib_entry_show_memory (void)
334 {
335     u32 n_srcs = 0, n_exts = 0;
336     fib_entry_src_t *esrc;
337     fib_entry_t *entry;
338
339     fib_show_memory_usage("Entry",
340                           pool_elts(fib_entry_pool),
341                           pool_len(fib_entry_pool),
342                           sizeof(fib_entry_t));
343
344     pool_foreach(entry, fib_entry_pool,
345     ({
346         n_srcs += vec_len(entry->fe_srcs);
347         vec_foreach(esrc, entry->fe_srcs)
348         {
349             n_exts += vec_len(esrc->fes_path_exts);
350         }
351     }));
352
353     fib_show_memory_usage("Entry Source",
354                           n_srcs, n_srcs, sizeof(fib_entry_src_t));
355     fib_show_memory_usage("Entry Path-Extensions",
356                           n_exts, n_exts,
357                           sizeof(fib_path_ext_t));
358 }
359
360 /*
361  * The FIB path-list's graph node virtual function table
362  */
363 static const fib_node_vft_t fib_entry_vft = {
364     .fnv_get = fib_entry_get_node,
365     .fnv_last_lock = fib_entry_last_lock_gone,
366     .fnv_back_walk = fib_entry_back_walk_notify,
367     .fnv_mem_show = fib_entry_show_memory,
368 };
369
370 /**
371  * @brief Contribute the set of Adjacencies that this entry forwards with
372  * to build the uRPF list of its children
373  */
374 void
375 fib_entry_contribute_urpf (fib_node_index_t entry_index,
376                            index_t urpf)
377 {
378     fib_entry_t *fib_entry;
379
380     fib_entry = fib_entry_get(entry_index);
381
382     return (fib_path_list_contribute_urpf(fib_entry->fe_parent, urpf));
383 }
384
385 /*
386  * fib_entry_contribute_forwarding
387  *
388  * Get an lock the forwarding information (DPO) contributed by the FIB entry.
389  */
390 void
391 fib_entry_contribute_forwarding (fib_node_index_t fib_entry_index,
392                                  fib_forward_chain_type_t fct,
393                                  dpo_id_t *dpo)
394 {
395     fib_entry_delegate_t *fed;
396     fib_entry_t *fib_entry;
397
398     fib_entry = fib_entry_get(fib_entry_index);
399
400     if (fct == fib_entry_get_default_chain_type(fib_entry))
401     {
402         dpo_copy(dpo, &fib_entry->fe_lb);
403     }
404     else
405     {
406         fed = fib_entry_delegate_get(fib_entry,
407                                      fib_entry_chain_type_to_delegate_type(fct));
408
409         if (NULL == fed)
410         {
411             fed = fib_entry_delegate_find_or_add(
412                       fib_entry,
413                       fib_entry_chain_type_to_delegate_type(fct));
414             /*
415              * on-demand create eos/non-eos.
416              * There is no on-demand delete because:
417              *   - memory versus complexity & reliability:
418              *      leaving unrequired [n]eos LB arounds wastes memory, cleaning
419              *      then up on the right trigger is more code. i favour the latter.
420              */
421             fib_entry_src_mk_lb(fib_entry,
422                                 fib_entry_get_best_src_i(fib_entry),
423                                 fct,
424                                 &fed->fd_dpo);
425         }
426
427         dpo_copy(dpo, &fed->fd_dpo);
428     }
429 }
430
431 const dpo_id_t *
432 fib_entry_contribute_ip_forwarding (fib_node_index_t fib_entry_index)
433 {
434     fib_forward_chain_type_t fct;
435     fib_entry_t *fib_entry;
436
437     fib_entry = fib_entry_get(fib_entry_index);
438     fct = fib_entry_get_default_chain_type(fib_entry);
439
440     ASSERT((fct == FIB_FORW_CHAIN_TYPE_UNICAST_IP4 ||
441             fct == FIB_FORW_CHAIN_TYPE_UNICAST_IP6));
442
443     return (&fib_entry->fe_lb);
444 }
445
446 adj_index_t
447 fib_entry_get_adj (fib_node_index_t fib_entry_index)
448 {
449     const dpo_id_t *dpo;
450
451     dpo = fib_entry_contribute_ip_forwarding(fib_entry_index);
452     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
453
454     if (dpo_is_adj(dpo))
455     {
456         return (dpo->dpoi_index);
457     }
458     return (ADJ_INDEX_INVALID);
459 }
460
461 fib_node_index_t
462 fib_entry_get_path_list (fib_node_index_t fib_entry_index)
463 {
464     fib_entry_t *fib_entry;
465
466     fib_entry = fib_entry_get(fib_entry_index);
467
468     return (fib_entry->fe_parent);
469 }
470
471 u32
472 fib_entry_child_add (fib_node_index_t fib_entry_index,
473                      fib_node_type_t child_type,
474                      fib_node_index_t child_index)
475 {
476     return (fib_node_child_add(FIB_NODE_TYPE_ENTRY,
477                                fib_entry_index,
478                                child_type,
479                                child_index));
480 };
481
482 void
483 fib_entry_child_remove (fib_node_index_t fib_entry_index,
484                         u32 sibling_index)
485 {
486     fib_node_child_remove(FIB_NODE_TYPE_ENTRY,
487                           fib_entry_index,
488                           sibling_index);
489
490     if (0 == fib_node_get_n_children(FIB_NODE_TYPE_ENTRY,
491                                      fib_entry_index))
492     {
493         /*
494          * if there are no children left then there is no reason to keep
495          * the non-default forwarding chains. those chains are built only
496          * because the children want them.
497          */
498         fib_entry_delegate_type_t fdt;
499         fib_entry_delegate_t *fed;
500         fib_entry_t *fib_entry;
501
502         fib_entry = fib_entry_get(fib_entry_index);
503
504         FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
505         {
506             dpo_reset(&fed->fd_dpo);
507             fib_entry_delegate_remove(fib_entry, fdt);
508         });
509     }
510 }
511
512 static fib_entry_t *
513 fib_entry_alloc (u32 fib_index,
514                  const fib_prefix_t *prefix,
515                  fib_node_index_t *fib_entry_index)
516 {
517     fib_entry_t *fib_entry;
518     fib_prefix_t *fep;
519
520     pool_get(fib_entry_pool, fib_entry);
521     memset(fib_entry, 0, sizeof(*fib_entry));
522
523     fib_node_init(&fib_entry->fe_node,
524                   FIB_NODE_TYPE_ENTRY);
525
526     fib_entry->fe_fib_index = fib_index;
527
528     /*
529      * the one time we need to update the const prefix is when
530      * the entry is first created
531      */
532     fep = (fib_prefix_t*)&(fib_entry->fe_prefix);
533     *fep = *prefix;
534
535     if (FIB_PROTOCOL_MPLS == fib_entry->fe_prefix.fp_proto)
536     {
537         fep->fp_len = 21;
538         if (MPLS_NON_EOS == fep->fp_eos)
539         {
540             fep->fp_payload_proto = DPO_PROTO_MPLS;
541         }
542         ASSERT(DPO_PROTO_NONE != fib_entry->fe_prefix.fp_payload_proto);
543     }
544
545     dpo_reset(&fib_entry->fe_lb);
546
547     *fib_entry_index = fib_entry_get_index(fib_entry);
548
549     FIB_ENTRY_DBG(fib_entry, "alloc");
550
551     return (fib_entry);
552 }
553
554 static void
555 fib_entry_post_flag_update_actions (fib_entry_t *fib_entry,
556                                     fib_source_t source,
557                                     fib_entry_flag_t old_flags)
558 {
559     /*
560      * handle changes to attached export for import entries
561      */
562     int is_import  = (FIB_ENTRY_FLAG_IMPORT & fib_entry_get_flags_i(fib_entry));
563     int was_import = (FIB_ENTRY_FLAG_IMPORT & old_flags);
564
565     if (!was_import && is_import)
566     {
567         /*
568          * transition from not exported to exported
569          */
570
571         /*
572          * there is an assumption here that the entry resolves via only
573          * one interface and that it is the cross VRF interface.
574          */
575         u32 sw_if_index = fib_path_list_get_resolving_interface(fib_entry->fe_parent);
576
577         fib_attached_export_import(fib_entry,
578                                    fib_table_get_index_for_sw_if_index(
579                                        fib_entry_get_proto(fib_entry),
580                                        sw_if_index));
581     }
582     else if (was_import && !is_import)
583     {
584         /*
585          * transition from exported to not exported
586          */
587         fib_attached_export_purge(fib_entry);
588     }
589     /*
590      * else
591      *   no change. nothing to do.
592      */
593
594     /*
595      * handle changes to attached export for export entries
596      */
597     int is_attached  = (FIB_ENTRY_FLAG_ATTACHED & fib_entry_get_flags_i(fib_entry));
598     int was_attached = (FIB_ENTRY_FLAG_ATTACHED & old_flags);
599
600     if (!was_attached && is_attached)
601     {
602         /*
603          * transition to attached. time to export
604          */
605         // FIXME
606     }
607     // else FIXME
608 }
609
610 static void
611 fib_entry_post_install_actions (fib_entry_t *fib_entry,
612                                 fib_source_t source,
613                                 fib_entry_flag_t old_flags)
614 {
615     fib_entry_post_flag_update_actions(fib_entry, source, old_flags);
616     fib_entry_src_action_installed(fib_entry, source);
617 }
618
619 fib_node_index_t
620 fib_entry_create (u32 fib_index,
621                   const fib_prefix_t *prefix,
622                   fib_source_t source,
623                   fib_entry_flag_t flags,
624                   const fib_route_path_t *paths)
625 {
626     fib_node_index_t fib_entry_index;
627     fib_entry_t *fib_entry;
628
629     ASSERT(0 < vec_len(paths));
630
631     fib_entry = fib_entry_alloc(fib_index, prefix, &fib_entry_index);
632
633     /*
634      * since this is a new entry create, we don't need to check for winning
635      * sources - there is only one.
636      */
637     fib_entry = fib_entry_src_action_add(fib_entry, source, flags,
638                                          drop_dpo_get(
639                                              fib_proto_to_dpo(
640                                                  fib_entry_get_proto(fib_entry))));
641     fib_entry_src_action_path_swap(fib_entry,
642                                    source,
643                                    flags,
644                                    paths);
645     /*
646      * handle possible realloc's by refetching the pointer
647      */
648     fib_entry = fib_entry_get(fib_entry_index);
649     fib_entry_src_action_activate(fib_entry, source);
650
651     fib_entry_post_install_actions(fib_entry, source, FIB_ENTRY_FLAG_NONE);
652
653     return (fib_entry_index);
654 }
655
656 fib_node_index_t
657 fib_entry_create_special (u32 fib_index,
658                           const fib_prefix_t *prefix,
659                           fib_source_t source,
660                           fib_entry_flag_t flags,
661                           const dpo_id_t *dpo)
662 {
663     fib_node_index_t fib_entry_index;
664     fib_entry_t *fib_entry;
665
666     /*
667      * create and initiliase the new enty
668      */
669     fib_entry = fib_entry_alloc(fib_index, prefix, &fib_entry_index);
670
671     /*
672      * create the path-list
673      */
674     fib_entry = fib_entry_src_action_add(fib_entry, source, flags, dpo);
675     fib_entry_src_action_activate(fib_entry, source);
676
677     fib_entry_post_install_actions(fib_entry, source, FIB_ENTRY_FLAG_NONE);
678
679     return (fib_entry_index);
680 }
681
682 static void
683 fib_entry_post_update_actions (fib_entry_t *fib_entry,
684                                fib_source_t source,
685                                fib_entry_flag_t old_flags)
686 {
687     /*
688      * backwalk to children to inform then of the change to forwarding.
689      */
690     fib_node_back_walk_ctx_t bw_ctx = {
691         .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
692     };
693
694     fib_walk_sync(FIB_NODE_TYPE_ENTRY, fib_entry_get_index(fib_entry), &bw_ctx);
695
696     /*
697      * then inform any covered prefixes
698      */
699     fib_entry_cover_update_notify(fib_entry);
700
701     fib_entry_post_install_actions(fib_entry, source, old_flags);
702 }
703
704 static void
705 fib_entry_source_change (fib_entry_t *fib_entry,
706                          fib_source_t best_source,
707                          fib_source_t new_source,
708                          fib_entry_flag_t old_flags)
709 {
710     /*
711      * if the path list for the source passed is invalid,
712      * then we need to create a new one. else we are updating
713      * an existing.
714      */
715     if (new_source < best_source)
716     {
717         /*
718          * we have a new winning source.
719          */
720         fib_entry_src_action_deactivate(fib_entry, best_source);
721         fib_entry_src_action_activate(fib_entry, new_source);
722     }
723     else if (new_source > best_source)
724     {
725         /*
726          * the new source loses. nothing to do here.
727          * the data from the source is saved in the path-list created
728          */
729         return;
730     }
731     else
732     {
733         /*
734          * the new source is one this entry already has.
735          * But the path-list was updated, which will contribute new forwarding,
736          * so install it.
737          */
738         fib_entry_src_action_deactivate(fib_entry, new_source);
739         fib_entry_src_action_activate(fib_entry, new_source);
740     }
741
742     fib_entry_post_update_actions(fib_entry, new_source, old_flags);
743 }
744
745 void
746 fib_entry_special_add (fib_node_index_t fib_entry_index,
747                        fib_source_t source,
748                        fib_entry_flag_t flags,
749                        const dpo_id_t *dpo)
750 {
751     fib_source_t best_source;
752     fib_entry_flag_t bflags;
753     fib_entry_t *fib_entry;
754     fib_entry_src_t *bsrc;
755
756     fib_entry = fib_entry_get(fib_entry_index);
757
758     bsrc = fib_entry_get_best_src_i(fib_entry);
759     best_source = fib_entry_src_get_source(bsrc);
760     bflags = fib_entry_src_get_flags(bsrc);
761
762     fib_entry = fib_entry_src_action_add(fib_entry, source, flags, dpo);
763     fib_entry_source_change(fib_entry, best_source, source, bflags);
764 }
765
766 void
767 fib_entry_special_update (fib_node_index_t fib_entry_index,
768                           fib_source_t source,
769                           fib_entry_flag_t flags,
770                           const dpo_id_t *dpo)
771 {
772     fib_source_t best_source;
773     fib_entry_flag_t bflags;
774     fib_entry_t *fib_entry;
775     fib_entry_src_t *bsrc;
776
777     fib_entry = fib_entry_get(fib_entry_index);
778
779     bsrc = fib_entry_get_best_src_i(fib_entry);
780     best_source = fib_entry_src_get_source(bsrc);
781     bflags = fib_entry_src_get_flags(bsrc);
782
783     fib_entry = fib_entry_src_action_update(fib_entry, source, flags, dpo);
784     fib_entry_source_change(fib_entry, best_source, source, bflags);
785 }
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
1262         vec_add1(entries, entry_index);
1263         was_looped = fib_path_list_is_looped(fib_entry->fe_parent);
1264         is_looped = fib_path_list_recursive_loop_detect(fib_entry->fe_parent,
1265                                                         &entries);
1266
1267         *entry_indicies = entries;
1268
1269         if (!!was_looped != !!is_looped)
1270         {
1271             /*
1272              * re-evaluate all the entry's forwarding
1273              * NOTE: this is an inplace modify
1274              */
1275             fib_entry_delegate_type_t fdt;
1276             fib_entry_delegate_t *fed;
1277
1278             FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
1279             {
1280                 fib_entry_src_mk_lb(fib_entry,
1281                                     fib_entry_get_best_src_i(fib_entry),
1282                                     fib_entry_delegate_type_to_chain_type(fdt),
1283                                     &fed->fd_dpo);
1284             });
1285         }
1286     }
1287     else
1288     {
1289         /*
1290          * the entry is currently not linked to a path-list. this happens
1291          * when it is this entry that is re-linking path-lists and has thus
1292          * broken the loop
1293          */
1294         is_looped = 0;
1295     }
1296
1297     return (is_looped);
1298 }
1299
1300 u32
1301 fib_entry_get_resolving_interface (fib_node_index_t entry_index)
1302 {
1303     fib_entry_t *fib_entry;
1304
1305     fib_entry = fib_entry_get(entry_index);
1306
1307     return (fib_path_list_get_resolving_interface(fib_entry->fe_parent));
1308 }
1309
1310 fib_source_t
1311 fib_entry_get_best_source (fib_node_index_t entry_index)
1312 {
1313     fib_entry_t *fib_entry;
1314     fib_entry_src_t *bsrc;
1315
1316     fib_entry = fib_entry_get(entry_index);
1317
1318     bsrc = fib_entry_get_best_src_i(fib_entry);
1319     return (fib_entry_src_get_source(bsrc));
1320 }
1321
1322 static int
1323 fib_ip4_address_compare (const ip4_address_t * a1,
1324                          const ip4_address_t * a2)
1325 {
1326     /*
1327      * IP addresses are unsiged ints. the return value here needs to be signed
1328      * a simple subtraction won't cut it.
1329      * If the addresses are the same, the sort order is undefiend, so phoey.
1330      */
1331     return ((clib_net_to_host_u32(a1->data_u32) >
1332              clib_net_to_host_u32(a2->data_u32) ) ?
1333             1 : -1);
1334 }
1335
1336 static int
1337 fib_ip6_address_compare (const ip6_address_t * a1,
1338                          const ip6_address_t * a2)
1339 {
1340   int i;
1341   for (i = 0; i < ARRAY_LEN (a1->as_u16); i++)
1342   {
1343       int cmp = (clib_net_to_host_u16 (a1->as_u16[i]) -
1344                  clib_net_to_host_u16 (a2->as_u16[i]));
1345       if (cmp != 0)
1346           return cmp;
1347   }
1348   return 0;
1349 }
1350
1351 static int
1352 fib_entry_cmp (fib_node_index_t fib_entry_index1,
1353                fib_node_index_t fib_entry_index2)
1354 {
1355     fib_entry_t *fib_entry1, *fib_entry2;
1356     int cmp = 0;
1357
1358     fib_entry1 = fib_entry_get(fib_entry_index1);
1359     fib_entry2 = fib_entry_get(fib_entry_index2);
1360
1361     switch (fib_entry1->fe_prefix.fp_proto)
1362     {
1363     case FIB_PROTOCOL_IP4:
1364         cmp = fib_ip4_address_compare(&fib_entry1->fe_prefix.fp_addr.ip4,
1365                                       &fib_entry2->fe_prefix.fp_addr.ip4);
1366         break;
1367     case FIB_PROTOCOL_IP6:
1368         cmp = fib_ip6_address_compare(&fib_entry1->fe_prefix.fp_addr.ip6,
1369                                       &fib_entry2->fe_prefix.fp_addr.ip6);
1370         break;
1371     case FIB_PROTOCOL_MPLS:
1372         cmp = (fib_entry1->fe_prefix.fp_label - fib_entry2->fe_prefix.fp_label);
1373
1374         if (0 == cmp)
1375         {
1376             cmp = (fib_entry1->fe_prefix.fp_eos - fib_entry2->fe_prefix.fp_eos);
1377         }
1378         break;
1379     }
1380
1381     if (0 == cmp) {
1382         cmp = (fib_entry1->fe_prefix.fp_len - fib_entry2->fe_prefix.fp_len);
1383     }
1384     return (cmp);   
1385 }
1386
1387 int
1388 fib_entry_cmp_for_sort (void *i1, void *i2)
1389 {
1390     fib_node_index_t *fib_entry_index1 = i1, *fib_entry_index2 = i2;
1391
1392     return (fib_entry_cmp(*fib_entry_index1,
1393                           *fib_entry_index2));
1394 }
1395
1396 void
1397 fib_entry_lock (fib_node_index_t fib_entry_index)
1398 {
1399     fib_entry_t *fib_entry;
1400
1401     fib_entry = fib_entry_get(fib_entry_index);
1402
1403     fib_node_lock(&fib_entry->fe_node);
1404 }
1405
1406 void
1407 fib_entry_unlock (fib_node_index_t fib_entry_index)
1408 {
1409     fib_entry_t *fib_entry;
1410
1411     fib_entry = fib_entry_get(fib_entry_index);
1412
1413     fib_node_unlock(&fib_entry->fe_node);
1414 }
1415
1416 void
1417 fib_entry_module_init (void)
1418 {
1419     fib_node_register_type (FIB_NODE_TYPE_ENTRY, &fib_entry_vft);
1420 }
1421
1422 void
1423 fib_entry_encode (fib_node_index_t fib_entry_index,
1424                   fib_route_path_encode_t **api_rpaths)
1425 {
1426     fib_entry_t *fib_entry;
1427
1428     fib_entry = fib_entry_get(fib_entry_index);
1429     fib_path_list_walk(fib_entry->fe_parent, fib_path_encode, api_rpaths);
1430 }
1431
1432 void
1433 fib_entry_get_prefix (fib_node_index_t fib_entry_index,
1434                       fib_prefix_t *pfx)
1435 {
1436     fib_entry_t *fib_entry;
1437
1438     fib_entry = fib_entry_get(fib_entry_index);
1439     *pfx = fib_entry->fe_prefix;
1440 }
1441
1442 u32
1443 fib_entry_get_fib_index (fib_node_index_t fib_entry_index)
1444 {
1445     fib_entry_t *fib_entry;
1446
1447     fib_entry = fib_entry_get(fib_entry_index);
1448
1449     return (fib_entry->fe_fib_index);
1450 }
1451
1452 u32
1453 fib_entry_pool_size (void)
1454 {
1455     return (pool_elts(fib_entry_pool));
1456 }
1457
1458 static clib_error_t *
1459 show_fib_entry_command (vlib_main_t * vm,
1460                         unformat_input_t * input,
1461                         vlib_cli_command_t * cmd)
1462 {
1463     fib_node_index_t fei;
1464
1465     if (unformat (input, "%d", &fei))
1466     {
1467         /*
1468          * show one in detail
1469          */
1470         if (!pool_is_free_index(fib_entry_pool, fei))
1471         {
1472             vlib_cli_output (vm, "%d@%U",
1473                              fei,
1474                              format_fib_entry, fei,
1475                              FIB_ENTRY_FORMAT_DETAIL2);
1476         }
1477         else
1478         {
1479             vlib_cli_output (vm, "entry %d invalid", fei);
1480         }
1481     }
1482     else
1483     {
1484         /*
1485          * show all
1486          */
1487         vlib_cli_output (vm, "FIB Entries:");
1488         pool_foreach_index(fei, fib_entry_pool,
1489         ({
1490             vlib_cli_output (vm, "%d@%U",
1491                              fei,
1492                              format_fib_entry, fei,
1493                              FIB_ENTRY_FORMAT_BRIEF);
1494         }));
1495     }
1496
1497     return (NULL);
1498 }
1499
1500 VLIB_CLI_COMMAND (show_fib_entry, static) = {
1501   .path = "show fib entry",
1502   .function = show_fib_entry_command,
1503   .short_help = "show fib entry",
1504 };