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