8e92d851b45ac7d6a8ae40ce1ff4a8542823affc
[vpp.git] / src / vnet / fib / ip4_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/fib_table.h>
17 #include <vnet/fib/fib_entry.h>
18 #include <vnet/fib/ip4_fib.h>
19
20 /*
21  * A table of pefixes to be added to tables and the sources for them
22  */
23 typedef struct ip4_fib_table_special_prefix_t_ {
24     fib_prefix_t ift_prefix;
25     fib_source_t ift_source;
26     fib_entry_flag_t ift_flag;
27 } ip4_fib_table_special_prefix_t;
28
29 static const ip4_fib_table_special_prefix_t ip4_specials[] = {
30     {
31         /* 0.0.0.0/0*/
32         .ift_prefix = {
33             .fp_addr = {
34                 .ip4.data_u32 = 0,
35             },
36             .fp_len  = 0,
37             .fp_proto = FIB_PROTOCOL_IP4,
38         },
39         .ift_source = FIB_SOURCE_DEFAULT_ROUTE,
40         .ift_flag   = FIB_ENTRY_FLAG_DROP,
41     },
42     {
43         /* 0.0.0.0/32*/
44         .ift_prefix = {
45             .fp_addr = {
46                 .ip4.data_u32 = 0,
47             },
48             .fp_len  = 32,
49             .fp_proto = FIB_PROTOCOL_IP4,
50         },
51         .ift_source = FIB_SOURCE_DEFAULT_ROUTE,
52         .ift_flag   = FIB_ENTRY_FLAG_DROP,
53     },
54     {
55         /*
56          * 240.0.0.0/4
57          * drop class E
58          */
59         .ift_prefix = {
60             .fp_addr = {
61                 .ip4.data_u32 = 0xf0000000,
62             },
63             .fp_len   = 4,
64             .fp_proto = FIB_PROTOCOL_IP4,
65         },
66         .ift_source = FIB_SOURCE_SPECIAL,
67         .ift_flag   = FIB_ENTRY_FLAG_DROP,
68
69     },
70     {
71         /*
72          * 224.0.0.0/4
73          * drop all mcast
74          */
75         .ift_prefix = {
76             .fp_addr = {
77                 .ip4.data_u32 = 0xe0000000,
78             },
79             .fp_len   = 4,
80             .fp_proto = FIB_PROTOCOL_IP4,
81         },
82         .ift_source = FIB_SOURCE_SPECIAL,
83         .ift_flag    = FIB_ENTRY_FLAG_DROP,
84     },
85     {
86         /*
87          * 255.255.255.255/32
88          * drop, but we'll allow it to be usurped by the likes of DHCP
89          */
90         .ift_prefix = {
91             .fp_addr = {
92                 .ip4.data_u32 = 0xffffffff,
93             },
94             .fp_len   = 32,
95             .fp_proto = FIB_PROTOCOL_IP4,
96         },
97         .ift_source = FIB_SOURCE_DEFAULT_ROUTE,
98         .ift_flag   = FIB_ENTRY_FLAG_DROP,
99     }
100 };
101
102
103 static u32
104 ip4_create_fib_with_table_id (u32 table_id)
105 {
106     fib_table_t *fib_table;
107     ip4_fib_t *v4_fib;
108
109     pool_get_aligned(ip4_main.fibs, fib_table, CLIB_CACHE_LINE_BYTES);
110     memset(fib_table, 0, sizeof(*fib_table));
111
112     pool_get_aligned(ip4_main.v4_fibs, v4_fib, CLIB_CACHE_LINE_BYTES);
113
114     ASSERT((fib_table - ip4_main.fibs) ==
115            (v4_fib - ip4_main.v4_fibs));
116
117     fib_table->ft_proto = FIB_PROTOCOL_IP4;
118     fib_table->ft_index =
119         v4_fib->index =
120             (fib_table - ip4_main.fibs);
121
122     hash_set (ip4_main.fib_index_by_table_id, table_id, fib_table->ft_index);
123
124     fib_table->ft_table_id =
125         v4_fib->table_id =
126             table_id;
127     fib_table->ft_flow_hash_config = 
128         v4_fib->flow_hash_config =
129             IP_FLOW_HASH_DEFAULT;
130     v4_fib->fwd_classify_table_index = ~0;
131     v4_fib->rev_classify_table_index = ~0;
132     
133     fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP4);
134
135     ip4_mtrie_init(&v4_fib->mtrie);
136
137     /*
138      * add the special entries into the new FIB
139      */
140     int ii;
141
142     for (ii = 0; ii < ARRAY_LEN(ip4_specials); ii++)
143     {
144         fib_prefix_t prefix = ip4_specials[ii].ift_prefix;
145
146         prefix.fp_addr.ip4.data_u32 =
147             clib_host_to_net_u32(prefix.fp_addr.ip4.data_u32);
148
149         fib_table_entry_special_add(fib_table->ft_index,
150                                     &prefix,
151                                     ip4_specials[ii].ift_source,
152                                     ip4_specials[ii].ift_flag);
153     }
154
155     return (fib_table->ft_index);
156 }
157
158 void
159 ip4_fib_table_destroy (u32 fib_index)
160 {
161     fib_table_t *fib_table = pool_elt_at_index(ip4_main.fibs, fib_index);
162     ip4_fib_t *v4_fib = pool_elt_at_index(ip4_main.v4_fibs, fib_index);
163     int ii;
164
165     /*
166      * remove all the specials we added when the table was created.
167      * In reverse order so the default route is last.
168      */
169     for (ii = ARRAY_LEN(ip4_specials) - 1; ii >= 0; ii--)
170     {
171         fib_prefix_t prefix = ip4_specials[ii].ift_prefix;
172
173         prefix.fp_addr.ip4.data_u32 =
174             clib_host_to_net_u32(prefix.fp_addr.ip4.data_u32);
175
176         fib_table_entry_special_remove(fib_table->ft_index,
177                                        &prefix,
178                                        ip4_specials[ii].ift_source);
179     }
180
181     /*
182      * validate no more routes.
183      */
184     ASSERT(0 == fib_table->ft_total_route_counts);
185     FOR_EACH_FIB_SOURCE(ii)
186     {
187         ASSERT(0 == fib_table->ft_src_route_counts[ii]);
188     }
189
190     if (~0 != fib_table->ft_table_id)
191     {
192         hash_unset (ip4_main.fib_index_by_table_id, fib_table->ft_table_id);
193     }
194
195     ip4_mtrie_free(&v4_fib->mtrie);
196
197     pool_put(ip4_main.v4_fibs, v4_fib);
198     pool_put(ip4_main.fibs, fib_table);
199 }
200
201
202 u32
203 ip4_fib_table_find_or_create_and_lock (u32 table_id)
204 {
205     u32 index;
206
207     index = ip4_fib_index_from_table_id(table_id);
208     if (~0 == index)
209         return ip4_create_fib_with_table_id(table_id);
210
211     fib_table_lock(index, FIB_PROTOCOL_IP4);
212
213     return (index);
214 }
215
216 u32
217 ip4_fib_table_create_and_lock (void)
218 {
219     return (ip4_create_fib_with_table_id(~0));
220 }
221
222 u32
223 ip4_fib_table_get_index_for_sw_if_index (u32 sw_if_index)
224 {
225     if (sw_if_index >= vec_len(ip4_main.fib_index_by_sw_if_index))
226     {
227         /*
228          * This is the case for interfaces that are not yet mapped to
229          * a IP table
230          */
231         return (~0);
232     }
233     return (ip4_main.fib_index_by_sw_if_index[sw_if_index]);
234 }
235
236 flow_hash_config_t
237 ip4_fib_table_get_flow_hash_config (u32 fib_index)
238 {
239     return (ip4_fib_get(fib_index)->flow_hash_config);
240 }
241
242 /*
243  * ip4_fib_table_lookup_exact_match
244  *
245  * Exact match prefix lookup
246  */
247 fib_node_index_t
248 ip4_fib_table_lookup_exact_match (const ip4_fib_t *fib,
249                                   const ip4_address_t *addr,
250                                   u32 len)
251 {
252     uword * hash, * result;
253     u32 key;
254
255     hash = fib->fib_entry_by_dst_address[len];
256     key  = (addr->data_u32 & ip4_main.fib_masks[len]);
257
258     result = hash_get(hash, key);
259
260     if (NULL != result) {
261         return (result[0]);
262     }
263     return (FIB_NODE_INDEX_INVALID);
264 }
265
266 /*
267  * ip4_fib_table_lookup_adj
268  *
269  * Longest prefix match
270  */
271 index_t
272 ip4_fib_table_lookup_lb (ip4_fib_t *fib,
273                          const ip4_address_t *addr)
274 {
275     fib_node_index_t fei;
276
277     fei = ip4_fib_table_lookup(fib, addr, 32);
278
279     if (FIB_NODE_INDEX_INVALID != fei)
280     {
281         const dpo_id_t *dpo;
282
283         dpo = fib_entry_contribute_ip_forwarding(fei);
284
285         return (dpo->dpoi_index);
286     }
287     return (INDEX_INVALID);
288 }
289
290 /*
291  * ip4_fib_table_lookup
292  *
293  * Longest prefix match
294  */
295 fib_node_index_t
296 ip4_fib_table_lookup (const ip4_fib_t *fib,
297                       const ip4_address_t *addr,
298                       u32 len)
299 {
300     uword * hash, * result;
301     i32 mask_len;
302     u32 key;
303
304     for (mask_len = len; mask_len >= 0; mask_len--)
305     {
306         hash = fib->fib_entry_by_dst_address[mask_len];
307         key = (addr->data_u32 & ip4_main.fib_masks[mask_len]);
308
309         result = hash_get (hash, key);
310
311         if (NULL != result) {
312             return (result[0]);
313         }
314     }
315     return (FIB_NODE_INDEX_INVALID);
316 }
317
318 void
319 ip4_fib_table_entry_insert (ip4_fib_t *fib,
320                             const ip4_address_t *addr,
321                             u32 len,
322                             fib_node_index_t fib_entry_index)
323 {
324     uword * hash, * result;
325     u32 key;
326
327     key = (addr->data_u32 & ip4_main.fib_masks[len]);
328     hash = fib->fib_entry_by_dst_address[len];
329     result = hash_get (hash, key);
330
331     if (NULL == result) {
332         /*
333          * adding a new entry
334          */
335         if (NULL == hash) {
336             hash = hash_create (32 /* elts */, sizeof (uword));
337             hash_set_flags (hash, HASH_FLAG_NO_AUTO_SHRINK);
338         }
339         hash = hash_set(hash, key, fib_entry_index);
340         fib->fib_entry_by_dst_address[len] = hash;
341     }
342     else
343     {
344         ASSERT(0);
345     }
346 }
347
348 void
349 ip4_fib_table_entry_remove (ip4_fib_t *fib,
350                             const ip4_address_t *addr,
351                             u32 len)
352 {
353     uword * hash, * result;
354     u32 key;
355
356     key = (addr->data_u32 & ip4_main.fib_masks[len]);
357     hash = fib->fib_entry_by_dst_address[len];
358     result = hash_get (hash, key);
359
360     if (NULL == result)
361     {
362         /*
363          * removing a non-existant entry. i'll allow it.
364          */
365     }
366     else 
367     {
368         hash_unset(hash, key);
369     }
370
371     fib->fib_entry_by_dst_address[len] = hash;
372 }
373
374 void
375 ip4_fib_table_fwding_dpo_update (ip4_fib_t *fib,
376                                  const ip4_address_t *addr,
377                                  u32 len,
378                                  const dpo_id_t *dpo)
379 {
380     ip4_fib_mtrie_route_add(&fib->mtrie, addr, len, dpo->dpoi_index);
381 }
382
383 void
384 ip4_fib_table_fwding_dpo_remove (ip4_fib_t *fib,
385                                  const ip4_address_t *addr,
386                                  u32 len,
387                                  const dpo_id_t *dpo,
388                                  u32 cover_index)
389 {
390     fib_prefix_t cover_prefix = {
391         .fp_len = 0,
392     };
393     const dpo_id_t *cover_dpo;
394
395     /*
396      * We need to pass the MTRIE the LB index and address length of the
397      * covering prefix, so it can fill the plys with the correct replacement
398      * for the entry being removed
399      */
400     fib_entry_get_prefix(cover_index, &cover_prefix);
401     cover_dpo = fib_entry_contribute_ip_forwarding(cover_index);
402
403     ip4_fib_mtrie_route_del(&fib->mtrie,
404                             addr, len, dpo->dpoi_index,
405                             cover_prefix.fp_len,
406                             cover_dpo->dpoi_index);
407 }
408
409 void
410 ip4_fib_table_walk (ip4_fib_t *fib,
411                     fib_table_walk_fn_t fn,
412                     void *ctx)
413 {
414     int i;
415
416     for (i = 0; i < ARRAY_LEN (fib->fib_entry_by_dst_address); i++)
417     {
418         uword * hash = fib->fib_entry_by_dst_address[i];
419
420         if (NULL != hash)
421         {
422             hash_pair_t * p;
423
424             hash_foreach_pair (p, hash,
425             ({
426                 fn(p->value[0], ctx);
427             }));
428         }
429     }
430 }
431
432 /**
433  * Walk show context
434  */
435 typedef struct ip4_fib_show_walk_ctx_t_
436 {
437     fib_node_index_t *ifsw_indicies;
438 } ip4_fib_show_walk_ctx_t;
439
440 static int
441 ip4_fib_show_walk_cb (fib_node_index_t fib_entry_index,
442                       void *arg)
443 {
444     ip4_fib_show_walk_ctx_t *ctx = arg;
445
446     vec_add1(ctx->ifsw_indicies, fib_entry_index);
447
448     return (1);
449 }
450
451 static void
452 ip4_fib_table_show_all (ip4_fib_t *fib,
453                         vlib_main_t * vm)
454 {
455     ip4_fib_show_walk_ctx_t ctx = {
456         .ifsw_indicies = NULL,
457     };
458     fib_node_index_t *fib_entry_index;
459
460     ip4_fib_table_walk(fib, ip4_fib_show_walk_cb, &ctx);
461     vec_sort_with_function(ctx.ifsw_indicies,
462                            fib_entry_cmp_for_sort);
463
464     vec_foreach(fib_entry_index, ctx.ifsw_indicies)
465     {
466         vlib_cli_output(vm, "%U",
467                         format_fib_entry,
468                         *fib_entry_index,
469                         FIB_ENTRY_FORMAT_BRIEF);
470     }
471
472     vec_free(ctx.ifsw_indicies);
473 }
474
475 static void
476 ip4_fib_table_show_one (ip4_fib_t *fib,
477                         vlib_main_t * vm,
478                         ip4_address_t *address,
479                         u32 mask_len,
480                         int detail)
481 {    
482     vlib_cli_output(vm, "%U",
483                     format_fib_entry,
484                     ip4_fib_table_lookup(fib, address, mask_len),
485                     (detail ?
486                      FIB_ENTRY_FORMAT_DETAIL2 :
487                      FIB_ENTRY_FORMAT_DETAIL));
488 }
489
490 static clib_error_t *
491 ip4_show_fib (vlib_main_t * vm,
492               unformat_input_t * input,
493               vlib_cli_command_t * cmd)
494 {
495     ip4_main_t * im4 = &ip4_main;
496     fib_table_t * fib_table;
497     int verbose, matching, mtrie;
498     ip4_address_t matching_address;
499     u32 matching_mask = 32;
500     int i, table_id = -1, fib_index = ~0;
501     int detail = 0;
502
503     verbose = 1;
504     matching = 0;
505     mtrie = 0;
506     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
507     {
508         if (unformat (input, "brief") || unformat (input, "summary")
509             || unformat (input, "sum"))
510             verbose = 0;
511
512         else if (unformat (input, "detail") || unformat (input, "det"))
513             detail = 1;
514
515         else if (unformat (input, "mtrie"))
516             mtrie = 1;
517
518         else if (unformat (input, "%U/%d",
519                            unformat_ip4_address, &matching_address, &matching_mask))
520             matching = 1;
521
522         else if (unformat (input, "%U", unformat_ip4_address, &matching_address))
523             matching = 1;
524
525         else if (unformat (input, "table %d", &table_id))
526             ;
527         else if (unformat (input, "index %d", &fib_index))
528             ;
529         else
530             break;
531     }
532
533     pool_foreach (fib_table, im4->fibs,
534     ({
535         ip4_fib_t *fib = pool_elt_at_index(im4->v4_fibs, fib_table->ft_index);
536
537         if (table_id >= 0 && table_id != (int)fib->table_id)
538             continue;
539         if (fib_index != ~0 && fib_index != (int)fib->index)
540             continue;
541
542         vlib_cli_output (vm, "%U, fib_index %d, flow hash: %U", 
543                          format_fib_table_name, fib->index, FIB_PROTOCOL_IP4,
544                          fib->index,
545                          format_ip_flow_hash_config, fib->flow_hash_config);
546
547         /* Show summary? */
548         if (! verbose)
549         {
550             vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
551             for (i = 0; i < ARRAY_LEN (fib->fib_entry_by_dst_address); i++)
552             {
553                 uword * hash = fib->fib_entry_by_dst_address[i];
554                 uword n_elts = hash_elts (hash);
555                 if (n_elts > 0)
556                     vlib_cli_output (vm, "%20d%16d", i, n_elts);
557             }
558             continue;
559         }
560         if (mtrie)
561         {
562             vlib_cli_output (vm, "%U", format_ip4_fib_mtrie, &fib->mtrie);
563             continue;
564         }
565
566         if (!matching)
567         {
568             ip4_fib_table_show_all(fib, vm);
569         }
570         else
571         {
572             ip4_fib_table_show_one(fib, vm, &matching_address,
573                                    matching_mask, detail);
574         }
575     }));
576
577     return 0;
578 }
579
580 /*?
581  * This command displays the IPv4 FIB Tables (VRF Tables) and the route
582  * entries for each table.
583  *
584  * @note This command will run for a long time when the FIB tables are
585  * comprised of millions of entries. For those senarios, consider displaying
586  * a single table or summary mode.
587  *
588  * @cliexpar
589  * Example of how to display all the IPv4 FIB tables:
590  * @cliexstart{show ip fib}
591  * ipv4-VRF:0, fib_index 0, flow hash: src dst sport dport proto
592  * 0.0.0.0/0
593  *   unicast-ip4-chain
594  *   [@0]: dpo-load-balance: [index:0 buckets:1 uRPF:0 to:[0:0]]
595  *     [0] [@0]: dpo-drop ip6
596  * 0.0.0.0/32
597  *   unicast-ip4-chain
598  *   [@0]: dpo-load-balance: [index:1 buckets:1 uRPF:1 to:[0:0]]
599  *     [0] [@0]: dpo-drop ip6
600  * 6.0.1.2/32
601  *   unicast-ip4-chain
602  *   [@0]: dpo-load-balance: [index:30 buckets:1 uRPF:29 to:[0:0]]
603  *     [0] [@3]: arp-ipv4: via 6.0.0.1 af_packet0
604  * 7.0.0.1/32
605  *   unicast-ip4-chain
606  *   [@0]: dpo-load-balance: [index:31 buckets:4 uRPF:30 to:[0:0]]
607  *     [0] [@3]: arp-ipv4: via 6.0.0.2 af_packet0
608  *     [1] [@3]: arp-ipv4: via 6.0.0.2 af_packet0
609  *     [2] [@3]: arp-ipv4: via 6.0.0.2 af_packet0
610  *     [3] [@3]: arp-ipv4: via 6.0.0.1 af_packet0
611  * 224.0.0.0/8
612  *   unicast-ip4-chain
613  *   [@0]: dpo-load-balance: [index:3 buckets:1 uRPF:3 to:[0:0]]
614  *     [0] [@0]: dpo-drop ip6
615  * 240.0.0.0/8
616  *   unicast-ip4-chain
617  *   [@0]: dpo-load-balance: [index:2 buckets:1 uRPF:2 to:[0:0]]
618  *     [0] [@0]: dpo-drop ip6
619  * 255.255.255.255/32
620  *   unicast-ip4-chain
621  *   [@0]: dpo-load-balance: [index:4 buckets:1 uRPF:4 to:[0:0]]
622  *     [0] [@0]: dpo-drop ip6
623  * ipv4-VRF:7, fib_index 1, flow hash: src dst sport dport proto
624  * 0.0.0.0/0
625  *   unicast-ip4-chain
626  *   [@0]: dpo-load-balance: [index:12 buckets:1 uRPF:11 to:[0:0]]
627  *     [0] [@0]: dpo-drop ip6
628  * 0.0.0.0/32
629  *   unicast-ip4-chain
630  *   [@0]: dpo-load-balance: [index:13 buckets:1 uRPF:12 to:[0:0]]
631  *     [0] [@0]: dpo-drop ip6
632  * 172.16.1.0/24
633  *   unicast-ip4-chain
634  *   [@0]: dpo-load-balance: [index:17 buckets:1 uRPF:16 to:[0:0]]
635  *     [0] [@4]: ipv4-glean: af_packet0
636  * 172.16.1.1/32
637  *   unicast-ip4-chain
638  *   [@0]: dpo-load-balance: [index:18 buckets:1 uRPF:17 to:[1:84]]
639  *     [0] [@2]: dpo-receive: 172.16.1.1 on af_packet0
640  * 172.16.1.2/32
641  *   unicast-ip4-chain
642  *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
643  *     [0] [@5]: ipv4 via 172.16.1.2 af_packet0: IP4: 02:fe:9e:70:7a:2b -> 26:a5:f6:9c:3a:36
644  * 172.16.2.0/24
645  *   unicast-ip4-chain
646  *   [@0]: dpo-load-balance: [index:19 buckets:1 uRPF:18 to:[0:0]]
647  *     [0] [@4]: ipv4-glean: af_packet1
648  * 172.16.2.1/32
649  *   unicast-ip4-chain
650  *   [@0]: dpo-load-balance: [index:20 buckets:1 uRPF:19 to:[0:0]]
651  *     [0] [@2]: dpo-receive: 172.16.2.1 on af_packet1
652  * 224.0.0.0/8
653  *   unicast-ip4-chain
654  *   [@0]: dpo-load-balance: [index:15 buckets:1 uRPF:14 to:[0:0]]
655  *     [0] [@0]: dpo-drop ip6
656  * 240.0.0.0/8
657  *   unicast-ip4-chain
658  *   [@0]: dpo-load-balance: [index:14 buckets:1 uRPF:13 to:[0:0]]
659  *     [0] [@0]: dpo-drop ip6
660  * 255.255.255.255/32
661  *   unicast-ip4-chain
662  *   [@0]: dpo-load-balance: [index:16 buckets:1 uRPF:15 to:[0:0]]
663  *     [0] [@0]: dpo-drop ip6
664  * @cliexend
665  * Example of how to display a single IPv4 FIB table:
666  * @cliexstart{show ip fib table 7}
667  * ipv4-VRF:7, fib_index 1, flow hash: src dst sport dport proto
668  * 0.0.0.0/0
669  *   unicast-ip4-chain
670  *   [@0]: dpo-load-balance: [index:12 buckets:1 uRPF:11 to:[0:0]]
671  *     [0] [@0]: dpo-drop ip6
672  * 0.0.0.0/32
673  *   unicast-ip4-chain
674  *   [@0]: dpo-load-balance: [index:13 buckets:1 uRPF:12 to:[0:0]]
675  *     [0] [@0]: dpo-drop ip6
676  * 172.16.1.0/24
677  *   unicast-ip4-chain
678  *   [@0]: dpo-load-balance: [index:17 buckets:1 uRPF:16 to:[0:0]]
679  *     [0] [@4]: ipv4-glean: af_packet0
680  * 172.16.1.1/32
681  *   unicast-ip4-chain
682  *   [@0]: dpo-load-balance: [index:18 buckets:1 uRPF:17 to:[1:84]]
683  *     [0] [@2]: dpo-receive: 172.16.1.1 on af_packet0
684  * 172.16.1.2/32
685  *   unicast-ip4-chain
686  *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
687  *     [0] [@5]: ipv4 via 172.16.1.2 af_packet0: IP4: 02:fe:9e:70:7a:2b -> 26:a5:f6:9c:3a:36
688  * 172.16.2.0/24
689  *   unicast-ip4-chain
690  *   [@0]: dpo-load-balance: [index:19 buckets:1 uRPF:18 to:[0:0]]
691  *     [0] [@4]: ipv4-glean: af_packet1
692  * 172.16.2.1/32
693  *   unicast-ip4-chain
694  *   [@0]: dpo-load-balance: [index:20 buckets:1 uRPF:19 to:[0:0]]
695  *     [0] [@2]: dpo-receive: 172.16.2.1 on af_packet1
696  * 224.0.0.0/8
697  *   unicast-ip4-chain
698  *   [@0]: dpo-load-balance: [index:15 buckets:1 uRPF:14 to:[0:0]]
699  *     [0] [@0]: dpo-drop ip6
700  * 240.0.0.0/8
701  *   unicast-ip4-chain
702  *   [@0]: dpo-load-balance: [index:14 buckets:1 uRPF:13 to:[0:0]]
703  *     [0] [@0]: dpo-drop ip6
704  * 255.255.255.255/32
705  *   unicast-ip4-chain
706  *   [@0]: dpo-load-balance: [index:16 buckets:1 uRPF:15 to:[0:0]]
707  *     [0] [@0]: dpo-drop ip6
708  * @cliexend
709  * Example of how to display a summary of all IPv4 FIB tables:
710  * @cliexstart{show ip fib summary}
711  * ipv4-VRF:0, fib_index 0, flow hash: src dst sport dport proto
712  *     Prefix length         Count
713  *                    0               1
714  *                    8               2
715  *                   32               4
716  * ipv4-VRF:7, fib_index 1, flow hash: src dst sport dport proto
717  *     Prefix length         Count
718  *                    0               1
719  *                    8               2
720  *                   24               2
721  *                   32               4
722  * @cliexend
723  ?*/
724 /* *INDENT-OFF* */
725 VLIB_CLI_COMMAND (ip4_show_fib_command, static) = {
726     .path = "show ip fib",
727     .short_help = "show ip fib [summary] [table <table-id>] [index <fib-id>] [<ip4-addr>[/<mask>]] [mtrie] [detail]",
728     .function = ip4_show_fib,
729 };
730 /* *INDENT-ON* */