options.sh \
propagation.sh \
refleak.sh \
+ transmit_failover.sh \
# end of TEST_PROGS
TEST_INCLUDES := \
+ team_lib.sh \
../bonding/lag_lib.sh \
../../../net/forwarding/lib.sh \
../../../net/in_netns.sh \
CONFIG_NET_IPGRE=y
CONFIG_NET_TEAM=y
CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=y
+CONFIG_NET_TEAM_MODE_BROADCAST=y
CONFIG_NET_TEAM_MODE_LOADBALANCE=y
+CONFIG_NET_TEAM_MODE_RANDOM=y
+CONFIG_NET_TEAM_MODE_ROUNDROBIN=y
+CONFIG_VETH=y
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+test_dir="$(dirname "$0")"
+export REQUIRE_MZ=no
+export NUM_NETIFS=0
+# shellcheck disable=SC1091
+source "${test_dir}/../../../net/forwarding/lib.sh"
+
+TCP_PORT="43434"
+
+# Create a team interface inside of a given network namespace with a given
+# mode, members, and IP address.
+# Arguments:
+# namespace - Network namespace to put the team interface into.
+# team - The name of the team interface to setup.
+# mode - The team mode of the interface.
+# ip_address - The IP address to assign to the team interface.
+# prefix_length - The prefix length for the IP address subnet.
+# $@ - members - The member interfaces of the aggregation.
+setup_team()
+{
+ local namespace=$1
+ local team=$2
+ local mode=$3
+ local ip_address=$4
+ local prefix_length=$5
+ shift 5
+ local members=("$@")
+
+ # Prerequisite: team must have no members
+ for member in "${members[@]}"; do
+ ip -n "${namespace}" link set "${member}" nomaster
+ done
+
+ # Prerequisite: team must have no address in order to set it
+ # shellcheck disable=SC2086
+ ip -n "${namespace}" addr del "${ip_address}/${prefix_length}" \
+ ${NODAD} dev "${team}"
+
+ echo "Setting team in ${namespace} to mode ${mode}"
+
+ if ! ip -n "${namespace}" link set "${team}" down; then
+ echo "Failed to bring team device down"
+ return 1
+ fi
+ if ! ip netns exec "${namespace}" teamnl "${team}" setoption mode \
+ "${mode}"; then
+ echo "Failed to set ${team} mode to '${mode}'"
+ return 1
+ fi
+
+ # Aggregate the members into teams.
+ for member in "${members[@]}"; do
+ ip -n "${namespace}" link set "${member}" master "${team}"
+ done
+
+ # Bring team devices up and give them addresses.
+ if ! ip -n "${namespace}" link set "${team}" up; then
+ echo "Failed to set ${team} up"
+ return 1
+ fi
+
+ # shellcheck disable=SC2086
+ if ! ip -n "${namespace}" addr add "${ip_address}/${prefix_length}" \
+ ${NODAD} dev "${team}"; then
+ echo "Failed to give ${team} IP address in ${namespace}"
+ return 1
+ fi
+}
+
+# This is global used to keep track of the sender's iperf3 process, so that it
+# can be terminated.
+declare sender_pid
+
+# Start sending and receiving TCP traffic with iperf3.
+# Globals:
+# sender_pid - The process ID of the iperf3 sender process. Used to kill it
+# later.
+start_listening_and_sending()
+{
+ ip netns exec "${NS2}" iperf3 -s -p "${TCP_PORT}" --logfile /dev/null &
+ # Wait for server to become reachable before starting client.
+ slowwait 5 ip netns exec "${NS1}" iperf3 -c "${NS2_IP}" -p \
+ "${TCP_PORT}" -t 1 --logfile /dev/null
+ ip netns exec "${NS1}" iperf3 -c "${NS2_IP}" -p "${TCP_PORT}" -b 1M -l \
+ 1K -t 0 --logfile /dev/null &
+ sender_pid=$!
+}
+
+# Stop sending TCP traffic with iperf3.
+# Globals:
+# sender_pid - The process ID of the iperf3 sender process.
+stop_sending_and_listening()
+{
+ kill "${sender_pid}" && wait "${sender_pid}" 2>/dev/null || true
+}
+
+# Monitor for TCP traffic with Tcpdump, save results to temp files.
+# Arguments:
+# namespace - The network namespace to run tcpdump inside of.
+# $@ - interfaces - The interfaces to listen to.
+save_tcpdump_outputs()
+{
+ local namespace=$1
+ shift 1
+ local interfaces=("$@")
+
+ for interface in "${interfaces[@]}"; do
+ tcpdump_start "${interface}" "${namespace}"
+ done
+
+ sleep 1
+
+ for interface in "${interfaces[@]}"; do
+ tcpdump_stop_nosleep "${interface}"
+ done
+}
+
+clear_tcpdump_outputs()
+{
+ local interfaces=("$@")
+
+ for interface in "${interfaces[@]}"; do
+ tcpdump_cleanup "${interface}"
+ done
+}
+
+# Read Tcpdump output, determine packet counts.
+# Arguments:
+# interface - The name of the interface to count packets for.
+# ip_address - The destination IP address.
+did_interface_receive()
+{
+ local interface="$1"
+ local ip_address="$2"
+ local packet_count
+
+ packet_count=$(tcpdump_show "$interface" | grep -c \
+ "> ${ip_address}.${TCP_PORT}")
+ echo "Packet count for ${interface} was ${packet_count}"
+
+ if [[ "${packet_count}" -gt 0 ]]; then
+ true
+ else
+ false
+ fi
+}
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# These tests verify the basic failover capability of the team driver via the
+# `enabled` team driver option across different team driver modes. This does not
+# rely on teamd, and instead just uses teamnl to set the `enabled` option
+# directly.
+#
+# Topology:
+#
+# +-------------------------+ NS1
+# | test_team1 |
+# | + |
+# | eth0 | eth1 |
+# | +---+---+ |
+# | | | |
+# +-------------------------+
+# | |
+# +-------------------------+ NS2
+# | | | |
+# | +-------+ |
+# | eth0 | eth1 |
+# | + |
+# | test_team2 |
+# +-------------------------+
+
+export ALL_TESTS="team_test_failover"
+
+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"
+MEMBERS=("eth0" "eth1")
+
+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
+
+# Create the network namespaces, veth pair, and team devices in the specified
+# mode.
+# Globals:
+# RET - Used by test infra, set by `check_err` functions.
+# Arguments:
+# mode - The team driver mode to use for the team devices.
+environment_create()
+{
+ trap cleanup_all_ns EXIT
+ setup_ns ns1 ns2
+ NS1="${NS_LIST[0]}"
+ NS2="${NS_LIST[1]}"
+
+ # Create the interfaces.
+ ip -n "${NS1}" link add eth0 type veth peer name eth0 netns "${NS2}"
+ ip -n "${NS1}" link add eth1 type veth peer name eth1 netns "${NS2}"
+ ip -n "${NS1}" link add test_team1 type team
+ ip -n "${NS2}" link add test_team2 type team
+
+ # Set up the receiving network namespace's team interface.
+ setup_team "${NS2}" test_team2 roundrobin "${NS2_IP}" \
+ "${PREFIX_LENGTH}" "${MEMBERS[@]}"
+}
+
+
+# Check that failover works for a specific team driver mode.
+# Globals:
+# RET - Used by test infra, set by `check_err` functions.
+# Arguments:
+# mode - The mode to set the team interfaces to.
+team_test_mode_failover()
+{
+ local mode="$1"
+ export RET=0
+
+ # Set up the sender team with the correct mode.
+ setup_team "${NS1}" test_team1 "${mode}" "${NS1_IP}" \
+ "${PREFIX_LENGTH}" "${MEMBERS[@]}"
+ check_err $? "Failed to set up sender team"
+
+ start_listening_and_sending
+
+ ### Scenario 1: All interfaces initially enabled.
+ save_tcpdump_outputs "${NS2}" "${MEMBERS[@]}"
+ did_interface_receive eth0 "${NS2_IP}"
+ check_err $? "eth0 not transmitting when both links enabled"
+ did_interface_receive eth1 "${NS2_IP}"
+ check_err $? "eth1 not transmitting when both links enabled"
+ clear_tcpdump_outputs "${MEMBERS[@]}"
+
+ ### Scenario 2: One tx-side interface disabled.
+ ip netns exec "${NS1}" teamnl test_team1 setoption enabled false \
+ --port=eth1
+ slowwait 2 bash -c "ip netns exec ${NS1} teamnl test_team1 getoption \
+ enabled --port=eth1 | grep -q false"
+
+ save_tcpdump_outputs "${NS2}" "${MEMBERS[@]}"
+ did_interface_receive eth0 "${NS2_IP}"
+ check_err $? "eth0 not transmitting when enabled"
+ did_interface_receive eth1 "${NS2_IP}"
+ check_fail $? "eth1 IS transmitting when disabled"
+ clear_tcpdump_outputs "${MEMBERS[@]}"
+
+ ### Scenario 3: The interface is re-enabled.
+ ip netns exec "${NS1}" teamnl test_team1 setoption enabled true \
+ --port=eth1
+ slowwait 2 bash -c "ip netns exec ${NS1} teamnl test_team1 getoption \
+ enabled --port=eth1 | grep -q true"
+
+ save_tcpdump_outputs "${NS2}" "${MEMBERS[@]}"
+ did_interface_receive eth0 "${NS2_IP}"
+ check_err $? "eth0 not transmitting when both links enabled"
+ did_interface_receive eth1 "${NS2_IP}"
+ check_err $? "eth1 not transmitting when both links enabled"
+ clear_tcpdump_outputs "${MEMBERS[@]}"
+
+ log_test "Failover of '${mode}' test"
+
+ # Clean up
+ stop_sending_and_listening
+}
+
+team_test_failover()
+{
+ team_test_mode_failover broadcast
+ team_test_mode_failover roundrobin
+ team_test_mode_failover random
+ # Don't test `activebackup` or `loadbalance` modes, since they are too
+ # complicated for just setting `enabled` to work. They use more than
+ # the `enabled` option for transmit.
+}
+
+require_command teamnl
+require_command iperf3
+require_command tcpdump
+environment_create
+tests_run
+exit "${EXIT_STATUS}"
sleep 1
}
-tcpdump_stop()
+tcpdump_stop_nosleep()
{
local if_name=$1
local pid=${cappid[$if_name]}
$ns_cmd kill "$pid" && wait "$pid"
+}
+
+tcpdump_stop()
+{
+ tcpdump_stop_nosleep "$1"
sleep 1
}
{
local if_name=$1
- tcpdump -e -n -r ${capfile[$if_name]} 2>&1
+ tcpdump -e -nn -r ${capfile[$if_name]} 2>&1
}
# return 0 if the packet wasn't seen on host2_if or 1 if it was