ddd70790759d0ea0dd96c2b424a6073d0a2a9f20
[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 #if CLIB_DEBUG > 0
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     u8 *old, *prefix_lengths_in_search_order = NULL;
252     int i;
253
254     /*
255      * build the list in a scratch space then cutover so the workers
256      * can continue uninterrupted.
257      */
258     old = table->prefix_lengths_in_search_order;
259
260     /* Note: bitmap reversed so this is in fact a longest prefix match */
261     clib_bitmap_foreach (i, table->non_empty_dst_address_length_bitmap,
262     ({
263         int dst_address_length = 128 - i;
264         vec_add1(prefix_lengths_in_search_order, dst_address_length);
265     }));
266
267     table->prefix_lengths_in_search_order = prefix_lengths_in_search_order;
268
269     /*
270      * let the workers go once round the track before we free the old set
271      */
272     vlib_worker_wait_one_loop();
273     vec_free(old);
274 }
275
276 void
277 ip6_fib_table_entry_remove (u32 fib_index,
278                             const ip6_address_t *addr,
279                             u32 len)
280 {
281     ip6_fib_table_instance_t *table;
282     clib_bihash_kv_24_8_t kv;
283     ip6_address_t *mask;
284     u64 fib;
285
286     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
287     mask = &ip6_main.fib_masks[len];
288     fib = ((u64)((fib_index))<<32);
289
290     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
291     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
292     kv.key[2] = fib | len;
293
294     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 0);
295
296     /* refcount accounting */
297     ASSERT (table->dst_address_length_refcounts[len] > 0);
298     if (--table->dst_address_length_refcounts[len] == 0)
299     {
300         table->non_empty_dst_address_length_bitmap =
301             clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
302                              128 - len, 0);
303         compute_prefix_lengths_in_search_order (table);
304     }
305 }
306
307 void
308 ip6_fib_table_entry_insert (u32 fib_index,
309                             const ip6_address_t *addr,
310                             u32 len,
311                             fib_node_index_t fib_entry_index)
312 {
313     ip6_fib_table_instance_t *table;
314     clib_bihash_kv_24_8_t kv;
315     ip6_address_t *mask;
316     u64 fib;
317
318     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
319     mask = &ip6_main.fib_masks[len];
320     fib = ((u64)((fib_index))<<32);
321
322     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
323     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
324     kv.key[2] = fib | len;
325     kv.value = fib_entry_index;
326
327     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 1);
328
329     if (0 == table->dst_address_length_refcounts[len]++)
330     {
331         table->non_empty_dst_address_length_bitmap =
332             clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
333                              128 - len, 1);
334         compute_prefix_lengths_in_search_order (table);
335     }
336 }
337
338 u32 ip6_fib_table_fwding_lookup_with_if_index (ip6_main_t * im,
339                                                u32 sw_if_index,
340                                                const ip6_address_t * dst)
341 {
342     u32 fib_index = vec_elt (im->fib_index_by_sw_if_index, sw_if_index);
343     return ip6_fib_table_fwding_lookup(fib_index, dst);
344 }
345
346 u32
347 ip6_fib_table_get_index_for_sw_if_index (u32 sw_if_index)
348 {
349     if (sw_if_index >= vec_len(ip6_main.fib_index_by_sw_if_index))
350     {
351         /*
352          * This is the case for interfaces that are not yet mapped to
353          * a IP table
354          */
355         return (~0);
356     }
357     return (ip6_main.fib_index_by_sw_if_index[sw_if_index]);
358 }
359
360 void
361 ip6_fib_table_fwding_dpo_update (u32 fib_index,
362                                  const ip6_address_t *addr,
363                                  u32 len,
364                                  const dpo_id_t *dpo)
365 {
366     ip6_fib_table_instance_t *table;
367     clib_bihash_kv_24_8_t kv;
368     ip6_address_t *mask;
369     u64 fib;
370
371     table = &ip6_main.ip6_table[IP6_FIB_TABLE_FWDING];
372     mask = &ip6_main.fib_masks[len];
373     fib = ((u64)((fib_index))<<32);
374
375     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
376     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
377     kv.key[2] = fib | len;
378     kv.value = dpo->dpoi_index;
379
380     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 1);
381
382     if (0 == table->dst_address_length_refcounts[len]++)
383     {
384         table->non_empty_dst_address_length_bitmap =
385             clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
386                              128 - len, 1);
387         compute_prefix_lengths_in_search_order (table);
388     }
389 }
390
391 void
392 ip6_fib_table_fwding_dpo_remove (u32 fib_index,
393                                  const ip6_address_t *addr,
394                                  u32 len,
395                                  const dpo_id_t *dpo)
396 {
397     ip6_fib_table_instance_t *table;
398     clib_bihash_kv_24_8_t kv;
399     ip6_address_t *mask;
400     u64 fib;
401
402     table = &ip6_main.ip6_table[IP6_FIB_TABLE_FWDING];
403     mask = &ip6_main.fib_masks[len];
404     fib = ((u64)((fib_index))<<32);
405
406     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
407     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
408     kv.key[2] = fib | len;
409     kv.value = dpo->dpoi_index;
410
411     clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 0);
412
413     /* refcount accounting */
414     ASSERT (table->dst_address_length_refcounts[len] > 0);
415     if (--table->dst_address_length_refcounts[len] == 0)
416     {
417         table->non_empty_dst_address_length_bitmap =
418             clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
419                              128 - len, 0);
420         compute_prefix_lengths_in_search_order (table);
421     }
422 }
423
424 /**
425  * @brief Context when walking the IPv6 table. Since all VRFs are in the
426  * same hash table, we need to filter only those we need as we walk
427  */
428 typedef struct ip6_fib_walk_ctx_t_
429 {
430     u32 i6w_fib_index;
431     fib_table_walk_fn_t i6w_fn;
432     void *i6w_ctx;
433     fib_prefix_t i6w_root;
434     fib_prefix_t *i6w_sub_trees;
435 } ip6_fib_walk_ctx_t;
436
437 static int
438 ip6_fib_walk_cb (clib_bihash_kv_24_8_t * kvp,
439                  void *arg)
440 {
441     ip6_fib_walk_ctx_t *ctx = arg;
442     ip6_address_t key;
443
444     if ((kvp->key[2] >> 32) == ctx->i6w_fib_index)
445     {
446         key.as_u64[0] = kvp->key[0];
447         key.as_u64[1] = kvp->key[1];
448
449         if (ip6_destination_matches_route(&ip6_main,
450                                           &key,
451                                           &ctx->i6w_root.fp_addr.ip6,
452                                           ctx->i6w_root.fp_len))
453         {
454             const fib_prefix_t *sub_tree;
455             int skip = 0;
456
457             /*
458              * exclude sub-trees the walk does not want to explore
459              */
460             vec_foreach(sub_tree, ctx->i6w_sub_trees)
461             {
462                 if (ip6_destination_matches_route(&ip6_main,
463                                                   &key,
464                                                   &sub_tree->fp_addr.ip6,
465                                                   sub_tree->fp_len))
466                 {
467                     skip = 1;
468                     break;
469                 }
470             }
471
472             if (!skip)
473             {
474                 switch (ctx->i6w_fn(kvp->value, ctx->i6w_ctx))
475                 {
476                 case FIB_TABLE_WALK_CONTINUE:
477                     break;
478                 case FIB_TABLE_WALK_SUB_TREE_STOP: {
479                     fib_prefix_t pfx = {
480                         .fp_proto = FIB_PROTOCOL_IP6,
481                         .fp_len = kvp->key[2] & 0xffffffff,
482                         .fp_addr.ip6 = key,
483                     };
484                     vec_add1(ctx->i6w_sub_trees, pfx);
485                     break;
486                 }
487                 case FIB_TABLE_WALK_STOP:
488                     goto done;
489                 }
490             }
491         }
492     }
493 done:
494
495     return (1);
496 }
497
498 void
499 ip6_fib_table_walk (u32 fib_index,
500                     fib_table_walk_fn_t fn,
501                     void *arg)
502 {
503     ip6_fib_walk_ctx_t ctx = {
504         .i6w_fib_index = fib_index,
505         .i6w_fn = fn,
506         .i6w_ctx = arg,
507         .i6w_root = {
508             .fp_proto = FIB_PROTOCOL_IP6,
509         },
510         .i6w_sub_trees = NULL,
511     };
512
513     clib_bihash_foreach_key_value_pair_24_8(
514         &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
515         ip6_fib_walk_cb,
516         &ctx);
517
518     vec_free(ctx.i6w_sub_trees);
519 }
520
521 void
522 ip6_fib_table_sub_tree_walk (u32 fib_index,
523                              const fib_prefix_t *root,
524                              fib_table_walk_fn_t fn,
525                              void *arg)
526 {
527     ip6_fib_walk_ctx_t ctx = {
528         .i6w_fib_index = fib_index,
529         .i6w_fn = fn,
530         .i6w_ctx = arg,
531         .i6w_root = *root,
532     };
533
534     clib_bihash_foreach_key_value_pair_24_8(
535         &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
536         ip6_fib_walk_cb,
537         &ctx);
538 }
539
540 typedef struct ip6_fib_show_ctx_t_ {
541     fib_node_index_t *entries;
542 } ip6_fib_show_ctx_t;
543
544 static fib_table_walk_rc_t
545 ip6_fib_table_show_walk (fib_node_index_t fib_entry_index,
546                          void *arg)
547 {
548     ip6_fib_show_ctx_t *ctx = arg;
549
550     vec_add1(ctx->entries, fib_entry_index);
551
552     return (FIB_TABLE_WALK_CONTINUE);
553 }
554
555 static void
556 ip6_fib_table_show_all (ip6_fib_t *fib,
557                         vlib_main_t * vm)
558 {
559     fib_node_index_t *fib_entry_index;
560     ip6_fib_show_ctx_t ctx = {
561         .entries = NULL,
562     };
563
564     ip6_fib_table_walk(fib->index, ip6_fib_table_show_walk, &ctx);
565     vec_sort_with_function(ctx.entries, fib_entry_cmp_for_sort);
566
567     vec_foreach(fib_entry_index, ctx.entries)
568     {
569         vlib_cli_output(vm, "%U",
570                         format_fib_entry,
571                         *fib_entry_index,
572                         FIB_ENTRY_FORMAT_BRIEF);
573     }
574
575     vec_free(ctx.entries);
576 }
577
578 static void
579 ip6_fib_table_show_one (ip6_fib_t *fib,
580                         vlib_main_t * vm,
581                         ip6_address_t *address,
582                         u32 mask_len,
583                         int detail)
584 {
585     vlib_cli_output(vm, "%U",
586                     format_fib_entry,
587                     ip6_fib_table_lookup(fib->index, address, mask_len),
588                     (detail ?
589                      FIB_ENTRY_FORMAT_DETAIL2:
590                      FIB_ENTRY_FORMAT_DETAIL));
591 }
592
593 u8 *
594 format_ip6_fib_table_memory (u8 * s, va_list * args)
595 {
596     uword bytes_inuse;
597
598     bytes_inuse = (alloc_arena_next(&(ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash)) +
599                    alloc_arena_next(&(ip6_main.ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash)));
600
601     s = format(s, "%=30s %=6d %=12ld\n",
602                "IPv6 unicast",
603                pool_elts(ip6_main.fibs),
604                bytes_inuse);
605     return (s);
606 }
607
608 typedef struct {
609   u32 fib_index;
610   u64 count_by_prefix_length[129];
611 } count_routes_in_fib_at_prefix_length_arg_t;
612
613 static int
614 count_routes_in_fib_at_prefix_length (clib_bihash_kv_24_8_t * kvp,
615                                       void *arg)
616 {
617   count_routes_in_fib_at_prefix_length_arg_t * ap = arg;
618   int mask_width;
619
620   if ((kvp->key[2]>>32) != ap->fib_index)
621       return (BIHASH_WALK_CONTINUE);
622
623   mask_width = kvp->key[2] & 0xFF;
624
625   ap->count_by_prefix_length[mask_width]++;
626
627   return (BIHASH_WALK_CONTINUE);
628 }
629
630 static clib_error_t *
631 ip6_show_fib (vlib_main_t * vm,
632               unformat_input_t * input,
633               vlib_cli_command_t * cmd)
634 {
635     count_routes_in_fib_at_prefix_length_arg_t _ca, *ca = &_ca;
636     ip6_main_t * im6 = &ip6_main;
637     fib_table_t *fib_table;
638     ip6_fib_t * fib;
639     int verbose, matching;
640     ip6_address_t matching_address;
641     u32 mask_len  = 128;
642     int table_id = -1, fib_index = ~0;
643     int detail = 0;
644     int hash = 0;
645
646     verbose = 1;
647     matching = 0;
648
649     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
650     {
651         if (unformat (input, "brief")   ||
652             unformat (input, "summary") ||
653             unformat (input, "sum"))
654             verbose = 0;
655  
656         else if (unformat (input, "detail")   ||
657                  unformat (input, "det"))
658             detail = 1;
659
660         else if (unformat (input, "hash") ||
661                  unformat (input, "mem") ||
662                  unformat (input, "memory"))
663             hash = 1;
664
665         else if (unformat (input, "%U/%d",
666                            unformat_ip6_address, &matching_address, &mask_len))
667             matching = 1;
668
669         else if (unformat (input, "%U", unformat_ip6_address, &matching_address))
670             matching = 1;
671
672         else if (unformat (input, "table %d", &table_id))
673             ;
674         else if (unformat (input, "index %d", &fib_index))
675             ;
676         else
677             break;
678     }
679
680     if (hash)
681     {
682         vlib_cli_output (vm, "IPv6 Non-Forwarding Hash Table:\n%U\n",
683                          BV (format_bihash),
684                          &im6->ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
685                          detail);
686         vlib_cli_output (vm, "IPv6 Forwarding Hash Table:\n%U\n",
687                          BV (format_bihash),
688                          &im6->ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash,
689                          detail);
690         return (NULL);
691     }
692
693     pool_foreach (fib_table, im6->fibs,
694     ({
695         fib_source_t source;
696         u8 *s = NULL;
697
698         fib = pool_elt_at_index(im6->v6_fibs, fib_table->ft_index);
699         if (table_id >= 0 && table_id != (int)fib->table_id)
700             continue;
701         if (fib_index != ~0 && fib_index != (int)fib->index)
702             continue;
703         if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL)
704             continue;
705
706         s = format(s, "%U, fib_index:%d, flow hash:[%U] epoch:%d flags:%U locks:[",
707                    format_fib_table_name, fib->index,
708                    FIB_PROTOCOL_IP6,
709                    fib->index,
710                    format_ip_flow_hash_config,
711                    fib_table->ft_flow_hash_config,
712                    fib_table->ft_epoch,
713                    format_fib_table_flags, fib_table->ft_flags);
714
715         vec_foreach_index(source, fib_table->ft_locks)
716         {
717             if (0 != fib_table->ft_locks[source])
718             {
719                 s = format(s, "%U:%d, ",
720                            format_fib_source, source,
721                            fib_table->ft_locks[source]);
722             }
723         }
724         s = format (s, "]");
725         vlib_cli_output (vm, "%v", s);
726         vec_free(s);
727
728         /* Show summary? */
729         if (! verbose)
730         {
731             clib_bihash_24_8_t * h = &im6->ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash;
732             int len;
733
734             vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
735
736             clib_memset (ca, 0, sizeof(*ca));
737             ca->fib_index = fib->index;
738
739             clib_bihash_foreach_key_value_pair_24_8
740                 (h, count_routes_in_fib_at_prefix_length, ca);
741
742             for (len = 128; len >= 0; len--)
743             {
744                 if (ca->count_by_prefix_length[len])
745                     vlib_cli_output (vm, "%=20d%=16lld", 
746                                      len, ca->count_by_prefix_length[len]);
747             }
748             continue;
749         }
750
751         if (!matching)
752         {
753             ip6_fib_table_show_all(fib, vm);
754         }
755         else
756         {
757             ip6_fib_table_show_one(fib, vm, &matching_address, mask_len, detail);
758         }
759     }));
760
761     return 0;
762 }
763
764 /*?
765  * This command displays the IPv6 FIB Tables (VRF Tables) and the route
766  * entries for each table.
767  *
768  * @note This command will run for a long time when the FIB tables are
769  * comprised of millions of entries. For those senarios, consider displaying
770  * in summary mode.
771  *
772  * @cliexpar
773  * @parblock
774  * Example of how to display all the IPv6 FIB tables:
775  * @cliexstart{show ip6 fib}
776  * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
777  * @::/0
778  *   unicast-ip6-chain
779  *   [@0]: dpo-load-balance: [index:5 buckets:1 uRPF:5 to:[0:0]]
780  *     [0] [@0]: dpo-drop ip6
781  * fe80::/10
782  *   unicast-ip6-chain
783  *   [@0]: dpo-load-balance: [index:10 buckets:1 uRPF:10 to:[0:0]]
784  *     [0] [@2]: dpo-receive
785  * ff02::1/128
786  *   unicast-ip6-chain
787  *   [@0]: dpo-load-balance: [index:8 buckets:1 uRPF:8 to:[0:0]]
788  *     [0] [@2]: dpo-receive
789  * ff02::2/128
790  *   unicast-ip6-chain
791  *   [@0]: dpo-load-balance: [index:7 buckets:1 uRPF:7 to:[0:0]]
792  *     [0] [@2]: dpo-receive
793  * ff02::16/128
794  *   unicast-ip6-chain
795  *   [@0]: dpo-load-balance: [index:9 buckets:1 uRPF:9 to:[0:0]]
796  *     [0] [@2]: dpo-receive
797  * ff02::1:ff00:0/104
798  *   unicast-ip6-chain
799  *   [@0]: dpo-load-balance: [index:6 buckets:1 uRPF:6 to:[0:0]]
800  *     [0] [@2]: dpo-receive
801  * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
802  * @::/0
803  *   unicast-ip6-chain
804  *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
805  *     [0] [@0]: dpo-drop ip6
806  * @::a:1:1:0:4/126
807  *   unicast-ip6-chain
808  *   [@0]: dpo-load-balance: [index:27 buckets:1 uRPF:26 to:[0:0]]
809  *     [0] [@4]: ipv6-glean: af_packet0
810  * @::a:1:1:0:7/128
811  *   unicast-ip6-chain
812  *   [@0]: dpo-load-balance: [index:28 buckets:1 uRPF:27 to:[0:0]]
813  *     [0] [@2]: dpo-receive: @::a:1:1:0:7 on af_packet0
814  * fe80::/10
815  *   unicast-ip6-chain
816  *   [@0]: dpo-load-balance: [index:26 buckets:1 uRPF:25 to:[0:0]]
817  *     [0] [@2]: dpo-receive
818  * fe80::fe:3eff:fe3e:9222/128
819  *   unicast-ip6-chain
820  *   [@0]: dpo-load-balance: [index:29 buckets:1 uRPF:28 to:[0:0]]
821  *     [0] [@2]: dpo-receive: fe80::fe:3eff:fe3e:9222 on af_packet0
822  * ff02::1/128
823  *   unicast-ip6-chain
824  *   [@0]: dpo-load-balance: [index:24 buckets:1 uRPF:23 to:[0:0]]
825  *     [0] [@2]: dpo-receive
826  * ff02::2/128
827  *   unicast-ip6-chain
828  *   [@0]: dpo-load-balance: [index:23 buckets:1 uRPF:22 to:[0:0]]
829  *     [0] [@2]: dpo-receive
830  * ff02::16/128
831  *   unicast-ip6-chain
832  *   [@0]: dpo-load-balance: [index:25 buckets:1 uRPF:24 to:[0:0]]
833  *     [0] [@2]: dpo-receive
834  * ff02::1:ff00:0/104
835  *   unicast-ip6-chain
836  *   [@0]: dpo-load-balance: [index:22 buckets:1 uRPF:21 to:[0:0]]
837  *     [0] [@2]: dpo-receive
838  * @cliexend
839  *
840  * Example of how to display a summary of all IPv6 FIB tables:
841  * @cliexstart{show ip6 fib summary}
842  * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
843  *     Prefix length         Count
844  *          128                3
845  *          104                1
846  *          10                 1
847  *           0                 1
848  * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
849  *     Prefix length         Count
850  *          128                5
851  *          126                1
852  *          104                1
853  *          10                 1
854  *           0                 1
855  * @cliexend
856  * @endparblock
857  ?*/
858 /* *INDENT-OFF* */
859 VLIB_CLI_COMMAND (ip6_show_fib_command, static) = {
860     .path = "show ip6 fib",
861     .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]] [detail]",
862     .function = ip6_show_fib,
863 };
864 /* *INDENT-ON* */