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