VPP-288: Documentation changes via doxygen for vnet/vnet/l2.
[vpp.git] / vnet / vnet / l2 / l2_bd.c
1 /*
2  * l2_bd.c : layer 2 bridge domain
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 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vlib/cli.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vnet/ip/format.h>
23 #include <vnet/l2/l2_input.h>
24 #include <vnet/l2/feat_bitmap.h>
25 #include <vnet/l2/l2_bd.h>
26 #include <vnet/l2/l2_fib.h>
27 #include <vnet/l2/l2_vtr.h>
28 #include <vnet/ip/ip4_packet.h>
29 #include <vnet/ip/ip6_packet.h>
30
31 #include <vppinfra/error.h>
32 #include <vppinfra/hash.h>
33 #include <vppinfra/vec.h>
34
35 /**
36  * @file
37  * @brief Ethernet Bridge Domain.
38  *
39  * Code in this file manages Layer 2 bridge domains.
40  *
41  */
42
43 bd_main_t bd_main;
44
45 /**
46   Init bridge domain if not done already.
47   For feature bitmap, set all bits except ARP termination
48 */
49 void
50 bd_validate (l2_bridge_domain_t * bd_config)
51 {
52   if (!bd_is_valid (bd_config))
53     {
54       bd_config->feature_bitmap = ~L2INPUT_FEAT_ARP_TERM;
55       bd_config->bvi_sw_if_index = ~0;
56       bd_config->members = 0;
57       bd_config->mac_by_ip4 = 0;
58       bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t),
59                                                sizeof (uword));
60     }
61 }
62
63 u32
64 bd_find_or_add_bd_index (bd_main_t * bdm, u32 bd_id)
65 {
66   uword *p;
67   u32 rv;
68
69   if (bd_id == ~0)
70     {
71       bd_id = 0;
72       while (hash_get (bdm->bd_index_by_bd_id, bd_id))
73         bd_id++;
74     }
75   else
76     {
77       p = hash_get (bdm->bd_index_by_bd_id, bd_id);
78       if (p)
79         return (p[0]);
80     }
81
82   rv = clib_bitmap_first_clear (bdm->bd_index_bitmap);
83
84   /* mark this index busy */
85   bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1);
86
87   hash_set (bdm->bd_index_by_bd_id, bd_id, rv);
88
89   vec_validate (l2input_main.bd_configs, rv);
90   l2input_main.bd_configs[rv].bd_id = bd_id;
91
92   return rv;
93 }
94
95 int
96 bd_delete_bd_index (bd_main_t * bdm, u32 bd_id)
97 {
98   uword *p;
99   u32 bd_index;
100
101   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
102   if (p == 0)
103     return -1;
104
105   bd_index = p[0];
106
107   /* mark this index clear */
108   bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0);
109   hash_unset (bdm->bd_index_by_bd_id, bd_id);
110
111   l2input_main.bd_configs[bd_index].bd_id = ~0;
112   l2input_main.bd_configs[bd_index].feature_bitmap = 0;
113
114   return 0;
115 }
116
117 void
118 bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member)
119 {
120   /*
121    * Add one element to the vector
122    *
123    * When flooding, the bvi interface (if present) must be the last member
124    * processed due to how BVI processing can change the packet. To enable
125    * this order, we make the bvi interface the first in the vector and
126    * flooding walks the vector in reverse.
127    */
128   if ((member->flags == L2_FLOOD_MEMBER_NORMAL) ||
129       (vec_len (bd_config->members) == 0))
130     {
131       vec_add1 (bd_config->members, *member);
132
133     }
134   else
135     {
136       /* Move 0th element to the end */
137       vec_add1 (bd_config->members, bd_config->members[0]);
138       bd_config->members[0] = *member;
139     }
140 }
141
142
143 #define BD_REMOVE_ERROR_OK        0
144 #define BD_REMOVE_ERROR_NOT_FOUND 1
145
146 u32
147 bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index)
148 {
149   u32 ix;
150
151   /* Find and delete the member */
152   vec_foreach_index (ix, bd_config->members)
153   {
154     if (vec_elt (bd_config->members, ix).sw_if_index == sw_if_index)
155       {
156         vec_del1 (bd_config->members, ix);
157         return BD_REMOVE_ERROR_OK;
158       }
159   }
160
161   return BD_REMOVE_ERROR_NOT_FOUND;
162 }
163
164
165 clib_error_t *
166 l2bd_init (vlib_main_t * vm)
167 {
168   bd_main_t *bdm = &bd_main;
169   u32 bd_index;
170   bdm->bd_index_by_bd_id = hash_create (0, sizeof (uword));
171   /*
172    * create a dummy bd with bd_id of 0 and bd_index of 0 with feature set
173    * to packet drop only. Thus, packets received from any L2 interface with
174    * uninitialized bd_index of 0 can be dropped safely.
175    */
176   bd_index = bd_find_or_add_bd_index (bdm, 0);
177   ASSERT (bd_index == 0);
178   l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP;
179   return 0;
180 }
181
182 VLIB_INIT_FUNCTION (l2bd_init);
183
184
185 /**
186     Set the learn/forward/flood flags for the bridge domain.
187     Return 0 if ok, non-zero if for an error.
188 */
189 u32
190 bd_set_flags (vlib_main_t * vm, u32 bd_index, u32 flags, u32 enable)
191 {
192
193   l2_bridge_domain_t *bd_config;
194   u32 feature_bitmap = 0;
195
196   vec_validate (l2input_main.bd_configs, bd_index);
197   bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
198
199   bd_validate (bd_config);
200
201   if (flags & L2_LEARN)
202     {
203       feature_bitmap |= L2INPUT_FEAT_LEARN;
204     }
205   if (flags & L2_FWD)
206     {
207       feature_bitmap |= L2INPUT_FEAT_FWD;
208     }
209   if (flags & L2_FLOOD)
210     {
211       feature_bitmap |= L2INPUT_FEAT_FLOOD;
212     }
213   if (flags & L2_UU_FLOOD)
214     {
215       feature_bitmap |= L2INPUT_FEAT_UU_FLOOD;
216     }
217   if (flags & L2_ARP_TERM)
218     {
219       feature_bitmap |= L2INPUT_FEAT_ARP_TERM;
220     }
221
222   if (enable)
223     {
224       bd_config->feature_bitmap |= feature_bitmap;
225     }
226   else
227     {
228       bd_config->feature_bitmap &= ~feature_bitmap;
229     }
230
231   return 0;
232 }
233
234 /**
235    Set bridge-domain learn enable/disable.
236    The CLI format is:
237    set bridge-domain learn <bd_id> [disable]
238 */
239 static clib_error_t *
240 bd_learn (vlib_main_t * vm,
241           unformat_input_t * input, vlib_cli_command_t * cmd)
242 {
243   bd_main_t *bdm = &bd_main;
244   clib_error_t *error = 0;
245   u32 bd_index, bd_id;
246   u32 enable;
247   uword *p;
248
249   if (!unformat (input, "%d", &bd_id))
250     {
251       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
252                                  format_unformat_error, input);
253       goto done;
254     }
255
256   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
257
258   if (p == 0)
259     return clib_error_return (0, "No such bridge domain %d", bd_id);
260
261   bd_index = p[0];
262
263   enable = 1;
264   if (unformat (input, "disable"))
265     {
266       enable = 0;
267     }
268
269   /* set the bridge domain flag */
270   if (bd_set_flags (vm, bd_index, L2_LEARN, enable))
271     {
272       error =
273         clib_error_return (0, "bridge-domain id %d out of range", bd_index);
274       goto done;
275     }
276
277 done:
278   return error;
279 }
280
281 /*?
282  * Layer 2 learning can be enabled and disabled on each
283  * interface and on each bridge-domain. Use this command to
284  * manage bridge-domains. It is enabled by default.
285  *
286  * @cliexpar
287  * Example of how to enable learning (where 200 is the bridge-domain-id):
288  * @cliexcmd{set bridge-domain learn 200}
289  * Example of how to disable learning (where 200 is the bridge-domain-id):
290  * @cliexcmd{set bridge-domain learn 200 disable}
291 ?*/
292 /* *INDENT-OFF* */
293 VLIB_CLI_COMMAND (bd_learn_cli, static) = {
294   .path = "set bridge-domain learn",
295   .short_help = "set bridge-domain learn <bridge-domain-id> [disable]",
296   .function = bd_learn,
297 };
298 /* *INDENT-ON* */
299
300 /**
301     Set bridge-domain forward enable/disable.
302     The CLI format is:
303     set bridge-domain forward <bd_index> [disable]
304 */
305 static clib_error_t *
306 bd_fwd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
307 {
308   bd_main_t *bdm = &bd_main;
309   clib_error_t *error = 0;
310   u32 bd_index, bd_id;
311   u32 enable;
312   uword *p;
313
314   if (!unformat (input, "%d", &bd_id))
315     {
316       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
317                                  format_unformat_error, input);
318       goto done;
319     }
320
321   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
322
323   if (p == 0)
324     return clib_error_return (0, "No such bridge domain %d", bd_id);
325
326   bd_index = p[0];
327
328   enable = 1;
329   if (unformat (input, "disable"))
330     {
331       enable = 0;
332     }
333
334   /* set the bridge domain flag */
335   if (bd_set_flags (vm, bd_index, L2_FWD, enable))
336     {
337       error =
338         clib_error_return (0, "bridge-domain id %d out of range", bd_index);
339       goto done;
340     }
341
342 done:
343   return error;
344 }
345
346
347 /*?
348  * Layer 2 unicast forwarding can be enabled and disabled on each
349  * interface and on each bridge-domain. Use this command to
350  * manage bridge-domains. It is enabled by default.
351  *
352  * @cliexpar
353  * Example of how to enable forwarding (where 200 is the bridge-domain-id):
354  * @cliexcmd{set bridge-domain forward 200}
355  * Example of how to disable forwarding (where 200 is the bridge-domain-id):
356  * @cliexcmd{set bridge-domain forward 200 disable}
357 ?*/
358 /* *INDENT-OFF* */
359 VLIB_CLI_COMMAND (bd_fwd_cli, static) = {
360   .path = "set bridge-domain forward",
361   .short_help = "set bridge-domain forward <bridge-domain-id> [disable]",
362   .function = bd_fwd,
363 };
364 /* *INDENT-ON* */
365
366 /**
367     Set bridge-domain flood enable/disable.
368     The CLI format is:
369     set bridge-domain flood <bd_index> [disable]
370 */
371 static clib_error_t *
372 bd_flood (vlib_main_t * vm,
373           unformat_input_t * input, vlib_cli_command_t * cmd)
374 {
375   bd_main_t *bdm = &bd_main;
376   clib_error_t *error = 0;
377   u32 bd_index, bd_id;
378   u32 enable;
379   uword *p;
380
381   if (!unformat (input, "%d", &bd_id))
382     {
383       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
384                                  format_unformat_error, input);
385       goto done;
386     }
387
388   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
389
390   if (p == 0)
391     return clib_error_return (0, "No such bridge domain %d", bd_id);
392
393   bd_index = p[0];
394
395   enable = 1;
396   if (unformat (input, "disable"))
397     {
398       enable = 0;
399     }
400
401   /* set the bridge domain flag */
402   if (bd_set_flags (vm, bd_index, L2_FLOOD, enable))
403     {
404       error =
405         clib_error_return (0, "bridge-domain id %d out of range", bd_index);
406       goto done;
407     }
408
409 done:
410   return error;
411 }
412
413 /*?
414  * Layer 2 flooding can be enabled and disabled on each
415  * interface and on each bridge-domain. Use this command to
416  * manage bridge-domains. It is enabled by default.
417  *
418  * @cliexpar
419  * Example of how to enable flooding (where 200 is the bridge-domain-id):
420  * @cliexcmd{set bridge-domain flood 200}
421  * Example of how to disable flooding (where 200 is the bridge-domain-id):
422  * @cliexcmd{set bridge-domain flood 200 disable}
423 ?*/
424 /* *INDENT-OFF* */
425 VLIB_CLI_COMMAND (bd_flood_cli, static) = {
426   .path = "set bridge-domain flood",
427   .short_help = "set bridge-domain flood <bridge-domain-id> [disable]",
428   .function = bd_flood,
429 };
430 /* *INDENT-ON* */
431
432 /**
433     Set bridge-domain unkown-unicast flood enable/disable.
434     The CLI format is:
435     set bridge-domain uu-flood <bd_index> [disable]
436 */
437 static clib_error_t *
438 bd_uu_flood (vlib_main_t * vm,
439              unformat_input_t * input, vlib_cli_command_t * cmd)
440 {
441   bd_main_t *bdm = &bd_main;
442   clib_error_t *error = 0;
443   u32 bd_index, bd_id;
444   u32 enable;
445   uword *p;
446
447   if (!unformat (input, "%d", &bd_id))
448     {
449       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
450                                  format_unformat_error, input);
451       goto done;
452     }
453
454   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
455
456   if (p == 0)
457     return clib_error_return (0, "No such bridge domain %d", bd_id);
458
459   bd_index = p[0];
460
461   enable = 1;
462   if (unformat (input, "disable"))
463     {
464       enable = 0;
465     }
466
467   /* set the bridge domain flag */
468   if (bd_set_flags (vm, bd_index, L2_UU_FLOOD, enable))
469     {
470       error =
471         clib_error_return (0, "bridge-domain id %d out of range", bd_index);
472       goto done;
473     }
474
475 done:
476   return error;
477 }
478
479 /*?
480  * Layer 2 unknown-unicast flooding can be enabled and disabled on each
481  * bridge-domain. It is enabled by default.
482  *
483  * @cliexpar
484  * Example of how to enable unknown-unicast flooding (where 200 is the
485  * bridge-domain-id):
486  * @cliexcmd{set bridge-domain uu-flood 200}
487  * Example of how to disable unknown-unicast flooding (where 200 is the bridge-domain-id):
488  * @cliexcmd{set bridge-domain uu-flood 200 disable}
489 ?*/
490 /* *INDENT-OFF* */
491 VLIB_CLI_COMMAND (bd_uu_flood_cli, static) = {
492   .path = "set bridge-domain uu-flood",
493   .short_help = "set bridge-domain uu-flood <bridge-domain-id> [disable]",
494   .function = bd_uu_flood,
495 };
496 /* *INDENT-ON* */
497
498 /**
499     Set bridge-domain arp term enable/disable.
500     The CLI format is:
501     set bridge-domain arp term <bridge-domain-id> [disable]
502 */
503 static clib_error_t *
504 bd_arp_term (vlib_main_t * vm,
505              unformat_input_t * input, vlib_cli_command_t * cmd)
506 {
507   bd_main_t *bdm = &bd_main;
508   clib_error_t *error = 0;
509   u32 bd_index, bd_id;
510   u32 enable;
511   uword *p;
512
513   if (!unformat (input, "%d", &bd_id))
514     {
515       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
516                                  format_unformat_error, input);
517       goto done;
518     }
519
520   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
521   if (p)
522     bd_index = *p;
523   else
524     return clib_error_return (0, "No such bridge domain %d", bd_id);
525
526   enable = 1;
527   if (unformat (input, "disable"))
528     enable = 0;
529
530   /* set the bridge domain flag */
531   if (bd_set_flags (vm, bd_index, L2_ARP_TERM, enable))
532     {
533       error =
534         clib_error_return (0, "bridge-domain id %d out of range", bd_index);
535       goto done;
536     }
537
538 done:
539   return error;
540 }
541
542 /*?
543  * Modify whether or not an existing bridge-domain should terminate and respond
544  * to ARP Requests. ARP Termination is disabled by default.
545  *
546  * @cliexpar
547  * Example of how to enable ARP termination (where 200 is the bridge-domain-id):
548  * @cliexcmd{set bridge-domain arp term 200}
549  * Example of how to disable ARP termination (where 200 is the bridge-domain-id):
550  * @cliexcmd{set bridge-domain arp term 200 disable}
551 ?*/
552 /* *INDENT-OFF* */
553 VLIB_CLI_COMMAND (bd_arp_term_cli, static) = {
554   .path = "set bridge-domain arp term",
555   .short_help = "set bridge-domain arp term <bridge-domain-id> [disable]",
556   .function = bd_arp_term,
557 };
558 /* *INDENT-ON* */
559
560
561 /**
562  * Add/delete IP address to MAC address mapping.
563  *
564  * The clib hash implementation stores uword entries in the hash table.
565  * The hash table mac_by_ip4 is keyed via IP4 address and store the
566  * 6-byte MAC address directly in the hash table entry uword.
567  *
568  * @warning This only works for 64-bit processor with 8-byte uword;
569  * which means this code *WILL NOT WORK* for a 32-bit prcessor with
570  * 4-byte uword.
571  */
572 u32
573 bd_add_del_ip_mac (u32 bd_index,
574                    u8 * ip_addr, u8 * mac_addr, u8 is_ip6, u8 is_add)
575 {
576   l2input_main_t *l2im = &l2input_main;
577   l2_bridge_domain_t *bd_cfg = l2input_bd_config_from_index (l2im, bd_index);
578   u64 new_mac = *(u64 *) mac_addr;
579   u64 *old_mac;
580   u16 *mac16 = (u16 *) & new_mac;
581
582   ASSERT (sizeof (uword) == sizeof (u64));      /* make sure uword is 8 bytes */
583
584   mac16[3] = 0;                 /* Clear last 2 unsed bytes of the 8-byte MAC address */
585   if (is_ip6)
586     {
587       ip6_address_t *ip6_addr_key;
588       hash_pair_t *hp;
589       old_mac = (u64 *) hash_get_mem (bd_cfg->mac_by_ip6, ip_addr);
590       if (is_add)
591         {
592           if (old_mac == 0)
593             {                   /* new entry - allocate and craete ip6 address key */
594               ip6_addr_key = clib_mem_alloc (sizeof (ip6_address_t));
595               clib_memcpy (ip6_addr_key, ip_addr, sizeof (ip6_address_t));
596             }
597           else if (*old_mac == new_mac)
598             {                   /* same mac entry already exist for ip6 address */
599               return 0;
600             }
601           else
602             {                   /* updat mac for ip6 address */
603               hp = hash_get_pair (bd_cfg->mac_by_ip6, ip_addr);
604               ip6_addr_key = (ip6_address_t *) hp->key;
605             }
606           hash_set_mem (bd_cfg->mac_by_ip6, ip6_addr_key, new_mac);
607         }
608       else
609         {
610           if (old_mac && (*old_mac == new_mac))
611             {
612               hp = hash_get_pair (bd_cfg->mac_by_ip6, ip_addr);
613               ip6_addr_key = (ip6_address_t *) hp->key;
614               hash_unset_mem (bd_cfg->mac_by_ip6, ip_addr);
615               clib_mem_free (ip6_addr_key);
616             }
617           else
618             return 1;
619         }
620     }
621   else
622     {
623       ip4_address_t ip4_addr = *(ip4_address_t *) ip_addr;
624       old_mac = (u64 *) hash_get (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
625       if (is_add)
626         {
627           if (old_mac && (*old_mac == new_mac))
628             return 0;           /* mac entry already exist */
629           hash_set (bd_cfg->mac_by_ip4, ip4_addr.as_u32, new_mac);
630         }
631       else
632         {
633           if (old_mac && (*old_mac == new_mac))
634             hash_unset (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
635           else
636             return 1;
637         }
638     }
639   return 0;
640 }
641
642 /**
643     Set bridge-domain arp entry add/delete.
644     The CLI format is:
645     set bridge-domain arp entry <bridge-domain-id> <ip-addr> <mac-addr> [del]
646 */
647 static clib_error_t *
648 bd_arp_entry (vlib_main_t * vm,
649               unformat_input_t * input, vlib_cli_command_t * cmd)
650 {
651   bd_main_t *bdm = &bd_main;
652   clib_error_t *error = 0;
653   u32 bd_index, bd_id;
654   u8 is_add = 1;
655   u8 is_ip6 = 0;
656   u8 ip_addr[16];
657   u8 mac_addr[6];
658   uword *p;
659
660   if (!unformat (input, "%d", &bd_id))
661     {
662       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
663                                  format_unformat_error, input);
664       goto done;
665     }
666
667   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
668
669   if (p)
670     bd_index = *p;
671   else
672     return clib_error_return (0, "No such bridge domain %d", bd_id);
673
674   if (unformat (input, "%U", unformat_ip4_address, ip_addr))
675     {
676       is_ip6 = 0;
677     }
678   else if (unformat (input, "%U", unformat_ip6_address, ip_addr))
679     {
680       is_ip6 = 1;
681     }
682   else
683     {
684       error = clib_error_return (0, "expecting IP address but got `%U'",
685                                  format_unformat_error, input);
686       goto done;
687     }
688
689   if (!unformat (input, "%U", unformat_ethernet_address, mac_addr))
690     {
691       error = clib_error_return (0, "expecting MAC address but got `%U'",
692                                  format_unformat_error, input);
693       goto done;
694     }
695
696   if (unformat (input, "del"))
697     {
698       is_add = 0;
699     }
700
701   /* set the bridge domain flagAdd IP-MAC entry into bridge domain */
702   if (bd_add_del_ip_mac (bd_index, ip_addr, mac_addr, is_ip6, is_add))
703     {
704       error = clib_error_return (0, "MAC %s for IP %U and MAC %U failed",
705                                  is_add ? "add" : "del",
706                                  is_ip6 ?
707                                  format_ip4_address : format_ip6_address,
708                                  ip_addr, format_ethernet_address, mac_addr);
709     }
710
711 done:
712   return error;
713 }
714
715 /*?
716  * Add an ARP entry to an existing bridge-domain.
717  *
718  * @cliexpar
719  * Example of how to add an ARP entry (where 200 is the bridge-domain-id):
720  * @cliexcmd{set bridge-domain arp entry 200 192.168.72.45 52:54:00:3b:83:1a}
721  * Example of how to delete an ARP entry (where 200 is the bridge-domain-id):
722  * @cliexcmd{set bridge-domain arp entry 200 192.168.72.45 52:54:00:3b:83:1a del}
723 ?*/
724 /* *INDENT-OFF* */
725 VLIB_CLI_COMMAND (bd_arp_entry_cli, static) = {
726   .path = "set bridge-domain arp entry",
727   .short_help = "set bridge-domain arp entry <bridge-domain-id> <ip-addr> <mac-addr> [del]",
728   .function = bd_arp_entry,
729 };
730 /* *INDENT-ON* */
731
732 u8 *
733 format_vtr (u8 * s, va_list * args)
734 {
735   u32 vtr_op = va_arg (*args, u32);
736   u32 dot1q = va_arg (*args, u32);
737   u32 tag1 = va_arg (*args, u32);
738   u32 tag2 = va_arg (*args, u32);
739   switch (vtr_op)
740     {
741     case L2_VTR_DISABLED:
742       return format (s, "none");
743     case L2_VTR_PUSH_1:
744       return format (s, "push-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
745     case L2_VTR_PUSH_2:
746       return format (s, "push-2 %s %d %d", dot1q ? "dot1q" : "dot1ad", tag1,
747                      tag2);
748     case L2_VTR_POP_1:
749       return format (s, "pop-1");
750     case L2_VTR_POP_2:
751       return format (s, "pop-2");
752     case L2_VTR_TRANSLATE_1_1:
753       return format (s, "trans-1-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
754     case L2_VTR_TRANSLATE_1_2:
755       return format (s, "trans-1-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
756                      tag1, tag2);
757     case L2_VTR_TRANSLATE_2_1:
758       return format (s, "trans-2-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
759     case L2_VTR_TRANSLATE_2_2:
760       return format (s, "trans-2-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
761                      tag1, tag2);
762     default:
763       return format (s, "none");
764     }
765 }
766
767 /**
768    Show bridge-domain state.
769    The CLI format is:
770    show bridge-domain [<bd_index>]
771 */
772 static clib_error_t *
773 bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
774 {
775   vnet_main_t *vnm = vnet_get_main ();
776   bd_main_t *bdm = &bd_main;
777   clib_error_t *error = 0;
778   u32 bd_index = ~0;
779   l2_bridge_domain_t *bd_config;
780   u32 start, end;
781   u32 printed;
782   u32 detail = 0;
783   u32 intf = 0;
784   u32 arp = 0;
785   u32 bd_id = ~0;
786   uword *p;
787
788   start = 0;
789   end = vec_len (l2input_main.bd_configs);
790
791   if (unformat (input, "%d", &bd_id))
792     {
793       if (unformat (input, "detail"))
794         detail = 1;
795       else if (unformat (input, "det"))
796         detail = 1;
797       if (unformat (input, "int"))
798         intf = 1;
799       if (unformat (input, "arp"))
800         arp = 1;
801
802       p = hash_get (bdm->bd_index_by_bd_id, bd_id);
803       if (p)
804         bd_index = *p;
805       else
806         return clib_error_return (0, "No such bridge domain %d", bd_id);
807
808       vec_validate (l2input_main.bd_configs, bd_index);
809       bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
810       if (bd_is_valid (bd_config))
811         {
812           start = bd_index;
813           end = start + 1;
814         }
815       else
816         {
817           vlib_cli_output (vm, "bridge-domain %d not in use", bd_id);
818           goto done;
819         }
820     }
821
822   /* Show all bridge-domains that have been initialized */
823   printed = 0;
824   for (bd_index = start; bd_index < end; bd_index++)
825     {
826       bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
827       if (bd_is_valid (bd_config))
828         {
829           if (!printed)
830             {
831               printed = 1;
832               vlib_cli_output (vm,
833                                "%=5s %=7s %=10s %=10s %=10s %=10s %=10s %=14s",
834                                "ID", "Index", "Learning", "U-Forwrd",
835                                "UU-Flood", "Flooding", "ARP-Term",
836                                "BVI-Intf");
837             }
838
839           vlib_cli_output (vm,
840                            "%=5d %=7d %=10s %=10s %=10s %=10s %=10s %=14U",
841                            bd_config->bd_id, bd_index,
842                            bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ?
843                            "on" : "off",
844                            bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on"
845                            : "off",
846                            bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD ?
847                            "on" : "off",
848                            bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ?
849                            "on" : "off",
850                            bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ?
851                            "on" : "off", format_vnet_sw_if_index_name_with_NA,
852                            vnm, bd_config->bvi_sw_if_index);
853
854           if (detail || intf)
855             {
856               /* Show all member interfaces */
857
858               l2_flood_member_t *member;
859               u32 header = 0;
860
861               vec_foreach (member, bd_config->members)
862               {
863                 u32 vtr_opr, dot1q, tag1, tag2;
864                 if (!header)
865                   {
866                     header = 1;
867                     vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=30s",
868                                      "Interface", "Index", "SHG", "BVI",
869                                      "VLAN-Tag-Rewrite");
870                   }
871                 l2vtr_get (vm, vnm, member->sw_if_index, &vtr_opr, &dot1q,
872                            &tag1, &tag2);
873                 vlib_cli_output (vm, "%=30U%=7d%=5d%=5s%=30U",
874                                  format_vnet_sw_if_index_name, vnm,
875                                  member->sw_if_index, member->sw_if_index,
876                                  member->shg,
877                                  member->flags & L2_FLOOD_MEMBER_BVI ? "*" :
878                                  "-", format_vtr, vtr_opr, dot1q, tag1, tag2);
879               }
880             }
881
882           if ((detail || arp) &&
883               (bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM))
884             {
885               u32 ip4_addr;
886               ip6_address_t *ip6_addr;
887               u64 mac_addr;
888               vlib_cli_output (vm,
889                                "\n  IP4/IP6 to MAC table for ARP Termination");
890
891               /* *INDENT-OFF* */
892               hash_foreach (ip4_addr, mac_addr, bd_config->mac_by_ip4,
893               ({
894                 vlib_cli_output (vm, "%=40U => %=20U",
895                                  format_ip4_address, &ip4_addr,
896                                  format_ethernet_address, &mac_addr);
897               }));
898
899               hash_foreach_mem (ip6_addr, mac_addr, bd_config->mac_by_ip6,
900               ({
901                 vlib_cli_output (vm, "%=40U => %=20U",
902                                  format_ip6_address, ip6_addr,
903                                  format_ethernet_address, &mac_addr);
904               }));
905               /* *INDENT-ON* */
906             }
907         }
908     }
909
910   if (!printed)
911     {
912       vlib_cli_output (vm, "no bridge-domains in use");
913     }
914
915 done:
916   return error;
917 }
918
919 /*?
920  * Show a summary of all the bridge-domain instances or detailed view of a
921  * single bridge-domain. Bridge-domains are created by adding an interface
922  * to a bridge using the '<em>set interface l2 bridge</em>' command.
923  *
924  * @cliexpar
925  * @parblock
926  * Example of displaying all bridge-domains:
927  * @cliexstart{show bridge-domain}
928  *  ID   Index   Learning   U-Forwrd   UU-Flood   Flooding   ARP-Term     BVI-Intf
929  *  0      0        off        off        off        off        off        local0
930  * 200     1        on         on         on         on         off          N/A
931  * @cliexend
932  *
933  * Example of displaying details of a single bridge-domains:
934  * @cliexstart{show bridge-domain 200 detail}
935  *  ID   Index   Learning   U-Forwrd   UU-Flood   Flooding   ARP-Term     BVI-Intf
936  * 200     1        on         on         on         on         off          N/A
937  *
938  *          Interface           Index  SHG  BVI        VLAN-Tag-Rewrite
939  *  GigabitEthernet0/8/0.200      3     0    -               none
940  *  GigabitEthernet0/9/0.200      4     0    -               none
941  * @cliexend
942  * @endparblock
943 ?*/
944 /* *INDENT-OFF* */
945 VLIB_CLI_COMMAND (bd_show_cli, static) = {
946   .path = "show bridge-domain",
947   .short_help = "show bridge-domain [bridge-domain-id [detail|int|arp]]",
948   .function = bd_show,
949 };
950 /* *INDENT-ON* */
951
952 /*
953  * fd.io coding-style-patch-verification: ON
954  *
955  * Local Variables:
956  * eval: (c-set-style "gnu")
957  * End:
958  */