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