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