Reorganize source tree to use single autotools instance
[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
19 static void
20 vnet_ip6_fib_init (u32 fib_index)
21 {
22     fib_prefix_t pfx = {
23         .fp_proto = FIB_PROTOCOL_IP6,
24         .fp_len = 0,
25         .fp_addr = {
26             .ip6 = {
27                 { 0, 0, },
28             },
29         }
30     };
31
32     /*
33      * Add the default route.
34      */
35     fib_table_entry_special_add(fib_index,
36                                 &pfx,
37                                 FIB_SOURCE_DEFAULT_ROUTE,
38                                 FIB_ENTRY_FLAG_DROP,
39                                 ADJ_INDEX_INVALID);
40
41     /*
42      * Add ff02::1:ff00:0/104 via local route for all tables.
43      *  This is required for neighbor discovery to work.
44      */
45     ip6_set_solicited_node_multicast_address(&pfx.fp_addr.ip6, 0);
46     pfx.fp_len = 104;
47     fib_table_entry_special_add(fib_index,
48                                 &pfx,
49                                 FIB_SOURCE_SPECIAL,
50                                 FIB_ENTRY_FLAG_LOCAL,
51                                 ADJ_INDEX_INVALID);
52
53     /*
54      * Add all-routers multicast address via local route for all tables
55      */
56     ip6_set_reserved_multicast_address (&pfx.fp_addr.ip6,
57                                         IP6_MULTICAST_SCOPE_link_local,
58                                         IP6_MULTICAST_GROUP_ID_all_routers);
59     pfx.fp_len = 128;
60     fib_table_entry_special_add(fib_index,
61                                 &pfx,
62                                 FIB_SOURCE_SPECIAL,
63                                 FIB_ENTRY_FLAG_LOCAL,
64                                 ADJ_INDEX_INVALID);
65
66     /*
67      * Add all-nodes multicast address via local route for all tables
68      */
69     ip6_set_reserved_multicast_address (&pfx.fp_addr.ip6,
70                                         IP6_MULTICAST_SCOPE_link_local,
71                                         IP6_MULTICAST_GROUP_ID_all_hosts);
72     pfx.fp_len = 128;
73     fib_table_entry_special_add(fib_index,
74                                 &pfx,
75                                 FIB_SOURCE_SPECIAL,
76                                 FIB_ENTRY_FLAG_LOCAL,
77                                 ADJ_INDEX_INVALID);
78
79     /*
80      *  Add all-mldv2  multicast address via local route for all tables
81      */
82     ip6_set_reserved_multicast_address (&pfx.fp_addr.ip6,
83                                         IP6_MULTICAST_SCOPE_link_local,
84                                         IP6_MULTICAST_GROUP_ID_mldv2_routers);
85     pfx.fp_len = 128;
86     fib_table_entry_special_add(fib_index,
87                                 &pfx,
88                                 FIB_SOURCE_SPECIAL,
89                                 FIB_ENTRY_FLAG_LOCAL,
90                                 ADJ_INDEX_INVALID);
91
92     /*
93      * all link local for us
94      */
95     pfx.fp_addr.ip6.as_u64[0] = clib_host_to_net_u64 (0xFE80000000000000ULL);
96     pfx.fp_addr.ip6.as_u64[1] = 0;
97     pfx.fp_len = 10;
98     fib_table_entry_special_add(fib_index,
99                                 &pfx,
100                                 FIB_SOURCE_SPECIAL,
101                                 FIB_ENTRY_FLAG_LOCAL,
102                                 ADJ_INDEX_INVALID);
103 }
104
105 static u32
106 create_fib_with_table_id (u32 table_id)
107 {
108     fib_table_t *fib_table;
109
110     pool_get_aligned(ip6_main.fibs, fib_table, CLIB_CACHE_LINE_BYTES);
111     memset(fib_table, 0, sizeof(*fib_table));
112
113     fib_table->ft_proto = FIB_PROTOCOL_IP6;
114     fib_table->ft_index =
115         fib_table->v6.index =
116             (fib_table - ip6_main.fibs);
117
118     hash_set(ip6_main.fib_index_by_table_id, table_id, fib_table->ft_index);
119
120     fib_table->ft_table_id =
121         fib_table->v6.table_id =
122             table_id;
123     fib_table->ft_flow_hash_config = 
124         fib_table->v6.flow_hash_config =
125             IP_FLOW_HASH_DEFAULT;
126
127     vnet_ip6_fib_init(fib_table->ft_index);
128     fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP6);
129
130     return (fib_table->ft_index);
131 }
132
133 u32
134 ip6_fib_table_find_or_create_and_lock (u32 table_id)
135 {
136     uword * p;
137
138     p = hash_get (ip6_main.fib_index_by_table_id, table_id);
139     if (NULL == p)
140         return create_fib_with_table_id(table_id);
141     
142     fib_table_lock(p[0], FIB_PROTOCOL_IP6);
143
144     return (p[0]);
145 }
146
147 u32
148 ip6_fib_table_create_and_lock (void)
149 {
150     return (create_fib_with_table_id(~0));
151 }
152
153 void
154 ip6_fib_table_destroy (u32 fib_index)
155 {
156     fib_prefix_t pfx = {
157         .fp_proto = FIB_PROTOCOL_IP6,
158         .fp_len = 0,
159         .fp_addr = {
160             .ip6 = {
161                 { 0, 0, },
162             },
163         }
164     };
165
166     /*
167      * the default route.
168      */
169     fib_table_entry_special_remove(fib_index,
170                                    &pfx,
171                                    FIB_SOURCE_DEFAULT_ROUTE);
172
173
174     /*
175      * ff02::1:ff00:0/104
176      */
177     ip6_set_solicited_node_multicast_address(&pfx.fp_addr.ip6, 0);
178     pfx.fp_len = 104;
179     fib_table_entry_special_remove(fib_index,
180                                    &pfx,
181                                    FIB_SOURCE_SPECIAL);
182
183     /*
184      * all-routers multicast address
185      */
186     ip6_set_reserved_multicast_address (&pfx.fp_addr.ip6,
187                                         IP6_MULTICAST_SCOPE_link_local,
188                                         IP6_MULTICAST_GROUP_ID_all_routers);
189     pfx.fp_len = 128;
190     fib_table_entry_special_remove(fib_index,
191                                    &pfx,
192                                    FIB_SOURCE_SPECIAL);
193
194     /*
195      * all-nodes multicast address
196      */
197     ip6_set_reserved_multicast_address (&pfx.fp_addr.ip6,
198                                         IP6_MULTICAST_SCOPE_link_local,
199                                         IP6_MULTICAST_GROUP_ID_all_hosts);
200     pfx.fp_len = 128;
201     fib_table_entry_special_remove(fib_index,
202                                    &pfx,
203                                    FIB_SOURCE_SPECIAL);
204
205     /*
206      * all-mldv2 multicast address
207      */
208     ip6_set_reserved_multicast_address (&pfx.fp_addr.ip6,
209                                         IP6_MULTICAST_SCOPE_link_local,
210                                         IP6_MULTICAST_GROUP_ID_mldv2_routers);
211     pfx.fp_len = 128;
212     fib_table_entry_special_remove(fib_index,
213                                    &pfx,
214                                    FIB_SOURCE_SPECIAL);
215
216     /*
217      * all link local 
218      */
219     pfx.fp_addr.ip6.as_u64[0] = clib_host_to_net_u64 (0xFE80000000000000ULL);
220     pfx.fp_addr.ip6.as_u64[1] = 0;
221     pfx.fp_len = 10;
222     fib_table_entry_special_remove(fib_index,
223                                    &pfx,
224                                    FIB_SOURCE_SPECIAL);
225
226     fib_table_t *fib_table = fib_table_get(fib_index, FIB_PROTOCOL_IP6);
227     fib_source_t source;
228     
229      /*
230      * validate no more routes.
231      */
232     ASSERT(0 == fib_table->ft_total_route_counts);
233     FOR_EACH_FIB_SOURCE(source)
234     {
235         ASSERT(0 == fib_table->ft_src_route_counts[source]);
236     }
237
238     if (~0 != fib_table->ft_table_id)
239     {
240         hash_unset (ip6_main.fib_index_by_table_id, fib_table->ft_table_id);
241     }
242     pool_put(ip6_main.fibs, fib_table);
243 }
244
245 fib_node_index_t
246 ip6_fib_table_lookup (u32 fib_index,
247                       const ip6_address_t *addr,
248                       u32 len)
249 {
250     const ip6_fib_table_instance_t *table;
251     BVT(clib_bihash_kv) kv, value;
252     int i, n_p, rv;
253     u64 fib;
254
255     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
256     n_p = vec_len (table->prefix_lengths_in_search_order);
257
258     kv.key[0] = addr->as_u64[0];
259     kv.key[1] = addr->as_u64[1];
260     fib = ((u64)((fib_index))<<32);
261
262     /*
263      * start search from a mask length same length or shorter.
264      * we don't want matches longer than the mask passed
265      */
266     i = 0;
267     while (i < n_p && table->prefix_lengths_in_search_order[i] > len)
268     {
269         i++;
270     }
271
272     for (; i < n_p; i++)
273     {
274         int dst_address_length = table->prefix_lengths_in_search_order[i];
275         ip6_address_t * mask = &ip6_main.fib_masks[dst_address_length];
276       
277         ASSERT(dst_address_length >= 0 && dst_address_length <= 128);
278         //As lengths are decreasing, masks are increasingly specific.
279         kv.key[0] &= mask->as_u64[0];
280         kv.key[1] &= mask->as_u64[1];
281         kv.key[2] = fib | dst_address_length;
282       
283         rv = BV(clib_bihash_search_inline_2)(&table->ip6_hash, &kv, &value);
284         if (rv == 0)
285             return value.value;
286     }
287
288     return (FIB_NODE_INDEX_INVALID);
289 }
290
291 fib_node_index_t
292 ip6_fib_table_lookup_exact_match (u32 fib_index,
293                                   const ip6_address_t *addr,
294                                   u32 len)
295 {
296     const ip6_fib_table_instance_t *table;
297     BVT(clib_bihash_kv) kv, value;
298     ip6_address_t *mask;
299     u64 fib;
300     int rv;
301
302     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
303     mask = &ip6_main.fib_masks[len];
304     fib = ((u64)((fib_index))<<32);
305
306     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
307     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
308     kv.key[2] = fib | len;
309       
310     rv = BV(clib_bihash_search_inline_2)(&table->ip6_hash, &kv, &value);
311     if (rv == 0)
312         return value.value;
313
314     return (FIB_NODE_INDEX_INVALID);
315 }
316
317 static void
318 compute_prefix_lengths_in_search_order (ip6_fib_table_instance_t *table)
319 {
320     int i;
321     vec_reset_length (table->prefix_lengths_in_search_order);
322     /* Note: bitmap reversed so this is in fact a longest prefix match */
323     clib_bitmap_foreach (i, table->non_empty_dst_address_length_bitmap,
324     ({
325         int dst_address_length = 128 - i;
326         vec_add1(table->prefix_lengths_in_search_order, dst_address_length);
327     }));
328 }
329
330 void
331 ip6_fib_table_entry_remove (u32 fib_index,
332                             const ip6_address_t *addr,
333                             u32 len)
334 {
335     ip6_fib_table_instance_t *table;
336     BVT(clib_bihash_kv) kv;
337     ip6_address_t *mask;
338     u64 fib;
339
340     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
341     mask = &ip6_main.fib_masks[len];
342     fib = ((u64)((fib_index))<<32);
343
344     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
345     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
346     kv.key[2] = fib | len;
347
348     BV(clib_bihash_add_del)(&table->ip6_hash, &kv, 0);
349
350     /* refcount accounting */
351     ASSERT (table->dst_address_length_refcounts[len] > 0);
352     if (--table->dst_address_length_refcounts[len] == 0)
353     {
354         table->non_empty_dst_address_length_bitmap =
355             clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
356                              128 - len, 0);
357         compute_prefix_lengths_in_search_order (table);
358     }
359 }
360
361 void
362 ip6_fib_table_entry_insert (u32 fib_index,
363                             const ip6_address_t *addr,
364                             u32 len,
365                             fib_node_index_t fib_entry_index)
366 {
367     ip6_fib_table_instance_t *table;
368     BVT(clib_bihash_kv) kv;
369     ip6_address_t *mask;
370     u64 fib;
371
372     table = &ip6_main.ip6_table[IP6_FIB_TABLE_NON_FWDING];
373     mask = &ip6_main.fib_masks[len];
374     fib = ((u64)((fib_index))<<32);
375
376     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
377     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
378     kv.key[2] = fib | len;
379     kv.value = fib_entry_index;
380
381     BV(clib_bihash_add_del)(&table->ip6_hash, &kv, 1);
382
383     table->dst_address_length_refcounts[len]++;
384
385     table->non_empty_dst_address_length_bitmap =
386         clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
387                          128 - len, 1);
388     compute_prefix_lengths_in_search_order (table);
389 }
390
391 u32 
392 ip6_fib_table_fwding_lookup (ip6_main_t * im,
393                              u32 fib_index,
394                              const ip6_address_t * dst)
395 {
396     const ip6_fib_table_instance_t *table;
397     int i, len;
398     int rv;
399     BVT(clib_bihash_kv) kv, value;
400     u64 fib;
401
402     table = &ip6_main.ip6_table[IP6_FIB_TABLE_FWDING];
403     len = vec_len (table->prefix_lengths_in_search_order);
404
405     kv.key[0] = dst->as_u64[0];
406     kv.key[1] = dst->as_u64[1];
407     fib = ((u64)((fib_index))<<32);
408
409     for (i = 0; i < len; i++)
410     {
411         int dst_address_length = table->prefix_lengths_in_search_order[i];
412         ip6_address_t * mask = &ip6_main.fib_masks[dst_address_length];
413       
414         ASSERT(dst_address_length >= 0 && dst_address_length <= 128);
415         //As lengths are decreasing, masks are increasingly specific.
416         kv.key[0] &= mask->as_u64[0];
417         kv.key[1] &= mask->as_u64[1];
418         kv.key[2] = fib | dst_address_length;
419       
420         rv = BV(clib_bihash_search_inline_2)(&table->ip6_hash, &kv, &value);
421         if (rv == 0)
422             return value.value;
423     }
424
425     /* default route is always present */
426     ASSERT(0);
427     return 0;
428 }
429
430 u32 ip6_fib_table_fwding_lookup_with_if_index (ip6_main_t * im,
431                                                u32 sw_if_index,
432                                                const ip6_address_t * dst)
433 {
434     u32 fib_index = vec_elt (im->fib_index_by_sw_if_index, sw_if_index);
435     return ip6_fib_table_fwding_lookup(im, fib_index, dst);
436 }
437
438 flow_hash_config_t
439 ip6_fib_table_get_flow_hash_config (u32 fib_index)
440 {
441     return (ip6_fib_get(fib_index)->flow_hash_config);
442 }
443
444 u32
445 ip6_fib_table_get_index_for_sw_if_index (u32 sw_if_index)
446 {
447     if (sw_if_index >= vec_len(ip6_main.fib_index_by_sw_if_index))
448     {
449         /*
450          * This is the case for interfaces that are not yet mapped to
451          * a IP table
452          */
453         return (~0);
454     }
455     return (ip6_main.fib_index_by_sw_if_index[sw_if_index]);
456 }
457
458 void
459 ip6_fib_table_fwding_dpo_update (u32 fib_index,
460                                  const ip6_address_t *addr,
461                                  u32 len,
462                                  const dpo_id_t *dpo)
463 {
464     ip6_fib_table_instance_t *table;
465     BVT(clib_bihash_kv) kv;
466     ip6_address_t *mask;
467     u64 fib;
468
469     table = &ip6_main.ip6_table[IP6_FIB_TABLE_FWDING];
470     mask = &ip6_main.fib_masks[len];
471     fib = ((u64)((fib_index))<<32);
472
473     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
474     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
475     kv.key[2] = fib | len;
476     kv.value = dpo->dpoi_index;
477
478     BV(clib_bihash_add_del)(&table->ip6_hash, &kv, 1);
479
480     table->dst_address_length_refcounts[len]++;
481
482     table->non_empty_dst_address_length_bitmap =
483         clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
484                          128 - len, 1);
485     compute_prefix_lengths_in_search_order (table);
486 }
487
488 void
489 ip6_fib_table_fwding_dpo_remove (u32 fib_index,
490                                  const ip6_address_t *addr,
491                                  u32 len,
492                                  const dpo_id_t *dpo)
493 {
494     ip6_fib_table_instance_t *table;
495     BVT(clib_bihash_kv) kv;
496     ip6_address_t *mask;
497     u64 fib;
498
499     table = &ip6_main.ip6_table[IP6_FIB_TABLE_FWDING];
500     mask = &ip6_main.fib_masks[len];
501     fib = ((u64)((fib_index))<<32);
502
503     kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
504     kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
505     kv.key[2] = fib | len;
506     kv.value = dpo->dpoi_index;
507
508     BV(clib_bihash_add_del)(&table->ip6_hash, &kv, 0);
509
510     /* refcount accounting */
511     ASSERT (table->dst_address_length_refcounts[len] > 0);
512     if (--table->dst_address_length_refcounts[len] == 0)
513     {
514         table->non_empty_dst_address_length_bitmap =
515             clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
516                              128 - len, 0);
517         compute_prefix_lengths_in_search_order (table);
518     }
519 }
520
521 typedef struct ip6_fib_show_ctx_t_ {
522     u32 fib_index;
523     fib_node_index_t *entries;
524 } ip6_fib_show_ctx_t;
525
526 static void
527 ip6_fib_table_collect_entries (clib_bihash_kv_24_8_t * kvp,
528                                void *arg)
529 {
530     ip6_fib_show_ctx_t *ctx = arg;
531
532     if ((kvp->key[2] >> 32) == ctx->fib_index)
533     {
534         vec_add1(ctx->entries, kvp->value);
535     }
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         .fib_index = fib->index,
545         .entries = NULL,
546     };
547     ip6_main_t *im = &ip6_main;
548
549     BV(clib_bihash_foreach_key_value_pair)(&im->ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
550                                            ip6_fib_table_collect_entries,
551                                            &ctx);
552
553     vec_sort_with_function(ctx.entries, fib_entry_cmp_for_sort);
554
555     vec_foreach(fib_entry_index, ctx.entries)
556     {
557         vlib_cli_output(vm, "%U",
558                         format_fib_entry,
559                         *fib_entry_index,
560                         FIB_ENTRY_FORMAT_BRIEF);
561     }
562
563     vec_free(ctx.entries);
564 }
565
566 static void
567 ip6_fib_table_show_one (ip6_fib_t *fib,
568                         vlib_main_t * vm,
569                         ip6_address_t *address,
570                         u32 mask_len)
571 {
572     vlib_cli_output(vm, "%U",
573                     format_fib_entry,
574                     ip6_fib_table_lookup(fib->index, address, mask_len),
575                     FIB_ENTRY_FORMAT_DETAIL);
576 }
577
578 typedef struct {
579   u32 fib_index;
580   u64 count_by_prefix_length[129];
581 } count_routes_in_fib_at_prefix_length_arg_t;
582
583 static void count_routes_in_fib_at_prefix_length 
584 (BVT(clib_bihash_kv) * kvp, void *arg)
585 {
586   count_routes_in_fib_at_prefix_length_arg_t * ap = arg;
587   int mask_width;
588
589   if ((kvp->key[2]>>32) != ap->fib_index)
590     return;
591
592   mask_width = kvp->key[2] & 0xFF;
593
594   ap->count_by_prefix_length[mask_width]++;
595 }
596
597 static clib_error_t *
598 ip6_show_fib (vlib_main_t * vm,
599               unformat_input_t * input,
600               vlib_cli_command_t * cmd)
601 {
602     count_routes_in_fib_at_prefix_length_arg_t _ca, *ca = &_ca;
603     ip6_main_t * im6 = &ip6_main;
604     fib_table_t *fib_table;
605     ip6_fib_t * fib;
606     int verbose, matching;
607     ip6_address_t matching_address;
608     u32 mask_len  = 128;
609     int table_id = -1, fib_index = ~0;
610
611     verbose = 1;
612     matching = 0;
613
614     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
615     {
616         if (unformat (input, "brief")   ||
617             unformat (input, "summary") ||
618             unformat (input, "sum"))
619             verbose = 0;
620
621         else if (unformat (input, "%U/%d",
622                            unformat_ip6_address, &matching_address, &mask_len))
623             matching = 1;
624
625         else if (unformat (input, "%U", unformat_ip6_address, &matching_address))
626             matching = 1;
627
628         else if (unformat (input, "table %d", &table_id))
629             ;
630         else if (unformat (input, "index %d", &fib_index))
631             ;
632         else
633             break;
634     }
635
636     pool_foreach (fib_table, im6->fibs,
637     ({
638         fib = &(fib_table->v6);
639         if (table_id >= 0 && table_id != (int)fib->table_id)
640             continue;
641         if (fib_index != ~0 && fib_index != (int)fib->index)
642             continue;
643
644         vlib_cli_output (vm, "%s, fib_index %d, flow hash: %U", 
645                          fib_table->ft_desc, fib->index,
646                          format_ip_flow_hash_config, fib->flow_hash_config);
647
648         /* Show summary? */
649         if (! verbose)
650         {
651             BVT(clib_bihash) * h = &im6->ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash;
652             int len;
653
654             vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
655
656             memset (ca, 0, sizeof(*ca));
657             ca->fib_index = fib->index;
658
659             BV(clib_bihash_foreach_key_value_pair)
660                 (h, count_routes_in_fib_at_prefix_length, ca);
661
662             for (len = 128; len >= 0; len--)
663             {
664                 if (ca->count_by_prefix_length[len])
665                     vlib_cli_output (vm, "%=20d%=16lld", 
666                                      len, ca->count_by_prefix_length[len]);
667             }
668             continue;
669         }
670
671         if (!matching)
672         {
673             ip6_fib_table_show_all(fib, vm);
674         }
675         else
676         {
677             ip6_fib_table_show_one(fib, vm, &matching_address, mask_len);
678         }
679     }));
680
681     return 0;
682 }
683
684 /*?
685  * This command displays the IPv6 FIB Tables (VRF Tables) and the route
686  * entries for each table.
687  *
688  * @note This command will run for a long time when the FIB tables are
689  * comprised of millions of entries. For those senarios, consider displaying
690  * in summary mode.
691  *
692  * @cliexpar
693  * @parblock
694  * Example of how to display all the IPv6 FIB tables:
695  * @cliexstart{show ip6 fib}
696  * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
697  * @::/0
698  *   unicast-ip6-chain
699  *   [@0]: dpo-load-balance: [index:5 buckets:1 uRPF:5 to:[0:0]]
700  *     [0] [@0]: dpo-drop ip6
701  * fe80::/10
702  *   unicast-ip6-chain
703  *   [@0]: dpo-load-balance: [index:10 buckets:1 uRPF:10 to:[0:0]]
704  *     [0] [@2]: dpo-receive
705  * ff02::1/128
706  *   unicast-ip6-chain
707  *   [@0]: dpo-load-balance: [index:8 buckets:1 uRPF:8 to:[0:0]]
708  *     [0] [@2]: dpo-receive
709  * ff02::2/128
710  *   unicast-ip6-chain
711  *   [@0]: dpo-load-balance: [index:7 buckets:1 uRPF:7 to:[0:0]]
712  *     [0] [@2]: dpo-receive
713  * ff02::16/128
714  *   unicast-ip6-chain
715  *   [@0]: dpo-load-balance: [index:9 buckets:1 uRPF:9 to:[0:0]]
716  *     [0] [@2]: dpo-receive
717  * ff02::1:ff00:0/104
718  *   unicast-ip6-chain
719  *   [@0]: dpo-load-balance: [index:6 buckets:1 uRPF:6 to:[0:0]]
720  *     [0] [@2]: dpo-receive
721  * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
722  * @::/0
723  *   unicast-ip6-chain
724  *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
725  *     [0] [@0]: dpo-drop ip6
726  * @::a:1:1:0:4/126
727  *   unicast-ip6-chain
728  *   [@0]: dpo-load-balance: [index:27 buckets:1 uRPF:26 to:[0:0]]
729  *     [0] [@4]: ipv6-glean: af_packet0
730  * @::a:1:1:0:7/128
731  *   unicast-ip6-chain
732  *   [@0]: dpo-load-balance: [index:28 buckets:1 uRPF:27 to:[0:0]]
733  *     [0] [@2]: dpo-receive: @::a:1:1:0:7 on af_packet0
734  * fe80::/10
735  *   unicast-ip6-chain
736  *   [@0]: dpo-load-balance: [index:26 buckets:1 uRPF:25 to:[0:0]]
737  *     [0] [@2]: dpo-receive
738  * fe80::fe:3eff:fe3e:9222/128
739  *   unicast-ip6-chain
740  *   [@0]: dpo-load-balance: [index:29 buckets:1 uRPF:28 to:[0:0]]
741  *     [0] [@2]: dpo-receive: fe80::fe:3eff:fe3e:9222 on af_packet0
742  * ff02::1/128
743  *   unicast-ip6-chain
744  *   [@0]: dpo-load-balance: [index:24 buckets:1 uRPF:23 to:[0:0]]
745  *     [0] [@2]: dpo-receive
746  * ff02::2/128
747  *   unicast-ip6-chain
748  *   [@0]: dpo-load-balance: [index:23 buckets:1 uRPF:22 to:[0:0]]
749  *     [0] [@2]: dpo-receive
750  * ff02::16/128
751  *   unicast-ip6-chain
752  *   [@0]: dpo-load-balance: [index:25 buckets:1 uRPF:24 to:[0:0]]
753  *     [0] [@2]: dpo-receive
754  * ff02::1:ff00:0/104
755  *   unicast-ip6-chain
756  *   [@0]: dpo-load-balance: [index:22 buckets:1 uRPF:21 to:[0:0]]
757  *     [0] [@2]: dpo-receive
758  * @cliexend
759  *
760  * Example of how to display a summary of all IPv6 FIB tables:
761  * @cliexstart{show ip6 fib summary}
762  * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
763  *     Prefix length         Count
764  *          128                3
765  *          104                1
766  *          10                 1
767  *           0                 1
768  * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
769  *     Prefix length         Count
770  *          128                5
771  *          126                1
772  *          104                1
773  *          10                 1
774  *           0                 1
775  * @cliexend
776  * @endparblock
777  ?*/
778 /* *INDENT-OFF* */
779 VLIB_CLI_COMMAND (ip6_show_fib_command, static) = {
780     .path = "show ip6 fib",
781     .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]]",
782     .function = ip6_show_fib,
783 };
784 /* *INDENT-ON* */