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>
37 // Init bridge domain if not done already
38 // For feature bitmap, set all bits except ARP termination
40 bd_validate (l2_bridge_domain_t * bd_config)
42 if (!bd_is_valid (bd_config)) {
43 bd_config->feature_bitmap = ~L2INPUT_FEAT_ARP_TERM;
44 bd_config->bvi_sw_if_index = ~0;
45 bd_config->members = 0;
46 bd_config->mac_by_ip4 = 0;
47 // bd_config->mac_by_ip6 = hash_create_mem (0, sizeof(ip6_address_t),
52 u32 bd_find_or_add_bd_index (bd_main_t * bdm, u32 bd_id)
59 while (hash_get (bdm->bd_index_by_bd_id, bd_id))
62 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
67 rv = clib_bitmap_first_clear (bdm->bd_index_bitmap);
69 // mark this index busy
70 bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1);
72 hash_set (bdm->bd_index_by_bd_id, bd_id, rv);
74 vec_validate (l2input_main.bd_configs, rv);
75 l2input_main.bd_configs[rv].bd_id = bd_id;
80 int bd_delete_bd_index (bd_main_t * bdm, u32 bd_id)
85 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
91 // mark this index clear
92 bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0);
93 hash_unset (bdm->bd_index_by_bd_id, bd_id);
95 l2input_main.bd_configs[bd_index].bd_id = ~0;
96 l2input_main.bd_configs[bd_index].feature_bitmap = 0;
102 bd_add_member (l2_bridge_domain_t * bd_config,
103 l2_flood_member_t * member)
105 // Add one element to the vector
107 // When flooding, the bvi interface (if present) must be the last member
108 // processed due to how BVI processing can change the packet. To enable
109 // this order, we make the bvi interface the first in the vector and
110 // flooding walks the vector in reverse.
111 if ((member->flags == L2_FLOOD_MEMBER_NORMAL) ||
112 (vec_len(bd_config->members) == 0)) {
113 vec_add1 (bd_config->members, *member);
116 // Move 0th element to the end
117 vec_add1 (bd_config->members, bd_config->members[0]);
118 bd_config->members[0] = *member;
123 #define BD_REMOVE_ERROR_OK 0
124 #define BD_REMOVE_ERROR_NOT_FOUND 1
127 bd_remove_member (l2_bridge_domain_t * bd_config,
132 // Find and delete the member
133 vec_foreach_index(ix, bd_config->members) {
134 if (vec_elt(bd_config->members, ix).sw_if_index == sw_if_index) {
135 vec_del1 (bd_config->members, ix);
136 return BD_REMOVE_ERROR_OK;
140 return BD_REMOVE_ERROR_NOT_FOUND;
144 clib_error_t *l2bd_init (vlib_main_t *vm)
146 bd_main_t *bdm = &bd_main;
148 bdm->bd_index_by_bd_id = hash_create (0, sizeof(uword));
149 // create a dummy bd with bd_id of 0 and bd_index of 0 with feature set
150 // to packet drop only. Thus, packets received from any L2 interface with
151 // uninitialized bd_index of 0 can be dropped safely.
152 bd_index = bd_find_or_add_bd_index (bdm, 0);
153 ASSERT (bd_index == 0);
154 l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP;
158 VLIB_INIT_FUNCTION (l2bd_init);
161 // Set the learn/forward/flood flags for the bridge domain
162 // Return 0 if ok, non-zero if for an error.
164 bd_set_flags (vlib_main_t * vm,
169 l2_bridge_domain_t * bd_config;
170 u32 feature_bitmap = 0;
172 vec_validate (l2input_main.bd_configs, bd_index);
173 bd_config = vec_elt_at_index(l2input_main.bd_configs, bd_index);
175 bd_validate (bd_config);
177 if (flags & L2_LEARN) {
178 feature_bitmap |= L2INPUT_FEAT_LEARN;
180 if (flags & L2_FWD) {
181 feature_bitmap |= L2INPUT_FEAT_FWD;
183 if (flags & L2_FLOOD) {
184 feature_bitmap |= L2INPUT_FEAT_FLOOD;
186 if (flags & L2_UU_FLOOD) {
187 feature_bitmap |= L2INPUT_FEAT_UU_FLOOD;
189 if (flags & L2_ARP_TERM) {
190 feature_bitmap |= L2INPUT_FEAT_ARP_TERM;
194 bd_config->feature_bitmap |= feature_bitmap;
196 bd_config->feature_bitmap &= ~feature_bitmap;
202 // set bridge-domain learn enable/disable
203 // The CLI format is:
204 // set bridge-domain learn <bd_id> [disable]
205 static clib_error_t *
206 bd_learn (vlib_main_t * vm,
207 unformat_input_t * input,
208 vlib_cli_command_t * cmd)
210 bd_main_t * bdm = &bd_main;
211 clib_error_t * error = 0;
216 if (! unformat (input, "%d", &bd_id))
218 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
219 format_unformat_error, input);
223 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
226 return clib_error_return (0, "No such bridge domain %d", bd_id);
231 if (unformat (input, "disable")) {
235 // set the bridge domain flag
236 if (bd_set_flags(vm, bd_index, L2_LEARN, enable)) {
237 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
245 VLIB_CLI_COMMAND (bd_learn_cli, static) = {
246 .path = "set bridge-domain learn",
247 .short_help = "set bridge-domain learn <bridge-domain-id> [disable]",
248 .function = bd_learn,
251 // set bridge-domain forward enable/disable
252 // The CLI format is:
253 // set bridge-domain forward <bd_index> [disable]
254 static clib_error_t *
255 bd_fwd (vlib_main_t * vm,
256 unformat_input_t * input,
257 vlib_cli_command_t * cmd)
259 bd_main_t * bdm = &bd_main;
260 clib_error_t * error = 0;
265 if (! unformat (input, "%d", &bd_id))
267 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
268 format_unformat_error, input);
272 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
275 return clib_error_return (0, "No such bridge domain %d", bd_id);
280 if (unformat (input, "disable")) {
284 // set the bridge domain flag
285 if (bd_set_flags(vm, bd_index, L2_FWD, enable)) {
286 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
294 VLIB_CLI_COMMAND (bd_fwd_cli, static) = {
295 .path = "set bridge-domain forward",
296 .short_help = "set bridge-domain forward <bridge-domain-id> [disable]",
300 // set bridge-domain flood enable/disable
301 // The CLI format is:
302 // set bridge-domain flood <bd_index> [disable]
303 static clib_error_t *
304 bd_flood (vlib_main_t * vm,
305 unformat_input_t * input,
306 vlib_cli_command_t * cmd)
308 bd_main_t * bdm = &bd_main;
309 clib_error_t * error = 0;
314 if (! unformat (input, "%d", &bd_id))
316 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
317 format_unformat_error, input);
321 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
324 return clib_error_return (0, "No such bridge domain %d", bd_id);
329 if (unformat (input, "disable")) {
333 // set the bridge domain flag
334 if (bd_set_flags(vm, bd_index, L2_FLOOD, enable)) {
335 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
343 VLIB_CLI_COMMAND (bd_flood_cli, static) = {
344 .path = "set bridge-domain flood",
345 .short_help = "set bridge-domain flood <bridge-domain-id> [disable]",
346 .function = bd_flood,
349 // set bridge-domain unkown-unicast flood enable/disable
350 // The CLI format is:
351 // set bridge-domain uu-flood <bd_index> [disable]
352 static clib_error_t *
353 bd_uu_flood (vlib_main_t * vm,
354 unformat_input_t * input,
355 vlib_cli_command_t * cmd)
357 bd_main_t * bdm = &bd_main;
358 clib_error_t * error = 0;
363 if (! unformat (input, "%d", &bd_id))
365 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
366 format_unformat_error, input);
370 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
373 return clib_error_return (0, "No such bridge domain %d", bd_id);
378 if (unformat (input, "disable")) {
382 // set the bridge domain flag
383 if (bd_set_flags(vm, bd_index, L2_UU_FLOOD, enable)) {
384 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
392 VLIB_CLI_COMMAND (bd_uu_flood_cli, static) = {
393 .path = "set bridge-domain uu-flood",
394 .short_help = "set bridge-domain uu-flood <bridge-domain-id> [disable]",
395 .function = bd_uu_flood,
398 // set bridge-domain arp term enable/disable
399 // The CLI format is:
400 // set bridge-domain arp term <bridge-domain-id> [disable]
401 static clib_error_t *
402 bd_arp_term (vlib_main_t * vm,
403 unformat_input_t * input,
404 vlib_cli_command_t * cmd)
406 bd_main_t * bdm = &bd_main;
407 clib_error_t * error = 0;
412 if (! unformat (input, "%d", &bd_id)) {
413 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
414 format_unformat_error, input);
418 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
419 if (p) bd_index = *p;
420 else return clib_error_return (0, "No such bridge domain %d", bd_id);
423 if (unformat (input, "disable")) enable = 0;
425 // set the bridge domain flag
426 if (bd_set_flags(vm, bd_index, L2_ARP_TERM, enable)) {
427 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
435 VLIB_CLI_COMMAND (bd_arp_term_cli, static) = {
436 .path = "set bridge-domain arp term",
437 .short_help = "set bridge-domain arp term <bridge-domain-id> [disable]",
438 .function = bd_arp_term,
442 // The clib hash implementation stores uword entries in the hash table.
443 // The hash table mac_by_ip4 is keyed via IP4 address and store the
444 // 6-byte MAC address directly in the hash table entry uword.
445 // This only works for 64-bit processor with 8-byte uword; which means
446 // this code *WILL NOT WORK* for a 32-bit prcessor with 4-byte uword.
447 u32 bd_add_del_ip_mac(u32 bd_index,
453 l2input_main_t * l2im = &l2input_main;
454 l2_bridge_domain_t * bd_cfg = l2input_bd_config_from_index (l2im, bd_index);
455 u64 new_mac = *(u64 *) mac_addr;
457 u16 * mac16 = (u16 *) &new_mac;
459 ASSERT (sizeof(uword) == sizeof(u64)); // make sure uword is 8 bytes
461 mac16[3] = 0; // Clear last 2 unsed bytes of the 8-byte MAC address
463 // ip6_address_t ip6_addr = *(ip6_address_t *) ip_addr;
464 return 1; // not yet implemented
466 ip4_address_t ip4_addr = *(ip4_address_t *) ip_addr;
467 old_mac = (u64 *) hash_get (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
469 if (old_mac && (*old_mac == new_mac)) return 0; // mac entry already exist
470 hash_set (bd_cfg->mac_by_ip4, ip4_addr.as_u32, new_mac);
472 if (old_mac && (*old_mac == new_mac)) { // mac entry match
473 hash_unset (bd_cfg->mac_by_ip4, ip4_addr.as_u32); // clear entry
482 // set bridge-domain arp entry add/delete
483 // The CLI format is:
484 // set bridge-domain arp entry <bd-id> <ip-addr> <mac-addr> [del]
485 static clib_error_t *
486 bd_arp_entry (vlib_main_t * vm,
487 unformat_input_t * input,
488 vlib_cli_command_t * cmd)
490 bd_main_t * bdm = &bd_main;
491 clib_error_t * error = 0;
499 if (! unformat (input, "%d", &bd_id)) {
500 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
501 format_unformat_error, input);
505 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
507 if (p) bd_index = *p;
508 else return clib_error_return (0, "No such bridge domain %d", bd_id);
510 if (unformat (input, "%U", unformat_ip4_address, ip_addr)) {
512 } else if (unformat (input, "%U", unformat_ip6_address, ip_addr)) {
515 error = clib_error_return (0, "expecting IP address but got `%U'",
516 format_unformat_error, input);
520 if (!unformat(input, "%U", unformat_ethernet_address, mac_addr)) {
521 error = clib_error_return (0, "expecting MAC address but got `%U'",
522 format_unformat_error, input);
526 if (unformat (input, "del")) {
530 // set the bridge domain flagAdd IP-MAC entry into bridge domain
531 if (bd_add_del_ip_mac(bd_index, ip_addr, mac_addr, is_ip6, is_add)) {
532 error = clib_error_return (0, "MAC %s for IP %U and MAC %U failed",
533 is_add ? "add" : "del",
534 format_ip4_address, ip_addr,
535 format_ethernet_address, mac_addr);
542 VLIB_CLI_COMMAND (bd_arp_entry_cli, static) = {
543 .path = "set bridge-domain arp entry",
544 .short_help = "set bridge-domain arp entry <bd-id> <ip-addr> <mac-addr> [del]",
545 .function = bd_arp_entry,
548 u8* format_vtr(u8 * s, va_list *args)
550 u32 vtr_op = va_arg (*args, u32);
551 u32 dot1q = va_arg (*args, u32);
552 u32 tag1 = va_arg (*args, u32);
553 u32 tag2 = va_arg (*args, u32);
555 case L2_VTR_DISABLED:
556 return format (s, "none");
558 return format (s, "push-1 %s %d", dot1q? "dot1q":"dot1ad", tag1);
560 return format (s, "push-2 %s %d %d", dot1q? "dot1q":"dot1ad", tag1, tag2);
562 return format (s, "pop-1");
564 return format (s, "pop-2");
565 case L2_VTR_TRANSLATE_1_1:
566 return format (s, "trans-1-1 %s %d", dot1q? "dot1q":"dot1ad", tag1);
567 case L2_VTR_TRANSLATE_1_2:
568 return format (s, "trans-1-2 %s %d %d",dot1q? "dot1q":"dot1ad", tag1, tag2);
569 case L2_VTR_TRANSLATE_2_1:
570 return format (s, "trans-2-1 %s %d", dot1q? "dot1q":"dot1ad", tag1);
571 case L2_VTR_TRANSLATE_2_2:
572 return format (s, "trans-2-2 %s %d %d", dot1q? "dot1q":"dot1ad", tag1, tag2);
574 return format (s, "none");
578 // show bridge-domain state
579 // The CLI format is:
580 // show bridge-domain [<bd_index>]
581 static clib_error_t *
582 bd_show (vlib_main_t * vm,
583 unformat_input_t * input,
584 vlib_cli_command_t * cmd)
586 vnet_main_t * vnm = vnet_get_main();
587 bd_main_t * bdm = &bd_main;
588 clib_error_t * error = 0;
590 l2_bridge_domain_t * bd_config;
600 end = vec_len(l2input_main.bd_configs);
602 if (unformat (input, "%d", &bd_id)) {
603 if (unformat (input, "detail")) detail = 1;
604 else if (unformat (input, "det")) detail = 1;
605 if (unformat (input, "int")) intf = 1;
606 if (unformat (input, "arp")) arp = 1;
608 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
609 if (p) bd_index = *p;
610 else return clib_error_return (0, "No such bridge domain %d", bd_id);
612 vec_validate (l2input_main.bd_configs, bd_index);
613 bd_config = vec_elt_at_index(l2input_main.bd_configs, bd_index);
614 if (bd_is_valid (bd_config)) {
618 vlib_cli_output (vm, "bridge-domain %d not in use", bd_id);
623 // Show all bridge-domains that have been initialized
626 for (bd_index=start; bd_index<end; bd_index++) {
627 bd_config = vec_elt_at_index(l2input_main.bd_configs, bd_index);
628 if (bd_is_valid(bd_config)) {
631 vlib_cli_output (vm, "%=5s %=7s %=10s %=10s %=10s %=10s %=10s %=14s",
643 vm, "%=5d %=7d %=10s %=10s %=10s %=10s %=10s %=14U",
644 bd_config->bd_id, bd_index,
645 bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? "on" : "off",
646 bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on" : "off",
647 bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD ? "on" : "off",
648 bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? "on" : "off",
649 bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? "on" : "off",
650 format_vnet_sw_if_index_name_with_NA, vnm, bd_config->bvi_sw_if_index);
652 if (detail || intf) {
653 // Show all member interfaces
655 l2_flood_member_t * member;
658 vec_foreach(member, bd_config->members) {
659 u32 vtr_opr, dot1q, tag1, tag2;
662 vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=30s",
663 "Interface", "Index", "SHG", "BVI","VLAN-Tag-Rewrite");
665 l2vtr_get(vm, vnm, member->sw_if_index, &vtr_opr, &dot1q, &tag1, &tag2);
666 vlib_cli_output (vm, "%=30U%=7d%=5d%=5s%=30U",
667 format_vnet_sw_if_index_name, vnm, member->sw_if_index,
670 member->flags & L2_FLOOD_MEMBER_BVI ? "*" : "-",
671 format_vtr, vtr_opr, dot1q, tag1, tag2);
675 if ((detail || arp) &&
676 (bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM)) {
679 vlib_cli_output (vm, "\n IP4 to MAC table for ARP Termination");
680 hash_foreach (ip4_addr, mac_addr, bd_config->mac_by_ip4, ({
681 vlib_cli_output (vm, "%=20U => %=20U",
682 format_ip4_address, &ip4_addr,
683 format_ethernet_address, &mac_addr);
690 vlib_cli_output (vm, "no bridge-domains in use");
697 VLIB_CLI_COMMAND (bd_show_cli, static) = {
698 .path = "show bridge-domain",
699 .short_help = "show bridge-domain [bridge-domain-id [detail|int|arp]]",