#!/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
}

# Return true if the given interface in the given namespace does NOT receive
# traffic over a 1 second period.
# Arguments:
#   interface - The name of the interface.
#   ip_address - The destination IP address.
#   namespace - The name of the namespace that the interface is in.
check_no_traffic()
{
	local interface="$1"
	local ip_address="$2"
	local namespace="$3"
	local rc

	save_tcpdump_outputs "${namespace}" "${interface}"
	did_interface_receive "${interface}" "${ip_address}"
	rc=$?

	clear_tcpdump_outputs "${interface}"

	if [[ "${rc}" -eq 0 ]]; then
		return 1
	else
		return 0
	fi
}