2 * l2_bd.c : layer 2 bridge domain
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.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>
31 #include <vppinfra/error.h>
32 #include <vppinfra/hash.h>
33 #include <vppinfra/vec.h>
38 Init bridge domain if not done already.
39 For feature bitmap, set all bits except ARP termination
42 bd_validate (l2_bridge_domain_t * bd_config)
44 if (!bd_is_valid (bd_config))
46 bd_config->feature_bitmap = ~L2INPUT_FEAT_ARP_TERM;
47 bd_config->bvi_sw_if_index = ~0;
48 bd_config->members = 0;
49 bd_config->mac_by_ip4 = 0;
50 bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t),
56 bd_find_or_add_bd_index (bd_main_t * bdm, u32 bd_id)
64 while (hash_get (bdm->bd_index_by_bd_id, bd_id))
69 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
74 rv = clib_bitmap_first_clear (bdm->bd_index_bitmap);
76 /* mark this index busy */
77 bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1);
79 hash_set (bdm->bd_index_by_bd_id, bd_id, rv);
81 vec_validate (l2input_main.bd_configs, rv);
82 l2input_main.bd_configs[rv].bd_id = bd_id;
88 bd_delete_bd_index (bd_main_t * bdm, u32 bd_id)
93 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
99 /* mark this index clear */
100 bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0);
101 hash_unset (bdm->bd_index_by_bd_id, bd_id);
103 l2input_main.bd_configs[bd_index].bd_id = ~0;
104 l2input_main.bd_configs[bd_index].feature_bitmap = 0;
110 bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member)
113 * Add one element to the vector
115 * When flooding, the bvi interface (if present) must be the last member
116 * processed due to how BVI processing can change the packet. To enable
117 * this order, we make the bvi interface the first in the vector and
118 * flooding walks the vector in reverse.
120 if ((member->flags == L2_FLOOD_MEMBER_NORMAL) ||
121 (vec_len (bd_config->members) == 0))
123 vec_add1 (bd_config->members, *member);
128 /* Move 0th element to the end */
129 vec_add1 (bd_config->members, bd_config->members[0]);
130 bd_config->members[0] = *member;
135 #define BD_REMOVE_ERROR_OK 0
136 #define BD_REMOVE_ERROR_NOT_FOUND 1
139 bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index)
143 /* Find and delete the member */
144 vec_foreach_index (ix, bd_config->members)
146 if (vec_elt (bd_config->members, ix).sw_if_index == sw_if_index)
148 vec_del1 (bd_config->members, ix);
149 return BD_REMOVE_ERROR_OK;
153 return BD_REMOVE_ERROR_NOT_FOUND;
158 l2bd_init (vlib_main_t * vm)
160 bd_main_t *bdm = &bd_main;
162 bdm->bd_index_by_bd_id = hash_create (0, sizeof (uword));
164 * create a dummy bd with bd_id of 0 and bd_index of 0 with feature set
165 * to packet drop only. Thus, packets received from any L2 interface with
166 * uninitialized bd_index of 0 can be dropped safely.
168 bd_index = bd_find_or_add_bd_index (bdm, 0);
169 ASSERT (bd_index == 0);
170 l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP;
174 VLIB_INIT_FUNCTION (l2bd_init);
178 Set the learn/forward/flood flags for the bridge domain.
179 Return 0 if ok, non-zero if for an error.
182 bd_set_flags (vlib_main_t * vm, u32 bd_index, u32 flags, u32 enable)
185 l2_bridge_domain_t *bd_config;
186 u32 feature_bitmap = 0;
188 vec_validate (l2input_main.bd_configs, bd_index);
189 bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
191 bd_validate (bd_config);
193 if (flags & L2_LEARN)
195 feature_bitmap |= L2INPUT_FEAT_LEARN;
199 feature_bitmap |= L2INPUT_FEAT_FWD;
201 if (flags & L2_FLOOD)
203 feature_bitmap |= L2INPUT_FEAT_FLOOD;
205 if (flags & L2_UU_FLOOD)
207 feature_bitmap |= L2INPUT_FEAT_UU_FLOOD;
209 if (flags & L2_ARP_TERM)
211 feature_bitmap |= L2INPUT_FEAT_ARP_TERM;
216 bd_config->feature_bitmap |= feature_bitmap;
220 bd_config->feature_bitmap &= ~feature_bitmap;
227 Set bridge-domain learn enable/disable.
229 set bridge-domain learn <bd_id> [disable]
231 static clib_error_t *
232 bd_learn (vlib_main_t * vm,
233 unformat_input_t * input, vlib_cli_command_t * cmd)
235 bd_main_t *bdm = &bd_main;
236 clib_error_t *error = 0;
241 if (!unformat (input, "%d", &bd_id))
243 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
244 format_unformat_error, input);
248 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
251 return clib_error_return (0, "No such bridge domain %d", bd_id);
256 if (unformat (input, "disable"))
261 /* set the bridge domain flag */
262 if (bd_set_flags (vm, bd_index, L2_LEARN, enable))
265 clib_error_return (0, "bridge-domain id %d out of range", bd_index);
274 VLIB_CLI_COMMAND (bd_learn_cli, static) = {
275 .path = "set bridge-domain learn",
276 .short_help = "set bridge-domain learn <bridge-domain-id> [disable]",
277 .function = bd_learn,
282 Set bridge-domain forward enable/disable.
284 set bridge-domain forward <bd_index> [disable]
286 static clib_error_t *
287 bd_fwd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
289 bd_main_t *bdm = &bd_main;
290 clib_error_t *error = 0;
295 if (!unformat (input, "%d", &bd_id))
297 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
298 format_unformat_error, input);
302 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
305 return clib_error_return (0, "No such bridge domain %d", bd_id);
310 if (unformat (input, "disable"))
315 /* set the bridge domain flag */
316 if (bd_set_flags (vm, bd_index, L2_FWD, enable))
319 clib_error_return (0, "bridge-domain id %d out of range", bd_index);
328 VLIB_CLI_COMMAND (bd_fwd_cli, static) = {
329 .path = "set bridge-domain forward",
330 .short_help = "set bridge-domain forward <bridge-domain-id> [disable]",
336 Set bridge-domain flood enable/disable.
338 set bridge-domain flood <bd_index> [disable]
340 static clib_error_t *
341 bd_flood (vlib_main_t * vm,
342 unformat_input_t * input, vlib_cli_command_t * cmd)
344 bd_main_t *bdm = &bd_main;
345 clib_error_t *error = 0;
350 if (!unformat (input, "%d", &bd_id))
352 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
353 format_unformat_error, input);
357 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
360 return clib_error_return (0, "No such bridge domain %d", bd_id);
365 if (unformat (input, "disable"))
370 /* set the bridge domain flag */
371 if (bd_set_flags (vm, bd_index, L2_FLOOD, enable))
374 clib_error_return (0, "bridge-domain id %d out of range", bd_index);
383 VLIB_CLI_COMMAND (bd_flood_cli, static) = {
384 .path = "set bridge-domain flood",
385 .short_help = "set bridge-domain flood <bridge-domain-id> [disable]",
386 .function = bd_flood,
391 Set bridge-domain unkown-unicast flood enable/disable.
393 set bridge-domain uu-flood <bd_index> [disable]
395 static clib_error_t *
396 bd_uu_flood (vlib_main_t * vm,
397 unformat_input_t * input, vlib_cli_command_t * cmd)
399 bd_main_t *bdm = &bd_main;
400 clib_error_t *error = 0;
405 if (!unformat (input, "%d", &bd_id))
407 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
408 format_unformat_error, input);
412 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
415 return clib_error_return (0, "No such bridge domain %d", bd_id);
420 if (unformat (input, "disable"))
425 /* set the bridge domain flag */
426 if (bd_set_flags (vm, bd_index, L2_UU_FLOOD, enable))
429 clib_error_return (0, "bridge-domain id %d out of range", bd_index);
438 VLIB_CLI_COMMAND (bd_uu_flood_cli, static) = {
439 .path = "set bridge-domain uu-flood",
440 .short_help = "set bridge-domain uu-flood <bridge-domain-id> [disable]",
441 .function = bd_uu_flood,
446 Set bridge-domain arp term enable/disable.
448 set bridge-domain arp term <bridge-domain-id> [disable]
450 static clib_error_t *
451 bd_arp_term (vlib_main_t * vm,
452 unformat_input_t * input, vlib_cli_command_t * cmd)
454 bd_main_t *bdm = &bd_main;
455 clib_error_t *error = 0;
460 if (!unformat (input, "%d", &bd_id))
462 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
463 format_unformat_error, input);
467 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
471 return clib_error_return (0, "No such bridge domain %d", bd_id);
474 if (unformat (input, "disable"))
477 /* set the bridge domain flag */
478 if (bd_set_flags (vm, bd_index, L2_ARP_TERM, enable))
481 clib_error_return (0, "bridge-domain id %d out of range", bd_index);
490 VLIB_CLI_COMMAND (bd_arp_term_cli, static) = {
491 .path = "set bridge-domain arp term",
492 .short_help = "set bridge-domain arp term <bridge-domain-id> [disable]",
493 .function = bd_arp_term,
499 * Add/delete IP address to MAC address mapping.
501 * The clib hash implementation stores uword entries in the hash table.
502 * The hash table mac_by_ip4 is keyed via IP4 address and store the
503 * 6-byte MAC address directly in the hash table entry uword.
505 * @warning This only works for 64-bit processor with 8-byte uword;
506 * which means this code *WILL NOT WORK* for a 32-bit prcessor with
510 bd_add_del_ip_mac (u32 bd_index,
511 u8 * ip_addr, u8 * mac_addr, u8 is_ip6, u8 is_add)
513 l2input_main_t *l2im = &l2input_main;
514 l2_bridge_domain_t *bd_cfg = l2input_bd_config_from_index (l2im, bd_index);
515 u64 new_mac = *(u64 *) mac_addr;
517 u16 *mac16 = (u16 *) & new_mac;
519 ASSERT (sizeof (uword) == sizeof (u64)); /* make sure uword is 8 bytes */
521 mac16[3] = 0; /* Clear last 2 unsed bytes of the 8-byte MAC address */
524 ip6_address_t *ip6_addr_key;
526 old_mac = (u64 *) hash_get_mem (bd_cfg->mac_by_ip6, ip_addr);
530 { /* new entry - allocate and craete ip6 address key */
531 ip6_addr_key = clib_mem_alloc (sizeof (ip6_address_t));
532 clib_memcpy (ip6_addr_key, ip_addr, sizeof (ip6_address_t));
534 else if (*old_mac == new_mac)
535 { /* same mac entry already exist for ip6 address */
539 { /* updat mac for ip6 address */
540 hp = hash_get_pair (bd_cfg->mac_by_ip6, ip_addr);
541 ip6_addr_key = (ip6_address_t *) hp->key;
543 hash_set_mem (bd_cfg->mac_by_ip6, ip6_addr_key, new_mac);
547 if (old_mac && (*old_mac == new_mac))
549 hp = hash_get_pair (bd_cfg->mac_by_ip6, ip_addr);
550 ip6_addr_key = (ip6_address_t *) hp->key;
551 hash_unset_mem (bd_cfg->mac_by_ip6, ip_addr);
552 clib_mem_free (ip6_addr_key);
560 ip4_address_t ip4_addr = *(ip4_address_t *) ip_addr;
561 old_mac = (u64 *) hash_get (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
564 if (old_mac && (*old_mac == new_mac))
565 return 0; /* mac entry already exist */
566 hash_set (bd_cfg->mac_by_ip4, ip4_addr.as_u32, new_mac);
570 if (old_mac && (*old_mac == new_mac))
571 hash_unset (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
580 Set bridge-domain arp entry add/delete.
582 set bridge-domain arp entry <bd-id> <ip-addr> <mac-addr> [del]
584 static clib_error_t *
585 bd_arp_entry (vlib_main_t * vm,
586 unformat_input_t * input, vlib_cli_command_t * cmd)
588 bd_main_t *bdm = &bd_main;
589 clib_error_t *error = 0;
597 if (!unformat (input, "%d", &bd_id))
599 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
600 format_unformat_error, input);
604 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
609 return clib_error_return (0, "No such bridge domain %d", bd_id);
611 if (unformat (input, "%U", unformat_ip4_address, ip_addr))
615 else if (unformat (input, "%U", unformat_ip6_address, ip_addr))
621 error = clib_error_return (0, "expecting IP address but got `%U'",
622 format_unformat_error, input);
626 if (!unformat (input, "%U", unformat_ethernet_address, mac_addr))
628 error = clib_error_return (0, "expecting MAC address but got `%U'",
629 format_unformat_error, input);
633 if (unformat (input, "del"))
638 /* set the bridge domain flagAdd IP-MAC entry into bridge domain */
639 if (bd_add_del_ip_mac (bd_index, ip_addr, mac_addr, is_ip6, is_add))
641 error = clib_error_return (0, "MAC %s for IP %U and MAC %U failed",
642 is_add ? "add" : "del",
644 format_ip4_address : format_ip6_address,
645 ip_addr, format_ethernet_address, mac_addr);
653 VLIB_CLI_COMMAND (bd_arp_entry_cli, static) = {
654 .path = "set bridge-domain arp entry",
655 .short_help = "set bridge-domain arp entry <bd-id> <ip-addr> <mac-addr> [del]",
656 .function = bd_arp_entry,
661 format_vtr (u8 * s, va_list * args)
663 u32 vtr_op = va_arg (*args, u32);
664 u32 dot1q = va_arg (*args, u32);
665 u32 tag1 = va_arg (*args, u32);
666 u32 tag2 = va_arg (*args, u32);
669 case L2_VTR_DISABLED:
670 return format (s, "none");
672 return format (s, "push-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
674 return format (s, "push-2 %s %d %d", dot1q ? "dot1q" : "dot1ad", tag1,
677 return format (s, "pop-1");
679 return format (s, "pop-2");
680 case L2_VTR_TRANSLATE_1_1:
681 return format (s, "trans-1-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
682 case L2_VTR_TRANSLATE_1_2:
683 return format (s, "trans-1-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
685 case L2_VTR_TRANSLATE_2_1:
686 return format (s, "trans-2-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
687 case L2_VTR_TRANSLATE_2_2:
688 return format (s, "trans-2-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
691 return format (s, "none");
696 Show bridge-domain state.
698 show bridge-domain [<bd_index>]
700 static clib_error_t *
701 bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
703 vnet_main_t *vnm = vnet_get_main ();
704 bd_main_t *bdm = &bd_main;
705 clib_error_t *error = 0;
707 l2_bridge_domain_t *bd_config;
717 end = vec_len (l2input_main.bd_configs);
719 if (unformat (input, "%d", &bd_id))
721 if (unformat (input, "detail"))
723 else if (unformat (input, "det"))
725 if (unformat (input, "int"))
727 if (unformat (input, "arp"))
730 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
734 return clib_error_return (0, "No such bridge domain %d", bd_id);
736 vec_validate (l2input_main.bd_configs, bd_index);
737 bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
738 if (bd_is_valid (bd_config))
745 vlib_cli_output (vm, "bridge-domain %d not in use", bd_id);
750 /* Show all bridge-domains that have been initialized */
752 for (bd_index = start; bd_index < end; bd_index++)
754 bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
755 if (bd_is_valid (bd_config))
761 "%=5s %=7s %=10s %=10s %=10s %=10s %=10s %=14s",
762 "ID", "Index", "Learning", "U-Forwrd",
763 "UU-Flood", "Flooding", "ARP-Term",
768 "%=5d %=7d %=10s %=10s %=10s %=10s %=10s %=14U",
769 bd_config->bd_id, bd_index,
770 bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ?
772 bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on"
774 bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD ?
776 bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ?
778 bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ?
779 "on" : "off", format_vnet_sw_if_index_name_with_NA,
780 vnm, bd_config->bvi_sw_if_index);
784 /* Show all member interfaces */
786 l2_flood_member_t *member;
789 vec_foreach (member, bd_config->members)
791 u32 vtr_opr, dot1q, tag1, tag2;
795 vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=30s",
796 "Interface", "Index", "SHG", "BVI",
799 l2vtr_get (vm, vnm, member->sw_if_index, &vtr_opr, &dot1q,
801 vlib_cli_output (vm, "%=30U%=7d%=5d%=5s%=30U",
802 format_vnet_sw_if_index_name, vnm,
803 member->sw_if_index, member->sw_if_index,
805 member->flags & L2_FLOOD_MEMBER_BVI ? "*" :
806 "-", format_vtr, vtr_opr, dot1q, tag1, tag2);
810 if ((detail || arp) &&
811 (bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM))
814 ip6_address_t *ip6_addr;
817 "\n IP4/IP6 to MAC table for ARP Termination");
820 hash_foreach (ip4_addr, mac_addr, bd_config->mac_by_ip4,
822 vlib_cli_output (vm, "%=40U => %=20U",
823 format_ip4_address, &ip4_addr,
824 format_ethernet_address, &mac_addr);
827 hash_foreach_mem (ip6_addr, mac_addr, bd_config->mac_by_ip6,
829 vlib_cli_output (vm, "%=40U => %=20U",
830 format_ip6_address, ip6_addr,
831 format_ethernet_address, &mac_addr);
840 vlib_cli_output (vm, "no bridge-domains in use");
848 VLIB_CLI_COMMAND (bd_show_cli, static) = {
849 .path = "show bridge-domain",
850 .short_help = "show bridge-domain [bridge-domain-id [detail|int|arp]]",
856 * fd.io coding-style-patch-verification: ON
859 * eval: (c-set-style "gnu")