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