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