| #!/bin/bash | |
| # SPDX-License-Identifier: GPL-2.0 | |
| # These tests verify that teamd is able to enable and disable ports via the | |
| # active backup runner. | |
| # | |
| # Topology: | |
| # | |
| # +-------------------------+ NS1 | |
| # | test_team1 | | |
| # | + | | |
| # | eth0 | eth1 | | |
| # | +---+---+ | | |
| # | | | | | |
| # +-------------------------+ | |
| # | | | |
| # +-------------------------+ NS2 | |
| # | | | | | |
| # | +-------+ | | |
| # | eth0 | eth1 | | |
| # | + | | |
| # | test_team2 | | |
| # +-------------------------+ | |
| export ALL_TESTS="teamd_test_active_backup" | |
| test_dir="$(dirname "$0")" | |
| # shellcheck disable=SC1091 | |
| source "${test_dir}/../../../net/lib.sh" | |
| # shellcheck disable=SC1091 | |
| source "${test_dir}/team_lib.sh" | |
| NS1="" | |
| NS2="" | |
| export NODAD="nodad" | |
| PREFIX_LENGTH="64" | |
| NS1_IP="fd00::1" | |
| NS2_IP="fd00::2" | |
| NS1_IP4="192.168.0.1" | |
| NS2_IP4="192.168.0.2" | |
| NS1_TEAMD_CONF="" | |
| NS2_TEAMD_CONF="" | |
| NS1_TEAMD_PID="" | |
| NS2_TEAMD_PID="" | |
| while getopts "4" opt; do | |
| case $opt in | |
| 4) | |
| echo "IPv4 mode selected." | |
| export NODAD= | |
| PREFIX_LENGTH="24" | |
| NS1_IP="${NS1_IP4}" | |
| NS2_IP="${NS2_IP4}" | |
| ;; | |
| \?) | |
| echo "Invalid option: -${OPTARG}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| teamd_config_create() | |
| { | |
| local runner=$1 | |
| local dev=$2 | |
| local conf | |
| conf=$(mktemp) | |
| cat > "${conf}" <<-EOF | |
| { | |
| "device": "${dev}", | |
| "runner": {"name": "${runner}"}, | |
| "ports": { | |
| "eth0": {}, | |
| "eth1": {} | |
| } | |
| } | |
| EOF | |
| echo "${conf}" | |
| } | |
| # Create the network namespaces, veth pair, and team devices in the specified | |
| # runner. | |
| # Globals: | |
| # RET - Used by test infra, set by `check_err` functions. | |
| # Arguments: | |
| # runner - The Teamd runner to use for the Team devices. | |
| environment_create() | |
| { | |
| local runner=$1 | |
| echo "Setting up two-link aggregation for runner ${runner}" | |
| echo "Teamd version is: $(teamd --version)" | |
| trap environment_destroy EXIT | |
| setup_ns ns1 ns2 | |
| NS1="${NS_LIST[0]}" | |
| NS2="${NS_LIST[1]}" | |
| for link in $(seq 0 1); do | |
| ip -n "${NS1}" link add "eth${link}" type veth peer name \ | |
| "eth${link}" netns "${NS2}" | |
| check_err $? "Failed to create veth pair" | |
| done | |
| NS1_TEAMD_CONF=$(teamd_config_create "${runner}" "test_team1") | |
| NS2_TEAMD_CONF=$(teamd_config_create "${runner}" "test_team2") | |
| echo "Conf files are ${NS1_TEAMD_CONF} and ${NS2_TEAMD_CONF}" | |
| ip netns exec "${NS1}" teamd -d -f "${NS1_TEAMD_CONF}" | |
| check_err $? "Failed to create team device in ${NS1}" | |
| NS1_TEAMD_PID=$(pgrep -f "teamd -d -f ${NS1_TEAMD_CONF}") | |
| ip netns exec "${NS2}" teamd -d -f "${NS2_TEAMD_CONF}" | |
| check_err $? "Failed to create team device in ${NS2}" | |
| NS2_TEAMD_PID=$(pgrep -f "teamd -d -f ${NS2_TEAMD_CONF}") | |
| echo "Created team devices" | |
| echo "Teamd PIDs are ${NS1_TEAMD_PID} and ${NS2_TEAMD_PID}" | |
| ip -n "${NS1}" link set test_team1 up | |
| check_err $? "Failed to set test_team1 up in ${NS1}" | |
| ip -n "${NS2}" link set test_team2 up | |
| check_err $? "Failed to set test_team2 up in ${NS2}" | |
| ip -n "${NS1}" addr add "${NS1_IP}/${PREFIX_LENGTH}" "${NODAD}" dev \ | |
| test_team1 | |
| check_err $? "Failed to add address to team device in ${NS1}" | |
| ip -n "${NS2}" addr add "${NS2_IP}/${PREFIX_LENGTH}" "${NODAD}" dev \ | |
| test_team2 | |
| check_err $? "Failed to add address to team device in ${NS2}" | |
| slowwait 2 timeout 0.5 ip netns exec "${NS1}" ping -W 1 -c 1 "${NS2_IP}" | |
| } | |
| # Tear down the environment: kill teamd and delete network namespaces. | |
| environment_destroy() | |
| { | |
| echo "Tearing down two-link aggregation" | |
| rm "${NS1_TEAMD_CONF}" | |
| rm "${NS2_TEAMD_CONF}" | |
| # First, try graceful teamd teardown. | |
| ip netns exec "${NS1}" teamd -k -t test_team1 | |
| ip netns exec "${NS2}" teamd -k -t test_team2 | |
| # If teamd can't be killed gracefully, then sigkill. | |
| if kill -0 "${NS1_TEAMD_PID}" 2>/dev/null; then | |
| echo "Sending sigkill to teamd for test_team1" | |
| kill -9 "${NS1_TEAMD_PID}" | |
| rm -f /var/run/teamd/test_team1.{pid,sock} | |
| fi | |
| if kill -0 "${NS2_TEAMD_PID}" 2>/dev/null; then | |
| echo "Sending sigkill to teamd for test_team2" | |
| kill -9 "${NS2_TEAMD_PID}" | |
| rm -f /var/run/teamd/test_team2.{pid,sock} | |
| fi | |
| cleanup_all_ns | |
| } | |
| # Change the active port for an active-backup mode team. | |
| # Arguments: | |
| # namespace - The network namespace that the team is in. | |
| # team - The name of the team. | |
| # active_port - The port to make active. | |
| set_active_port() | |
| { | |
| local namespace=$1 | |
| local team=$2 | |
| local active_port=$3 | |
| ip netns exec "${namespace}" teamdctl "${team}" state item set \ | |
| runner.active_port "${active_port}" | |
| slowwait 2 bash -c "ip netns exec ${namespace} teamdctl ${team} state \ | |
| item get runner.active_port | grep -q ${active_port}" | |
| } | |
| # Wait for an interface to stop receiving traffic. If it keeps receiving traffic | |
| # for the duration of the timeout, then return an error. | |
| # Arguments: | |
| # - namespace - The network namespace that the interface is in. | |
| # - interface - The name of the interface. | |
| wait_to_stop_receiving() | |
| { | |
| local namespace=$1 | |
| local interface=$2 | |
| echo "Waiting for ${interface} in ${namespace} to stop receiving" | |
| slowwait 10 check_no_traffic "${interface}" "${NS2_IP}" \ | |
| "${namespace}" | |
| } | |
| # Test that active backup runner can change active ports. | |
| # Globals: | |
| # RET - Used by test infra, set by `check_err` functions. | |
| teamd_test_active_backup() | |
| { | |
| export RET=0 | |
| start_listening_and_sending | |
| ### Scenario 1: Don't manually set active port, just make sure team | |
| # works. | |
| save_tcpdump_outputs "${NS2}" test_team2 | |
| did_interface_receive test_team2 "${NS2_IP}" | |
| check_err $? "Traffic did not reach team interface in NS2." | |
| clear_tcpdump_outputs test_team2 | |
| ### Scenario 2: Choose active port. | |
| set_active_port "${NS1}" test_team1 eth1 | |
| set_active_port "${NS2}" test_team2 eth1 | |
| wait_to_stop_receiving "${NS2}" eth0 | |
| save_tcpdump_outputs "${NS2}" eth0 eth1 | |
| did_interface_receive eth0 "${NS2_IP}" | |
| check_fail $? "eth0 IS transmitting when inactive" | |
| did_interface_receive eth1 "${NS2_IP}" | |
| check_err $? "eth1 not transmitting when active" | |
| clear_tcpdump_outputs eth0 eth1 | |
| ### Scenario 3: Change active port. | |
| set_active_port "${NS1}" test_team1 eth0 | |
| set_active_port "${NS2}" test_team2 eth0 | |
| wait_to_stop_receiving "${NS2}" eth1 | |
| save_tcpdump_outputs "${NS2}" eth0 eth1 | |
| did_interface_receive eth0 "${NS2_IP}" | |
| check_err $? "eth0 not transmitting when active" | |
| did_interface_receive eth1 "${NS2_IP}" | |
| check_fail $? "eth1 IS transmitting when inactive" | |
| clear_tcpdump_outputs eth0 eth1 | |
| log_test "teamd active backup runner test" | |
| stop_sending_and_listening | |
| } | |
| require_command teamd | |
| require_command teamdctl | |
| require_command iperf3 | |
| require_command tcpdump | |
| environment_create activebackup | |
| tests_run | |
| exit "${EXIT_STATUS}" |