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