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)
57 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
61 rv = clib_bitmap_first_clear (bdm->bd_index_bitmap);
63 // mark this index busy
64 bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1);
66 hash_set (bdm->bd_index_by_bd_id, bd_id, rv);
68 vec_validate (l2input_main.bd_configs, rv);
69 l2input_main.bd_configs[rv].bd_id = bd_id;
74 int bd_delete_bd_index (bd_main_t * bdm, u32 bd_id)
79 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
85 // mark this index clear
86 bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0);
87 hash_unset (bdm->bd_index_by_bd_id, bd_id);
89 l2input_main.bd_configs[bd_index].bd_id = ~0;
90 l2input_main.bd_configs[bd_index].feature_bitmap = 0;
96 bd_add_member (l2_bridge_domain_t * bd_config,
97 l2_flood_member_t * member)
99 // Add one element to the vector
101 // When flooding, the bvi interface (if present) must be the last member
102 // processed due to how BVI processing can change the packet. To enable
103 // this order, we make the bvi interface the first in the vector and
104 // flooding walks the vector in reverse.
105 if ((member->flags == L2_FLOOD_MEMBER_NORMAL) ||
106 (vec_len(bd_config->members) == 0)) {
107 vec_add1 (bd_config->members, *member);
110 // Move 0th element to the end
111 vec_add1 (bd_config->members, bd_config->members[0]);
112 bd_config->members[0] = *member;
117 #define BD_REMOVE_ERROR_OK 0
118 #define BD_REMOVE_ERROR_NOT_FOUND 1
121 bd_remove_member (l2_bridge_domain_t * bd_config,
126 // Find and delete the member
127 vec_foreach_index(ix, bd_config->members) {
128 if (vec_elt(bd_config->members, ix).sw_if_index == sw_if_index) {
129 vec_del1 (bd_config->members, ix);
130 return BD_REMOVE_ERROR_OK;
134 return BD_REMOVE_ERROR_NOT_FOUND;
138 clib_error_t *l2bd_init (vlib_main_t *vm)
140 bd_main_t *bdm = &bd_main;
142 bdm->bd_index_by_bd_id = hash_create (0, sizeof(uword));
143 // create a dummy bd with bd_id of 0 and bd_index of 0 with feature set
144 // to packet drop only. Thus, packets received from any L2 interface with
145 // uninitialized bd_index of 0 can be dropped safely.
146 bd_index = bd_find_or_add_bd_index (bdm, 0);
147 ASSERT (bd_index == 0);
148 l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP;
152 VLIB_INIT_FUNCTION (l2bd_init);
155 // Set the learn/forward/flood flags for the bridge domain
156 // Return 0 if ok, non-zero if for an error.
158 bd_set_flags (vlib_main_t * vm,
163 l2_bridge_domain_t * bd_config;
164 u32 feature_bitmap = 0;
166 vec_validate (l2input_main.bd_configs, bd_index);
167 bd_config = vec_elt_at_index(l2input_main.bd_configs, bd_index);
169 bd_validate (bd_config);
171 if (flags & L2_LEARN) {
172 feature_bitmap |= L2INPUT_FEAT_LEARN;
174 if (flags & L2_FWD) {
175 feature_bitmap |= L2INPUT_FEAT_FWD;
177 if (flags & L2_FLOOD) {
178 feature_bitmap |= L2INPUT_FEAT_FLOOD;
180 if (flags & L2_UU_FLOOD) {
181 feature_bitmap |= L2INPUT_FEAT_UU_FLOOD;
183 if (flags & L2_ARP_TERM) {
184 feature_bitmap |= L2INPUT_FEAT_ARP_TERM;
188 bd_config->feature_bitmap |= feature_bitmap;
190 bd_config->feature_bitmap &= ~feature_bitmap;
196 // set bridge-domain learn enable/disable
197 // The CLI format is:
198 // set bridge-domain learn <bd_id> [disable]
199 static clib_error_t *
200 bd_learn (vlib_main_t * vm,
201 unformat_input_t * input,
202 vlib_cli_command_t * cmd)
204 bd_main_t * bdm = &bd_main;
205 clib_error_t * error = 0;
210 if (! unformat (input, "%d", &bd_id))
212 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
213 format_unformat_error, input);
217 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
220 return clib_error_return (0, "No such bridge domain %d", bd_id);
225 if (unformat (input, "disable")) {
229 // set the bridge domain flag
230 if (bd_set_flags(vm, bd_index, L2_LEARN, enable)) {
231 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
239 VLIB_CLI_COMMAND (bd_learn_cli, static) = {
240 .path = "set bridge-domain learn",
241 .short_help = "set bridge-domain learn <bridge-domain-id> [disable]",
242 .function = bd_learn,
245 // set bridge-domain forward enable/disable
246 // The CLI format is:
247 // set bridge-domain forward <bd_index> [disable]
248 static clib_error_t *
249 bd_fwd (vlib_main_t * vm,
250 unformat_input_t * input,
251 vlib_cli_command_t * cmd)
253 bd_main_t * bdm = &bd_main;
254 clib_error_t * error = 0;
259 if (! unformat (input, "%d", &bd_id))
261 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
262 format_unformat_error, input);
266 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
269 return clib_error_return (0, "No such bridge domain %d", bd_id);
274 if (unformat (input, "disable")) {
278 // set the bridge domain flag
279 if (bd_set_flags(vm, bd_index, L2_FWD, enable)) {
280 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
288 VLIB_CLI_COMMAND (bd_fwd_cli, static) = {
289 .path = "set bridge-domain forward",
290 .short_help = "set bridge-domain forward <bridge-domain-id> [disable]",
294 // set bridge-domain flood enable/disable
295 // The CLI format is:
296 // set bridge-domain flood <bd_index> [disable]
297 static clib_error_t *
298 bd_flood (vlib_main_t * vm,
299 unformat_input_t * input,
300 vlib_cli_command_t * cmd)
302 bd_main_t * bdm = &bd_main;
303 clib_error_t * error = 0;
308 if (! unformat (input, "%d", &bd_id))
310 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
311 format_unformat_error, input);
315 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
318 return clib_error_return (0, "No such bridge domain %d", bd_id);
323 if (unformat (input, "disable")) {
327 // set the bridge domain flag
328 if (bd_set_flags(vm, bd_index, L2_FLOOD, enable)) {
329 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
337 VLIB_CLI_COMMAND (bd_flood_cli, static) = {
338 .path = "set bridge-domain flood",
339 .short_help = "set bridge-domain flood <bridge-domain-id> [disable]",
340 .function = bd_flood,
343 // set bridge-domain unkown-unicast flood enable/disable
344 // The CLI format is:
345 // set bridge-domain uu-flood <bd_index> [disable]
346 static clib_error_t *
347 bd_uu_flood (vlib_main_t * vm,
348 unformat_input_t * input,
349 vlib_cli_command_t * cmd)
351 bd_main_t * bdm = &bd_main;
352 clib_error_t * error = 0;
357 if (! unformat (input, "%d", &bd_id))
359 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
360 format_unformat_error, input);
364 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
367 return clib_error_return (0, "No such bridge domain %d", bd_id);
372 if (unformat (input, "disable")) {
376 // set the bridge domain flag
377 if (bd_set_flags(vm, bd_index, L2_UU_FLOOD, enable)) {
378 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
386 VLIB_CLI_COMMAND (bd_uu_flood_cli, static) = {
387 .path = "set bridge-domain uu-flood",
388 .short_help = "set bridge-domain uu-flood <bridge-domain-id> [disable]",
389 .function = bd_uu_flood,
392 // set bridge-domain arp term enable/disable
393 // The CLI format is:
394 // set bridge-domain arp term <bridge-domain-id> [disable]
395 static clib_error_t *
396 bd_arp_term (vlib_main_t * vm,
397 unformat_input_t * input,
398 vlib_cli_command_t * cmd)
400 bd_main_t * bdm = &bd_main;
401 clib_error_t * error = 0;
406 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);
413 if (p) bd_index = *p;
414 else return clib_error_return (0, "No such bridge domain %d", bd_id);
417 if (unformat (input, "disable")) enable = 0;
419 // set the bridge domain flag
420 if (bd_set_flags(vm, bd_index, L2_ARP_TERM, enable)) {
421 error = clib_error_return (0, "bridge-domain id %d out of range", bd_index);
429 VLIB_CLI_COMMAND (bd_arp_term_cli, static) = {
430 .path = "set bridge-domain arp term",
431 .short_help = "set bridge-domain arp term <bridge-domain-id> [disable]",
432 .function = bd_arp_term,
436 // The clib hash implementation stores uword entries in the hash table.
437 // The hash table mac_by_ip4 is keyed via IP4 address and store the
438 // 6-byte MAC address directly in the hash table entry uword.
439 // This only works for 64-bit processor with 8-byte uword; which means
440 // this code *WILL NOT WORK* for a 32-bit prcessor with 4-byte uword.
441 u32 bd_add_del_ip_mac(u32 bd_index,
447 l2input_main_t * l2im = &l2input_main;
448 l2_bridge_domain_t * bd_cfg = l2input_bd_config_from_index (l2im, bd_index);
449 u64 new_mac = *(u64 *) mac_addr;
451 u16 * mac16 = (u16 *) &new_mac;
453 ASSERT (sizeof(uword) == sizeof(u64)); // make sure uword is 8 bytes
455 mac16[3] = 0; // Clear last 2 unsed bytes of the 8-byte MAC address
457 // ip6_address_t ip6_addr = *(ip6_address_t *) ip_addr;
458 return 1; // not yet implemented
460 ip4_address_t ip4_addr = *(ip4_address_t *) ip_addr;
461 old_mac = (u64 *) hash_get (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
463 if (old_mac && (*old_mac == new_mac)) return 0; // mac entry already exist
464 hash_set (bd_cfg->mac_by_ip4, ip4_addr.as_u32, new_mac);
466 if (old_mac && (*old_mac == new_mac)) { // mac entry match
467 hash_unset (bd_cfg->mac_by_ip4, ip4_addr.as_u32); // clear entry
476 // set bridge-domain arp entry add/delete
477 // The CLI format is:
478 // set bridge-domain arp entry <bd-id> <ip-addr> <mac-addr> [del]
479 static clib_error_t *
480 bd_arp_entry (vlib_main_t * vm,
481 unformat_input_t * input,
482 vlib_cli_command_t * cmd)
484 bd_main_t * bdm = &bd_main;
485 clib_error_t * error = 0;
493 if (! unformat (input, "%d", &bd_id)) {
494 error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
495 format_unformat_error, input);
499 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
501 if (p) bd_index = *p;
502 else return clib_error_return (0, "No such bridge domain %d", bd_id);
504 if (unformat (input, "%U", unformat_ip4_address, ip_addr)) {
506 } else if (unformat (input, "%U", unformat_ip6_address, ip_addr)) {
509 error = clib_error_return (0, "expecting IP address but got `%U'",
510 format_unformat_error, input);
514 if (!unformat(input, "%U", unformat_ethernet_address, mac_addr)) {
515 error = clib_error_return (0, "expecting MAC address but got `%U'",
516 format_unformat_error, input);
520 if (unformat (input, "del")) {
524 // set the bridge domain flagAdd IP-MAC entry into bridge domain
525 if (bd_add_del_ip_mac(bd_index, ip_addr, mac_addr, is_ip6, is_add)) {
526 error = clib_error_return (0, "MAC %s for IP %U and MAC %U failed",
527 is_add ? "add" : "del",
528 format_ip4_address, ip_addr,
529 format_ethernet_address, mac_addr);
536 VLIB_CLI_COMMAND (bd_arp_entry_cli, static) = {
537 .path = "set bridge-domain arp entry",
538 .short_help = "set bridge-domain arp entry <bd-id> <ip-addr> <mac-addr> [del]",
539 .function = bd_arp_entry,
542 u8* format_vtr(u8 * s, va_list *args)
544 u32 vtr_op = va_arg (*args, u32);
545 u32 dot1q = va_arg (*args, u32);
546 u32 tag1 = va_arg (*args, u32);
547 u32 tag2 = va_arg (*args, u32);
549 case L2_VTR_DISABLED:
550 return format (s, "none");
552 return format (s, "push-1 %s %d", dot1q? "dot1q":"dot1ad", tag1);
554 return format (s, "push-2 %s %d %d", dot1q? "dot1q":"dot1ad", tag1, tag2);
556 return format (s, "pop-1");
558 return format (s, "pop-2");
559 case L2_VTR_TRANSLATE_1_1:
560 return format (s, "trans-1-1 %s %d", dot1q? "dot1q":"dot1ad", tag1);
561 case L2_VTR_TRANSLATE_1_2:
562 return format (s, "trans-1-2 %s %d %d",dot1q? "dot1q":"dot1ad", tag1, tag2);
563 case L2_VTR_TRANSLATE_2_1:
564 return format (s, "trans-2-1 %s %d", dot1q? "dot1q":"dot1ad", tag1);
565 case L2_VTR_TRANSLATE_2_2:
566 return format (s, "trans-2-2 %s %d %d", dot1q? "dot1q":"dot1ad", tag1, tag2);
568 return format (s, "none");
572 // show bridge-domain state
573 // The CLI format is:
574 // show bridge-domain [<bd_index>]
575 static clib_error_t *
576 bd_show (vlib_main_t * vm,
577 unformat_input_t * input,
578 vlib_cli_command_t * cmd)
580 vnet_main_t * vnm = vnet_get_main();
581 bd_main_t * bdm = &bd_main;
582 clib_error_t * error = 0;
584 l2_bridge_domain_t * bd_config;
594 end = vec_len(l2input_main.bd_configs);
596 if (unformat (input, "%d", &bd_id)) {
597 if (unformat (input, "detail")) detail = 1;
598 else if (unformat (input, "det")) detail = 1;
599 if (unformat (input, "int")) intf = 1;
600 if (unformat (input, "arp")) arp = 1;
602 p = hash_get (bdm->bd_index_by_bd_id, bd_id);
603 if (p) bd_index = *p;
604 else return clib_error_return (0, "No such bridge domain %d", bd_id);
606 vec_validate (l2input_main.bd_configs, bd_index);
607 bd_config = vec_elt_at_index(l2input_main.bd_configs, bd_index);
608 if (bd_is_valid (bd_config)) {
612 vlib_cli_output (vm, "bridge-domain %d not in use", bd_id);
617 // Show all bridge-domains that have been initialized
620 for (bd_index=start; bd_index<end; bd_index++) {
621 bd_config = vec_elt_at_index(l2input_main.bd_configs, bd_index);
622 if (bd_is_valid(bd_config)) {
625 vlib_cli_output (vm, "%=5s %=7s %=10s %=10s %=10s %=10s %=10s %=14s",
637 vm, "%=5d %=7d %=10s %=10s %=10s %=10s %=10s %=14U",
638 bd_config->bd_id, bd_index,
639 bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? "on" : "off",
640 bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on" : "off",
641 bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD ? "on" : "off",
642 bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? "on" : "off",
643 bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? "on" : "off",
644 format_vnet_sw_if_index_name_with_NA, vnm, bd_config->bvi_sw_if_index);
646 if (detail || intf) {
647 // Show all member interfaces
649 l2_flood_member_t * member;
652 vec_foreach(member, bd_config->members) {
653 u32 vtr_opr, dot1q, tag1, tag2;
656 vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=30s",
657 "Interface", "Index", "SHG", "BVI","VLAN-Tag-Rewrite");
659 l2vtr_get(vm, vnm, member->sw_if_index, &vtr_opr, &dot1q, &tag1, &tag2);
660 vlib_cli_output (vm, "%=30U%=7d%=5d%=5s%=30U",
661 format_vnet_sw_if_index_name, vnm, member->sw_if_index,
664 member->flags & L2_FLOOD_MEMBER_BVI ? "*" : "-",
665 format_vtr, vtr_opr, dot1q, tag1, tag2);
669 if ((detail || arp) &&
670 (bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM)) {
673 vlib_cli_output (vm, "\n IP4 to MAC table for ARP Termination");
674 hash_foreach (ip4_addr, mac_addr, bd_config->mac_by_ip4, ({
675 vlib_cli_output (vm, "%=20U => %=20U",
676 format_ip4_address, &ip4_addr,
677 format_ethernet_address, &mac_addr);
684 vlib_cli_output (vm, "no bridge-domains in use");
691 VLIB_CLI_COMMAND (bd_show_cli, static) = {
692 .path = "show bridge-domain",
693 .short_help = "show bridge-domain [bridge-domain-id [detail|int|arp]]",