7e59b09848ac23b559d7c75d39bdbf9889a75bf3
[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 /**
35  * @file
36  * @brief Ethernet MAC Address FIB Table Management.
37  *
38  * The MAC Address forwarding table for bridge-domains is called the l2fib.
39  * Entries are added automatically as part of mac learning, but MAC Addresses
40  * entries can also be added manually.
41  *
42  */
43
44 l2fib_main_t l2fib_main;
45
46 /** Format sw_if_index. If the value is ~0, use the text "N/A" */
47 u8 *
48 format_vnet_sw_if_index_name_with_NA (u8 * s, va_list * args)
49 {
50   vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
51   u32 sw_if_index = va_arg (*args, u32);
52   if (sw_if_index == ~0)
53     return format (s, "N/A");
54
55   vnet_sw_interface_t *swif = vnet_get_sw_interface_safe (vnm, sw_if_index);
56   if (!swif)
57     return format (s, "Stale");
58
59   return format (s, "%U", format_vnet_sw_interface_name, vnm,
60                  vnet_get_sw_interface_safe (vnm, sw_if_index));
61 }
62
63 void
64 l2fib_table_dump (u32 bd_index, l2fib_entry_key_t ** l2fe_key,
65                   l2fib_entry_result_t ** l2fe_res)
66 {
67   l2fib_main_t *msm = &l2fib_main;
68   BVT (clib_bihash) * h = &msm->mac_table;
69   BVT (clib_bihash_bucket) * b;
70   BVT (clib_bihash_value) * v;
71   l2fib_entry_key_t key;
72   l2fib_entry_result_t result;
73   int i, j, k;
74
75   for (i = 0; i < h->nbuckets; i++)
76     {
77       b = &h->buckets[i];
78       if (b->offset == 0)
79         continue;
80       v = BV (clib_bihash_get_value) (h, b->offset);
81       for (j = 0; j < (1 << b->log2_pages); j++)
82         {
83           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
84             {
85               if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
86                 continue;
87
88               key.raw = v->kvp[k].key;
89               result.raw = v->kvp[k].value;
90
91               if ((bd_index == ~0) || (bd_index == key.fields.bd_index))
92                 {
93                   vec_add1 (*l2fe_key, key);
94                   vec_add1 (*l2fe_res, result);
95                 }
96             }
97           v++;
98         }
99     }
100 }
101
102 /** Display the contents of the l2fib. */
103 static clib_error_t *
104 show_l2fib (vlib_main_t * vm,
105             unformat_input_t * input, vlib_cli_command_t * cmd)
106 {
107   bd_main_t *bdm = &bd_main;
108   l2fib_main_t *msm = &l2fib_main;
109   l2_bridge_domain_t *bd_config;
110   BVT (clib_bihash) * h = &msm->mac_table;
111   BVT (clib_bihash_bucket) * b;
112   BVT (clib_bihash_value) * v;
113   l2fib_entry_key_t key;
114   l2fib_entry_result_t result;
115   u32 first_entry = 1;
116   u64 total_entries = 0;
117   int i, j, k;
118   u8 verbose = 0;
119   u8 raw = 0;
120   u32 bd_id, bd_index = ~0;
121   u8 now = (u8) (vlib_time_now (vm) / 60);
122   u8 *s = 0;
123
124   if (unformat (input, "raw"))
125     raw = 1;
126   else if (unformat (input, "verbose"))
127     verbose = 1;
128   else if (unformat (input, "bd_index %d", &bd_index))
129     verbose = 1;
130   else if (unformat (input, "bd_id %d", &bd_id))
131     {
132       uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id);
133       if (p)
134         {
135           verbose = 1;
136           bd_index = p[0];
137         }
138       else
139         {
140           vlib_cli_output (vm, "no such bridge domain id");
141           return 0;
142         }
143     }
144
145   for (i = 0; i < h->nbuckets; i++)
146     {
147       b = &h->buckets[i];
148       if (b->offset == 0)
149         continue;
150       v = BV (clib_bihash_get_value) (h, b->offset);
151       for (j = 0; j < (1 << b->log2_pages); j++)
152         {
153           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
154             {
155               if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
156                 continue;
157
158               if (verbose && first_entry)
159                 {
160                   first_entry = 0;
161                   vlib_cli_output (vm,
162                                    "%=19s%=7s%=7s%=8s%=9s%=7s%=7s%=5s%=30s",
163                                    "Mac-Address", "BD-Idx", "If-Idx",
164                                    "BSN-ISN", "Age(min)", "static", "filter",
165                                    "bvi", "Interface-Name");
166                 }
167
168               key.raw = v->kvp[k].key;
169               result.raw = v->kvp[k].value;
170
171               if (verbose
172                   & ((bd_index >> 31) || (bd_index == key.fields.bd_index)))
173                 {
174                   bd_config = vec_elt_at_index (l2input_main.bd_configs,
175                                                 key.fields.bd_index);
176
177                   if (bd_config->mac_age && !result.fields.static_mac)
178                     {
179                       i16 delta = now - result.fields.timestamp;
180                       delta += delta < 0 ? 256 : 0;
181                       s = format (s, "%d", delta);
182                     }
183                   else
184                     s = format (s, "-");
185
186                   vlib_cli_output (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                                    msm->vnet_main, result.fields.sw_if_index);
198                   vec_reset_length (s);
199                 }
200               total_entries++;
201             }
202           v++;
203         }
204     }
205
206   if (total_entries == 0)
207     vlib_cli_output (vm, "no l2fib entries");
208   else
209     vlib_cli_output (vm,
210                      "%lld l2fib entries with %d learned (or non-static) entries",
211                      total_entries, l2learn_main.global_learn_count);
212
213   if (raw)
214     vlib_cli_output (vm, "Raw Hash Table:\n%U\n",
215                      BV (format_bihash), h, 1 /* verbose */ );
216
217   vec_free (s);
218   return 0;
219 }
220
221 /*?
222  * This command dispays the MAC Address entries of the L2 FIB table.
223  * Output can be filtered to just get the number of MAC Addresses or display
224  * each MAC Address for all bridge domains or just a single bridge domain.
225  *
226  * @cliexpar
227  * Example of how to display the number of MAC Address entries in the L2
228  * FIB table:
229  * @cliexstart{show l2fib}
230  * 3 l2fib entries
231  * @cliexend
232  * Example of how to display all the MAC Address entries in the L2
233  * FIB table:
234  * @cliexstart{show l2fib verbose}
235  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
236  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
237  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
238  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
239  * 3 l2fib entries
240  * @cliexend
241 ?*/
242 /* *INDENT-OFF* */
243 VLIB_CLI_COMMAND (show_l2fib_cli, static) = {
244   .path = "show l2fib",
245   .short_help = "show l2fib [verbose | bd_id <nn> | bd_index <nn> | raw]",
246   .function = show_l2fib,
247 };
248 /* *INDENT-ON* */
249
250
251 /* Remove all entries from the l2fib */
252 void
253 l2fib_clear_table (void)
254 {
255   l2fib_main_t *mp = &l2fib_main;
256
257   /* Remove all entries */
258   BV (clib_bihash_free) (&mp->mac_table);
259   BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
260                          L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
261   l2learn_main.global_learn_count = 0;
262 }
263
264 /** Clear all entries in L2FIB.
265  * @TODO: Later we may want a way to remove only the non-static entries
266  */
267 static clib_error_t *
268 clear_l2fib (vlib_main_t * vm,
269              unformat_input_t * input, vlib_cli_command_t * cmd)
270 {
271   l2fib_clear_table ();
272   return 0;
273 }
274
275 /*?
276  * This command clears all the MAC Address entries from the L2 FIB table.
277  *
278  * @cliexpar
279  * Example of how to clear the L2 FIB Table:
280  * @cliexcmd{clear l2fib}
281  * Example to show the L2 FIB Table has been cleared:
282  * @cliexstart{show l2fib verbose}
283  * no l2fib entries
284  * @cliexend
285 ?*/
286 /* *INDENT-OFF* */
287 VLIB_CLI_COMMAND (clear_l2fib_cli, static) = {
288   .path = "clear l2fib",
289   .short_help = "clear l2fib",
290   .function = clear_l2fib,
291 };
292 /* *INDENT-ON* */
293
294 static inline l2fib_seq_num_t
295 l2fib_cur_seq_num (u32 bd_index, u32 sw_if_index)
296 {
297   l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
298   /* *INDENT-OFF* */
299   return (l2fib_seq_num_t) {
300     .swif = *l2fib_swif_seq_num (sw_if_index),
301     .bd = bd_config->seq_num,
302   };
303   /* *INDENT-ON* */
304 }
305
306 /**
307  * Add an entry to the l2fib.
308  * If the entry already exists then overwrite it
309  */
310 void
311 l2fib_add_entry (u64 mac, u32 bd_index,
312                  u32 sw_if_index, u32 static_mac, u32 filter_mac, u32 bvi_mac)
313 {
314   l2fib_entry_key_t key;
315   l2fib_entry_result_t result;
316   __attribute__ ((unused)) u32 bucket_contents;
317   l2fib_main_t *mp = &l2fib_main;
318   BVT (clib_bihash_kv) kv;
319
320   /* set up key */
321   key.raw = l2fib_make_key ((u8 *) & mac, bd_index);
322
323   /* set up result */
324   result.raw = 0;               /* clear all fields */
325   result.fields.sw_if_index = sw_if_index;
326   result.fields.static_mac = static_mac;
327   result.fields.filter = filter_mac;
328   result.fields.bvi = bvi_mac;
329   if (!static_mac)
330     result.fields.sn = l2fib_cur_seq_num (bd_index, sw_if_index);
331
332   kv.key = key.raw;
333   kv.value = result.raw;
334
335   BV (clib_bihash_add_del) (&mp->mac_table, &kv, 1 /* is_add */ );
336
337   /* increment counter if dynamically learned mac */
338   if (result.fields.static_mac == 0)
339     {
340       l2learn_main.global_learn_count++;
341     }
342 }
343
344 /**
345  * Add an entry to the L2FIB.
346  * The CLI format is:
347  *    l2fib add <mac> <bd> <intf> [static] [bvi]
348  *    l2fib add <mac> <bd> filter
349  * Note that filter and bvi entries are always static
350  */
351 static clib_error_t *
352 l2fib_add (vlib_main_t * vm,
353            unformat_input_t * input, vlib_cli_command_t * cmd)
354 {
355   bd_main_t *bdm = &bd_main;
356   vnet_main_t *vnm = vnet_get_main ();
357   clib_error_t *error = 0;
358   u64 mac;
359   u32 bd_id;
360   u32 bd_index;
361   u32 sw_if_index = ~0;
362   u32 filter_mac = 0;
363   u32 static_mac = 0;
364   u32 bvi_mac = 0;
365   uword *p;
366
367   if (!unformat_user (input, unformat_ethernet_address, &mac))
368     {
369       error = clib_error_return (0, "expected mac address `%U'",
370                                  format_unformat_error, input);
371       goto done;
372     }
373
374   if (!unformat (input, "%d", &bd_id))
375     {
376       error = clib_error_return (0, "expected bridge domain ID `%U'",
377                                  format_unformat_error, input);
378       goto done;
379     }
380
381   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
382   if (!p)
383     {
384       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
385       goto done;
386     }
387   bd_index = p[0];
388
389   if (unformat (input, "filter"))
390     {
391       filter_mac = 1;
392       static_mac = 1;
393
394     }
395   else
396     {
397
398       if (!unformat_user
399           (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
400         {
401           error = clib_error_return (0, "unknown interface `%U'",
402                                      format_unformat_error, input);
403           goto done;
404         }
405       if (unformat (input, "static"))
406         {
407           static_mac = 1;
408         }
409       else if (unformat (input, "bvi"))
410         {
411           bvi_mac = 1;
412           static_mac = 1;
413         }
414     }
415
416   if (vec_len (l2input_main.configs) <= sw_if_index)
417     {
418       error = clib_error_return (0, "Interface sw_if_index %d not in L2 mode",
419                                  sw_if_index);
420       goto done;
421     }
422
423   if (filter_mac)
424     l2fib_add_filter_entry (mac, bd_index);
425   else
426     l2fib_add_fwd_entry (mac, bd_index, sw_if_index, static_mac, bvi_mac);
427
428 done:
429   return error;
430 }
431
432 /*?
433  * This command adds a MAC Address entry to the L2 FIB table
434  * of an existing bridge-domain. The MAC Address can be static
435  * or dynamic. This command also allows a filter to be added,
436  * such that packets with given MAC Addresses (source mac or
437  * destination mac match) are dropped.
438  *
439  * @cliexpar
440  * Example of how to add a dynamic MAC Address entry to the L2 FIB table
441  * of a bridge-domain (where 200 is the bridge-domain-id):
442  * @cliexcmd{l2fib add 52:54:00:53:18:33 200 GigabitEthernet0/8/0.200}
443  * Example of how to add a static MAC Address entry to the L2 FIB table
444  * of a bridge-domain (where 200 is the bridge-domain-id):
445  * @cliexcmd{l2fib add 52:54:00:53:18:55 200 GigabitEthernet0/8/0.200 static}
446  * Example of how to add a filter such that a packet with the given MAC
447  * Address will be dropped in a given bridge-domain (where 200 is the
448  * bridge-domain-id):
449  * @cliexcmd{l2fib add 52:54:00:53:18:77 200 filter}
450  * Example of show command of the provisioned MAC Addresses and filters:
451  * @cliexstart{show l2fib verbose}
452  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
453  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
454  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
455  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
456  * 3 l2fib entries
457  * @cliexend
458 ?*/
459 /* *INDENT-OFF* */
460 VLIB_CLI_COMMAND (l2fib_add_cli, static) = {
461   .path = "l2fib add",
462   .short_help = "l2fib add <mac> <bridge-domain-id> filter | <intf> [static | bvi]",
463   .function = l2fib_add,
464 };
465 /* *INDENT-ON* */
466
467
468 static clib_error_t *
469 l2fib_test_command_fn (vlib_main_t * vm,
470                        unformat_input_t * input, vlib_cli_command_t * cmd)
471 {
472   clib_error_t *error = 0;
473   u64 mac, save_mac;
474   u32 bd_index = 0;
475   u32 sw_if_index = 8;
476   u32 bvi_mac = 0;
477   u32 is_add = 0;
478   u32 is_del = 0;
479   u32 is_check = 0;
480   u32 count = 1;
481   int mac_set = 0;
482   int i;
483
484   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
485     {
486       if (unformat (input, "mac %U", unformat_ethernet_address, &mac))
487         mac_set = 1;
488       else if (unformat (input, "add"))
489         is_add = 1;
490       else if (unformat (input, "del"))
491         is_del = 1;
492       else if (unformat (input, "check"))
493         is_check = 1;
494       else if (unformat (input, "count %d", &count))
495         ;
496       else
497         break;
498     }
499
500   if (mac_set == 0)
501     return clib_error_return (0, "mac not set");
502
503   if (is_add == 0 && is_del == 0 && is_check == 0)
504     return clib_error_return (0,
505                               "noop: pick at least one of (add,del,check)");
506
507   save_mac = mac;
508
509   if (is_add)
510     {
511       for (i = 0; i < count; i++)
512         {
513           u64 tmp;
514           l2fib_add_fwd_entry (mac, bd_index, sw_if_index, mac, bvi_mac);
515           tmp = clib_net_to_host_u64 (mac);
516           tmp >>= 16;
517           tmp++;
518           tmp <<= 16;
519           mac = clib_host_to_net_u64 (tmp);
520         }
521     }
522
523   if (is_check)
524     {
525       BVT (clib_bihash_kv) kv;
526       l2fib_main_t *mp = &l2fib_main;
527
528       mac = save_mac;
529
530       for (i = 0; i < count; i++)
531         {
532           u64 tmp;
533           kv.key = l2fib_make_key ((u8 *) & mac, bd_index);
534           if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
535             {
536               clib_warning ("key %U AWOL", format_ethernet_address, &mac);
537               break;
538             }
539           tmp = clib_net_to_host_u64 (mac);
540           tmp >>= 16;
541           tmp++;
542           tmp <<= 16;
543           mac = clib_host_to_net_u64 (tmp);
544         }
545     }
546
547   if (is_del)
548     {
549       for (i = 0; i < count; i++)
550         {
551           u64 tmp;
552
553           l2fib_del_entry (mac, bd_index);
554
555           tmp = clib_net_to_host_u64 (mac);
556           tmp >>= 16;
557           tmp++;
558           tmp <<= 16;
559           mac = clib_host_to_net_u64 (tmp);
560         }
561     }
562
563   return error;
564 }
565
566 /*?
567  * The set of '<em>test l2fib</em>' commands allow the L2 FIB table of the default
568  * bridge domain (bridge-domain-id of 0) to be modified.
569  *
570  * @cliexpar
571  * @parblock
572  * Example of how to add a set of 4 sequential MAC Address entries to L2
573  * FIB table of the default bridge-domain:
574  * @cliexcmd{test l2fib add mac 52:54:00:53:00:00 count 4}
575  *
576  * Show the set of 4 sequential MAC Address entries that were added:
577  * @cliexstart{show l2fib verbose}
578  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
579  * 52:54:00:53:00:00    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
580  * 52:54:00:53:00:01    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
581  * 52:54:00:53:00:03    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
582  * 52:54:00:53:00:02    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
583  * 4 l2fib entries
584  * @cliexend
585  *
586  * Example of how to check that the set of 4 sequential MAC Address
587  * entries were added to L2 FIB table of the default
588  * bridge-domain. Used a count of 5 to produce an error:
589  *
590  * @cliexcmd{test l2fib check mac 52:54:00:53:00:00 count 5}
591  * The output of the check command is in the log files. Log file
592  * location may vary based on your OS and Version:
593  *
594  * <b><em># tail -f /var/log/messages | grep l2fib_test_command_fn</em></b>
595  *
596  * Sep  7 17:15:24 localhost vnet[4952]: l2fib_test_command_fn:446: key 52:54:00:53:00:04 AWOL
597  *
598  * Example of how to delete a set of 4 sequential MAC Address entries
599  * from L2 FIB table of the default bridge-domain:
600  * @cliexcmd{test l2fib del mac 52:54:00:53:00:00 count 4}
601  * @endparblock
602 ?*/
603 /* *INDENT-OFF* */
604 VLIB_CLI_COMMAND (l2fib_test_command, static) = {
605   .path = "test l2fib",
606   .short_help = "test l2fib [add|del|check] mac <base-addr> count <nn>",
607   .function = l2fib_test_command_fn,
608 };
609 /* *INDENT-ON* */
610
611
612 /**
613  * Delete an entry from the l2fib.
614  * Return 0 if the entry was deleted, or 1 if it was not found
615  */
616 static u32
617 l2fib_del_entry_by_key (u64 raw_key)
618 {
619
620   l2fib_entry_result_t result;
621   l2fib_main_t *mp = &l2fib_main;
622   BVT (clib_bihash_kv) kv;
623
624   /* set up key */
625   kv.key = raw_key;
626
627   if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
628     return 1;
629
630   result.raw = kv.value;
631
632   /* decrement counter if dynamically learned mac */
633   if (result.fields.static_mac == 0)
634     {
635       if (l2learn_main.global_learn_count > 0)
636         {
637           l2learn_main.global_learn_count--;
638         }
639     }
640
641   /* Remove entry from hash table */
642   BV (clib_bihash_add_del) (&mp->mac_table, &kv, 0 /* is_add */ );
643   return 0;
644 }
645
646 /**
647  * Delete an entry from the l2fib.
648  * Return 0 if the entry was deleted, or 1 if it was not found
649  */
650 u32
651 l2fib_del_entry (u64 mac, u32 bd_index)
652 {
653   return l2fib_del_entry_by_key (l2fib_make_key ((u8 *) & mac, bd_index));
654 }
655
656 /**
657  * Delete an entry from the L2FIB.
658  * The CLI format is:
659  *    l2fib del <mac> <bd-id>
660  */
661 static clib_error_t *
662 l2fib_del (vlib_main_t * vm,
663            unformat_input_t * input, vlib_cli_command_t * cmd)
664 {
665   bd_main_t *bdm = &bd_main;
666   clib_error_t *error = 0;
667   u64 mac;
668   u32 bd_id;
669   u32 bd_index;
670   uword *p;
671
672   if (!unformat_user (input, unformat_ethernet_address, &mac))
673     {
674       error = clib_error_return (0, "expected mac address `%U'",
675                                  format_unformat_error, input);
676       goto done;
677     }
678
679   if (!unformat (input, "%d", &bd_id))
680     {
681       error = clib_error_return (0, "expected bridge domain ID `%U'",
682                                  format_unformat_error, input);
683       goto done;
684     }
685
686   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
687   if (!p)
688     {
689       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
690       goto done;
691     }
692   bd_index = p[0];
693
694   /* Delete the entry */
695   if (l2fib_del_entry (mac, bd_index))
696     {
697       error = clib_error_return (0, "mac entry not found");
698       goto done;
699     }
700
701 done:
702   return error;
703 }
704
705 /*?
706  * This command deletes an existing MAC Address entry from the L2 FIB
707  * table of an existing bridge-domain.
708  *
709  * @cliexpar
710  * 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):
711  * @cliexcmd{l2fib del 52:54:00:53:18:33 200}
712 ?*/
713 /* *INDENT-OFF* */
714 VLIB_CLI_COMMAND (l2fib_del_cli, static) = {
715   .path = "l2fib del",
716   .short_help = "l2fib del <mac> <bridge-domain-id>",
717   .function = l2fib_del,
718 };
719 /* *INDENT-ON* */
720
721 /**
722     Kick off ager to scan MACs to age/delete MAC entries
723 */
724 void
725 l2fib_start_ager_scan (vlib_main_t * vm)
726 {
727   l2_bridge_domain_t *bd_config;
728   int enable = 0;
729
730   /* check if there is at least one bd with mac aging enabled */
731   vec_foreach (bd_config, l2input_main.bd_configs)
732     if (bd_config->bd_id != ~0 && bd_config->mac_age != 0)
733     enable = 1;
734
735   vlib_process_signal_event (vm, l2fib_mac_age_scanner_process_node.index,
736                              enable ? L2_MAC_AGE_PROCESS_EVENT_START :
737                              L2_MAC_AGE_PROCESS_EVENT_ONE_PASS, 0);
738 }
739
740 /**
741     Flush all non static MACs from an interface
742 */
743 void
744 l2fib_flush_int_mac (vlib_main_t * vm, u32 sw_if_index)
745 {
746   *l2fib_swif_seq_num (sw_if_index) += 1;
747   l2fib_start_ager_scan (vm);
748 }
749
750 /**
751     Flush all non static MACs in a bridge domain
752 */
753 void
754 l2fib_flush_bd_mac (vlib_main_t * vm, u32 bd_index)
755 {
756   l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
757   bd_config->seq_num += 1;
758   l2fib_start_ager_scan (vm);
759 }
760
761 /**
762     Flush all non static MACs - flushes all valid BDs
763 */
764 void
765 l2fib_flush_all_mac (vlib_main_t * vm)
766 {
767   l2_bridge_domain_t *bd_config;
768   vec_foreach (bd_config, l2input_main.bd_configs)
769     if (bd_is_valid (bd_config))
770     bd_config->seq_num += 1;
771
772   l2fib_start_ager_scan (vm);
773 }
774
775
776 /**
777     Flush MACs, except static ones, associated with an interface
778     The CLI format is:
779     l2fib flush-mac interface <if-name>
780 */
781 static clib_error_t *
782 l2fib_flush_mac_int (vlib_main_t * vm,
783                      unformat_input_t * input, vlib_cli_command_t * cmd)
784 {
785   vnet_main_t *vnm = vnet_get_main ();
786   clib_error_t *error = 0;
787   u32 sw_if_index;
788
789   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
790     {
791       error = clib_error_return (0, "unknown interface `%U'",
792                                  format_unformat_error, input);
793       goto done;
794     }
795
796   l2fib_flush_int_mac (vm, sw_if_index);
797
798 done:
799   return error;
800 }
801
802 /**
803     Flush all MACs, except static ones
804     The CLI format is:
805     l2fib flush-mac all
806 */
807 static clib_error_t *
808 l2fib_flush_mac_all (vlib_main_t * vm,
809                      unformat_input_t * input, vlib_cli_command_t * cmd)
810 {
811   l2fib_flush_all_mac (vm);
812   return 0;
813 }
814
815 /*?
816  * This command kick off ager to delete all existing MAC Address entries,
817  * except static ones, associated with an interface from the L2 FIB table.
818  *
819  * @cliexpar
820  * Example of how to flush MAC Address entries learned on an interface from the L2 FIB table:
821  * @cliexcmd{l2fib flush-mac interface GigabitEthernet2/1/0}
822 ?*/
823 /* *INDENT-OFF* */
824 VLIB_CLI_COMMAND (l2fib_flush_mac_all_cli, static) = {
825   .path = "l2fib flush-mac all",
826   .short_help = "l2fib flush-mac all",
827   .function = l2fib_flush_mac_all,
828 };
829 /* *INDENT-ON* */
830
831 /*?
832  * This command kick off ager to delete all existing MAC Address entries,
833  * except static ones, associated with an interface from the L2 FIB table.
834  *
835  * @cliexpar
836  * Example of how to flush MAC Address entries learned on an interface from the L2 FIB table:
837  * @cliexcmd{l2fib flush-mac interface GigabitEthernet2/1/0}
838 ?*/
839 /* *INDENT-OFF* */
840 VLIB_CLI_COMMAND (l2fib_flush_mac_int_cli, static) = {
841   .path = "l2fib flush-mac interface",
842   .short_help = "l2fib flush-mac interface <if-name>",
843   .function = l2fib_flush_mac_int,
844 };
845 /* *INDENT-ON* */
846
847 /**
848     Flush bridge-domain MACs except static ones.
849     The CLI format is:
850     l2fib flush-mac bridge-domain <bd-id>
851 */
852 static clib_error_t *
853 l2fib_flush_mac_bd (vlib_main_t * vm,
854                     unformat_input_t * input, vlib_cli_command_t * cmd)
855 {
856   bd_main_t *bdm = &bd_main;
857   clib_error_t *error = 0;
858   u32 bd_index, bd_id;
859   uword *p;
860
861   if (!unformat (input, "%d", &bd_id))
862     {
863       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
864                                  format_unformat_error, input);
865       goto done;
866     }
867
868   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
869   if (p)
870     bd_index = *p;
871   else
872     return clib_error_return (0, "No such bridge domain %d", bd_id);
873
874   l2fib_flush_bd_mac (vm, bd_index);
875
876 done:
877   return error;
878 }
879
880 /*?
881  * This command kick off ager to delete all existing MAC Address entries,
882  * except static ones, in a bridge domain from the L2 FIB table.
883  *
884  * @cliexpar
885  * Example of how to flush MAC Address entries learned in a bridge domain from the L2 FIB table:
886  * @cliexcmd{l2fib flush-mac bridge-domain 1000}
887 ?*/
888 /* *INDENT-OFF* */
889 VLIB_CLI_COMMAND (l2fib_flush_mac_bd_cli, static) = {
890   .path = "l2fib flush-mac bridge-domain",
891   .short_help = "l2fib flush-mac bridge-domain <bd-id>",
892   .function = l2fib_flush_mac_bd,
893 };
894 /* *INDENT-ON* */
895
896 clib_error_t *
897 l2fib_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
898 {
899   l2_input_config_t *config = l2input_intf_config (sw_if_index);
900   if ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0 && config->bridge)
901     l2fib_flush_int_mac (vnm->vlib_main, sw_if_index);
902   return 0;
903 }
904
905 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (l2fib_sw_interface_up_down);
906
907 BVT (clib_bihash) * get_mac_table (void)
908 {
909   l2fib_main_t *mp = &l2fib_main;
910   return &mp->mac_table;
911 }
912
913 static uword
914 l2fib_mac_age_scanner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
915                                vlib_frame_t * f)
916 {
917   uword event_type, *event_data = 0;
918   l2fib_main_t *msm = &l2fib_main;
919   bool enabled = 0;
920   f64 start_time, last_run_duration = 0, t;
921
922   while (1)
923     {
924       if (enabled)
925         vlib_process_wait_for_event_or_clock (vm, 60 - last_run_duration);
926       else
927         vlib_process_wait_for_event (vm);
928
929       event_type = vlib_process_get_events (vm, &event_data);
930       vec_reset_length (event_data);
931
932       switch (event_type)
933         {
934         case ~0:
935           break;
936         case L2_MAC_AGE_PROCESS_EVENT_START:
937           enabled = 1;
938           break;
939         case L2_MAC_AGE_PROCESS_EVENT_STOP:
940           enabled = 0;
941           continue;
942         case L2_MAC_AGE_PROCESS_EVENT_ONE_PASS:
943           enabled = 0;
944           break;
945         default:
946           ASSERT (0);
947         }
948       last_run_duration = start_time = vlib_time_now (vm);
949
950       BVT (clib_bihash) * h = &msm->mac_table;
951       int i, j, k;
952       for (i = 0; i < h->nbuckets; i++)
953         {
954           /* Allow no more than 10us without a pause */
955           t = vlib_time_now (vm);
956           if (t > start_time + 10e-6)
957             {
958               vlib_process_suspend (vm, 100e-6);        /* suspend for 100 us */
959               start_time = vlib_time_now (vm);
960             }
961
962           if (i < (h->nbuckets - 3))
963             {
964               BVT (clib_bihash_bucket) * b = &h->buckets[i + 3];
965               CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD);
966               b = &h->buckets[i + 1];
967               if (b->offset)
968                 {
969                   BVT (clib_bihash_value) * v =
970                     BV (clib_bihash_get_value) (h, b->offset);
971                   CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD);
972                 }
973             }
974
975           BVT (clib_bihash_bucket) * b = &h->buckets[i];
976           if (b->offset == 0)
977             continue;
978           BVT (clib_bihash_value) * v =
979             BV (clib_bihash_get_value) (h, b->offset);
980           for (j = 0; j < (1 << b->log2_pages); j++)
981             {
982               for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
983                 {
984                   if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
985                     continue;
986
987                   l2fib_entry_key_t key = {.raw = v->kvp[k].key };
988                   l2fib_entry_result_t result = {.raw = v->kvp[k].value };
989
990                   if (result.fields.static_mac)
991                     continue;
992
993                   u32 bd_index = key.fields.bd_index;
994                   u32 sw_if_index = result.fields.sw_if_index;
995                   u16 sn = l2fib_cur_seq_num (bd_index, sw_if_index).as_u16;
996                   if (result.fields.sn.as_u16 != sn)
997                     {
998                       l2fib_del_entry_by_key (key.raw);
999                       continue;
1000                     }
1001                   l2_bridge_domain_t *bd_config =
1002                     vec_elt_at_index (l2input_main.bd_configs, bd_index);
1003
1004                   if (bd_config->mac_age == 0)
1005                     continue;
1006
1007                   i16 delta =
1008                     (u8) (start_time / 60) - result.fields.timestamp;
1009                   delta += delta < 0 ? 256 : 0;
1010
1011                   if (delta > bd_config->mac_age)
1012                     l2fib_del_entry_by_key (key.raw);
1013                 }
1014               v++;
1015             }
1016         }
1017       last_run_duration = vlib_time_now (vm) - last_run_duration;
1018     }
1019   return 0;
1020 }
1021
1022 /* *INDENT-OFF* */
1023 VLIB_REGISTER_NODE (l2fib_mac_age_scanner_process_node) = {
1024     .function = l2fib_mac_age_scanner_process,
1025     .type = VLIB_NODE_TYPE_PROCESS,
1026     .name = "l2fib-mac-age-scanner-process",
1027 };
1028 /* *INDENT-ON* */
1029
1030 clib_error_t *
1031 l2fib_init (vlib_main_t * vm)
1032 {
1033   l2fib_main_t *mp = &l2fib_main;
1034   l2fib_entry_key_t test_key;
1035   u8 test_mac[6];
1036
1037   mp->vlib_main = vm;
1038   mp->vnet_main = vnet_get_main ();
1039
1040   /* Create the hash table  */
1041   BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
1042                          L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
1043
1044   /* verify the key constructor is good, since it is endian-sensitive */
1045   memset (test_mac, 0, sizeof (test_mac));
1046   test_mac[0] = 0x11;
1047   test_key.raw = 0;
1048   test_key.raw = l2fib_make_key ((u8 *) & test_mac, 0x1234);
1049   ASSERT (test_key.fields.mac[0] == 0x11);
1050   ASSERT (test_key.fields.bd_index == 0x1234);
1051
1052   return 0;
1053 }
1054
1055 VLIB_INIT_FUNCTION (l2fib_init);
1056
1057 /*
1058  * fd.io coding-style-patch-verification: ON
1059  *
1060  * Local Variables:
1061  * eval: (c-set-style "gnu")
1062  * End:
1063  */