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