VPP-288: Documentation changes via doxygen 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 /**
34  * @file
35  * @brief Ethernet MAC Address FIB Table Management.
36  *
37  * The MAC Address forwarding table for bridge-domains is called the l2fib.
38  * Entries are added automatically as part of mac learning, but MAC Addresses
39  * entries can also be added manually.
40  *
41  */
42
43 typedef struct
44 {
45
46   /* hash table */
47   BVT (clib_bihash) mac_table;
48
49   /* convenience variables */
50   vlib_main_t *vlib_main;
51   vnet_main_t *vnet_main;
52 } l2fib_main_t;
53
54 l2fib_main_t l2fib_main;
55
56
57 /** Format sw_if_index. If the value is ~0, use the text "N/A" */
58 u8 *
59 format_vnet_sw_if_index_name_with_NA (u8 * s, va_list * args)
60 {
61   vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
62   u32 sw_if_index = va_arg (*args, u32);
63   if (sw_if_index == ~0)
64     return format (s, "N/A");
65   else
66     return format (s, "%U",
67                    format_vnet_sw_interface_name, vnm,
68                    vnet_get_sw_interface (vnm, sw_if_index));
69 }
70
71 void
72 l2fib_table_dump (u32 bd_index, l2fib_entry_key_t ** l2fe_key,
73                   l2fib_entry_result_t ** l2fe_res)
74 {
75   l2fib_main_t *msm = &l2fib_main;
76   BVT (clib_bihash) * h = &msm->mac_table;
77   clib_bihash_bucket_t *b;
78   BVT (clib_bihash_value) * v;
79   l2fib_entry_key_t key;
80   l2fib_entry_result_t result;
81   int i, j, k;
82
83   for (i = 0; i < h->nbuckets; i++)
84     {
85       b = &h->buckets[i];
86       if (b->offset == 0)
87         continue;
88       v = BV (clib_bihash_get_value) (h, b->offset);
89       for (j = 0; j < (1 << b->log2_pages); j++)
90         {
91           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
92             {
93               if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
94                 continue;
95
96               key.raw = v->kvp[k].key;
97               result.raw = v->kvp[k].value;
98
99               if ((bd_index == ~0) || (bd_index == key.fields.bd_index))
100                 {
101                   vec_add1 (*l2fe_key, key);
102                   vec_add1 (*l2fe_res, result);
103                 }
104             }
105           v++;
106         }
107     }
108 }
109
110 /** Display the contents of the l2fib. */
111 static clib_error_t *
112 show_l2fib (vlib_main_t * vm,
113             unformat_input_t * input, vlib_cli_command_t * cmd)
114 {
115   bd_main_t *bdm = &bd_main;
116   l2fib_main_t *msm = &l2fib_main;
117   BVT (clib_bihash) * h = &msm->mac_table;
118   clib_bihash_bucket_t *b;
119   BVT (clib_bihash_value) * v;
120   l2fib_entry_key_t key;
121   l2fib_entry_result_t result;
122   u32 first_entry = 1;
123   u64 total_entries = 0;
124   int i, j, k;
125   u8 verbose = 0;
126   u8 raw = 0;
127   u32 bd_id, bd_index = ~0;
128
129   if (unformat (input, "raw"))
130     raw = 1;
131   else if (unformat (input, "verbose"))
132     verbose = 1;
133   else if (unformat (input, "bd_index %d", &bd_index))
134     verbose = 1;
135   else if (unformat (input, "bd_id %d", &bd_id))
136     {
137       uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id);
138       if (p)
139         {
140           verbose = 1;
141           bd_index = p[0];
142         }
143       else
144         {
145           vlib_cli_output (vm, "no such bridge domain id");
146           return 0;
147         }
148     }
149
150   for (i = 0; i < h->nbuckets; i++)
151     {
152       b = &h->buckets[i];
153       if (b->offset == 0)
154         continue;
155       v = BV (clib_bihash_get_value) (h, b->offset);
156       for (j = 0; j < (1 << b->log2_pages); j++)
157         {
158           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
159             {
160               if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
161                 continue;
162
163               if (verbose && first_entry)
164                 {
165                   first_entry = 0;
166                   vlib_cli_output (vm,
167                                    "%=19s%=7s%=30s%=7s%=8s%=8s%=5s%=9s%=11s",
168                                    "Mac Address", "BD Idx", "Interface",
169                                    "Index", "static", "filter", "bvi",
170                                    "refresh", "timestamp");
171                 }
172
173               key.raw = v->kvp[k].key;
174               result.raw = v->kvp[k].value;
175
176               if (verbose
177                   & ((bd_index >> 31) || (bd_index == key.fields.bd_index)))
178                 {
179                   vlib_cli_output (vm,
180                                    "%=19U%=7d%=30U%=7d%=8d%=8d%=5d%=9d%=11X",
181                                    format_ethernet_address, key.fields.mac,
182                                    key.fields.bd_index,
183                                    format_vnet_sw_if_index_name_with_NA,
184                                    msm->vnet_main, result.fields.sw_if_index,
185                                    result.fields.sw_if_index == ~0
186                                    ? -1 : result.fields.sw_if_index,
187                                    result.fields.static_mac,
188                                    result.fields.filter,
189                                    result.fields.bvi,
190                                    result.fields.refresh,
191                                    result.fields.timestamp);
192                 }
193               total_entries++;
194             }
195           v++;
196         }
197     }
198
199   if (total_entries == 0)
200     vlib_cli_output (vm, "no l2fib entries");
201   else
202     vlib_cli_output (vm, "%lld l2fib entries", total_entries);
203
204   if (raw)
205     vlib_cli_output (vm, "Raw Hash Table:\n%U\n",
206                      BV (format_bihash), h, 1 /* verbose */ );
207
208   return 0;
209 }
210
211 /*?
212  * This command dispays the MAC Address entries of the L2 FIB table.
213  * Output can be filtered to just get the number of MAC Addresses or display
214  * each MAC Address for all bridge domains or just a single bridge domain.
215  *
216  * @cliexpar
217  * Example of how to display the number of MAC Address entries in the L2
218  * FIB table:
219  * @cliexstart{show l2fib}
220  * 3 l2fib entries
221  * @cliexend
222  * Example of how to display all the MAC Address entries in the L2
223  * FIB table:
224  * @cliexstart{show l2fib verbose}
225  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
226  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
227  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
228  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
229  * 3 l2fib entries
230  * @cliexend
231 ?*/
232 /* *INDENT-OFF* */
233 VLIB_CLI_COMMAND (show_l2fib_cli, static) = {
234   .path = "show l2fib",
235   .short_help = "show l2fib [verbose | bd_id <nn> | bd_index <nn> | raw]",
236   .function = show_l2fib,
237 };
238 /* *INDENT-ON* */
239
240
241 /* Remove all entries from the l2fib */
242 void
243 l2fib_clear_table (uint keep_static)
244 {
245   l2fib_main_t *mp = &l2fib_main;
246
247   if (keep_static)
248     {
249       /* TODO: remove only non-static entries */
250     }
251   else
252     {
253       /* Remove all entries */
254       BV (clib_bihash_free) (&mp->mac_table);
255       BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
256                              L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
257     }
258
259   l2learn_main.global_learn_count = 0;
260 }
261
262 /** Clear all entries in L2FIB.
263  * @TODO: Later we may want a way to remove only the non-static entries
264  */
265 static clib_error_t *
266 clear_l2fib (vlib_main_t * vm,
267              unformat_input_t * input, vlib_cli_command_t * cmd)
268 {
269   l2fib_clear_table (0);
270   return 0;
271 }
272
273 /*?
274  * This command clears all the MAC Address entries from the L2 FIB table.
275  *
276  * @cliexpar
277  * Example of how to clear the L2 FIB Table:
278  * @cliexcmd{clear l2fib}
279  * Example to show the L2 FIB Table has been cleared:
280  * @cliexstart{show l2fib verbose}
281  * no l2fib entries
282  * @cliexend
283 ?*/
284 /* *INDENT-OFF* */
285 VLIB_CLI_COMMAND (clear_l2fib_cli, static) = {
286   .path = "clear l2fib",
287   .short_help = "clear l2fib",
288   .function = clear_l2fib,
289 };
290 /* *INDENT-ON* */
291
292
293 /**
294  * Add an entry to the l2fib.
295  * If the entry already exists then overwrite it
296  */
297 void
298 l2fib_add_entry (u64 mac,
299                  u32 bd_index,
300                  u32 sw_if_index, u32 static_mac, u32 filter_mac, u32 bvi_mac)
301 {
302   l2fib_entry_key_t key;
303   l2fib_entry_result_t result;
304   __attribute__ ((unused)) u32 bucket_contents;
305   l2fib_main_t *mp = &l2fib_main;
306   BVT (clib_bihash_kv) kv;
307
308   /* set up key */
309   key.raw = l2fib_make_key ((u8 *) & mac, bd_index);
310
311   /* set up result */
312   result.raw = 0;               /* clear all fields */
313   result.fields.sw_if_index = sw_if_index;
314   result.fields.static_mac = static_mac;
315   result.fields.filter = filter_mac;
316   result.fields.bvi = bvi_mac;
317
318   kv.key = key.raw;
319   kv.value = result.raw;
320
321   BV (clib_bihash_add_del) (&mp->mac_table, &kv, 1 /* is_add */ );
322
323   /* increment counter if dynamically learned mac */
324   if (result.fields.static_mac)
325     {
326       l2learn_main.global_learn_count++;
327     }
328 }
329
330 /**
331  * Add an entry to the L2FIB.
332  * The CLI format is:
333  *    l2fib add <mac> <bd> <intf> [static] [bvi]
334  *    l2fib add <mac> <bd> filter
335  * Note that filter and bvi entries are always static
336  */
337 static clib_error_t *
338 l2fib_add (vlib_main_t * vm,
339            unformat_input_t * input, vlib_cli_command_t * cmd)
340 {
341   bd_main_t *bdm = &bd_main;
342   vnet_main_t *vnm = vnet_get_main ();
343   clib_error_t *error = 0;
344   u64 mac;
345   u32 bd_id;
346   u32 bd_index;
347   u32 sw_if_index = ~0;
348   u32 filter_mac = 0;
349   u32 static_mac = 0;
350   u32 bvi_mac = 0;
351   uword *p;
352
353   if (!unformat_user (input, unformat_ethernet_address, &mac))
354     {
355       error = clib_error_return (0, "expected mac address `%U'",
356                                  format_unformat_error, input);
357       goto done;
358     }
359
360   if (!unformat (input, "%d", &bd_id))
361     {
362       error = clib_error_return (0, "expected bridge domain ID `%U'",
363                                  format_unformat_error, input);
364       goto done;
365     }
366
367   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
368   if (!p)
369     {
370       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
371       goto done;
372     }
373   bd_index = p[0];
374
375   if (unformat (input, "filter"))
376     {
377       filter_mac = 1;
378       static_mac = 1;
379
380     }
381   else
382     {
383
384       if (!unformat_user
385           (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
386         {
387           error = clib_error_return (0, "unknown interface `%U'",
388                                      format_unformat_error, input);
389           goto done;
390         }
391       if (unformat (input, "static"))
392         {
393           static_mac = 1;
394         }
395       else if (unformat (input, "bvi"))
396         {
397           bvi_mac = 1;
398           static_mac = 1;
399         }
400     }
401
402   l2fib_add_entry (mac, bd_index, sw_if_index, static_mac, filter_mac,
403                    bvi_mac);
404
405 done:
406   return error;
407 }
408
409 /*?
410  * This command adds a MAC Address entry to the L2 FIB table
411  * of an existing bridge-domain. The MAC Address can be static
412  * or dynamic. This command also allows a filter to be added,
413  * such that packets with given MAC Addresses (source mac or
414  * destination mac match) are dropped.
415  *
416  * @cliexpar
417  * Example of how to add a dynamic MAC Address entry to the L2 FIB table
418  * of a bridge-domain (where 200 is the bridge-domain-id):
419  * @cliexcmd{l2fib add 52:54:00:53:18:33 200 GigabitEthernet0/8/0.200}
420  * Example of how to add a static MAC Address entry to the L2 FIB table
421  * of a bridge-domain (where 200 is the bridge-domain-id):
422  * @cliexcmd{l2fib add 52:54:00:53:18:55 200 GigabitEthernet0/8/0.200 static}
423  * Example of how to add a filter such that a packet with the given MAC
424  * Address will be dropped in a given bridge-domain (where 200 is the
425  * bridge-domain-id):
426  * @cliexcmd{l2fib add 52:54:00:53:18:77 200 filter}
427  * Example of show command of the provisioned MAC Addresses and filters:
428  * @cliexstart{show l2fib verbose}
429  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
430  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
431  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
432  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
433  * 3 l2fib entries
434  * @cliexend
435 ?*/
436 /* *INDENT-OFF* */
437 VLIB_CLI_COMMAND (l2fib_add_cli, static) = {
438   .path = "l2fib add",
439   .short_help = "l2fib add <mac> <bridge-domain-id> filter | <intf> [static | bvi]",
440   .function = l2fib_add,
441 };
442 /* *INDENT-ON* */
443
444
445 static clib_error_t *
446 l2fib_test_command_fn (vlib_main_t * vm,
447                        unformat_input_t * input, vlib_cli_command_t * cmd)
448 {
449   clib_error_t *error = 0;
450   u64 mac, save_mac;
451   u32 bd_index = 0;
452   u32 sw_if_index = 8;
453   u32 filter_mac = 0;
454   u32 bvi_mac = 0;
455   u32 is_add = 0;
456   u32 is_del = 0;
457   u32 is_check = 0;
458   u32 count = 1;
459   int mac_set = 0;
460   int i;
461
462   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
463     {
464       if (unformat (input, "mac %U", unformat_ethernet_address, &mac))
465         mac_set = 1;
466       else if (unformat (input, "add"))
467         is_add = 1;
468       else if (unformat (input, "del"))
469         is_del = 1;
470       else if (unformat (input, "check"))
471         is_check = 1;
472       else if (unformat (input, "count %d", &count))
473         ;
474       else
475         break;
476     }
477
478   if (mac_set == 0)
479     return clib_error_return (0, "mac not set");
480
481   if (is_add == 0 && is_del == 0 && is_check == 0)
482     return clib_error_return (0,
483                               "noop: pick at least one of (add,del,check)");
484
485   save_mac = mac;
486
487   if (is_add)
488     {
489       for (i = 0; i < count; i++)
490         {
491           u64 tmp;
492           l2fib_add_entry (mac, bd_index, sw_if_index, mac,
493                            filter_mac, bvi_mac);
494           tmp = clib_net_to_host_u64 (mac);
495           tmp >>= 16;
496           tmp++;
497           tmp <<= 16;
498           mac = clib_host_to_net_u64 (tmp);
499         }
500     }
501
502   if (is_check)
503     {
504       BVT (clib_bihash_kv) kv;
505       l2fib_main_t *mp = &l2fib_main;
506
507       mac = save_mac;
508
509       for (i = 0; i < count; i++)
510         {
511           u64 tmp;
512           kv.key = l2fib_make_key ((u8 *) & mac, bd_index);
513           if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
514             {
515               clib_warning ("key %U AWOL", format_ethernet_address, &mac);
516               break;
517             }
518           tmp = clib_net_to_host_u64 (mac);
519           tmp >>= 16;
520           tmp++;
521           tmp <<= 16;
522           mac = clib_host_to_net_u64 (tmp);
523         }
524     }
525
526   if (is_del)
527     {
528       for (i = 0; i < count; i++)
529         {
530           u64 tmp;
531
532           l2fib_del_entry (mac, bd_index);
533
534           tmp = clib_net_to_host_u64 (mac);
535           tmp >>= 16;
536           tmp++;
537           tmp <<= 16;
538           mac = clib_host_to_net_u64 (tmp);
539         }
540     }
541
542   return error;
543 }
544
545 /*?
546  * The set of '<em>test l2fib</em>' commands allow the L2 FIB table of the default
547  * bridge domain (bridge-domain-id of 0) to be modified.
548  *
549  * @cliexpar
550  * @parblock
551  * Example of how to add a set of 4 sequential MAC Address entries to L2
552  * FIB table of the default bridge-domain:
553  * @cliexcmd{test l2fib add mac 52:54:00:53:00:00 count 4}
554  *
555  * Show the set of 4 sequential MAC Address entries that were added:
556  * @cliexstart{show l2fib verbose}
557  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
558  * 52:54:00:53:00:00    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
559  * 52:54:00:53:00:01    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
560  * 52:54:00:53:00:03    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
561  * 52:54:00:53:00:02    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
562  * 4 l2fib entries
563  * @cliexend
564  *
565  * Example of how to check that the set of 4 sequential MAC Address
566  * entries were added to L2 FIB table of the default
567  * bridge-domain. Used a count of 5 to produce an error:
568  *
569  * @cliexcmd{test l2fib check mac 52:54:00:53:00:00 count 5}
570  * The output of the check command is in the log files. Log file
571  * location may vary based on your OS and Version:
572  *
573  * <b><em># tail -f /var/log/messages | grep l2fib_test_command_fn</em></b>
574  *
575  * Sep  7 17:15:24 localhost vnet[4952]: l2fib_test_command_fn:446: key 52:54:00:53:00:04 AWOL
576  *
577  * Example of how to delete a set of 4 sequential MAC Address entries
578  * from L2 FIB table of the default bridge-domain:
579  * @cliexcmd{test l2fib del mac 52:54:00:53:00:00 count 4}
580  * @endparblock
581 ?*/
582 /* *INDENT-OFF* */
583 VLIB_CLI_COMMAND (l2fib_test_command, static) = {
584   .path = "test l2fib",
585   .short_help = "test l2fib [add|del|check] mac <base-addr> count <nn>",
586   .function = l2fib_test_command_fn,
587 };
588 /* *INDENT-ON* */
589
590
591 /**
592  * Delete an entry from the l2fib.
593  * Return 0 if the entry was deleted, or 1 if it was not found
594  */
595 u32
596 l2fib_del_entry (u64 mac, u32 bd_index)
597 {
598
599   l2fib_entry_result_t result;
600   l2fib_main_t *mp = &l2fib_main;
601   BVT (clib_bihash_kv) kv;
602
603   /* set up key */
604   kv.key = l2fib_make_key ((u8 *) & mac, bd_index);
605
606   if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
607     return 1;
608
609   result.raw = kv.value;
610
611   /* decrement counter if dynamically learned mac */
612   if (result.fields.static_mac)
613     {
614       if (l2learn_main.global_learn_count > 0)
615         {
616           l2learn_main.global_learn_count--;
617         }
618     }
619
620   /* Remove entry from hash table */
621   BV (clib_bihash_add_del) (&mp->mac_table, &kv, 0 /* is_add */ );
622   return 0;
623 }
624
625 /**
626  * Delete an entry from the L2FIB.
627  * The CLI format is:
628  *    l2fib del <mac> <bd-id>
629  */
630 static clib_error_t *
631 l2fib_del (vlib_main_t * vm,
632            unformat_input_t * input, vlib_cli_command_t * cmd)
633 {
634   bd_main_t *bdm = &bd_main;
635   clib_error_t *error = 0;
636   u64 mac;
637   u32 bd_id;
638   u32 bd_index;
639   uword *p;
640
641   if (!unformat_user (input, unformat_ethernet_address, &mac))
642     {
643       error = clib_error_return (0, "expected mac address `%U'",
644                                  format_unformat_error, input);
645       goto done;
646     }
647
648   if (!unformat (input, "%d", &bd_id))
649     {
650       error = clib_error_return (0, "expected bridge domain ID `%U'",
651                                  format_unformat_error, input);
652       goto done;
653     }
654
655   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
656   if (!p)
657     {
658       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
659       goto done;
660     }
661   bd_index = p[0];
662
663   /* Delete the entry */
664   if (l2fib_del_entry (mac, bd_index))
665     {
666       error = clib_error_return (0, "mac entry not found");
667       goto done;
668     }
669
670 done:
671   return error;
672 }
673
674 /*?
675  * This command deletes an existing MAC Address entry from the L2 FIB
676  * table of an existing bridge-domain.
677  *
678  * @cliexpar
679  * 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):
680  * @cliexcmd{l2fib del 52:54:00:53:18:33 200}
681 ?*/
682 /* *INDENT-OFF* */
683 VLIB_CLI_COMMAND (l2fib_del_cli, static) = {
684   .path = "l2fib del",
685   .short_help = "l2fib del <mac> <bridge-domain-id>",
686   .function = l2fib_del,
687 };
688 /* *INDENT-ON* */
689
690
691 BVT (clib_bihash) * get_mac_table (void)
692 {
693   l2fib_main_t *mp = &l2fib_main;
694   return &mp->mac_table;
695 }
696
697 clib_error_t *
698 l2fib_init (vlib_main_t * vm)
699 {
700   l2fib_main_t *mp = &l2fib_main;
701   l2fib_entry_key_t test_key;
702   u8 test_mac[6];
703
704   mp->vlib_main = vm;
705   mp->vnet_main = vnet_get_main ();
706
707   /* Create the hash table  */
708   BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
709                          L2FIB_NUM_BUCKETS, L2FIB_MEMORY_SIZE);
710
711   /* verify the key constructor is good, since it is endian-sensitive */
712   memset (test_mac, 0, sizeof (test_mac));
713   test_mac[0] = 0x11;
714   test_key.raw = 0;
715   test_key.raw = l2fib_make_key ((u8 *) & test_mac, 0x1234);
716   ASSERT (test_key.fields.mac[0] == 0x11);
717   ASSERT (test_key.fields.bd_index == 0x1234);
718
719   return 0;
720 }
721
722 VLIB_INIT_FUNCTION (l2fib_init);
723
724 /*
725  * fd.io coding-style-patch-verification: ON
726  *
727  * Local Variables:
728  * eval: (c-set-style "gnu")
729  * End:
730  */