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