Remove c-11 memcpy checks from perf-critical code
[vpp.git] / src / vnet / l2 / l2_fib.c
1 /*
2  * l2_fib.c : layer 2 forwarding table (aka mac table)
3  *
4  * Copyright (c) 2013 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18
19 #include <vlib/vlib.h>
20 #include <vnet/vnet.h>
21 #include <vnet/pg/pg.h>
22 #include <vnet/ethernet/ethernet.h>
23 #include <vlib/cli.h>
24
25 #include <vppinfra/error.h>
26 #include <vppinfra/hash.h>
27 #include <vnet/l2/l2_input.h>
28 #include <vnet/l2/l2_fib.h>
29 #include <vnet/l2/l2_learn.h>
30 #include <vnet/l2/l2_bd.h>
31
32 #include <vppinfra/bihash_template.c>
33
34 #include <vlibmemory/api.h>
35 #include <vnet/vnet_msg_enum.h>
36
37 #define vl_typedefs             /* define message structures */
38 #include <vnet/vnet_all_api_h.h>
39 #undef vl_typedefs
40
41 #define vl_endianfun            /* define message structures */
42 #include <vnet/vnet_all_api_h.h>
43 #undef vl_endianfun
44
45 /**
46  * @file
47  * @brief Ethernet MAC Address FIB Table Management.
48  *
49  * The MAC Address forwarding table for bridge-domains is called the l2fib.
50  * Entries are added automatically as part of mac learning, but MAC Addresses
51  * entries can also be added manually.
52  *
53  */
54
55 l2fib_main_t l2fib_main;
56
57 u8 *
58 format_l2fib_entry_result_flags (u8 * s, va_list * args)
59 {
60   l2fib_entry_result_flags_t flags = va_arg (*args, int);
61
62   if (L2FIB_ENTRY_RESULT_FLAG_NONE == flags)
63     {
64       s = format (s, "none");
65     }
66   else
67     {
68 #define _(a,v,t) {                              \
69       if (flags & L2FIB_ENTRY_RESULT_FLAG_##a)  \
70         s = format (s, "%s ", t);               \
71     }
72       foreach_l2fib_entry_result_attr
73 #undef _
74     }
75   return (s);
76 }
77
78 static void
79 incr_mac_address (u8 * mac)
80 {
81   u64 tmp = *((u64 *) mac);
82   tmp = clib_net_to_host_u64 (tmp);
83   tmp += 1 << 16;               /* skip unused (least significant) octets */
84   tmp = clib_host_to_net_u64 (tmp);
85
86   clib_memcpy_fast (mac, &tmp, 6);
87 }
88
89 /** Format sw_if_index. If the value is ~0, use the text "N/A" */
90 u8 *
91 format_vnet_sw_if_index_name_with_NA (u8 * s, va_list * args)
92 {
93   vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
94   u32 sw_if_index = va_arg (*args, u32);
95   if (sw_if_index == ~0)
96     return format (s, "N/A");
97
98   vnet_sw_interface_t *swif = vnet_get_sw_interface_safe (vnm, sw_if_index);
99   if (!swif)
100     return format (s, "Stale");
101
102   return format (s, "%U", format_vnet_sw_interface_name, vnm,
103                  vnet_get_sw_interface_safe (vnm, sw_if_index));
104 }
105
106 typedef struct l2fib_dump_walk_ctx_t_
107 {
108   u32 bd_index;
109   l2fib_entry_key_t *l2fe_key;
110   l2fib_entry_result_t *l2fe_res;
111 } l2fib_dump_walk_ctx_t;
112
113 static void
114 l2fib_dump_walk_cb (BVT (clib_bihash_kv) * kvp, void *arg)
115 {
116   l2fib_dump_walk_ctx_t *ctx = arg;
117   l2fib_entry_result_t result;
118   l2fib_entry_key_t key;
119
120   key.raw = kvp->key;
121   result.raw = kvp->value;
122
123   if ((ctx->bd_index == ~0) || (ctx->bd_index == key.fields.bd_index))
124     {
125       vec_add1 (ctx->l2fe_key, key);
126       vec_add1 (ctx->l2fe_res, result);
127     }
128 }
129
130 void
131 l2fib_table_dump (u32 bd_index,
132                   l2fib_entry_key_t ** l2fe_key,
133                   l2fib_entry_result_t ** l2fe_res)
134 {
135   l2fib_main_t *msm = &l2fib_main;
136   l2fib_dump_walk_ctx_t ctx = {
137     .bd_index = bd_index,
138   };
139
140   BV (clib_bihash_foreach_key_value_pair)
141     (&msm->mac_table, l2fib_dump_walk_cb, &ctx);
142
143   *l2fe_key = ctx.l2fe_key;
144   *l2fe_res = ctx.l2fe_res;
145 }
146
147 typedef struct l2fib_show_walk_ctx_t_
148 {
149   u8 first_entry;
150   u8 verbose;
151   vlib_main_t *vm;
152   vnet_main_t *vnm;
153   u32 total_entries;
154   u32 bd_index;
155   u8 learn;
156   u8 add;
157   u8 now;
158 } l2fib_show_walk_ctx_t;
159
160 static void
161 l2fib_show_walk_cb (BVT (clib_bihash_kv) * kvp, void *arg)
162 {
163   l2fib_show_walk_ctx_t *ctx = arg;
164   l2_bridge_domain_t *bd_config;
165   l2fib_entry_result_t result;
166   l2fib_entry_key_t key;
167
168   if (ctx->verbose && ctx->first_entry)
169     {
170       ctx->first_entry = 0;
171       vlib_cli_output (ctx->vm,
172                        "%=19s%=7s%=7s%=8s%=9s%=7s%=7s%=5s%=30s",
173                        "Mac-Address", "BD-Idx", "If-Idx",
174                        "BSN-ISN", "Age(min)", "static", "filter",
175                        "bvi", "Interface-Name");
176     }
177
178   key.raw = kvp->key;
179   result.raw = kvp->value;
180   ctx->total_entries++;
181
182   if (ctx->verbose &&
183       ((ctx->bd_index >> 31) || (ctx->bd_index == key.fields.bd_index)))
184     {
185       u8 *s = NULL;
186
187       if (ctx->learn && l2fib_entry_result_is_set_AGE_NOT (&result))
188         return;                 /* skip provisioned macs */
189
190       if (ctx->add && !l2fib_entry_result_is_set_AGE_NOT (&result))
191         return;                 /* skip learned macs */
192
193       bd_config = vec_elt_at_index (l2input_main.bd_configs,
194                                     key.fields.bd_index);
195
196       if (l2fib_entry_result_is_set_AGE_NOT (&result))
197         s = format (s, "no");
198       else if (bd_config->mac_age == 0)
199         s = format (s, "-");
200       else
201         {
202           i16 delta = ctx->now - result.fields.timestamp;
203           delta += delta < 0 ? 256 : 0;
204           s = format (s, "%d", delta);
205         }
206
207       vlib_cli_output (ctx->vm,
208                        "%=19U%=7d%=7d %3d/%-3d%=9v%=7s%=7s%=5s%=30U",
209                        format_ethernet_address, key.fields.mac,
210                        key.fields.bd_index,
211                        result.fields.sw_if_index == ~0
212                        ? -1 : result.fields.sw_if_index,
213                        result.fields.sn.bd, result.fields.sn.swif, s,
214                        l2fib_entry_result_is_set_STATIC (&result) ? "*" : "-",
215                        l2fib_entry_result_is_set_FILTER (&result) ? "*" : "-",
216                        l2fib_entry_result_is_set_BVI (&result) ? "*" : "-",
217                        format_vnet_sw_if_index_name_with_NA,
218                        ctx->vnm, result.fields.sw_if_index);
219       vec_free (s);
220     }
221 }
222
223 /** Display the contents of the l2fib. */
224 static clib_error_t *
225 show_l2fib (vlib_main_t * vm,
226             unformat_input_t * input, vlib_cli_command_t * cmd)
227 {
228   bd_main_t *bdm = &bd_main;
229   l2fib_main_t *msm = &l2fib_main;
230   u8 raw = 0;
231   u32 bd_id;
232   l2fib_show_walk_ctx_t ctx = {
233     .first_entry = 1,
234     .bd_index = ~0,
235     .now = (u8) (vlib_time_now (vm) / 60),
236     .vm = vm,
237     .vnm = msm->vnet_main,
238   };
239
240   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
241     {
242       if (unformat (input, "raw"))
243         {
244           raw = 1;
245           ctx.verbose = 0;
246           break;
247         }
248       else if (unformat (input, "verbose"))
249         ctx.verbose = 1;
250       else if (unformat (input, "all"))
251         ctx.verbose = 1;
252       else if (unformat (input, "bd_index %d", &ctx.bd_index))
253         ctx.verbose = 1;
254       else if (unformat (input, "learn"))
255         {
256           ctx.add = 0;
257           ctx.learn = 1;
258           ctx.verbose = 1;
259         }
260       else if (unformat (input, "add"))
261         {
262           ctx.learn = 0;
263           ctx.add = 1;
264           ctx.verbose = 1;
265         }
266       else if (unformat (input, "bd_id %d", &bd_id))
267         {
268           uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id);
269           if (p)
270             {
271               ctx.verbose = 1;
272               ctx.bd_index = p[0];
273             }
274           else
275             return clib_error_return (0,
276                                       "bridge domain id %d doesn't exist\n",
277                                       bd_id);
278         }
279       else
280         break;
281     }
282
283   BV (clib_bihash_foreach_key_value_pair)
284     (&msm->mac_table, l2fib_show_walk_cb, &ctx);
285
286   if (ctx.total_entries == 0)
287     vlib_cli_output (vm, "no l2fib entries");
288   else
289     {
290       l2learn_main_t *lm = &l2learn_main;
291       vlib_cli_output (vm, "L2FIB total/learned entries: %d/%d  "
292                        "Last scan time: %.4esec  Learn limit: %d ",
293                        ctx.total_entries, lm->global_learn_count,
294                        msm->age_scan_duration, lm->global_learn_limit);
295       if (lm->client_pid)
296         vlib_cli_output (vm, "L2MAC events client PID: %d  "
297                          "Last e-scan time: %.4esec  Delay: %.2esec  "
298                          "Max macs in event: %d",
299                          lm->client_pid, msm->evt_scan_duration,
300                          msm->event_scan_delay, msm->max_macs_in_event);
301     }
302
303   if (raw)
304     vlib_cli_output (vm, "Raw Hash Table:\n%U\n",
305                      BV (format_bihash), &msm->mac_table, 1 /* verbose */ );
306
307   return 0;
308 }
309
310 /*?
311  * This command displays the MAC Address entries of the L2 FIB table.
312  * Output can be filtered to just get the number of MAC Addresses or display
313  * each MAC Address for all bridge domains or just a single bridge domain.
314  *
315  * @cliexpar
316  * Example of how to display the number of MAC Address entries in the L2
317  * FIB table:
318  * @cliexstart{show l2fib}
319  * 3 l2fib entries
320  * @cliexend
321  * Example of how to display all the MAC Address entries in the L2
322  * FIB table:
323  * @cliexstart{show l2fib all}
324  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
325  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
326  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
327  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
328  * 3 l2fib entries
329  * @cliexend
330 ?*/
331 /* *INDENT-OFF* */
332 VLIB_CLI_COMMAND (show_l2fib_cli, static) = {
333   .path = "show l2fib",
334   .short_help = "show l2fib [all] | [bd_id <nn> | bd_index <nn>] [learn | add] | [raw]",
335   .function = show_l2fib,
336 };
337 /* *INDENT-ON* */
338
339
340 /* Remove all entries from the l2fib */
341 void
342 l2fib_clear_table (void)
343 {
344   l2fib_main_t *mp = &l2fib_main;
345
346   /* Remove all entries */
347   BV (clib_bihash_free) (&mp->mac_table);
348   BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
349                          L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
350   l2learn_main.global_learn_count = 0;
351 }
352
353 /** Clear all entries in L2FIB.
354  * @TODO: Later we may want a way to remove only the non-static entries
355  */
356 static clib_error_t *
357 clear_l2fib (vlib_main_t * vm,
358              unformat_input_t * input, vlib_cli_command_t * cmd)
359 {
360   l2fib_clear_table ();
361   return 0;
362 }
363
364 /*?
365  * This command clears all the MAC Address entries from the L2 FIB table.
366  *
367  * @cliexpar
368  * Example of how to clear the L2 FIB Table:
369  * @cliexcmd{clear l2fib}
370  * Example to show the L2 FIB Table has been cleared:
371  * @cliexstart{show l2fib verbose}
372  * no l2fib entries
373  * @cliexend
374 ?*/
375 /* *INDENT-OFF* */
376 VLIB_CLI_COMMAND (clear_l2fib_cli, static) = {
377   .path = "clear l2fib",
378   .short_help = "clear l2fib",
379   .function = clear_l2fib,
380 };
381 /* *INDENT-ON* */
382
383 static inline l2fib_seq_num_t
384 l2fib_cur_seq_num (u32 bd_index, u32 sw_if_index)
385 {
386   l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
387   /* *INDENT-OFF* */
388   return (l2fib_seq_num_t) {
389     .swif = *l2fib_swif_seq_num (sw_if_index),
390     .bd = bd_config->seq_num,
391   };
392   /* *INDENT-ON* */
393 }
394
395 /**
396  * Add an entry to the l2fib.
397  * If the entry already exists then overwrite it
398  */
399 void
400 l2fib_add_entry (const u8 * mac, u32 bd_index,
401                  u32 sw_if_index, l2fib_entry_result_flags_t flags)
402 {
403   l2fib_entry_key_t key;
404   l2fib_entry_result_t result;
405   __attribute__ ((unused)) u32 bucket_contents;
406   l2fib_main_t *fm = &l2fib_main;
407   l2learn_main_t *lm = &l2learn_main;
408   BVT (clib_bihash_kv) kv;
409
410   /* set up key */
411   key.raw = l2fib_make_key (mac, bd_index);
412
413   /* check if entry already exist */
414   if (BV (clib_bihash_search) (&fm->mac_table, &kv, &kv))
415     {
416       /* decrement counter if overwriting a learned mac  */
417       result.raw = kv.value;
418       if ((!l2fib_entry_result_is_set_AGE_NOT (&result))
419           && (lm->global_learn_count))
420         lm->global_learn_count--;
421     }
422
423   /* set up result */
424   result.raw = 0;               /* clear all fields */
425   result.fields.sw_if_index = sw_if_index;
426   result.fields.flags = flags;
427
428   /* no aging for provisioned entry */
429   l2fib_entry_result_set_AGE_NOT (&result);
430
431   kv.key = key.raw;
432   kv.value = result.raw;
433
434   BV (clib_bihash_add_del) (&fm->mac_table, &kv, 1 /* is_add */ );
435 }
436
437 /**
438  * Add an entry to the L2FIB.
439  * The CLI format is:
440  *    l2fib add <mac> <bd> <intf> [static] [bvi]
441  *    l2fib add <mac> <bd> filter
442  * Note that filter and bvi entries are always static
443  */
444 static clib_error_t *
445 l2fib_add (vlib_main_t * vm,
446            unformat_input_t * input, vlib_cli_command_t * cmd)
447 {
448   bd_main_t *bdm = &bd_main;
449   vnet_main_t *vnm = vnet_get_main ();
450   clib_error_t *error = 0;
451   u8 mac[6];
452   u32 bd_id;
453   u32 bd_index;
454   u32 sw_if_index = ~0;
455   uword *p;
456   l2fib_entry_result_flags_t flags;
457
458   flags = L2FIB_ENTRY_RESULT_FLAG_NONE;
459
460   if (!unformat (input, "%U", unformat_ethernet_address, mac))
461     {
462       error = clib_error_return (0, "expected mac address `%U'",
463                                  format_unformat_error, input);
464       goto done;
465     }
466
467   if (!unformat (input, "%d", &bd_id))
468     {
469       error = clib_error_return (0, "expected bridge domain ID `%U'",
470                                  format_unformat_error, input);
471       goto done;
472     }
473
474   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
475   if (!p)
476     {
477       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
478       goto done;
479     }
480   bd_index = p[0];
481
482   if (unformat (input, "filter"))
483     {
484       l2fib_add_filter_entry (mac, bd_index);
485       return 0;
486     }
487
488   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
489     {
490       error = clib_error_return (0, "unknown interface `%U'",
491                                  format_unformat_error, input);
492       goto done;
493     }
494
495   if (unformat (input, "static"))
496     flags |= L2FIB_ENTRY_RESULT_FLAG_STATIC;
497   else if (unformat (input, "bvi"))
498     flags |= (L2FIB_ENTRY_RESULT_FLAG_STATIC | L2FIB_ENTRY_RESULT_FLAG_BVI);
499
500   if (vec_len (l2input_main.configs) <= sw_if_index)
501     {
502       error = clib_error_return (0, "Interface sw_if_index %d not in L2 mode",
503                                  sw_if_index);
504       goto done;
505     }
506
507   l2fib_add_entry (mac, bd_index, sw_if_index, flags);
508
509 done:
510   return error;
511 }
512
513 /*?
514  * This command adds a MAC Address entry to the L2 FIB table
515  * of an existing bridge-domain. The MAC Address can be static
516  * or dynamic. This command also allows a filter to be added,
517  * such that packets with given MAC Addresses (source mac or
518  * destination mac match) are dropped.
519  *
520  * @cliexpar
521  * Example of how to add a dynamic MAC Address entry to the L2 FIB table
522  * of a bridge-domain (where 200 is the bridge-domain-id):
523  * @cliexcmd{l2fib add 52:54:00:53:18:33 200 GigabitEthernet0/8/0.200}
524  * Example of how to add a static MAC Address entry to the L2 FIB table
525  * of a bridge-domain (where 200 is the bridge-domain-id):
526  * @cliexcmd{l2fib add 52:54:00:53:18:55 200 GigabitEthernet0/8/0.200 static}
527  * Example of how to add a filter such that a packet with the given MAC
528  * Address will be dropped in a given bridge-domain (where 200 is the
529  * bridge-domain-id):
530  * @cliexcmd{l2fib add 52:54:00:53:18:77 200 filter}
531  * Example of show command of the provisioned MAC Addresses and filters:
532  * @cliexstart{show l2fib verbose}
533  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
534  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
535  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
536  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
537  * 3 l2fib entries
538  * @cliexend
539 ?*/
540 /* *INDENT-OFF* */
541 VLIB_CLI_COMMAND (l2fib_add_cli, static) = {
542   .path = "l2fib add",
543   .short_help = "l2fib add <mac> <bridge-domain-id> filter | <intf> [static | bvi]",
544   .function = l2fib_add,
545 };
546 /* *INDENT-ON* */
547
548
549 static clib_error_t *
550 l2fib_test_command_fn (vlib_main_t * vm,
551                        unformat_input_t * input, vlib_cli_command_t * cmd)
552 {
553   clib_error_t *error = 0;
554   u8 mac[6], save_mac[6];
555   u32 bd_index = 0;
556   u32 sw_if_index = 8;
557   u32 is_add = 0;
558   u32 is_del = 0;
559   u32 is_check = 0;
560   u32 count = 1;
561   int mac_set = 0;
562   int i;
563
564   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
565     {
566       if (unformat (input, "mac %U", unformat_ethernet_address, mac))
567         mac_set = 1;
568       else if (unformat (input, "add"))
569         is_add = 1;
570       else if (unformat (input, "del"))
571         is_del = 1;
572       else if (unformat (input, "check"))
573         is_check = 1;
574       else if (unformat (input, "count %d", &count))
575         ;
576       else
577         break;
578     }
579
580   if (mac_set == 0)
581     return clib_error_return (0, "mac not set");
582
583   if (is_add == 0 && is_del == 0 && is_check == 0)
584     return clib_error_return (0,
585                               "noop: pick at least one of (add,del,check)");
586
587   clib_memcpy_fast (save_mac, mac, 6);
588
589   if (is_add)
590     {
591       for (i = 0; i < count; i++)
592         {
593           l2fib_add_entry (mac, bd_index, sw_if_index,
594                            L2FIB_ENTRY_RESULT_FLAG_NONE);
595           incr_mac_address (mac);
596         }
597     }
598
599   if (is_check)
600     {
601       BVT (clib_bihash_kv) kv;
602       l2fib_main_t *mp = &l2fib_main;
603
604       clib_memcpy_fast (mac, save_mac, 6);
605
606       for (i = 0; i < count; i++)
607         {
608           kv.key = l2fib_make_key (mac, bd_index);
609           if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
610             {
611               clib_warning ("key %U AWOL", format_ethernet_address, mac);
612               break;
613             }
614           incr_mac_address (mac);
615         }
616     }
617
618   if (is_del)
619     {
620       clib_memcpy_fast (mac, save_mac, 6);
621
622       for (i = 0; i < count; i++)
623         {
624           l2fib_del_entry (mac, bd_index, 0);
625           incr_mac_address (mac);
626         }
627     }
628
629   return error;
630 }
631
632 /*?
633  * The set of '<em>test l2fib</em>' commands allow the L2 FIB table of the default
634  * bridge domain (bridge-domain-id of 0) to be modified.
635  *
636  * @cliexpar
637  * @parblock
638  * Example of how to add a set of 4 sequential MAC Address entries to L2
639  * FIB table of the default bridge-domain:
640  * @cliexcmd{test l2fib add mac 52:54:00:53:00:00 count 4}
641  *
642  * Show the set of 4 sequential MAC Address entries that were added:
643  * @cliexstart{show l2fib verbose}
644  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
645  * 52:54:00:53:00:00    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
646  * 52:54:00:53:00:01    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
647  * 52:54:00:53:00:03    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
648  * 52:54:00:53:00:02    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
649  * 4 l2fib entries
650  * @cliexend
651  *
652  * Example of how to check that the set of 4 sequential MAC Address
653  * entries were added to L2 FIB table of the default
654  * bridge-domain. Used a count of 5 to produce an error:
655  *
656  * @cliexcmd{test l2fib check mac 52:54:00:53:00:00 count 5}
657  * The output of the check command is in the log files. Log file
658  * location may vary based on your OS and Version:
659  *
660  * <b><em># tail -f /var/log/messages | grep l2fib_test_command_fn</em></b>
661  *
662  * Sep  7 17:15:24 localhost vnet[4952]: l2fib_test_command_fn:446: key 52:54:00:53:00:04 AWOL
663  *
664  * Example of how to delete a set of 4 sequential MAC Address entries
665  * from L2 FIB table of the default bridge-domain:
666  * @cliexcmd{test l2fib del mac 52:54:00:53:00:00 count 4}
667  * @endparblock
668 ?*/
669 /* *INDENT-OFF* */
670 VLIB_CLI_COMMAND (l2fib_test_command, static) = {
671   .path = "test l2fib",
672   .short_help = "test l2fib [add|del|check] mac <base-addr> count <nn>",
673   .function = l2fib_test_command_fn,
674 };
675 /* *INDENT-ON* */
676
677
678 /**
679  * Delete an entry from the l2fib.
680  * Return 0 if the entry was deleted, or 1 it was not found or if
681  * sw_if_index is non-zero and does not match that in the entry.
682  */
683 u32
684 l2fib_del_entry (const u8 * mac, u32 bd_index, u32 sw_if_index)
685 {
686   l2fib_entry_result_t result;
687   l2fib_main_t *mp = &l2fib_main;
688   BVT (clib_bihash_kv) kv;
689
690   /* set up key */
691   kv.key = l2fib_make_key (mac, bd_index);
692
693   if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
694     return 1;
695
696   result.raw = kv.value;
697
698   /*  check if sw_if_index of entry match */
699   if ((sw_if_index != 0) && (sw_if_index != result.fields.sw_if_index))
700     return 1;
701
702   /* decrement counter if dynamically learned mac */
703   if ((!l2fib_entry_result_is_set_AGE_NOT (&result)) &&
704       (l2learn_main.global_learn_count))
705     l2learn_main.global_learn_count--;
706
707   /* Remove entry from hash table */
708   BV (clib_bihash_add_del) (&mp->mac_table, &kv, 0 /* is_add */ );
709   return 0;
710 }
711
712 /**
713  * Delete an entry from the L2FIB.
714  * The CLI format is:
715  *    l2fib del <mac> <bd-id>
716  */
717 static clib_error_t *
718 l2fib_del (vlib_main_t * vm,
719            unformat_input_t * input, vlib_cli_command_t * cmd)
720 {
721   bd_main_t *bdm = &bd_main;
722   clib_error_t *error = 0;
723   u8 mac[6];
724   u32 bd_id;
725   u32 bd_index;
726   uword *p;
727
728   if (!unformat (input, "%U", unformat_ethernet_address, mac))
729     {
730       error = clib_error_return (0, "expected mac address `%U'",
731                                  format_unformat_error, input);
732       goto done;
733     }
734
735   if (!unformat (input, "%d", &bd_id))
736     {
737       error = clib_error_return (0, "expected bridge domain ID `%U'",
738                                  format_unformat_error, input);
739       goto done;
740     }
741
742   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
743   if (!p)
744     {
745       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
746       goto done;
747     }
748   bd_index = p[0];
749
750   /* Delete the entry */
751   if (l2fib_del_entry (mac, bd_index, 0))
752     {
753       error = clib_error_return (0, "mac entry not found");
754       goto done;
755     }
756
757 done:
758   return error;
759 }
760
761 /*?
762  * This command deletes an existing MAC Address entry from the L2 FIB
763  * table of an existing bridge-domain.
764  *
765  * @cliexpar
766  * Example of how to delete a MAC Address entry from the L2 FIB table of a bridge-domain (where 200 is the bridge-domain-id):
767  * @cliexcmd{l2fib del 52:54:00:53:18:33 200}
768 ?*/
769 /* *INDENT-OFF* */
770 VLIB_CLI_COMMAND (l2fib_del_cli, static) = {
771   .path = "l2fib del",
772   .short_help = "l2fib del <mac> <bridge-domain-id> []",
773   .function = l2fib_del,
774 };
775 /* *INDENT-ON* */
776
777 /**
778     Kick off ager to scan MACs to age/delete MAC entries
779 */
780 void
781 l2fib_start_ager_scan (vlib_main_t * vm)
782 {
783   uword evt = L2_MAC_AGE_PROCESS_EVENT_ONE_PASS;
784
785   /* check if there is at least one bd with mac aging enabled */
786   l2_bridge_domain_t *bd_config;
787   vec_foreach (bd_config, l2input_main.bd_configs)
788   {
789     if (bd_config->bd_id != ~0 && bd_config->mac_age != 0)
790       {
791         evt = L2_MAC_AGE_PROCESS_EVENT_START;
792         break;
793       }
794   }
795
796   vlib_process_signal_event (vm, l2fib_mac_age_scanner_process_node.index,
797                              evt, 0);
798 }
799
800 /**
801     Flush all non static MACs from an interface
802 */
803 void
804 l2fib_flush_int_mac (vlib_main_t * vm, u32 sw_if_index)
805 {
806   *l2fib_swif_seq_num (sw_if_index) += 1;
807   l2fib_start_ager_scan (vm);
808 }
809
810 /**
811     Flush all non static MACs in a bridge domain
812 */
813 void
814 l2fib_flush_bd_mac (vlib_main_t * vm, u32 bd_index)
815 {
816   l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
817   bd_config->seq_num += 1;
818   l2fib_start_ager_scan (vm);
819 }
820
821 /**
822     Flush all non static MACs - flushes all valid BDs
823 */
824 void
825 l2fib_flush_all_mac (vlib_main_t * vm)
826 {
827   l2_bridge_domain_t *bd_config;
828   vec_foreach (bd_config, l2input_main.bd_configs)
829     if (bd_is_valid (bd_config))
830     bd_config->seq_num += 1;
831
832   l2fib_start_ager_scan (vm);
833 }
834
835
836 /**
837     Flush MACs, except static ones, associated with an interface
838     The CLI format is:
839     l2fib flush-mac interface <if-name>
840 */
841 static clib_error_t *
842 l2fib_flush_mac_int (vlib_main_t * vm,
843                      unformat_input_t * input, vlib_cli_command_t * cmd)
844 {
845   vnet_main_t *vnm = vnet_get_main ();
846   clib_error_t *error = 0;
847   u32 sw_if_index;
848
849   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
850     {
851       error = clib_error_return (0, "unknown interface `%U'",
852                                  format_unformat_error, input);
853       goto done;
854     }
855
856   l2fib_flush_int_mac (vm, sw_if_index);
857
858 done:
859   return error;
860 }
861
862 /**
863     Flush all MACs, except static ones
864     The CLI format is:
865     l2fib flush-mac all
866 */
867 static clib_error_t *
868 l2fib_flush_mac_all (vlib_main_t * vm,
869                      unformat_input_t * input, vlib_cli_command_t * cmd)
870 {
871   l2fib_flush_all_mac (vm);
872   return 0;
873 }
874
875 /*?
876  * This command kick off ager to delete all existing MAC Address entries,
877  * except static ones, associated with an interface from the L2 FIB table.
878  *
879  * @cliexpar
880  * Example of how to flush MAC Address entries learned on an interface from the L2 FIB table:
881  * @cliexcmd{l2fib flush-mac interface GigabitEthernet2/1/0}
882 ?*/
883 /* *INDENT-OFF* */
884 VLIB_CLI_COMMAND (l2fib_flush_mac_all_cli, static) = {
885   .path = "l2fib flush-mac all",
886   .short_help = "l2fib flush-mac all",
887   .function = l2fib_flush_mac_all,
888 };
889 /* *INDENT-ON* */
890
891 /*?
892  * This command kick off ager to delete all existing MAC Address entries,
893  * except static ones, associated with an interface from the L2 FIB table.
894  *
895  * @cliexpar
896  * Example of how to flush MAC Address entries learned on an interface from the L2 FIB table:
897  * @cliexcmd{l2fib flush-mac interface GigabitEthernet2/1/0}
898 ?*/
899 /* *INDENT-OFF* */
900 VLIB_CLI_COMMAND (l2fib_flush_mac_int_cli, static) = {
901   .path = "l2fib flush-mac interface",
902   .short_help = "l2fib flush-mac interface <if-name>",
903   .function = l2fib_flush_mac_int,
904 };
905 /* *INDENT-ON* */
906
907 /**
908     Flush bridge-domain MACs except static ones.
909     The CLI format is:
910     l2fib flush-mac bridge-domain <bd-id>
911 */
912 static clib_error_t *
913 l2fib_flush_mac_bd (vlib_main_t * vm,
914                     unformat_input_t * input, vlib_cli_command_t * cmd)
915 {
916   bd_main_t *bdm = &bd_main;
917   clib_error_t *error = 0;
918   u32 bd_index, bd_id;
919   uword *p;
920
921   if (!unformat (input, "%d", &bd_id))
922     {
923       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
924                                  format_unformat_error, input);
925       goto done;
926     }
927
928   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
929   if (p)
930     bd_index = *p;
931   else
932     return clib_error_return (0, "No such bridge domain %d", bd_id);
933
934   l2fib_flush_bd_mac (vm, bd_index);
935
936 done:
937   return error;
938 }
939
940 /*?
941  * This command kick off ager to delete all existing MAC Address entries,
942  * except static ones, in a bridge domain from the L2 FIB table.
943  *
944  * @cliexpar
945  * Example of how to flush MAC Address entries learned in a bridge domain from the L2 FIB table:
946  * @cliexcmd{l2fib flush-mac bridge-domain 1000}
947 ?*/
948 /* *INDENT-OFF* */
949 VLIB_CLI_COMMAND (l2fib_flush_mac_bd_cli, static) = {
950   .path = "l2fib flush-mac bridge-domain",
951   .short_help = "l2fib flush-mac bridge-domain <bd-id>",
952   .function = l2fib_flush_mac_bd,
953 };
954 /* *INDENT-ON* */
955
956 clib_error_t *
957 l2fib_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
958 {
959   l2_input_config_t *config = l2input_intf_config (sw_if_index);
960   if ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0 && config->bridge)
961     l2fib_flush_int_mac (vnm->vlib_main, sw_if_index);
962   return 0;
963 }
964
965 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (l2fib_sw_interface_up_down);
966
967 BVT (clib_bihash) * get_mac_table (void)
968 {
969   l2fib_main_t *mp = &l2fib_main;
970   return &mp->mac_table;
971 }
972
973 static_always_inline void *
974 allocate_mac_evt_buf (u32 client, u32 client_index)
975 {
976   l2fib_main_t *fm = &l2fib_main;
977   vl_api_l2_macs_event_t *mp = vl_msg_api_alloc
978     (sizeof (*mp) + (fm->max_macs_in_event * sizeof (vl_api_mac_entry_t)));
979   mp->_vl_msg_id = htons (VL_API_L2_MACS_EVENT);
980   mp->pid = htonl (client);
981   mp->client_index = client_index;
982   return mp;
983 }
984
985 static_always_inline f64
986 l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only)
987 {
988   l2fib_main_t *fm = &l2fib_main;
989   l2learn_main_t *lm = &l2learn_main;
990
991   BVT (clib_bihash) * h = &fm->mac_table;
992   int i, j, k;
993   f64 last_start = start_time;
994   f64 accum_t = 0;
995   f64 delta_t = 0;
996   u32 evt_idx = 0;
997   u32 learn_count = 0;
998   u32 client = lm->client_pid;
999   u32 cl_idx = lm->client_index;
1000   vl_api_l2_macs_event_t *mp = 0;
1001   vl_api_registration_t *reg = 0;
1002
1003   if (client)
1004     {
1005       mp = allocate_mac_evt_buf (client, cl_idx);
1006       reg = vl_api_client_index_to_registration (lm->client_index);
1007     }
1008
1009   for (i = 0; i < h->nbuckets; i++)
1010     {
1011       /* allow no more than 20us without a pause */
1012       delta_t = vlib_time_now (vm) - last_start;
1013       if (delta_t > 20e-6)
1014         {
1015           vlib_process_suspend (vm, 100e-6);    /* suspend for 100 us */
1016           last_start = vlib_time_now (vm);
1017           accum_t += delta_t;
1018         }
1019
1020       if (i < (h->nbuckets - 3))
1021         {
1022           BVT (clib_bihash_bucket) * b = &h->buckets[i + 3];
1023           CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD);
1024           b = &h->buckets[i + 1];
1025           if (b->offset)
1026             {
1027               BVT (clib_bihash_value) * v =
1028                 BV (clib_bihash_get_value) (h, b->offset);
1029               CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD);
1030             }
1031         }
1032
1033       BVT (clib_bihash_bucket) * b = &h->buckets[i];
1034       if (b->offset == 0)
1035         continue;
1036       BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset);
1037       for (j = 0; j < (1 << b->log2_pages); j++)
1038         {
1039           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
1040             {
1041               if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
1042                 continue;
1043
1044               l2fib_entry_key_t key = {.raw = v->kvp[k].key };
1045               l2fib_entry_result_t result = {.raw = v->kvp[k].value };
1046
1047               if (!l2fib_entry_result_is_set_AGE_NOT (&result))
1048                 learn_count++;
1049
1050               if (client)
1051                 {
1052                   if (PREDICT_FALSE (evt_idx >= fm->max_macs_in_event))
1053                     {
1054                       /* event message full, send it and start a new one */
1055                       if (reg && vl_api_can_send_msg (reg))
1056                         {
1057                           mp->n_macs = htonl (evt_idx);
1058                           vl_api_send_msg (reg, (u8 *) mp);
1059                           mp = allocate_mac_evt_buf (client, cl_idx);
1060                         }
1061                       else
1062                         {
1063                           if (reg)
1064                             clib_warning ("MAC event to pid %d queue stuffed!"
1065                                           " %d MAC entries lost", client,
1066                                           evt_idx);
1067                         }
1068                       evt_idx = 0;
1069                     }
1070
1071                   if (l2fib_entry_result_is_set_LRN_EVT (&result))
1072                     {
1073                       /* copy mac entry to event msg */
1074                       clib_memcpy_fast (mp->mac[evt_idx].mac_addr,
1075                                         key.fields.mac, 6);
1076                       mp->mac[evt_idx].action =
1077                         l2fib_entry_result_is_set_LRN_MOV (&result) ?
1078                         MAC_EVENT_ACTION_MOVE : MAC_EVENT_ACTION_ADD;
1079                       mp->mac[evt_idx].sw_if_index =
1080                         htonl (result.fields.sw_if_index);
1081                       /* clear event bits and update mac entry */
1082                       l2fib_entry_result_clear_LRN_EVT (&result);
1083                       l2fib_entry_result_clear_LRN_MOV (&result);
1084                       BVT (clib_bihash_kv) kv;
1085                       kv.key = key.raw;
1086                       kv.value = result.raw;
1087                       BV (clib_bihash_add_del) (&fm->mac_table, &kv, 1);
1088                       evt_idx++;
1089                       continue; /* skip aging */
1090                     }
1091                 }
1092
1093               if (event_only || l2fib_entry_result_is_set_AGE_NOT (&result))
1094                 continue;       /* skip aging - static_mac always age_not */
1095
1096               /* start aging processing */
1097               u32 bd_index = key.fields.bd_index;
1098               u32 sw_if_index = result.fields.sw_if_index;
1099               u16 sn = l2fib_cur_seq_num (bd_index, sw_if_index).as_u16;
1100               if (result.fields.sn.as_u16 != sn)
1101                 goto age_out;   /* stale mac */
1102
1103               l2_bridge_domain_t *bd_config =
1104                 vec_elt_at_index (l2input_main.bd_configs, bd_index);
1105
1106               if (bd_config->mac_age == 0)
1107                 continue;       /* skip aging */
1108
1109               i16 delta = (u8) (start_time / 60) - result.fields.timestamp;
1110               delta += delta < 0 ? 256 : 0;
1111
1112               if (delta < bd_config->mac_age)
1113                 continue;       /* still valid */
1114
1115             age_out:
1116               if (client)
1117                 {
1118                   /* copy mac entry to event msg */
1119                   clib_memcpy_fast (mp->mac[evt_idx].mac_addr, key.fields.mac,
1120                                     6);
1121                   mp->mac[evt_idx].action = MAC_EVENT_ACTION_DELETE;
1122                   mp->mac[evt_idx].sw_if_index =
1123                     htonl (result.fields.sw_if_index);
1124                   evt_idx++;
1125                 }
1126               /* delete mac entry */
1127               BVT (clib_bihash_kv) kv;
1128               kv.key = key.raw;
1129               BV (clib_bihash_add_del) (&fm->mac_table, &kv, 0);
1130               learn_count--;
1131               /*
1132                * Note: we may have just freed the bucket's backing
1133                * storage, so check right here...
1134                */
1135               if (b->offset == 0)
1136                 goto doublebreak;
1137             }
1138           v++;
1139         }
1140     doublebreak:
1141       ;
1142     }
1143
1144   /* keep learn count consistent */
1145   l2learn_main.global_learn_count = learn_count;
1146
1147   if (mp)
1148     {
1149       /*  send any outstanding mac event message else free message buffer */
1150       if (evt_idx)
1151         {
1152           if (reg && vl_api_can_send_msg (reg))
1153             {
1154               mp->n_macs = htonl (evt_idx);
1155               vl_api_send_msg (reg, (u8 *) mp);
1156             }
1157           else
1158             {
1159               if (reg)
1160                 clib_warning ("MAC event to pid %d queue stuffed!"
1161                               " %d MAC entries lost", client, evt_idx);
1162               vl_msg_api_free (mp);
1163             }
1164         }
1165       else
1166         vl_msg_api_free (mp);
1167     }
1168   return delta_t + accum_t;
1169 }
1170
1171 static uword
1172 l2fib_mac_age_scanner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
1173                                vlib_frame_t * f)
1174 {
1175   uword event_type, *event_data = 0;
1176   l2fib_main_t *fm = &l2fib_main;
1177   l2learn_main_t *lm = &l2learn_main;
1178   bool enabled = 0;
1179   f64 start_time, next_age_scan_time = CLIB_TIME_MAX;
1180
1181   while (1)
1182     {
1183       if (lm->client_pid)
1184         vlib_process_wait_for_event_or_clock (vm, fm->event_scan_delay);
1185       else if (enabled)
1186         {
1187           f64 t = next_age_scan_time - vlib_time_now (vm);
1188           vlib_process_wait_for_event_or_clock (vm, t);
1189         }
1190       else
1191         vlib_process_wait_for_event (vm);
1192
1193       event_type = vlib_process_get_events (vm, &event_data);
1194       vec_reset_length (event_data);
1195
1196       start_time = vlib_time_now (vm);
1197       enum
1198       { SCAN_MAC_AGE, SCAN_MAC_EVENT, SCAN_DISABLE } scan = SCAN_MAC_AGE;
1199
1200       switch (event_type)
1201         {
1202         case ~0:                /* timer expired */
1203           if (lm->client_pid != 0 && start_time < next_age_scan_time)
1204             scan = SCAN_MAC_EVENT;
1205           break;
1206
1207         case L2_MAC_AGE_PROCESS_EVENT_START:
1208           enabled = 1;
1209           break;
1210
1211         case L2_MAC_AGE_PROCESS_EVENT_STOP:
1212           enabled = 0;
1213           scan = SCAN_DISABLE;
1214           break;
1215
1216         case L2_MAC_AGE_PROCESS_EVENT_ONE_PASS:
1217           break;
1218
1219         default:
1220           ASSERT (0);
1221         }
1222
1223       if (scan == SCAN_MAC_EVENT)
1224         l2fib_main.evt_scan_duration = l2fib_scan (vm, start_time, 1);
1225       else
1226         {
1227           if (scan == SCAN_MAC_AGE)
1228             l2fib_main.age_scan_duration = l2fib_scan (vm, start_time, 0);
1229           if (scan == SCAN_DISABLE)
1230             {
1231               l2fib_main.age_scan_duration = 0;
1232               l2fib_main.evt_scan_duration = 0;
1233             }
1234           /* schedule next scan */
1235           if (enabled)
1236             next_age_scan_time = start_time + L2FIB_AGE_SCAN_INTERVAL;
1237           else
1238             next_age_scan_time = CLIB_TIME_MAX;
1239         }
1240     }
1241   return 0;
1242 }
1243
1244 /* *INDENT-OFF* */
1245 VLIB_REGISTER_NODE (l2fib_mac_age_scanner_process_node) = {
1246     .function = l2fib_mac_age_scanner_process,
1247     .type = VLIB_NODE_TYPE_PROCESS,
1248     .name = "l2fib-mac-age-scanner-process",
1249 };
1250 /* *INDENT-ON* */
1251
1252 clib_error_t *
1253 l2fib_init (vlib_main_t * vm)
1254 {
1255   l2fib_main_t *mp = &l2fib_main;
1256   l2fib_entry_key_t test_key;
1257   u8 test_mac[6];
1258
1259   mp->vlib_main = vm;
1260   mp->vnet_main = vnet_get_main ();
1261
1262   /* Create the hash table  */
1263   BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
1264                          L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
1265
1266   /* verify the key constructor is good, since it is endian-sensitive */
1267   clib_memset (test_mac, 0, sizeof (test_mac));
1268   test_mac[0] = 0x11;
1269   test_key.raw = 0;
1270   test_key.raw = l2fib_make_key ((u8 *) & test_mac, 0x1234);
1271   ASSERT (test_key.fields.mac[0] == 0x11);
1272   ASSERT (test_key.fields.bd_index == 0x1234);
1273
1274   return 0;
1275 }
1276
1277 VLIB_INIT_FUNCTION (l2fib_init);
1278
1279 /*
1280  * fd.io coding-style-patch-verification: ON
1281  *
1282  * Local Variables:
1283  * eval: (c-set-style "gnu")
1284  * End:
1285  */