#!/usr/bin/env bash
#
# Set up a virtual environment in a network namespace, and run arping
# in that controlled environment.
#
# This needs to run as root, since it creates network namespaces and
# virtual ethernet devices.
#

set -e
set -o pipefail

setup() {
    ip netns del arping_test0 2>/dev/null || true
    ip netns del arping_test1 2>/dev/null  || true

    ip netns add arping_test0
    ip netns add arping_test1

    ip link add veth0 type veth peer name veth1

    ip link set veth0 netns arping_test0
    ip link set veth1 netns arping_test1

    ip netns exec arping_test0 ip l set up veth0
    ip netns exec arping_test1 ip l set up veth1

    ip netns exec arping_test0 ip l set dev veth0 address 00:01:02:33:44:00
    ip netns exec arping_test1 ip l set dev veth1 address 00:01:02:33:44:01

    ip netns exec arping_test0 ip a a 192.0.2.100/24 dev veth0
    ip netns exec arping_test1 ip a a 192.0.2.101/24 dev veth1
}

#
# Run tests.
# run_test [ -t ] [ -i ] <test name> <arping options…>
#
#   -t    Skip capturing packets
#   -i    Set net.ipv4.icmp_echo_ignore_broadcasts=0
run_test() {
    setup

    DO_CAPTURE=yes
    if [[ "$1" = "-t" ]]; then
	DO_CAPTURE=no
	shift
    fi

    if [[ "$1" = "-i" ]]; then
	ip netns exec arping_test1 sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=0
	shift
    fi

    NAME="$1"
    shift
    ECODE="$1"
    shift

    echo "-----------------------------"
    echo "     Test: ${NAME?}"
    echo "-----------------------------"
    
    if [[ "${DO_CAPTURE?}" = "yes" ]]; then
	CAP="$(mktemp)"
	# TODO: in the future also capture icmp. But we need regex matching for that.
	ip netns exec arping_test1 \
	   tcpdump \
	   -s0 \
	   -nlpi veth1 \
	   --immediate-mode \
	   -w "${CAP?}" \
	   arp \
	   &
        PID=$!
        # wait for tcpdump to wake up.
        sleep "${SLEEP?}"
    fi


    # Run arping.
    set +e
    # TODO: test that errors go to stderr.
    OUT="$(ip netns exec arping_test0 ./src/arping -W0.05 "$@" 2>&1)"
    E=$?
    set -e

    if [[ ! $ECODE = $E ]]; then
	echo "FAIL: wrong exit code ${E?}, want ${ECODE?}"
	echo "Output:"
	echo "---"
	echo "${OUT?}"
	exit 1
    fi

    if ! echo "${OUT?}" | tests/regmatch.py "tests/${NAME?}.out"; then
	echo "FAIL: wrong output:"
	echo "----"
	echo "${OUT?}"
	#echo "${OUT?}" > a
	echo "---- Should match -----"
	cat "tests/${NAME?}.out"
	exit 1
    fi

    if [[ "${DO_CAPTURE?}" = "yes" ]]; then
	# Wait for tcpdump to see the packets.
	sleep "${SLEEP?}"
	kill -INT "${PID?}"
	wait "${PID?}" || true

	TXT="$(mktemp)"
	chown root:root "${CAP?}"
	tcpdump -txnlpr "${CAP?}" > "${TXT?}"
	diff -Nu "tests/${NAME?}.cap" "${TXT?}" || (echo "FAIL ${TXT?}"; exit 1)
	rm "${CAP?}"
    fi
    cleanup
}

cleanup() {
    ip netns del arping_test0 || true
    ip netns del arping_test1 || true
}    

run_tests() {
    # Usage stuff.
    run_test -t "no_args"     1
    run_test -t "h"           0 -h
    run_test -t "help"        0 --help

    # Simple IP examples.
    run_test "simple"              0 -c 1 192.0.2.101
    run_test "simple_verbose"      0 -v -c 1 192.0.2.101
    run_test "no_reply"            1 -c 1 192.0.2.1

    # Simple MAC examples.
    run_test -t "simplemac_timeout"   1 -c 1 -i veth0 00:01:02:33:44:01
    run_test -t "simplemac_target"    0 -c 1 -T 192.0.2.101 00:01:02:33:44:01
    run_test -t -i "simplemac_noignore"    0 -c 1 -i veth0 00:01:02:33:44:01

    # TODO: ping mac address.

    # -0
    run_test "simple0"     0 -c 1 -0 192.0.2.101

    # TODO: -a
    # TODO: -A
    # TODO: -b
    # TODO: -B

    # -c
    run_test "simple3"     0 -c  3 192.0.2.101
    run_test "bad_count"   1 -c -1 192.0.2.101

    # TODO: -C
    # TODO: -d
    # TODO: -D
    # TODO: -e
    # TODO: -F
    # TODO: -g
    # TODO: -i
    # TODO: -m
    # TODO: -p

    # -P
    run_test "reply"             0 -c 1 -P    192.0.2.101
    run_test "unsolicited_reply" 0 -c 1 -P -U 192.0.2.101

    # TODO: -q
    # TODO: -Q
    # TODO: -r
    # TODO: -R
    # TODO: -s
    # TODO: -S
    # TODO: -t
    # TODO: -T
    # TODO: -u

    run_test "unsolicited"       0 -c 1 -U 192.0.2.101

    # TODO: -v
    # TODO: -V
    # TODO: -w
    # TODO: -W
    # TODO: -z
    # TODO: -Z
}

main() {
    cd "$(dirname $0)/.."
    SLEEP="${1:-0.1}"
    cleanup
    run_tests

    echo "All OK"
}

main "$@"
