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