ip: Protocol Independent IP Neighbors
[vpp.git] / src / vnet / fib / ip6_fib.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/fib/ip6_fib.h>
17 #include <vnet/fib/fib_table.h>
18 #include <vnet/dpo/ip6_ll_dpo.h>
19
20 #include <vppinfra/bihash_24_8.h>
21 #include <vppinfra/bihash_template.c>
22
23 static void
24 vnet_ip6_fib_init (u32 fib_index)
25 {
26     fib_prefix_t pfx = {
27         .fp_proto = FIB_PROTOCOL_IP6,
28         .fp_len = 0,
29         .fp_addr = {
30             .ip6 = {
31                 { 0, 0, },
32             },
33         }
34     };
35
36     /*
37      * Add the default route.
38      */
39     fib_table_entry_special_add(fib_index,
40                                 &pfx,
41                                 FIB_SOURCE_DEFAULT_ROUTE,
42                                 FIB_ENTRY_FLAG_DROP);
43
44     /*
45      * all link local via the link local lookup DPO
46      */
47     pfx.fp_addr.ip6.as_u64[0] = clib_host_to_net_u64 (0xFE80000000000000ULL);
48     pfx.fp_addr.ip6.as_u64[1] = 0;
49     pfx.fp_len = 10;
50     fib_table_entry_special_dpo_add(fib_index,
51                                     &pfx,
52                                     FIB_SOURCE_SPECIAL,
53                                     FIB_ENTRY_FLAG_NONE,
54                                     ip6_ll_dpo_get());
55 }
56
57 static u32
58 create_fib_with_table_id (u32 table_id,
59                           fib_source_t src,
60                           fib_table_flags_t flags,
61                           u8 *desc)
62 {
63     fib_table_t *fib_table;
64     ip6_fib_t *v6_fib;
65
66     pool_get(ip6_main.fibs, fib_table);
67     pool_get_aligned(ip6_main.v6_fibs, v6_fib, CLIB_CACHE_LINE_BYTES);
68
69     clib_memset(fib_table, 0, sizeof(*fib_table));
70     clib_memset(v6_fib, 0, sizeof(*v6_fib));
71
72     ASSERT((fib_table - ip6_main.fibs) ==
73            (v6_fib - ip6_main.v6_fibs));
74     
75     fib_table->ft_proto = FIB_PROTOCOL_IP6;
76     fib_table->ft_index =
77             v6_fib->index =
78                 (fib_table - ip6_main.fibs);
79
80     hash_set(ip6_main.fib_index_by_table_id, table_id, fib_table->ft_index);
81
82     fib_table->ft_table_id =
83         v6_fib->table_id =
84             table_id;
85     fib_table->ft_flow_hash_config = IP_FLOW_HASH_DEFAULT;
86     fib_table->ft_flags = flags;
87     fib_table->ft_desc = desc;
88
89     vnet_ip6_fib_init(fib_table->ft_index);
90     fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP6, src);
91
92     return (fib_table->ft_index);
93 }
94
95 u32
96 ip6_fib_table_find_or_create_and_lock (u32 table_id,
97                                        fib_source_t src)
98 {
99     uword * p;
100
101     p = hash_get (ip6_main.fib_index_by_table_id, table_id);
102     if (NULL == p)
103         return create_fib_with_table_id(table_id, src,
104                                         FIB_TABLE_FLAG_NONE,
105                                         NULL);
106
107     fib_table_lock(p[0], FIB_PROTOCOL_IP6, src);
108
109     return (p[0]);
110 }
111
112 u32
113 ip6_fib_table_create_and_lock (fib_source_t src,
114                                fib_table_flags_t flags,
115                                u8 *desc)
116 {
117     return (create_fib_with_table_id(~0, src, flags, desc));
118 }
119
120 void
121 ip6_fib_table_destroy (u32 fib_index)
122 {
123     /*
124      * all link local first ...
125      */
126     fib_prefix_t pfx = {
127         .fp_proto = FIB_PROTOCOL_IP6,
128         .fp_len = 10,
129         .fp_addr = {
130             .ip6 = {
131                 .as_u8 = {
132                     [0] = 0xFE,
133                     [1] = 0x80,
134                 },
135             },
136         }
137     };
138     fib_table_entry_delete(fib_index,
139                            &pfx,
140                            FIB_SOURCE_SPECIAL);
141
142     /*
143      * ... then the default route.
144      */
145     pfx.fp_addr.ip6.as_u64[0] = 0;
146     pfx.fp_len = 00;
147     fib_table_entry_special_remove(fib_index,
148                                    &pfx,
149                                    FIB_SOURCE_DEFAULT_ROUTE);
150
151     fib_table_t *fib_table = fib_table_get(fib_index, FIB_PROTOCOL_IP6);
152     fib_source_t source;
153
154     /*
155      * validate no more routes.
156      */
157 #ifdef CLIB_DEBUG
158     if (0 != fib_table->ft_total_route_counts)
159         fib_table_assert_empty(fib_table);
160 #endif
161
162     vec_foreach_index(source, fib_table->ft_src_route_counts)
163     {
164         ASSERT(0 == fib_table->ft_src_route_counts[source]);
165     }
166
167     if (~0 != fib_table->ft_table_id)
168     {
169         hash_unset (ip6_main.fib_index_by_table_id, fib_table->ft_table_id);
170     }
171     vec_free(fib_table->ft_src_route_counts);
172     pool_put_index(ip6_main.v6_fibs, fib_table->ft_index);
173     pool_put(ip6_main.fibs, fib_table);
174 }
175
176 fib_node_index_t
177 ip6_fib_table_lookup (u32 fib_index,
178                       const ip6_address_t *addr,
179                       u32 len)
180 {
181     ip6_fib_table_instance_t *table;
182     clib_bihash_kv_24_8_t kv, value;
183     int i, n_p, rv;
184     u64 fib;
185
186     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
187     n_p = vec_len (table->prefix_lengths_in_search_order);
188
189     kv.key[0] = addr->as_u64[0];
190     kv.key[1] = addr->as_u64[1];
191     fib = ((u64)((fib_index))<<32);
192
193     /*
194      * start search from a mask length same length or shorter.
195      * we don't want matches longer than the mask passed
196      */
197     i = 0;
198     while (i < n_p && table->prefix_lengths_in_search_order[i] > len)
199     {
200         i++;
201     }
202
203     for (; i < n_p; i++)
204     {
205         int dst_address_length = table->prefix_lengths_in_search_order[i];
206         ip6_address_t * mask = &ip6_main.fib_masks[dst_address_length];
207       
208         ASSERT(dst_address_length >= 0 && dst_address_length <= 128);
209         //As lengths are decreasing, masks are increasingly specific.
210         kv.key[0] &= mask->as_u64[0];
211         kv.key[1] &= mask->as_u64[1];
212         kv.key[2] = fib | dst_address_length;
213       
214         rv = clib_bihash_search_inline_2_24_8(&table->ip6_hash, &kv, &value);
215         if (rv == 0)
216             return value.value;
217     }
218
219     return (FIB_NODE_INDEX_INVALID);
220 }
221
222 fib_node_index_t
223 ip6_fib_table_lookup_exact_match (u32 fib_index,
224                                   const ip6_address_t *addr,
225                                   u32 len)
226 {
227     ip6_fib_table_instance_t *table;
228     clib_bihash_kv_24_8_t kv, value;
229     ip6_address_t *mask;
230     u64 fib;
231     int rv;
232
233     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
234     mask = &ip6_main.fib_masks[len];
235     fib = ((u64)((fib_index))<<32);
236
237     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
238     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
239     kv.key[2] = fib | len;
240       
241     rv = clib_bihash_search_inline_2_24_8(&table->ip6_hash, &kv, &value);
242     if (rv == 0)
243         return value.value;
244
245     return (FIB_NODE_INDEX_INVALID);
246 }
247
248 static void
249 compute_prefix_lengths_in_search_order (ip6_fib_table_instance_t *table)
250 {
251     int i;
252     vec_reset_length (table->prefix_lengths_in_search_order);
253     /* Note: bitmap reversed so this is in fact a longest prefix match */
254     clib_bitmap_foreach (i, table->non_empty_dst_address_length_bitmap,
255     ({
256         int dst_address_length = 128 - i;
257         vec_add1(table->prefix_lengths_in_search_order, dst_address_length);
258     }));
259 }
260
261 void
262 ip6_fib_table_entry_remove (u32 fib_index,
263                             const ip6_address_t *addr,
264                             u32 len)
265 {
266     ip6_fib_table_instance_t *table;
267     clib_bihash_kv_24_8_t kv;
268     ip6_address_t *mask;
269     u64 fib;
270
271     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
272     mask = &ip6_main.fib_masks[len];
273     fib = ((u64)((fib_index))<<32);
274
275     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
276     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
277     kv.key[2] = fib | len;
278
279     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 0);
280
281     /* refcount accounting */
282     ASSERT (table->dst_address_length_refcounts[len] > 0);
283     if (--table->dst_address_length_refcounts[len] == 0)
284     {
285         table->non_empty_dst_address_length_bitmap =
286             clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
287                              128 - len, 0);
288         compute_prefix_lengths_in_search_order (table);
289     }
290 }
291
292 void
293 ip6_fib_table_entry_insert (u32 fib_index,
294                             const ip6_address_t *addr,
295                             u32 len,
296                             fib_node_index_t fib_entry_index)
297 {
298     ip6_fib_table_instance_t *table;
299     clib_bihash_kv_24_8_t kv;
300     ip6_address_t *mask;
301     u64 fib;
302
303     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
304     mask = &ip6_main.fib_masks[len];
305     fib = ((u64)((fib_index))<<32);
306
307     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
308     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
309     kv.key[2] = fib | len;
310     kv.value = fib_entry_index;
311
312     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 1);
313
314     table->dst_address_length_refcounts[len]++;
315
316     table->non_empty_dst_address_length_bitmap =
317         clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
318                          128 - len, 1);
319     compute_prefix_lengths_in_search_order (table);
320 }
321
322 u32 ip6_fib_table_fwding_lookup_with_if_index (ip6_main_t * im,
323                                                u32 sw_if_index,
324                                                const ip6_address_t * dst)
325 {
326     u32 fib_index = vec_elt (im->fib_index_by_sw_if_index, sw_if_index);
327     return ip6_fib_table_fwding_lookup(fib_index, dst);
328 }
329
330 u32
331 ip6_fib_table_get_index_for_sw_if_index (u32 sw_if_index)
332 {
333     if (sw_if_index >= vec_len(ip6_main.fib_index_by_sw_if_index))
334     {
335         /*
336          * This is the case for interfaces that are not yet mapped to
337          * a IP table
338          */
339         return (~0);
340     }
341     return (ip6_main.fib_index_by_sw_if_index[sw_if_index]);
342 }
343
344 void
345 ip6_fib_table_fwding_dpo_update (u32 fib_index,
346                                  const ip6_address_t *addr,
347                                  u32 len,
348                                  const dpo_id_t *dpo)
349 {
350     ip6_fib_table_instance_t *table;
351     clib_bihash_kv_24_8_t kv;
352     ip6_address_t *mask;
353     u64 fib;
354
355     table = &ip6_main.ip6_table[IP6_FIB_TABLE_FWDING];
356     mask = &ip6_main.fib_masks[len];
357     fib = ((u64)((fib_index))<<32);
358
359     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
360     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
361     kv.key[2] = fib | len;
362     kv.value = dpo->dpoi_index;
363
364     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 1);
365
366     table->dst_address_length_refcounts[len]++;
367
368     table->non_empty_dst_address_length_bitmap =
369         clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
370                          128 - len, 1);
371     compute_prefix_lengths_in_search_order (table);
372 }
373
374 void
375 ip6_fib_table_fwding_dpo_remove (u32 fib_index,
376                                  const ip6_address_t *addr,
377                                  u32 len,
378                                  const dpo_id_t *dpo)
379 {
380     ip6_fib_table_instance_t *table;
381     clib_bihash_kv_24_8_t kv;
382     ip6_address_t *mask;
383     u64 fib;
384
385     table = &ip6_main.ip6_table[IP6_FIB_TABLE_FWDING];
386     mask = &ip6_main.fib_masks[len];
387     fib = ((u64)((fib_index))<<32);
388
389     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
390     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
391     kv.key[2] = fib | len;
392     kv.value = dpo->dpoi_index;
393
394     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 0);
395
396     /* refcount accounting */
397     ASSERT (table->dst_address_length_refcounts[len] > 0);
398     if (--table->dst_address_length_refcounts[len] == 0)
399     {
400         table->non_empty_dst_address_length_bitmap =
401             clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
402                              128 - len, 0);
403         compute_prefix_lengths_in_search_order (table);
404     }
405 }
406
407 /**
408  * @brief Context when walking the IPv6 table. Since all VRFs are in the
409  * same hash table, we need to filter only those we need as we walk
410  */
411 typedef struct ip6_fib_walk_ctx_t_
412 {
413     u32 i6w_fib_index;
414     fib_table_walk_fn_t i6w_fn;
415     void *i6w_ctx;
416     fib_prefix_t i6w_root;
417     fib_prefix_t *i6w_sub_trees;
418 } ip6_fib_walk_ctx_t;
419
420 static int
421 ip6_fib_walk_cb (clib_bihash_kv_24_8_t * kvp,
422                  void *arg)
423 {
424     ip6_fib_walk_ctx_t *ctx = arg;
425     ip6_address_t key;
426
427     if ((kvp->key[2] >> 32) == ctx->i6w_fib_index)
428     {
429         key.as_u64[0] = kvp->key[0];
430         key.as_u64[1] = kvp->key[1];
431
432         if (ip6_destination_matches_route(&ip6_main,
433                                           &key,
434                                           &ctx->i6w_root.fp_addr.ip6,
435                                           ctx->i6w_root.fp_len))
436         {
437             const fib_prefix_t *sub_tree;
438             int skip = 0;
439
440             /*
441              * exclude sub-trees the walk does not want to explore
442              */
443             vec_foreach(sub_tree, ctx->i6w_sub_trees)
444             {
445                 if (ip6_destination_matches_route(&ip6_main,
446                                                   &key,
447                                                   &sub_tree->fp_addr.ip6,
448                                                   sub_tree->fp_len))
449                 {
450                     skip = 1;
451                     break;
452                 }
453             }
454
455             if (!skip)
456             {
457                 switch (ctx->i6w_fn(kvp->value, ctx->i6w_ctx))
458                 {
459                 case FIB_TABLE_WALK_CONTINUE:
460                     break;
461                 case FIB_TABLE_WALK_SUB_TREE_STOP: {
462                     fib_prefix_t pfx = {
463                         .fp_proto = FIB_PROTOCOL_IP6,
464                         .fp_len = kvp->key[2] & 0xffffffff,
465                         .fp_addr.ip6 = key,
466                     };
467                     vec_add1(ctx->i6w_sub_trees, pfx);
468                     break;
469                 }
470                 case FIB_TABLE_WALK_STOP:
471                     goto done;
472                 }
473             }
474         }
475     }
476 done:
477
478     return (1);
479 }
480
481 void
482 ip6_fib_table_walk (u32 fib_index,
483                     fib_table_walk_fn_t fn,
484                     void *arg)
485 {
486     ip6_fib_walk_ctx_t ctx = {
487         .i6w_fib_index = fib_index,
488         .i6w_fn = fn,
489         .i6w_ctx = arg,
490         .i6w_root = {
491             .fp_proto = FIB_PROTOCOL_IP6,
492         },
493         .i6w_sub_trees = NULL,
494     };
495
496     clib_bihash_foreach_key_value_pair_24_8(
497         &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
498         ip6_fib_walk_cb,
499         &ctx);
500
501     vec_free(ctx.i6w_sub_trees);
502 }
503
504 void
505 ip6_fib_table_sub_tree_walk (u32 fib_index,
506                              const fib_prefix_t *root,
507                              fib_table_walk_fn_t fn,
508                              void *arg)
509 {
510     ip6_fib_walk_ctx_t ctx = {
511         .i6w_fib_index = fib_index,
512         .i6w_fn = fn,
513         .i6w_ctx = arg,
514         .i6w_root = *root,
515     };
516
517     clib_bihash_foreach_key_value_pair_24_8(
518         &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
519         ip6_fib_walk_cb,
520         &ctx);
521 }
522
523 typedef struct ip6_fib_show_ctx_t_ {
524     fib_node_index_t *entries;
525 } ip6_fib_show_ctx_t;
526
527 static fib_table_walk_rc_t
528 ip6_fib_table_show_walk (fib_node_index_t fib_entry_index,
529                          void *arg)
530 {
531     ip6_fib_show_ctx_t *ctx = arg;
532
533     vec_add1(ctx->entries, fib_entry_index);
534
535     return (FIB_TABLE_WALK_CONTINUE);
536 }
537
538 static void
539 ip6_fib_table_show_all (ip6_fib_t *fib,
540                         vlib_main_t * vm)
541 {
542     fib_node_index_t *fib_entry_index;
543     ip6_fib_show_ctx_t ctx = {
544         .entries = NULL,
545     };
546
547     ip6_fib_table_walk(fib->index, ip6_fib_table_show_walk, &ctx);
548     vec_sort_with_function(ctx.entries, fib_entry_cmp_for_sort);
549
550     vec_foreach(fib_entry_index, ctx.entries)
551     {
552         vlib_cli_output(vm, "%U",
553                         format_fib_entry,
554                         *fib_entry_index,
555                         FIB_ENTRY_FORMAT_BRIEF);
556     }
557
558     vec_free(ctx.entries);
559 }
560
561 static void
562 ip6_fib_table_show_one (ip6_fib_t *fib,
563                         vlib_main_t * vm,
564                         ip6_address_t *address,
565                         u32 mask_len,
566                         int detail)
567 {
568     vlib_cli_output(vm, "%U",
569                     format_fib_entry,
570                     ip6_fib_table_lookup(fib->index, address, mask_len),
571                     (detail ?
572                      FIB_ENTRY_FORMAT_DETAIL2:
573                      FIB_ENTRY_FORMAT_DETAIL));
574 }
575
576 u8 *
577 format_ip6_fib_table_memory (u8 * s, va_list * args)
578 {
579     uword bytes_inuse;
580
581     bytes_inuse = (alloc_arena_next(&(ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash)) +
582                    alloc_arena_next(&(ip6_main.ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash)));
583
584     s = format(s, "%=30s %=6d %=12ld\n",
585                "IPv6 unicast",
586                pool_elts(ip6_main.fibs),
587                bytes_inuse);
588     return (s);
589 }
590
591 typedef struct {
592   u32 fib_index;
593   u64 count_by_prefix_length[129];
594 } count_routes_in_fib_at_prefix_length_arg_t;
595
596 static int
597 count_routes_in_fib_at_prefix_length (clib_bihash_kv_24_8_t * kvp,
598                                       void *arg)
599 {
600   count_routes_in_fib_at_prefix_length_arg_t * ap = arg;
601   int mask_width;
602
603   if ((kvp->key[2]>>32) != ap->fib_index)
604       return (BIHASH_WALK_CONTINUE);
605
606   mask_width = kvp->key[2] & 0xFF;
607
608   ap->count_by_prefix_length[mask_width]++;
609
610   return (BIHASH_WALK_CONTINUE);
611 }
612
613 static clib_error_t *
614 ip6_show_fib (vlib_main_t * vm,
615               unformat_input_t * input,
616               vlib_cli_command_t * cmd)
617 {
618     count_routes_in_fib_at_prefix_length_arg_t _ca, *ca = &_ca;
619     ip6_main_t * im6 = &ip6_main;
620     fib_table_t *fib_table;
621     ip6_fib_t * fib;
622     int verbose, matching;
623     ip6_address_t matching_address;
624     u32 mask_len  = 128;
625     int table_id = -1, fib_index = ~0;
626     int detail = 0;
627     int hash = 0;
628
629     verbose = 1;
630     matching = 0;
631
632     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
633     {
634         if (unformat (input, "brief")   ||
635             unformat (input, "summary") ||
636             unformat (input, "sum"))
637             verbose = 0;
638  
639         else if (unformat (input, "detail")   ||
640                  unformat (input, "det"))
641             detail = 1;
642
643         else if (unformat (input, "hash") ||
644                  unformat (input, "mem") ||
645                  unformat (input, "memory"))
646             hash = 1;
647
648         else if (unformat (input, "%U/%d",
649                            unformat_ip6_address, &matching_address, &mask_len))
650             matching = 1;
651
652         else if (unformat (input, "%U", unformat_ip6_address, &matching_address))
653             matching = 1;
654
655         else if (unformat (input, "table %d", &table_id))
656             ;
657         else if (unformat (input, "index %d", &fib_index))
658             ;
659         else
660             break;
661     }
662
663     if (hash)
664     {
665         vlib_cli_output (vm, "IPv6 Non-Forwarding Hash Table:\n%U\n",
666                          BV (format_bihash),
667                          &im6->ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
668                          detail);
669         vlib_cli_output (vm, "IPv6 Forwarding Hash Table:\n%U\n",
670                          BV (format_bihash),
671                          &im6->ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash,
672                          detail);
673         return (NULL);
674     }
675
676     pool_foreach (fib_table, im6->fibs,
677     ({
678         fib_source_t source;
679         u8 *s = NULL;
680
681         fib = pool_elt_at_index(im6->v6_fibs, fib_table->ft_index);
682         if (table_id >= 0 && table_id != (int)fib->table_id)
683             continue;
684         if (fib_index != ~0 && fib_index != (int)fib->index)
685             continue;
686         if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL)
687             continue;
688
689         s = format(s, "%U, fib_index:%d, flow hash:[%U] epoch:%d flags:%U locks:[",
690                    format_fib_table_name, fib->index,
691                    FIB_PROTOCOL_IP6,
692                    fib->index,
693                    format_ip_flow_hash_config,
694                    fib_table->ft_flow_hash_config,
695                    fib_table->ft_epoch,
696                    format_fib_table_flags, fib_table->ft_flags);
697
698         vec_foreach_index(source, fib_table->ft_locks)
699         {
700             if (0 != fib_table->ft_locks[source])
701             {
702                 s = format(s, "%U:%d, ",
703                            format_fib_source, source,
704                            fib_table->ft_locks[source]);
705             }
706         }
707         s = format (s, "]");
708         vlib_cli_output (vm, "%v", s);
709         vec_free(s);
710
711         /* Show summary? */
712         if (! verbose)
713         {
714             clib_bihash_24_8_t * h = &im6->ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash;
715             int len;
716
717             vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
718
719             clib_memset (ca, 0, sizeof(*ca));
720             ca->fib_index = fib->index;
721
722             clib_bihash_foreach_key_value_pair_24_8
723                 (h, count_routes_in_fib_at_prefix_length, ca);
724
725             for (len = 128; len >= 0; len--)
726             {
727                 if (ca->count_by_prefix_length[len])
728                     vlib_cli_output (vm, "%=20d%=16lld", 
729                                      len, ca->count_by_prefix_length[len]);
730             }
731             continue;
732         }
733
734         if (!matching)
735         {
736             ip6_fib_table_show_all(fib, vm);
737         }
738         else
739         {
740             ip6_fib_table_show_one(fib, vm, &matching_address, mask_len, detail);
741         }
742     }));
743
744     return 0;
745 }
746
747 /*?
748  * This command displays the IPv6 FIB Tables (VRF Tables) and the route
749  * entries for each table.
750  *
751  * @note This command will run for a long time when the FIB tables are
752  * comprised of millions of entries. For those senarios, consider displaying
753  * in summary mode.
754  *
755  * @cliexpar
756  * @parblock
757  * Example of how to display all the IPv6 FIB tables:
758  * @cliexstart{show ip6 fib}
759  * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
760  * @::/0
761  *   unicast-ip6-chain
762  *   [@0]: dpo-load-balance: [index:5 buckets:1 uRPF:5 to:[0:0]]
763  *     [0] [@0]: dpo-drop ip6
764  * fe80::/10
765  *   unicast-ip6-chain
766  *   [@0]: dpo-load-balance: [index:10 buckets:1 uRPF:10 to:[0:0]]
767  *     [0] [@2]: dpo-receive
768  * ff02::1/128
769  *   unicast-ip6-chain
770  *   [@0]: dpo-load-balance: [index:8 buckets:1 uRPF:8 to:[0:0]]
771  *     [0] [@2]: dpo-receive
772  * ff02::2/128
773  *   unicast-ip6-chain
774  *   [@0]: dpo-load-balance: [index:7 buckets:1 uRPF:7 to:[0:0]]
775  *     [0] [@2]: dpo-receive
776  * ff02::16/128
777  *   unicast-ip6-chain
778  *   [@0]: dpo-load-balance: [index:9 buckets:1 uRPF:9 to:[0:0]]
779  *     [0] [@2]: dpo-receive
780  * ff02::1:ff00:0/104
781  *   unicast-ip6-chain
782  *   [@0]: dpo-load-balance: [index:6 buckets:1 uRPF:6 to:[0:0]]
783  *     [0] [@2]: dpo-receive
784  * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
785  * @::/0
786  *   unicast-ip6-chain
787  *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
788  *     [0] [@0]: dpo-drop ip6
789  * @::a:1:1:0:4/126
790  *   unicast-ip6-chain
791  *   [@0]: dpo-load-balance: [index:27 buckets:1 uRPF:26 to:[0:0]]
792  *     [0] [@4]: ipv6-glean: af_packet0
793  * @::a:1:1:0:7/128
794  *   unicast-ip6-chain
795  *   [@0]: dpo-load-balance: [index:28 buckets:1 uRPF:27 to:[0:0]]
796  *     [0] [@2]: dpo-receive: @::a:1:1:0:7 on af_packet0
797  * fe80::/10
798  *   unicast-ip6-chain
799  *   [@0]: dpo-load-balance: [index:26 buckets:1 uRPF:25 to:[0:0]]
800  *     [0] [@2]: dpo-receive
801  * fe80::fe:3eff:fe3e:9222/128
802  *   unicast-ip6-chain
803  *   [@0]: dpo-load-balance: [index:29 buckets:1 uRPF:28 to:[0:0]]
804  *     [0] [@2]: dpo-receive: fe80::fe:3eff:fe3e:9222 on af_packet0
805  * ff02::1/128
806  *   unicast-ip6-chain
807  *   [@0]: dpo-load-balance: [index:24 buckets:1 uRPF:23 to:[0:0]]
808  *     [0] [@2]: dpo-receive
809  * ff02::2/128
810  *   unicast-ip6-chain
811  *   [@0]: dpo-load-balance: [index:23 buckets:1 uRPF:22 to:[0:0]]
812  *     [0] [@2]: dpo-receive
813  * ff02::16/128
814  *   unicast-ip6-chain
815  *   [@0]: dpo-load-balance: [index:25 buckets:1 uRPF:24 to:[0:0]]
816  *     [0] [@2]: dpo-receive
817  * ff02::1:ff00:0/104
818  *   unicast-ip6-chain
819  *   [@0]: dpo-load-balance: [index:22 buckets:1 uRPF:21 to:[0:0]]
820  *     [0] [@2]: dpo-receive
821  * @cliexend
822  *
823  * Example of how to display a summary of all IPv6 FIB tables:
824  * @cliexstart{show ip6 fib summary}
825  * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
826  *     Prefix length         Count
827  *          128                3
828  *          104                1
829  *          10                 1
830  *           0                 1
831  * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
832  *     Prefix length         Count
833  *          128                5
834  *          126                1
835  *          104                1
836  *          10                 1
837  *           0                 1
838  * @cliexend
839  * @endparblock
840  ?*/
841 /* *INDENT-OFF* */
842 VLIB_CLI_COMMAND (ip6_show_fib_command, static) = {
843     .path = "show ip6 fib",
844     .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]] [detail]",
845     .function = ip6_show_fib,
846 };
847 /* *INDENT-ON* */