VPP-257 Coding standards cleanup for vnet/vnet/l2
[vpp.git] / vnet / 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_fib.h>
28 #include <vnet/l2/l2_learn.h>
29 #include <vnet/l2/l2_bd.h>
30
31 #include <vppinfra/bihash_template.c>
32
33 typedef struct
34 {
35
36   /* hash table */
37   BVT (clib_bihash) mac_table;
38
39   /* convenience variables */
40   vlib_main_t *vlib_main;
41   vnet_main_t *vnet_main;
42 } l2fib_main_t;
43
44 l2fib_main_t l2fib_main;
45
46
47 /** Format sw_if_index. If the value is ~0, use the text "N/A" */
48 u8 *
49 format_vnet_sw_if_index_name_with_NA (u8 * s, va_list * args)
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",
57                    format_vnet_sw_interface_name, vnm,
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;
67   clib_bihash_bucket_t *b;
68   BVT (clib_bihash_value) * v;
69   l2fib_entry_key_t key;
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 *
102 show_l2fib (vlib_main_t * vm,
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;
108   clib_bihash_bucket_t *b;
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",
171                                    format_ethernet_address, key.fields.mac,
172                                    key.fields.bd_index,
173                                    format_vnet_sw_if_index_name_with_NA,
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",
225                              L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
226     }
227
228   l2learn_main.global_learn_count = 0;
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 *
235 clear_l2fib (vlib_main_t * vm,
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
256 l2fib_add_entry (u64 mac,
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     {
284       l2learn_main.global_learn_count++;
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 *
296 l2fib_add (vlib_main_t * vm,
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 *
377 l2fib_test_command_fn (vlib_main_t * vm,
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
393   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
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     {
508       if (l2learn_main.global_learn_count > 0)
509         {
510           l2learn_main.global_learn_count--;
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 *
525 l2fib_del (vlib_main_t * vm,
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 *
584 l2fib_init (vlib_main_t * vm)
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",
595                          L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
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
608 VLIB_INIT_FUNCTION (l2fib_init);
609
610 /*
611  * fd.io coding-style-patch-verification: ON
612  *
613  * Local Variables:
614  * eval: (c-set-style "gnu")
615  * End:
616  */