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