vlib: improvement to automatic core pinning
[vpp.git] / src / vnet / mfib / ip4_mfib.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/mfib/ip4_mfib.h>
17
18 #include <vnet/mfib/mfib_table.h>
19 #include <vnet/mfib/mfib_entry.h>
20
21 static const mfib_prefix_t all_zeros =
22 {
23     .fp_proto = FIB_PROTOCOL_IP4,
24 };
25 static const mfib_prefix_t ip4_specials[] =
26 {
27     /* ALL prefixes are in network order */
28     {
29         /* (*,224.0.0.1)/32 - all hosts */
30         .fp_grp_addr = {
31             .ip4.data_u32 = 0x010000e0,
32         },
33         .fp_len = 32,
34         .fp_proto = FIB_PROTOCOL_IP4,
35     },
36     {
37         /* (*,224.0.0.2)/32 - all routers */
38         .fp_grp_addr = {
39             .ip4.data_u32 = 0x020000e0,
40         },
41         .fp_len = 32,
42         .fp_proto = FIB_PROTOCOL_IP4,
43     },
44 };
45 static const fib_route_path_t ip4_special_path =
46   {
47    .frp_proto = DPO_PROTO_IP4,
48    .frp_addr = {
49                 .ip4.data_u32 = 0x0,
50                 },
51    .frp_sw_if_index = ~0,
52    .frp_fib_index = ~0,
53    .frp_weight = 1,
54    .frp_flags = FIB_ROUTE_PATH_LOCAL,
55    .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
56   };
57
58 static u32
59 ip4_create_mfib_with_table_id (u32 table_id,
60                                mfib_source_t src)
61 {
62     mfib_table_t *mfib_table;
63
64     pool_get_aligned(ip4_main.mfibs, mfib_table, CLIB_CACHE_LINE_BYTES);
65     clib_memset(mfib_table, 0, sizeof(*mfib_table));
66
67     mfib_table->mft_proto = FIB_PROTOCOL_IP4;
68     mfib_table->mft_index =
69         mfib_table->v4.index =
70             (mfib_table - ip4_main.mfibs);
71
72     hash_set (ip4_main.mfib_index_by_table_id,
73               table_id,
74               mfib_table->mft_index);
75
76     mfib_table->mft_table_id =
77         mfib_table->v4.table_id =
78             table_id;
79
80     mfib_table_lock(mfib_table->mft_index, FIB_PROTOCOL_IP4, src);
81
82     /*
83      * add the default route into the new FIB
84      */
85     mfib_table_entry_update(mfib_table->mft_index,
86                             &all_zeros,
87                             MFIB_SOURCE_DEFAULT_ROUTE,
88                             MFIB_RPF_ID_NONE,
89                             MFIB_ENTRY_FLAG_DROP);
90
91     int ii;
92
93     for (ii = 0; ii < ARRAY_LEN(ip4_specials); ii++)
94     {
95         mfib_table_entry_path_update(mfib_table->mft_index,
96                                      &ip4_specials[ii],
97                                      MFIB_SOURCE_SPECIAL,
98                                      MFIB_ENTRY_FLAG_NONE,
99                                      &ip4_special_path);
100     }
101
102     return (mfib_table->mft_index);
103 }
104
105 void
106 ip4_mfib_table_destroy (ip4_mfib_t *mfib)
107 {
108     mfib_table_t *mfib_table = (mfib_table_t*)mfib;
109     int ii;
110
111     /*
112      * remove all the specials we added when the table was created.
113      */
114     mfib_table_entry_delete(mfib_table->mft_index,
115                             &all_zeros,
116                             MFIB_SOURCE_DEFAULT_ROUTE);
117
118     for (ii = 0; ii < ARRAY_LEN(ip4_specials); ii++)
119       {
120         mfib_table_entry_path_remove(mfib_table->mft_index,
121                                      &ip4_specials[ii],
122                                      MFIB_SOURCE_SPECIAL,
123                                      &ip4_special_path);
124       }
125
126     /*
127      * validate no more routes.
128      */
129     ASSERT(0 == mfib_table->mft_total_route_counts);
130     ASSERT(~0 != mfib_table->mft_table_id);
131
132     for (u32 i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++)
133       hash_free (mfib->fib_entry_by_dst_address[i]);
134     hash_unset (ip4_main.mfib_index_by_table_id, mfib_table->mft_table_id);
135     pool_put(ip4_main.mfibs, mfib_table);
136 }
137
138 void
139 ip4_mfib_interface_enable_disable (u32 sw_if_index, int is_enable)
140 {
141     const fib_route_path_t path = {
142         .frp_proto = DPO_PROTO_IP4,
143         .frp_addr = zero_addr,
144         .frp_sw_if_index = sw_if_index,
145         .frp_fib_index = ~0,
146         .frp_weight = 1,
147         .frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT,
148     };
149     u32 mfib_index;
150     int ii;
151
152     mfib_index = ip4_mfib_table_get_index_for_sw_if_index(sw_if_index);
153
154     for (ii = 0; ii < ARRAY_LEN(ip4_specials); ii++)
155     {
156         if (is_enable)
157         {
158             mfib_table_entry_path_update(mfib_index,
159                                          &ip4_specials[ii],
160                                          MFIB_SOURCE_SPECIAL,
161                                          MFIB_ENTRY_FLAG_NONE,
162                                          &path);
163         }
164         else
165         {
166             mfib_table_entry_path_remove(mfib_index,
167                                          &ip4_specials[ii],
168                                          MFIB_SOURCE_SPECIAL,
169                                          &path);
170         }
171     }
172 }
173
174 u32
175 ip4_mfib_table_find_or_create_and_lock (u32 table_id,
176                                         mfib_source_t src)
177 {
178     u32 index;
179
180     index = ip4_mfib_index_from_table_id(table_id);
181     if (~0 == index)
182         return ip4_create_mfib_with_table_id(table_id, src);
183     mfib_table_lock(index, FIB_PROTOCOL_IP4, src);
184
185     return (index);
186 }
187
188 u32
189 ip4_mfib_table_get_index_for_sw_if_index (u32 sw_if_index)
190 {
191     if (sw_if_index >= vec_len(ip4_main.mfib_index_by_sw_if_index))
192     {
193         /*
194          * This is the case for interfaces that are not yet mapped to
195          * a IP table
196          */
197         return (~0);
198     }
199     return (ip4_main.mfib_index_by_sw_if_index[sw_if_index]);
200 }
201
202 #define IPV4_MFIB_GRP_LEN(_len)\
203     (_len > 32 ? 32 : _len)
204
205 #define IP4_MFIB_MK_KEY(_grp, _src, _len, _key)                         \
206 {                                                                       \
207     _key  = ((u64)(_grp->data_u32 &                                     \
208                    ip4_main.fib_masks[IPV4_MFIB_GRP_LEN(_len)])) << 32; \
209     _key |= _src->data_u32;                                             \
210 }
211 #define IP4_MFIB_MK_GRP_KEY(_grp, _len, _key)                           \
212 {                                                                       \
213     _key  = ((u64)(_grp->data_u32 &                                     \
214                    ip4_main.fib_masks[IPV4_MFIB_GRP_LEN(_len)])) << 32; \
215 }
216
217 /*
218  * ip4_fib_table_lookup_exact_match
219  *
220  * Exact match prefix lookup
221  */
222 fib_node_index_t
223 ip4_mfib_table_lookup_exact_match (const ip4_mfib_t *mfib,
224                                    const ip4_address_t *grp,
225                                    const ip4_address_t *src,
226                                    u32 len)
227 {
228     uword * hash, * result;
229     u64 key;
230
231     hash = mfib->fib_entry_by_dst_address[len];
232     IP4_MFIB_MK_KEY(grp, src, len, key);
233
234     result = hash_get(hash, key);
235
236     if (NULL != result) {
237         return (result[0]);
238     }
239     return (FIB_NODE_INDEX_INVALID);
240 }
241
242 /*
243  * ip4_fib_table_lookup
244  *
245  * Longest prefix match
246  */
247 fib_node_index_t
248 ip4_mfib_table_lookup (const ip4_mfib_t *mfib,
249                        const ip4_address_t *src,
250                        const ip4_address_t *grp,
251                        u32 len)
252 {
253     uword * hash, * result;
254     i32 mask_len;
255     u64 key;
256
257     mask_len = len;
258
259     if (PREDICT_TRUE(64 == mask_len))
260     {
261         hash = mfib->fib_entry_by_dst_address[mask_len];
262         IP4_MFIB_MK_KEY(grp, src, mask_len, key);
263
264         result = hash_get (hash, key);
265
266         if (NULL != result) {
267             return (result[0]);
268         }
269     }
270
271     for (mask_len = (len == 64 ? 32 : len); mask_len >= 0; mask_len--)
272     {
273         hash = mfib->fib_entry_by_dst_address[mask_len];
274         IP4_MFIB_MK_GRP_KEY(grp, mask_len, key);
275
276         result = hash_get (hash, key);
277
278         if (NULL != result) {
279             return (result[0]);
280         }
281     }
282     return (FIB_NODE_INDEX_INVALID);
283 }
284
285 fib_node_index_t
286 ip4_mfib_table_get_less_specific (const ip4_mfib_t *mfib,
287                                   const ip4_address_t *src,
288                                   const ip4_address_t *grp,
289                                   u32 len)
290 {
291     u32 mask_len;
292
293     /*
294      * in the absence of a tree structure for the table that allows for an O(1)
295      * parent get, a cheeky way to find the cover is to LPM for the prefix with
296      * mask-1.
297      * there should always be a cover, though it may be the default route. the
298      * default route's cover is the default route.
299      */
300     if (len == 64)
301     {
302         /* go from (S,G) to (*,G*) */
303         mask_len = 32;
304     }
305     else if (len != 0)
306     {
307         mask_len = len - 1;
308     }
309     else
310     {
311         mask_len = len;
312     }
313
314     return (ip4_mfib_table_lookup(mfib, src, grp, mask_len));
315 }
316
317 void
318 ip4_mfib_table_entry_insert (ip4_mfib_t *mfib,
319                              const ip4_address_t *grp,
320                              const ip4_address_t *src,
321                              u32 len,
322                              fib_node_index_t fib_entry_index)
323 {
324     uword * hash, * result;
325     u64 key;
326
327     IP4_MFIB_MK_KEY(grp, src, len, key);
328     hash = mfib->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         mfib->fib_entry_by_dst_address[len] = hash;
341     }
342     else
343     {
344         ASSERT(0);
345     }
346 }
347
348 void
349 ip4_mfib_table_entry_remove (ip4_mfib_t *mfib,
350                              const ip4_address_t *grp,
351                              const ip4_address_t *src,
352                              u32 len)
353 {
354     uword * hash, * result;
355     u64 key;
356
357     IP4_MFIB_MK_KEY(grp, src, len, key);
358     hash = mfib->fib_entry_by_dst_address[len];
359     result = hash_get (hash, key);
360
361     if (NULL == result)
362     {
363         /*
364          * removing a non-existent entry. i'll allow it.
365          */
366     }
367     else
368     {
369         hash_unset(hash, key);
370     }
371
372     mfib->fib_entry_by_dst_address[len] = hash;
373 }
374
375 void
376 ip4_mfib_table_walk (ip4_mfib_t *mfib,
377                      mfib_table_walk_fn_t fn,
378                      void *ctx)
379 {
380     int i;
381
382     for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++)
383     {
384         uword * hash = mfib->fib_entry_by_dst_address[i];
385
386         if (NULL != hash)
387         {
388             hash_pair_t * p;
389
390             hash_foreach_pair (p, hash,
391             ({
392                 fn(p->value[0], ctx);
393             }));
394         }
395     }
396 }
397
398 u8 *
399 format_ip4_mfib_table_memory (u8 * s, va_list * args)
400 {
401     mfib_table_t *mfib_table;
402     u64 total_memory;
403
404     total_memory = 0;
405
406     pool_foreach (mfib_table, ip4_main.mfibs)
407      {
408         ip4_mfib_t *mfib = &mfib_table->v4;
409         uword mfib_size;
410         int i;
411
412         mfib_size = 0;
413
414         for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++)
415         {
416             uword * hash = mfib->fib_entry_by_dst_address[i];
417
418             if (NULL != hash)
419             {
420                 mfib_size += hash_bytes(hash);
421             }
422         }
423
424         total_memory += mfib_size;
425     }
426
427     s = format(s, "%=30s %=6d %=12ld\n",
428                "IPv4 multicast",
429                pool_elts(ip4_main.mfibs), total_memory);
430
431     return (s);
432 }
433
434 static void
435 ip4_mfib_table_show_all (ip4_mfib_t *mfib,
436                          vlib_main_t * vm)
437 {
438     fib_node_index_t *mfib_entry_indicies;
439     fib_node_index_t *mfib_entry_index;
440     int i;
441
442     mfib_entry_indicies = NULL;
443
444     for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++)
445     {
446         uword * hash = mfib->fib_entry_by_dst_address[i];
447
448         if (NULL != hash)
449         {
450             hash_pair_t * p;
451
452             hash_foreach_pair (p, hash,
453             ({
454                 vec_add1(mfib_entry_indicies, p->value[0]);
455             }));
456         }
457     }
458
459     vec_sort_with_function(mfib_entry_indicies, mfib_entry_cmp_for_sort);
460
461     vec_foreach(mfib_entry_index, mfib_entry_indicies)
462     {
463         vlib_cli_output(vm, "%U",
464                         format_mfib_entry,
465                         *mfib_entry_index,
466                         MFIB_ENTRY_FORMAT_BRIEF);
467     }
468
469     vec_free(mfib_entry_indicies);
470 }
471
472 static void
473 ip4_mfib_table_show_one (ip4_mfib_t *mfib,
474                          vlib_main_t * vm,
475                          ip4_address_t *src,
476                          ip4_address_t *grp,
477                          u32 mask_len)
478 {
479     vlib_cli_output(vm, "%U",
480                     format_mfib_entry,
481                     ip4_mfib_table_lookup(mfib, src, grp, mask_len),
482                     MFIB_ENTRY_FORMAT_DETAIL);
483 }
484
485 static clib_error_t *
486 ip4_show_mfib (vlib_main_t * vm,
487                unformat_input_t * input,
488                vlib_cli_command_t * cmd)
489 {
490     ip4_main_t * im4 = &ip4_main;
491     mfib_table_t *mfib_table;
492     int verbose, matching, memory;
493     ip4_address_t grp, src = {{0}};
494     u32 mask = 32;
495     u64 total_hash_memory;
496     int i, table_id = -1, fib_index = ~0;
497
498     verbose = 1;
499     memory = matching = 0;
500     total_hash_memory = 0;
501
502     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
503     {
504         if (unformat (input, "brief") || unformat (input, "summary")
505             || unformat (input, "sum"))
506             verbose = 0;
507         else if (unformat (input, "mem") || unformat (input, "memory"))
508             memory = 1;
509         else if (unformat (input, "%U %U",
510                            unformat_ip4_address, &src,
511                            unformat_ip4_address, &grp))
512         {
513             matching = 1;
514             mask = 64;
515         }
516         else if (unformat (input, "%U/%d", unformat_ip4_address, &grp, &mask))
517         {
518             clib_memset(&src, 0, sizeof(src));
519             matching = 1;
520         }
521         else if (unformat (input, "%U", unformat_ip4_address, &grp))
522         {
523             clib_memset(&src, 0, sizeof(src));
524             matching = 1;
525             mask = 32;
526         }
527         else if (unformat (input, "table %d", &table_id))
528             ;
529         else if (unformat (input, "index %d", &fib_index))
530             ;
531         else
532             break;
533     }
534
535     pool_foreach (mfib_table, im4->mfibs)
536      {
537         ip4_mfib_t *mfib = &mfib_table->v4;
538
539         if (table_id >= 0 && table_id != (int)mfib->table_id)
540             continue;
541         if (fib_index != ~0 && fib_index != (int)mfib->index)
542             continue;
543
544         if (memory)
545         {
546             uword hash_size;
547
548             hash_size = 0;
549
550             for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++)
551             {
552                 uword * hash = mfib->fib_entry_by_dst_address[i];
553                 if (NULL != hash)
554                 {
555                     hash_size += hash_bytes(hash);
556                 }
557             }
558             if (verbose)
559                 vlib_cli_output (vm, "%U hash:%d",
560                                  format_mfib_table_name, mfib->index,
561                                  FIB_PROTOCOL_IP4,
562                                  hash_size);
563             total_hash_memory += hash_size;
564             continue;
565         }
566
567         vlib_cli_output (vm, "%U, fib_index:%d flags:%U",
568                          format_mfib_table_name, mfib->index, FIB_PROTOCOL_IP4,
569                          mfib->index,
570                          format_mfib_table_flags, mfib_table->mft_flags);
571
572         /* Show summary? */
573         if (! verbose)
574         {
575             vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");
576             for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++)
577             {
578                 uword * hash = mfib->fib_entry_by_dst_address[i];
579                 uword n_elts = hash_elts (hash);
580                 if (n_elts > 0)
581                     vlib_cli_output (vm, "%20d%16d", i, n_elts);
582             }
583             continue;
584         }
585
586         if (!matching)
587         {
588             ip4_mfib_table_show_all(mfib, vm);
589         }
590         else
591         {
592             ip4_mfib_table_show_one(mfib, vm, &src, &grp, mask);
593         }
594     }
595     if (memory)
596         vlib_cli_output (vm, "totals: hash:%ld", total_hash_memory);
597
598     return 0;
599 }
600
601 /* clang-format off */
602 /*?
603  * This command displays the IPv4 MulticasrFIB Tables (VRF Tables) and
604  * the route entries for each table.
605  *
606  * @note This command will run for a long time when the FIB tables are
607  * comprised of millions of entries. For those scenarios, consider displaying
608  * a single table or summary mode.
609  *
610  * @cliexpar
611  * Example of how to display all the IPv4 Multicast FIB tables:
612  * @cliexstart{show ip fib}
613  * ipv4-VRF:0, fib_index 0
614  * (*, 0.0.0.0/0):  flags:D,
615  *  Interfaces:
616  *  multicast-ip4-chain
617  *   [@1]: dpo-drop ip4
618  * (*, 232.1.1.1/32):
619  * Interfaces:
620  *  test-eth1: Forward,
621  *  test-eth2: Forward,
622  *  test-eth0: Accept,
623  * multicast-ip4-chain
624  * [@2]: dpo-replicate: [index:1 buckets:2 to:[0:0]]
625  *   [0] [@1]: ipv4-mcast: test-eth1: IP4: d0:d1:d2:d3:d4:01 -> 01:00:05:00:00:00
626  *   [1] [@1]: ipv4-mcast: test-eth2: IP4: d0:d1:d2:d3:d4:02 -> 01:00:05:00:00:00
627  *
628  * @cliexend
629  * Example of how to display a summary of all IPv4 FIB tables:
630  * @cliexstart{show ip fib summary}
631  * ipv4-VRF:0, fib_index 0, flow hash: src dst sport dport proto
632  *     Prefix length         Count
633  *                    0               1
634  *                    8               2
635  *                   32               4
636  * ipv4-VRF:7, fib_index 1, flow hash: src dst sport dport proto
637  *     Prefix length         Count
638  *                    0               1
639  *                    8               2
640  *                   24               2
641  *                   32               4
642  * @cliexend
643  ?*/
644 /* clang-format on */
645 VLIB_CLI_COMMAND (ip4_show_mfib_command, static) = {
646     .path = "show ip mfib",
647     .short_help = "show ip mfib [summary] [table <table-id>] [index <fib-id>] [<grp-addr>[/<mask>]] [<grp-addr>] [<src-addr> <grp-addr>]",
648     .function = ip4_show_mfib,
649 };