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