/* * l2_bd.c : layer 2 bridge domain * * Copyright (c) 2013 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * @file * @brief Ethernet Bridge Domain. * * Code in this file manages Layer 2 bridge domains. * */ bd_main_t bd_main; /** Init bridge domain if not done already. For feature bitmap, set all bits except ARP termination */ void bd_validate (l2_bridge_domain_t * bd_config) { if (bd_is_valid (bd_config)) return; bd_config->feature_bitmap = ~(L2INPUT_FEAT_ARP_TERM | L2INPUT_FEAT_UU_FWD | L2INPUT_FEAT_ARP_UFWD); bd_config->bvi_sw_if_index = ~0; bd_config->uu_fwd_sw_if_index = ~0; bd_config->members = 0; bd_config->flood_count = 0; bd_config->tun_master_count = 0; bd_config->tun_normal_count = 0; bd_config->no_flood_count = 0; bd_config->mac_by_ip4 = 0; bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); } u32 bd_find_index (bd_main_t * bdm, u32 bd_id) { u32 *p = (u32 *) hash_get (bdm->bd_index_by_bd_id, bd_id); if (!p) return ~0; return p[0]; } u32 bd_add_bd_index (bd_main_t * bdm, u32 bd_id) { ASSERT (!hash_get (bdm->bd_index_by_bd_id, bd_id)); u32 rv = clib_bitmap_first_clear (bdm->bd_index_bitmap); /* mark this index taken */ bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1); hash_set (bdm->bd_index_by_bd_id, bd_id, rv); vec_validate (l2input_main.bd_configs, rv); l2input_main.bd_configs[rv].bd_id = bd_id; return rv; } static inline void bd_free_ip_mac_tables (l2_bridge_domain_t * bd) { u64 mac_addr; ip6_address_t *ip6_addr_key; hash_free (bd->mac_by_ip4); /* *INDENT-OFF* */ hash_foreach_mem (ip6_addr_key, mac_addr, bd->mac_by_ip6, ({ clib_mem_free (ip6_addr_key); /* free memory used for ip6 addr key */ })); /* *INDENT-ON* */ hash_free (bd->mac_by_ip6); } static int bd_delete (bd_main_t * bdm, u32 bd_index) { l2_bridge_domain_t *bd = &l2input_main.bd_configs[bd_index]; u32 bd_id = bd->bd_id; /* flush non-static MACs in BD and removed bd_id from hash table */ l2fib_flush_bd_mac (vlib_get_main (), bd_index); hash_unset (bdm->bd_index_by_bd_id, bd_id); /* mark this index clear */ bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0); /* clear BD config for reuse: bd_id to -1 and clear feature_bitmap */ bd->bd_id = ~0; bd->feature_bitmap = 0; /* free BD tag */ vec_free (bd->bd_tag); /* free memory used by BD */ vec_free (bd->members); bd_free_ip_mac_tables (bd); return 0; } static void update_flood_count (l2_bridge_domain_t * bd_config) { bd_config->flood_count = (vec_len (bd_config->members) - (bd_config->tun_master_count ? bd_config->tun_normal_count : 0)); bd_config->flood_count -= bd_config->no_flood_count; } void bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member) { u32 ix = 0; vnet_sw_interface_t *sw_if = vnet_get_sw_interface (vnet_get_main (), member->sw_if_index); /* * Add one element to the vector * vector is ordered [ bvi, normal/tun_masters..., tun_normals... no_flood] * When flooding, the bvi interface (if present) must be the last member * processed due to how BVI processing can change the packet. To enable * this order, we make the bvi interface the first in the vector and * flooding walks the vector in reverse. The flood-count determines where * in the member list to start the walk from. */ switch (sw_if->flood_class) { case VNET_FLOOD_CLASS_NO_FLOOD: bd_config->no_flood_count++; ix = vec_len (bd_config->members); break; case VNET_FLOOD_CLASS_BVI: ix = 0; break; case VNET_FLOOD_CLASS_TUNNEL_MASTER: bd_config->tun_master_count++; /* Fall through */ case VNET_FLOOD_CLASS_NORMAL: ix = (vec_len (bd_config->members) - bd_config->tun_normal_count - bd_config->no_flood_count); break; case VNET_FLOOD_CLASS_TUNNEL_NORMAL: ix = (vec_len (bd_config->members) - bd_config->no_flood_count); bd_config->tun_normal_count++; break; } vec_insert_elts (bd_config->members, member, 1, ix); update_flood_count (bd_config); } #define BD_REMOVE_ERROR_OK 0 #define BD_REMOVE_ERROR_NOT_FOUND 1 u32 bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index) { u32 ix; /* Find and delete the member */ vec_foreach_index (ix, bd_config->members) { l2_flood_member_t *m = vec_elt_at_index (bd_config->members, ix); if (m->sw_if_index == sw_if_index) { vnet_sw_interface_t *sw_if = vnet_get_sw_interface (vnet_get_main (), sw_if_index); if (sw_if->flood_class != VNET_FLOOD_CLASS_NORMAL) { if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_MASTER) bd_config->tun_master_count--; else if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_NORMAL) bd_config->tun_normal_count--; else if (sw_if->flood_class == VNET_FLOOD_CLASS_NO_FLOOD) bd_config->no_flood_count--; } vec_delete (bd_config->members, 1, ix); update_flood_count (bd_config); return BD_REMOVE_ERROR_OK; } } return BD_REMOVE_ERROR_NOT_FOUND; } clib_error_t * l2bd_init (vlib_main_t * vm) { bd_main_t *bdm = &bd_main; bdm->bd_index_by_bd_id = hash_create (0, sizeof (uword)); /* * create a placeholder bd with bd_id of 0 and bd_index of 0 with feature set * to packet drop only. Thus, packets received from any L2 interface with * uninitialized bd_index of 0 can be dropped safely. */ u32 bd_index = bd_add_bd_index (bdm, 0); ASSERT (bd_index == 0); l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP; bdm->vlib_main = vm; return 0; } VLIB_INIT_FUNCTION (l2bd_init); l2_bridge_domain_t * bd_get (u32 bd_index) { if (bd_index < vec_len (l2input_main.bd_configs)) return (vec_elt_at_index (l2input_main.bd_configs, bd_index)); return (NULL); } u32 bd_input_walk (u32 bd_index, bd_input_walk_fn_t fn, void *data) { l2_flood_member_t *member; l2_bridge_domain_t *bd; u32 sw_if_index; sw_if_index = ~0; bd = bd_get (bd_index); ASSERT (bd); vec_foreach (member, bd->members) { if (WALK_STOP == fn (bd_index, member->sw_if_index)) { sw_if_index = member->sw_if_index; break; } } return (sw_if_index); } static void b2_input_recache (u32 bd_index) { bd_input_walk (bd_index, l2input_recache, NULL); } /** Set the learn/forward/flood flags for the bridge domain. Return 0 if ok, non-zero if for an error. */ u32 bd_set_flags (vlib_main_t * vm, u32 bd_index, bd_flags_t flags, u32 enable) { l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index); bd_validate (bd_config); u32 feature_bitmap = 0; if (flags & L2_LEARN) { feature_bitmap |= L2INPUT_FEAT_LEARN; } if (flags & L2_FWD) { feature_bitmap |= L2INPUT_FEAT_FWD; } if (flags & L2_FLOOD) { feature_bitmap |= L2INPUT_FEAT_FLOOD; } if (flags & L2_UU_FLOOD) { feature_bitmap |= L2INPUT_FEAT_UU_FLOOD; } if (flags & L2_ARP_TERM) { feature_bitmap |= L2INPUT_FEAT_ARP_TERM; } if (flags & L2_ARP_UFWD) { feature_bitmap |= L2INPUT_FEAT_ARP_UFWD; } if (enable) { bd_config->feature_bitmap |= feature_bitmap; } else { bd_config->feature_bitmap &= ~feature_bitmap; } b2_input_recache (bd_index); return bd_config->feature_bitmap; } /** Set the mac age for the bridge domain. */ void bd_set_mac_age (vlib_main_t * vm, u32 bd_index, u8 age) { l2_bridge_domain_t *
#!/bin/bash

function usage() {
 echo "$0" 1>&2
 echo "" 1>&2
 echo "Usage: $0 [-p <pre-exec-cmd>] [-m <email>] -- <make test options|verify>" 1>&2
 echo "" 1>&2
 echo "Parameters:" 1>&2
 echo "    -p <pre-exec-cmd> - run a command before each test loop (e.g. 'git pull')" 1>&2
 echo "    -m <email>        - if set, email is sent to this address on failure" 1>&2
 echo "" 1>&2
 echo "Examples:" 1>&2
 echo "    $0 -m <somebody@cisco.com> -- test-debug TEST=l2bd" 1>&2
 echo "    $0 -m <somebody@cisco.com> -- verify" 1>&2
 exit 1;
}

PRE_EXEC_CMD=""
EMAIL=""

while getopts "p:m:h" o; do
	case "${o}" in
	p)
		PRE_EXEC_CMD=${OPTARG}
		;;
	m)
		regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"
		m=${OPTARG}
		if [[ ! $m =~ $regex ]]
		then
			echo "Invalid -m parameter value: \`$m'" >&2
			usage
		fi
		EMAIL="$m"
		;;
	h)
		usage
		;;
	?)
		usage
		;;
esac
	done
shift $((OPTIND-1))

if ! echo $* | grep test >/dev/null
then
	if ! echo $* | grep verify >/dev/null
	then
		echo "Error: command line doesn't look right - should contain \`test' or \`verify' token..." >&2
		usage
	fi
fi

function finish {
	NOW=`date +%s`
	RUNTIME=$((NOW - START))
	AVG=$(echo "scale=2; $RUNTIME/$COUNT" | bc)
	OUT="*********************************************************************"
	OUT="$OUT\n* tail -n 30 $TMP:"
	OUT="$OUT\n*********************************************************************"
	OUT="$OUT\n`tail -n 30 $TMP`"
	OUT="$OUT\n*********************************************************************"
	OUT="$OUT\n* Total runtime: ${RUNTIME}s"
	OUT="$OUT\n* Iterations:    ${COUNT}"
	OUT="$OUT\n* Average time:  ${AVG}s"
	OUT="$OUT\n* Log file:      ${TMP}"
	OUT="$OUT\n*********************************************************************"
	echo -e "$OUT"
	if [[ "$EMAIL" != "" && "$REASON" != "" ]]
	then
		SUBJECT="test loop finished ($REASON)"
		echo -e "$OUT" | mail -s "$SUBJECT" $EMAIL
	fi
}

trap "echo Caught signal, exiting...; REASON=\"received signal\"; finish; exit -1" SIGINT SIGTERM

TMP=`mktemp`
START=`date +%s`
COUNT=0

if ! test -f "$TMP"
then
	echo "Couldn't create temporary file!"
	exit -1
fi

echo "Temporary file is $TMP"
CMD="make $*"
echo "Command line is \`$CMD'"

REASON=""
while true
do
	COUNT=$((COUNT+1))
	BEFORE=`date +%s`
	if [[ "$PRE_EXEC_CMD" != "" ]]
	then
		echo "Executing \`$PRE_EXEC_CMD' before test.."
		if ! ($PRE_EXEC_CMD 2>&1 | tee $TMP)
		then
			echo "\`$PRE_EXEC_CMD' failed!" >&2
			REASON="$PRE_EXEC_CMD failed"
			break
		fi
	fi
	echo -n "Running test iteration #$COUNT..."
	if ! ($CMD >$TMP 2>&1)
	then
		AFTER=`date +%s`
		RUNTIME=$((AFTER-BEFORE))
		echo "FAILED! (after ${RUNTIME}s)"
		REASON="test failed"
		break
	fi
	AFTER=`date +%s`
	RUNTIME=$((AFTER-BEFORE))
	echo "PASSED (after ${RUNTIME}s)"
done

finish
exit 1
input, "det")) detail = 1; if (unformat (input, "int")) intf = 1; if (unformat (input, "arp")) arp = 1; if (unformat (input, "bd-tag")) bd_tag = 1; if (bd_id == 0) return clib_error_return (0, "No operations on the default bridge domain are supported"); p = hash_get (bdm->bd_index_by_bd_id, bd_id); if (p) bd_index = *p; else return clib_error_return (0, "No such bridge domain %d", bd_id); vec_validate (l2input_main.bd_configs, bd_index); bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); if (bd_is_valid (bd_config)) { start = bd_index; end = start + 1; } else { vlib_cli_output (vm, "bridge-domain %d not in use", bd_id); goto done; } } /* Show all bridge-domains that have been initialized */ u32 printed = 0; u8 *as = 0; for (bd_index = start; bd_index < end; bd_index++) { bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); if (bd_is_valid (bd_config)) { if (!printed) { printed = 1; vlib_cli_output (vm, "%=8s %=7s %=4s %=9s %=9s %=9s %=11s %=9s %=9s %=9s %=11s", "BD-ID", "Index", "BSN", "Age(min)", "Learning", "U-Forwrd", "UU-Flood", "Flooding", "ARP-Term", "arp-ufwd", "BVI-Intf"); } if (bd_config->mac_age) as = format (as, "%d", bd_config->mac_age); else as = format (as, "off"); vlib_cli_output (vm, "%=8d %=7d %=9v %=9s %=9s %=11U %=9s %=9s %=9s %=11U", bd_config->bd_id, bd_index, as, bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on" : "off", format_uu_cfg, bd_config, bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_ARP_UFWD ? "on" : "off", format_vnet_sw_if_index_name_with_NA, vnm, bd_config->bvi_sw_if_index); vlib_cli_output (vm, "%U", format_l2_input_feature_bitmap, bd_config->feature_bitmap); vec_reset_length (as); if (detail || intf) { /* Show all member interfaces */ int i; vec_foreach_index (i, bd_config->members) { l2_flood_member_t *member = vec_elt_at_index (bd_config->members, i); u8 swif_seq_num = l2_input_seq_num (member->sw_if_index); u32 vtr_opr, dot1q, tag1, tag2; if (i == 0) { vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=5s%=9s%=30s", "Interface", "If-idx", "ISN", "SHG", "BVI", "TxFlood", "VLAN-Tag-Rewrite"); } l2vtr_get (vm, vnm, member->sw_if_index, &vtr_opr, &dot1q, &tag1, &tag2); vlib_cli_output (vm, "%=30U%=7d%=5d%=5d%=5s%=9s%=30U", format_vnet_sw_if_index_name, vnm, member->sw_if_index, member->sw_if_index, swif_seq_num, member->shg, member->flags & L2_FLOOD_MEMBER_BVI ? "*" : "-", i < bd_config->flood_count ? "*" : "-", format_vtr, vtr_opr, dot1q, tag1, tag2); } if (~0 != bd_config->uu_fwd_sw_if_index) vlib_cli_output (vm, "%=30U%=7d%=5d%=5d%=5s%=9s%=30s", format_vnet_sw_if_index_name, vnm, bd_config->uu_fwd_sw_if_index, bd_config->uu_fwd_sw_if_index, 0, 0, "uu", "-", "None"); } if ((detail || arp) && (bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM)) { u32 ip4_addr; ip6_address_t *ip6_addr; u64 mac_addr; vlib_cli_output (vm, "\n IP4/IP6 to MAC table for ARP Termination"); /* *INDENT-OFF* */ hash_foreach (ip4_addr, mac_addr, bd_config->mac_by_ip4, ({ vlib_cli_output (vm, "%=40U => %=20U", format_ip4_address, &ip4_addr, format_ethernet_address, &mac_addr); })); hash_foreach_mem (ip6_addr, mac_addr, bd_config->mac_by_ip6, ({ vlib_cli_output (vm, "%=40U => %=20U", format_ip6_address, ip6_addr, format_ethernet_address, &mac_addr); })); /* *INDENT-ON* */ } if ((detail || bd_tag) && (bd_config->bd_tag)) { vlib_cli_output (vm, "\n BD-Tag: %s", bd_config->bd_tag); } } } vec_free (as); if (!printed) { vlib_cli_output (vm, "no bridge-domains in use"); } done: return error; } /*? * Show a summary of all the bridge-domain instances or detailed view of a * single bridge-domain. Bridge-domains are created by adding an interface * to a bridge using the 'set interface l2 bridge' command. * * @cliexpar * @parblock * Example of displaying all bridge-domains: * @cliexstart{show bridge-domain} * ID Index Learning U-Forwrd UU-Flood Flooding ARP-Term BVI-Intf * 0 0 off off off off off local0 * 200 1 on on on on off N/A * @cliexend * * Example of displaying details of a single bridge-domains: * @cliexstart{show bridge-domain 200 detail} * ID Index Learning U-Forwrd UU-Flood Flooding ARP-Term BVI-Intf * 200 1 on on on on off N/A * * Interface Index SHG BVI VLAN-Tag-Rewrite * GigabitEthernet0/8/0.200 3 0 - none * GigabitEthernet0/9/0.200 4 0 - none * @cliexend * @endparblock ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (bd_show_cli, static) = { .path = "show bridge-domain", .short_help = "show bridge-domain [bridge-domain-id [detail|int|arp|bd-tag]]", .function = bd_show, }; /* *INDENT-ON* */ int bd_add_del (l2_bridge_domain_add_del_args_t * a) { bd_main_t *bdm = &bd_main; l2fib_main_t *fm = &l2fib_main; vlib_main_t *vm = bdm->vlib_main; int rv = 0; if (fm->mac_table_initialized == 0) l2fib_table_init (); u32 bd_index = bd_find_index (bdm, a->bd_id); if (a->is_add) { if (bd_index != ~0) return VNET_API_ERROR_BD_ALREADY_EXISTS; if (a->bd_id > L2_BD_ID_MAX) return VNET_API_ERROR_BD_ID_EXCEED_MAX; bd_index = bd_add_bd_index (bdm, a->bd_id); bd_flags_t enable_flags = 0, disable_flags = 0; if (a->flood) enable_flags |= L2_FLOOD; else disable_flags |= L2_FLOOD; if (a->uu_flood) enable_flags |= L2_UU_FLOOD; else disable_flags |= L2_UU_FLOOD; if (a->forward) enable_flags |= L2_FWD; else disable_flags |= L2_FWD; if (a->learn) enable_flags |= L2_LEARN; else disable_flags |= L2_LEARN; if (a->arp_term) enable_flags |= L2_ARP_TERM; else disable_flags |= L2_ARP_TERM; if (a->arp_ufwd) enable_flags |= L2_ARP_UFWD; else disable_flags |= L2_ARP_UFWD; if (enable_flags) bd_set_flags (vm, bd_index, enable_flags, 1 /* enable */ ); if (disable_flags) bd_set_flags (vm, bd_index, disable_flags, 0 /* disable */ ); bd_set_mac_age (vm, bd_index, a->mac_age); if (a->bd_tag) bd_set_bd_tag (vm, bd_index, a->bd_tag); } else { if (bd_index == ~0) return VNET_API_ERROR_NO_SUCH_ENTRY; if (bd_index == 0) return VNET_API_ERROR_BD_NOT_MODIFIABLE; if (vec_len (l2input_main.bd_configs[bd_index].members)) return VNET_API_ERROR_BD_IN_USE; rv = bd_delete (bdm, bd_index); } return rv; } /** Create or delete bridge-domain. The CLI format: create bridge-domain [learn <0|1>] [forward <0|1>] [uu-flood <0|1>] [flood <0|1>] [arp-term <0|1>] [mac-age ] [bd-tag ] [del] */ static clib_error_t * bd_add_del_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *error = 0; u8 is_add = 1; u32 bd_id = ~0; u32 flood = 1, forward = 1, learn = 1, uu_flood = 1, arp_term = 0, arp_ufwd = 0; u32 mac_age = 0; u8 *bd_tag = NULL; l2_bridge_domain_add_del_args_t _a, *a = &_a; int rv; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return 0; while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "%d", &bd_id)) ; else if (unformat (line_input, "flood %d", &flood)) ; else if (unformat (line_input, "uu-flood %d", &uu_flood)) ; else if (unformat (line_input, "forward %d", &forward)) ; else if (unformat (line_input, "learn %d", &learn)) ; else if (unformat (line_input, "arp-term %d", &arp_term)) ; else if (unformat (line_input, "arp-ufwd %d", &arp_ufwd)) ; else if (unformat (line_input, "mac-age %d", &mac_age)) ; else if (unformat (line_input, "bd-tag %s", &bd_tag)) ; else if (unformat (line_input, "del")) { is_add = 0; flood = uu_flood = forward = learn = 0; } else break; } if (bd_id == ~0) { error = clib_error_return (0, "bridge-domain-id not specified"); goto done; } if (bd_id == 0) { error = clib_error_return (0, "bridge domain 0 can not be modified"); goto done; } if (mac_age > 255) { error = clib_error_return (0, "mac age must be less than 256"); goto done; } if ((bd_tag) && (strlen ((char *) bd_tag) > 63)) { error = clib_error_return (0, "bd-tag cannot be longer than 63"); goto done; } clib_memset (a, 0, sizeof (*a)); a->is_add = is_add; a->bd_id = bd_id; a->flood = (u8) flood; a->uu_flood = (u8) uu_flood; a->forward = (u8) forward; a->learn = (u8) learn; a->arp_term = (u8) arp_term; a->arp_ufwd = (u8) arp_ufwd; a->mac_age = (u8) mac_age; a->bd_tag = bd_tag; rv = bd_add_del (a); switch (rv) { case 0: if (is_add) vlib_cli_output (vm, "bridge-domain %d", bd_id); break; case VNET_API_ERROR_BD_IN_USE: error = clib_error_return (0, "bridge domain in use - remove members"); goto done; case VNET_API_ERROR_NO_SUCH_ENTRY: error = clib_error_return (0, "bridge domain ID does not exist"); goto done; case VNET_API_ERROR_BD_NOT_MODIFIABLE: error = clib_error_return (0, "bridge domain 0 can not be modified"); goto done; case VNET_API_ERROR_BD_ID_EXCEED_MAX: error = clib_error_return (0, "bridge domain ID exceed 16M limit"); goto done; default: error = clib_error_return (0, "bd_add_del returned %d", rv); goto done; } done: vec_free (bd_tag); unformat_free (line_input); return error; } /*? * Create/Delete bridge-domain instance * * @cliexpar * @parblock * Example of creating bridge-domain 1: * @cliexstart{create bridge-domain 1} * bridge-domain 1 * @cliexend * * Example of creating bridge-domain 2 with enabling arp-term, mac-age 60: * @cliexstart{create bridge-domain 2 arp-term 1 mac-age 60} * bridge-domain 2 * * vpp# show bridge-domain * ID Index BSN Age(min) Learning U-Forwrd UU-Flood Flooding ARP-Term BVI-Intf * 0 0 0 off off off off off off local0 * 1 1 0 off on on off on off N/A * 2 2 0 60 on on off on on N/A * * @cliexend * * Example of delete bridge-domain 1: * @cliexstart{create bridge-domain 1 del} * @cliexend * @endparblock ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (bd_create_cli, static) = { .path = "create bridge-domain", .short_help = "create bridge-domain " " [learn <0|1>] [forward <0|1>] [uu-flood <0|1>] [flood <0|1>] [arp-term <0|1>]" " [arp-ufwd <0|1>] [mac-age ] [bd-tag ] [del]", .function = bd_add_del_command_fn, }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */