FD.io VPP  v16.09
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_fib.h>
28 #include <vnet/l2/l2_learn.h>
29 #include <vnet/l2/l2_bd.h>
30 
32 
33 typedef struct
34 {
35 
36  /* hash table */
37  BVT (clib_bihash) mac_table;
38 
39  /* convenience variables */
42 } l2fib_main_t;
43 
45 
46 
47 /** Format sw_if_index. If the value is ~0, use the text "N/A" */
48 u8 *
50 {
51  vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
52  u32 sw_if_index = va_arg (*args, u32);
53  if (sw_if_index == ~0)
54  return format (s, "N/A");
55  else
56  return format (s, "%U",
58  vnet_get_sw_interface (vnm, sw_if_index));
59 }
60 
61 void
62 l2fib_table_dump (u32 bd_index, l2fib_entry_key_t ** l2fe_key,
63  l2fib_entry_result_t ** l2fe_res)
64 {
65  l2fib_main_t *msm = &l2fib_main;
66  BVT (clib_bihash) * h = &msm->mac_table;
68  BVT (clib_bihash_value) * v;
70  l2fib_entry_result_t result;
71  int i, j, k;
72 
73  for (i = 0; i < h->nbuckets; i++)
74  {
75  b = &h->buckets[i];
76  if (b->offset == 0)
77  continue;
78  v = BV (clib_bihash_get_value) (h, b->offset);
79  for (j = 0; j < (1 << b->log2_pages); j++)
80  {
81  for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
82  {
83  if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
84  continue;
85 
86  key.raw = v->kvp[k].key;
87  result.raw = v->kvp[k].value;
88 
89  if ((bd_index == ~0) || (bd_index == key.fields.bd_index))
90  {
91  vec_add1 (*l2fe_key, key);
92  vec_add1 (*l2fe_res, result);
93  }
94  }
95  v++;
96  }
97  }
98 }
99 
100 /** Display the contents of the l2fib. */
101 static clib_error_t *
103  unformat_input_t * input, vlib_cli_command_t * cmd)
104 {
105  bd_main_t *bdm = &bd_main;
106  l2fib_main_t *msm = &l2fib_main;
107  BVT (clib_bihash) * h = &msm->mac_table;
109  BVT (clib_bihash_value) * v;
110  l2fib_entry_key_t key;
111  l2fib_entry_result_t result;
112  u32 first_entry = 1;
113  u64 total_entries = 0;
114  int i, j, k;
115  u8 verbose = 0;
116  u8 raw = 0;
117  u32 bd_id, bd_index = ~0;
118 
119  if (unformat (input, "raw"))
120  raw = 1;
121  else if (unformat (input, "verbose"))
122  verbose = 1;
123  else if (unformat (input, "bd_index %d", &bd_index))
124  verbose = 1;
125  else if (unformat (input, "bd_id %d", &bd_id))
126  {
127  uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id);
128  if (p)
129  {
130  verbose = 1;
131  bd_index = p[0];
132  }
133  else
134  {
135  vlib_cli_output (vm, "no such bridge domain id");
136  return 0;
137  }
138  }
139 
140  for (i = 0; i < h->nbuckets; i++)
141  {
142  b = &h->buckets[i];
143  if (b->offset == 0)
144  continue;
145  v = BV (clib_bihash_get_value) (h, b->offset);
146  for (j = 0; j < (1 << b->log2_pages); j++)
147  {
148  for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
149  {
150  if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
151  continue;
152 
153  if (verbose && first_entry)
154  {
155  first_entry = 0;
156  vlib_cli_output (vm,
157  "%=19s%=7s%=30s%=7s%=8s%=8s%=5s%=9s%=11s",
158  "Mac Address", "BD Idx", "Interface",
159  "Index", "static", "filter", "bvi",
160  "refresh", "timestamp");
161  }
162 
163  key.raw = v->kvp[k].key;
164  result.raw = v->kvp[k].value;
165 
166  if (verbose
167  & ((bd_index >> 31) || (bd_index == key.fields.bd_index)))
168  {
169  vlib_cli_output (vm,
170  "%=19U%=7d%=30U%=7d%=8d%=8d%=5d%=9d%=11X",
172  key.fields.bd_index,
174  msm->vnet_main, result.fields.sw_if_index,
175  result.fields.sw_if_index == ~0
176  ? -1 : result.fields.sw_if_index,
177  result.fields.static_mac,
178  result.fields.filter,
179  result.fields.bvi,
180  result.fields.refresh,
181  result.fields.timestamp);
182  }
183  total_entries++;
184  }
185  v++;
186  }
187  }
188 
189  if (total_entries == 0)
190  vlib_cli_output (vm, "no l2fib entries");
191  else
192  vlib_cli_output (vm, "%lld l2fib entries", total_entries);
193 
194  if (raw)
195  vlib_cli_output (vm, "Raw Hash Table:\n%U\n",
196  BV (format_bihash), h, 1 /* verbose */ );
197 
198  return 0;
199 }
200 
201 /* *INDENT-OFF* */
202 VLIB_CLI_COMMAND (show_l2fib_cli, static) = {
203  .path = "show l2fib",
204  .short_help = "show l2fib [verbose | bd_id <nn> | bd_index <nn> | raw]",
205  .function = show_l2fib,
206 };
207 /* *INDENT-ON* */
208 
209 
210 /* Remove all entries from the l2fib */
211 void
212 l2fib_clear_table (uint keep_static)
213 {
214  l2fib_main_t *mp = &l2fib_main;
215 
216  if (keep_static)
217  {
218  /* TODO: remove only non-static entries */
219  }
220  else
221  {
222  /* Remove all entries */
223  BV (clib_bihash_free) (&mp->mac_table);
224  BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
226  }
227 
229 }
230 
231 /** Clear all entries in L2FIB.
232  * @TODO: Later we may want a way to remove only the non-static entries
233  */
234 static clib_error_t *
236  unformat_input_t * input, vlib_cli_command_t * cmd)
237 {
238  l2fib_clear_table (0);
239  return 0;
240 }
241 
242 /* *INDENT-OFF* */
243 VLIB_CLI_COMMAND (clear_l2fib_cli, static) = {
244  .path = "clear l2fib",
245  .short_help = "Clear l2fib mac forwarding entries",
246  .function = clear_l2fib,
247 };
248 /* *INDENT-ON* */
249 
250 
251 /**
252  * Add an entry to the l2fib.
253  * If the entry already exists then overwrite it
254  */
255 void
257  u32 bd_index,
258  u32 sw_if_index, u32 static_mac, u32 filter_mac, u32 bvi_mac)
259 {
260  l2fib_entry_key_t key;
261  l2fib_entry_result_t result;
262  __attribute__ ((unused)) u32 bucket_contents;
263  l2fib_main_t *mp = &l2fib_main;
264  BVT (clib_bihash_kv) kv;
265 
266  /* set up key */
267  key.raw = l2fib_make_key ((u8 *) & mac, bd_index);
268 
269  /* set up result */
270  result.raw = 0; /* clear all fields */
271  result.fields.sw_if_index = sw_if_index;
272  result.fields.static_mac = static_mac;
273  result.fields.filter = filter_mac;
274  result.fields.bvi = bvi_mac;
275 
276  kv.key = key.raw;
277  kv.value = result.raw;
278 
279  BV (clib_bihash_add_del) (&mp->mac_table, &kv, 1 /* is_add */ );
280 
281  /* increment counter if dynamically learned mac */
282  if (result.fields.static_mac)
283  {
285  }
286 }
287 
288 /**
289  * Add an entry to the L2FIB.
290  * The CLI format is:
291  * l2fib add <mac> <bd> <intf> [static] [bvi]
292  * l2fib add <mac> <bd> filter
293  * Note that filter and bvi entries are always static
294  */
295 static clib_error_t *
297  unformat_input_t * input, vlib_cli_command_t * cmd)
298 {
299  bd_main_t *bdm = &bd_main;
300  vnet_main_t *vnm = vnet_get_main ();
301  clib_error_t *error = 0;
302  u64 mac;
303  u32 bd_id;
304  u32 bd_index;
305  u32 sw_if_index = ~0;
306  u32 filter_mac = 0;
307  u32 static_mac = 0;
308  u32 bvi_mac = 0;
309  uword *p;
310 
311  if (!unformat_user (input, unformat_ethernet_address, &mac))
312  {
313  error = clib_error_return (0, "expected mac address `%U'",
314  format_unformat_error, input);
315  goto done;
316  }
317 
318  if (!unformat (input, "%d", &bd_id))
319  {
320  error = clib_error_return (0, "expected bridge domain ID `%U'",
321  format_unformat_error, input);
322  goto done;
323  }
324 
325  p = hash_get (bdm->bd_index_by_bd_id, bd_id);
326  if (!p)
327  {
328  error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
329  goto done;
330  }
331  bd_index = p[0];
332 
333  if (unformat (input, "filter"))
334  {
335  filter_mac = 1;
336  static_mac = 1;
337 
338  }
339  else
340  {
341 
342  if (!unformat_user
343  (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
344  {
345  error = clib_error_return (0, "unknown interface `%U'",
346  format_unformat_error, input);
347  goto done;
348  }
349  if (unformat (input, "static"))
350  {
351  static_mac = 1;
352  }
353  else if (unformat (input, "bvi"))
354  {
355  bvi_mac = 1;
356  static_mac = 1;
357  }
358  }
359 
360  l2fib_add_entry (mac, bd_index, sw_if_index, static_mac, filter_mac,
361  bvi_mac);
362 
363 done:
364  return error;
365 }
366 
367 /* *INDENT-OFF* */
368 VLIB_CLI_COMMAND (l2fib_add_cli, static) = {
369  .path = "l2fib add",
370  .short_help = "Add l2fib mac forwarding entry <mac> <bd-id> filter | <intf> [static | bvi]",
371  .function = l2fib_add,
372 };
373 /* *INDENT-ON* */
374 
375 
376 static clib_error_t *
378  unformat_input_t * input, vlib_cli_command_t * cmd)
379 {
380  clib_error_t *error = 0;
381  u64 mac, save_mac;
382  u32 bd_index = 0;
383  u32 sw_if_index = 8;
384  u32 filter_mac = 0;
385  u32 bvi_mac = 0;
386  u32 is_add = 0;
387  u32 is_del = 0;
388  u32 is_check = 0;
389  u32 count = 1;
390  int mac_set = 0;
391  int i;
392 
394  {
395  if (unformat (input, "mac %U", unformat_ethernet_address, &mac))
396  mac_set = 1;
397  else if (unformat (input, "add"))
398  is_add = 1;
399  else if (unformat (input, "del"))
400  is_del = 1;
401  else if (unformat (input, "check"))
402  is_check = 1;
403  else if (unformat (input, "count %d", &count))
404  ;
405  else
406  break;
407  }
408 
409  if (mac_set == 0)
410  return clib_error_return (0, "mac not set");
411 
412  if (is_add == 0 && is_del == 0 && is_check == 0)
413  return clib_error_return (0,
414  "noop: pick at least one of (add,del,check)");
415 
416  save_mac = mac;
417 
418  if (is_add)
419  {
420  for (i = 0; i < count; i++)
421  {
422  u64 tmp;
423  l2fib_add_entry (mac, bd_index, sw_if_index, mac,
424  filter_mac, bvi_mac);
425  tmp = clib_net_to_host_u64 (mac);
426  tmp >>= 16;
427  tmp++;
428  tmp <<= 16;
429  mac = clib_host_to_net_u64 (tmp);
430  }
431  }
432 
433  if (is_check)
434  {
435  BVT (clib_bihash_kv) kv;
436  l2fib_main_t *mp = &l2fib_main;
437 
438  mac = save_mac;
439 
440  for (i = 0; i < count; i++)
441  {
442  u64 tmp;
443  kv.key = l2fib_make_key ((u8 *) & mac, bd_index);
444  if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
445  {
446  clib_warning ("key %U AWOL", format_ethernet_address, &mac);
447  break;
448  }
449  tmp = clib_net_to_host_u64 (mac);
450  tmp >>= 16;
451  tmp++;
452  tmp <<= 16;
453  mac = clib_host_to_net_u64 (tmp);
454  }
455  }
456 
457  if (is_del)
458  {
459  for (i = 0; i < count; i++)
460  {
461  u64 tmp;
462 
463  l2fib_del_entry (mac, bd_index);
464 
465  tmp = clib_net_to_host_u64 (mac);
466  tmp >>= 16;
467  tmp++;
468  tmp <<= 16;
469  mac = clib_host_to_net_u64 (tmp);
470  }
471  }
472 
473  return error;
474 }
475 
476 /* *INDENT-OFF* */
477 VLIB_CLI_COMMAND (l2fib_test_command, static) = {
478  .path = "test l2fib",
479  .short_help = "test l2fib [del] mac <base-addr> count <nn>",
480  .function = l2fib_test_command_fn,
481 };
482 /* *INDENT-ON* */
483 
484 
485 /**
486  * Delete an entry from the l2fib.
487  * Return 0 if the entry was deleted, or 1 if it was not found
488  */
489 u32
490 l2fib_del_entry (u64 mac, u32 bd_index)
491 {
492 
493  l2fib_entry_result_t result;
494  l2fib_main_t *mp = &l2fib_main;
495  BVT (clib_bihash_kv) kv;
496 
497  /* set up key */
498  kv.key = l2fib_make_key ((u8 *) & mac, bd_index);
499 
500  if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
501  return 1;
502 
503  result.raw = kv.value;
504 
505  /* decrement counter if dynamically learned mac */
506  if (result.fields.static_mac)
507  {
509  {
511  }
512  }
513 
514  /* Remove entry from hash table */
515  BV (clib_bihash_add_del) (&mp->mac_table, &kv, 0 /* is_add */ );
516  return 0;
517 }
518 
519 /**
520  * Delete an entry from the L2FIB.
521  * The CLI format is:
522  * l2fib del <mac> <bd-id>
523  */
524 static clib_error_t *
526  unformat_input_t * input, vlib_cli_command_t * cmd)
527 {
528  bd_main_t *bdm = &bd_main;
529  clib_error_t *error = 0;
530  u64 mac;
531  u32 bd_id;
532  u32 bd_index;
533  uword *p;
534 
535  if (!unformat_user (input, unformat_ethernet_address, &mac))
536  {
537  error = clib_error_return (0, "expected mac address `%U'",
538  format_unformat_error, input);
539  goto done;
540  }
541 
542  if (!unformat (input, "%d", &bd_id))
543  {
544  error = clib_error_return (0, "expected bridge domain ID `%U'",
545  format_unformat_error, input);
546  goto done;
547  }
548 
549  p = hash_get (bdm->bd_index_by_bd_id, bd_id);
550  if (!p)
551  {
552  error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
553  goto done;
554  }
555  bd_index = p[0];
556 
557  /* Delete the entry */
558  if (l2fib_del_entry (mac, bd_index))
559  {
560  error = clib_error_return (0, "mac entry not found");
561  goto done;
562  }
563 
564 done:
565  return error;
566 }
567 
568 /* *INDENT-OFF* */
569 VLIB_CLI_COMMAND (l2fib_del_cli, static) = {
570  .path = "l2fib del",
571  .short_help = "Delete l2fib mac forwarding entry <mac> <bd-id>",
572  .function = l2fib_del,
573 };
574 /* *INDENT-ON* */
575 
576 
577 BVT (clib_bihash) * get_mac_table (void)
578 {
579  l2fib_main_t *mp = &l2fib_main;
580  return &mp->mac_table;
581 }
582 
583 clib_error_t *
585 {
586  l2fib_main_t *mp = &l2fib_main;
587  l2fib_entry_key_t test_key;
588  u8 test_mac[6];
589 
590  mp->vlib_main = vm;
591  mp->vnet_main = vnet_get_main ();
592 
593  /* Create the hash table */
594  BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
596 
597  /* verify the key constructor is good, since it is endian-sensitive */
598  memset (test_mac, 0, sizeof (test_mac));
599  test_mac[0] = 0x11;
600  test_key.raw = 0;
601  test_key.raw = l2fib_make_key ((u8 *) & test_mac, 0x1234);
602  ASSERT (test_key.fields.mac[0] == 0x11);
603  ASSERT (test_key.fields.bd_index == 0x1234);
604 
605  return 0;
606 }
607 
609 
610 /*
611  * fd.io coding-style-patch-verification: ON
612  *
613  * Local Variables:
614  * eval: (c-set-style "gnu")
615  * End:
616  */
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:377
u32 global_learn_count
Definition: l2_learn.h:32
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:343
u8 * format_vnet_sw_if_index_name_with_NA(u8 *s, va_list *args)
Format sw_if_index.
Definition: l2_fib.c:49
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:966
clib_bihash_bucket_t
Definition: bihash_doc.h:65
void clib_bihash_free(clib_bihash *h)
Destroy a bounded index extensible hash table.
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
struct l2fib_entry_result_t::@159::@161 fields
u32 l2fib_del_entry(u64 mac, u32 bd_index)
Delete an entry from the l2fib.
Definition: l2_fib.c:490
clib_error_t * l2fib_init(vlib_main_t *vm)
Definition: l2_fib.c:584
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:482
static vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
#define L2FIB_MEMORY_SIZE
Definition: l2_fib.h:28
unformat_function_t unformat_vnet_sw_interface
Definition: l2_fib.h:54
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:525
vnet_main_t * vnet_get_main(void)
Definition: misc.c:45
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
#define clib_warning(format, args...)
Definition: error.h:59
unsigned long u64
Definition: types.h:89
int vlib_main(vlib_main_t *vm, unformat_input_t *input)
Definition: main.c:1572
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:977
uword * bd_index_by_bd_id
Definition: l2_bd.h:27
#define hash_get(h, key)
Definition: hash.h:248
format_function_t format_vnet_sw_interface_name
#define BIHASH_KVP_PER_PAGE
Definition: bihash_24_8.h:18
vnet_main_t vnet_main
Definition: misc.c:42
#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
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:575
l2fib_main_t l2fib_main
Definition: l2_fib.c:44
u64 raw
Definition: l2_fib.h:70
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:235
BVT(clib_bihash)
Definition: l2_fib.c:577
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:102
void l2fib_clear_table(uint keep_static)
Definition: l2_fib.c:212
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:256
uword unformat_ethernet_address(unformat_input_t *input, va_list *args)
Definition: format.c:206
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
int clib_bihash_search(clib_bihash *h, clib_bihash_kv *search_v, clib_bihash_kv *return_v)
Search a bi-hash table.
u8 * format(u8 *s, char *fmt,...)
Definition: format.c:418
Definition: l2_fib.h:33
u64 uword
Definition: types.h:112
template key/value backing page structure
Definition: bihash_doc.h:44
VLIB_CLI_COMMAND(set_interface_ip_source_and_port_range_check_command, static)
unsigned char u8
Definition: types.h:56
static u64 l2fib_make_key(u8 *mac_address, u16 bd_index)
Definition: l2_fib.h:95
void l2fib_table_dump(u32 bd_index, l2fib_entry_key_t **l2fe_key, l2fib_entry_result_t **l2fe_res)
Definition: l2_fib.c:62
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
l2learn_main_t l2learn_main
Definition: l2_learn.h:46
static void * clib_bihash_get_value(clib_bihash *h, uword offset)
Get pointer to value page given its clib mheap offset.
#define clib_error_return(e, args...)
Definition: error.h:111
struct _unformat_input_t unformat_input_t
bd_main_t bd_main
Definition: l2_bd.c:35
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:296
u64 raw
Definition: l2_fib.h:47
struct l2fib_entry_key_t::@155::@157 fields