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