FD.io VPP  v17.07.01-10-g3be13f0
Vector Packet Processing
l2_fib.c
Go to the documentation of this file.
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 
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 
45 
46 /** Format sw_if_index. If the value is ~0, use the text "N/A" */
47 u8 *
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;
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 *
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;
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  {
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",
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 ? "*" : "-",
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
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",
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 *
269  unformat_input_t * input, vlib_cli_command_t * cmd)
270 {
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  {
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 *
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 *
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 
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
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  {
636  {
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 *
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
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 
738 }
739 
740 /**
741  Flush all non static MACs from an interface
742 */
743 void
745 {
746  *l2fib_swif_seq_num (sw_if_index) += 1;
748 }
749 
750 /**
751  Flush all non static MACs in a bridge domain
752 */
753 void
755 {
756  l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
757  bd_config->seq_num += 1;
759 }
760 
761 /**
762  Flush all non static MACs - flushes all valid BDs
763 */
764 void
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 
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 *
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 *
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 *
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 *
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 
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
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
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;
937  enabled = 1;
938  break;
940  enabled = 0;
941  continue;
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  clib_bihash_bucket_t *b = &h->buckets[i + 3];
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);
972  }
973  }
974 
975  clib_bihash_bucket_t *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  {
999  continue;
1000  }
1001  l2_bridge_domain_t *bd_config =
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)
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* */
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 *
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",
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 
1056 
1057 /*
1058  * fd.io coding-style-patch-verification: ON
1059  *
1060  * Local Variables:
1061  * eval: (c-set-style "gnu")
1062  * End:
1063  */
static clib_error_t * l2fib_test_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: l2_fib.c:469
u32 global_learn_count
Definition: l2_learn.h:32
l2_input_config_t * configs
Definition: l2_input.h:66
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
u8 * format_vnet_sw_if_index_name_with_NA(u8 *s, va_list *args)
Format sw_if_index.
Definition: l2_fib.c:48
vlib_main_t * vlib_main
Definition: l2_fib.h:40
clib_bihash_bucket_t
Definition: bihash_doc.h:65
static f64 vlib_process_wait_for_event_or_clock(vlib_main_t *vm, f64 dt)
Suspend a cooperative multi-tasking thread Waits for an event, or for the indicated number of seconds...
Definition: node_funcs.h:699
#define BIHASH_KVP_PER_PAGE
Definition: bihash_16_8.h:18
static uword * vlib_process_wait_for_event(vlib_main_t *vm)
Definition: node_funcs.h:619
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
vnet_main_t * vnet_main
Definition: l2_fib.h:41
void clib_bihash_free(clib_bihash *h)
Destroy a bounded index extensible hash table.
l2_input_config_t * l2input_intf_config(u32 sw_if_index)
Get a pointer to the config for the given interface.
Definition: l2_input.c:476
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:192
u32 l2fib_del_entry(u64 mac, u32 bd_index)
Delete an entry from the l2fib.
Definition: l2_fib.c:651
clib_error_t * l2fib_init(vlib_main_t *vm)
Definition: l2_fib.c:1031
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:522
static_always_inline u8 * l2fib_swif_seq_num(u32 sw_if_index)
Definition: l2_fib.h:383
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:983
#define L2FIB_MEMORY_SIZE
Definition: l2_fib.h:28
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
unformat_function_t unformat_vnet_sw_interface
Definition: l2_fib.h:86
static clib_error_t * l2fib_del(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Delete an entry from the L2FIB.
Definition: l2_fib.c:662
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
static uword vlib_process_suspend(vlib_main_t *vm, f64 dt)
Suspend a vlib cooperative multi-tasking thread for a period of time.
Definition: node_funcs.h:448
u8 * format_ethernet_address(u8 *s, va_list *args)
Definition: format.c:44
int clib_bihash_add_del(clib_bihash *h, clib_bihash_kv *add_v, int is_add)
Add or delete a (key,value) pair from a bi-hash table.
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
static uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Return the first event type which has occurred and a vector of per-event data of that type...
Definition: node_funcs.h:542
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
#define clib_error_return(e, args...)
Definition: error.h:99
unsigned long u64
Definition: types.h:89
uword * bd_index_by_bd_id
Definition: l2_bd.h:27
void l2fib_start_ager_scan(vlib_main_t *vm)
Kick off ager to scan MACs to age/delete MAC entries.
Definition: l2_fib.c:725
#define hash_get(h, key)
Definition: hash.h:248
format_function_t format_vnet_sw_interface_name
vlib_main_t * vlib_main
Definition: vnet.h:78
static void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:946
static void l2fib_add_filter_entry(u64 mac, u32 bd_index)
Definition: l2_fib.h:361
#define v
Definition: acl.c:320
struct _unformat_input_t unformat_input_t
void l2fib_flush_bd_mac(vlib_main_t *vm, u32 bd_index)
Flush all non static MACs in a bridge domain.
Definition: l2_fib.c:754
static void l2fib_add_fwd_entry(u64 mac, u32 bd_index, u32 sw_if_index, u32 static_mac, u32 bvi_mac)
Definition: l2_fib.h:354
void l2fib_flush_all_mac(vlib_main_t *vm)
Flush all non static MACs - flushes all valid BDs.
Definition: l2_fib.c:765
#define L2FIB_NUM_BUCKETS
Definition: l2_fib.h:27
void clib_bihash_init(clib_bihash *h, char *name, u32 nbuckets, uword memory_size)
initialize a bounded index extensible hash table
l2fib_main_t l2fib_main
Definition: l2_fib.c:44
u64 raw
Definition: l2_fib.h:101
static u32 bd_is_valid(l2_bridge_domain_t *bd_config)
Definition: l2_bd.h:110
static clib_error_t * clear_l2fib(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Clear all entries in L2FIB.
Definition: l2_fib.c:268
BVT(clib_bihash)
Definition: l2_fib.c:907
clib_error_t * l2fib_sw_interface_up_down(vnet_main_t *vnm, u32 sw_if_index, u32 flags)
Definition: l2_fib.c:897
static clib_error_t * show_l2fib(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Display the contents of the l2fib.
Definition: l2_fib.c:104
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
static clib_error_t * l2fib_flush_mac_all(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Flush all MACs, except static ones The CLI format is: l2fib flush-mac all.
Definition: l2_fib.c:808
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:82
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:340
static clib_error_t * l2fib_flush_mac_int(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Flush MACs, except static ones, associated with an interface The CLI format is: l2fib flush-mac inter...
Definition: l2_fib.c:782
#define clib_warning(format, args...)
Definition: error.h:59
static vnet_sw_interface_t * vnet_get_sw_interface_safe(vnet_main_t *vnm, u32 sw_if_index)
void l2fib_clear_table(void)
Definition: l2_fib.c:253
static l2fib_seq_num_t l2fib_cur_seq_num(u32 bd_index, u32 sw_if_index)
Definition: l2_fib.c:295
static_always_inline l2_bridge_domain_t * l2input_bd_config(u32 bd_index)
Definition: l2_input.h:90
void l2fib_add_entry(u64 mac, u32 bd_index, u32 sw_if_index, u32 static_mac, u32 filter_mac, u32 bvi_mac)
Add an entry to the l2fib.
Definition: l2_fib.c:311
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
#define VNET_SW_INTERFACE_FLAG_ADMIN_UP
Definition: interface.h:560
uword unformat_ethernet_address(unformat_input_t *input, va_list *args)
Definition: format.c:227
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
int clib_bihash_search(clib_bihash *h, clib_bihash_kv *search_v, clib_bihash_kv *return_v)
Search a bi-hash table.
Definition: l2_fib.h:49
static uword l2fib_mac_age_scanner_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Definition: l2_fib.c:914
struct l2fib_entry_result_t::@184::@186 fields
VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(l2fib_sw_interface_up_down)
u64 uword
Definition: types.h:112
template key/value backing page structure
Definition: bihash_doc.h:44
static clib_error_t * l2fib_flush_mac_bd(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Flush bridge-domain MACs except static ones.
Definition: l2_fib.c:853
unsigned short u16
Definition: types.h:57
l2input_main_t l2input_main
Definition: l2_input.c:88
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:142
unsigned char u8
Definition: types.h:56
static u64 l2fib_make_key(u8 *mac_address, u16 bd_index)
Definition: l2_fib.h:127
l2_bridge_domain_t * bd_configs
Definition: l2_input.h:69
void l2fib_table_dump(u32 bd_index, l2fib_entry_key_t **l2fe_key, l2fib_entry_result_t **l2fe_res)
Definition: l2_fib.c:64
short i16
Definition: types.h:46
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:144
l2learn_main_t l2learn_main
Definition: l2_learn.h:46
#define vec_foreach(var, vec)
Vector iterator.
struct l2fib_entry_key_t::@176::@178 fields
static void * clib_bihash_get_value(clib_bihash *h, uword offset)
Get pointer to value page given its clib mheap offset.
u32 flags
Definition: vhost-user.h:76
bd_main_t bd_main
Definition: l2_bd.c:44
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
static clib_error_t * l2fib_add(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Add an entry to the L2FIB.
Definition: l2_fib.c:352
vlib_node_registration_t l2fib_mac_age_scanner_process_node
(constructor) VLIB_REGISTER_NODE (l2fib_mac_age_scanner_process_node)
Definition: l2_fib.c:1023
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:680
static u32 l2fib_del_entry_by_key(u64 raw_key)
Delete an entry from the l2fib.
Definition: l2_fib.c:617
void l2fib_flush_int_mac(vlib_main_t *vm, u32 sw_if_index)
Flush all non static MACs from an interface.
Definition: l2_fib.c:744
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
u64 raw
Definition: l2_fib.h:63
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169