#!/bin/bash -x

: "${IFACE0:=veth_test_arp_0}"
: "${IFACE1:=veth_test_arp_1}"

NETNS0=/var/run/netns/"$IFACE0"
NETNS1=/var/run/netns/"$IFACE1"

: "${IFACE0_MAC:=40:00:00:80:00:f0}"
: "${IFACE1_MAC:=40:00:00:80:00:f1}"

: "${IP_CLASS:=24}"
: "${IFACE0_ADDR:=192.168.42.10}"
: "${IFACE1_ADDR:=192.168.42.11}"

CONF=tmp/test_arp.conf

########################################################################

if [[ "$(realpath $0)" =~ "/build/" ]]; then
  UNIT_TEST=$(dirname $0)
elif [[ $# == 1 ]]; then
  UNIT_TEST=$1/unit-test
else
  echo ""
  echo "        build directory not specified"
  echo ""
  echo "        Usage: $0 [BUILD_DIRECTORY]"
  echo ""
  echo "        Creates two network namespaces with a network config in each"
  echo "        Runs ARP tests between them"
  echo ""
  exit 1
fi

# Disable permanent log for all the controls we are going to run in here

FD_LOG_PATH=""
export FD_LOG_PATH

# Delete any existing netns and interface
ip netns delete "$IFACE0" &> /dev/null
ip netns delete "$IFACE1" &> /dev/null

ip link del dev "$IFACE0" &> /dev/null # Destroys IFACE1 too. Okay if this fails
ip link del dev "$IFACE1" &> /dev/null # Just in case

# (Re-)create veth virtual network devices

# create namespaces
if ! ip netns add "$IFACE0"; then
  echo "Failed to create netns. Insufficient capabilities?" >&2
  exit 0
fi
ip netns add "$IFACE1" || exit $?

# create pair of connected interfaces
ip link add dev "$IFACE0"       \
            type veth           \
            peer name "$IFACE1" \
  || exit $?

IFACE0_IDX="$(ip -json a s $IFACE0 | jq '.[0].ifindex')"
IFACE1_IDX="$(ip -json a s $IFACE1 | jq '.[0].ifindex')"

# add MAC addresses
ip link set dev "$IFACE0" address "$IFACE0_MAC" || exit $?
ip link set dev "$IFACE1" address "$IFACE1_MAC" || exit $?

# attach interfaces to namespaces
ip link set "$IFACE0" netns "$IFACE0" || exit $?
ip link set "$IFACE1" netns "$IFACE1" || exit $?

# add IP addresses
ip netns exec "$IFACE0" ip address add "$IFACE0_ADDR/$IP_CLASS" dev "$IFACE0" || exit $?
ip netns exec "$IFACE1" ip address add "$IFACE1_ADDR/$IP_CLASS" dev "$IFACE1" || exit $?

# raise interfaces
ip netns exec "$IFACE0" ip link set dev "$IFACE0" up || exit $?
ip netns exec "$IFACE1" ip link set dev "$IFACE1" up || exit $?

# add routes
ip netns exec "$IFACE0" ip route add unicast 192.168.36.0/24 dev "$IFACE0" || exit $?
ip netns exec "$IFACE0" ip route add unicast default via 192.168.42.11 dev "$IFACE0" || exit $?

# dump routes
echo dump route:
ip netns exec "$IFACE0" ip route || exit $?
echo done

# ping to get ARP entry
ip netns exec "$IFACE0" ping -c1 "${IFACE1_ADDR}" || exit $?
sleep "0.15s" || exit $?

# run test in namespace "$IFACE0"
ACTUAL="$(ip netns exec "$IFACE0" $UNIT_TEST/test_ip_dump)" || exit $?

set +x
read -r -d '' EXPECTED <<EOF
ARP table:
  192.168. 42. 11  40:00:00:80:00:f1  $(printf '%2u' $IFACE0_IDX)  1d
Routing table:
  192.168. 42. 11    0.  0.  0.  0    0.  0.  0.  0   0    0.  0.  0.  0  $(printf '%2u' $IFACE0_IDX)  51
    0.  0.  0.  0  192.168. 36.  0  255.255.255.  0  24    0.  0.  0.  0  $(printf '%2u' $IFACE0_IDX)  4d
    0.  0.  0.  0  192.168. 42.  0  255.255.255.  0  24  192.168. 42. 10  $(printf '%2u' $IFACE0_IDX)  6d
EOF

if [[ "$ACTUAL" != "$EXPECTED" ]]; then
  echo "Unexpected output" >&2
  exit 1
fi
set -x

# TODO verify output of previous command, or possibly
#      change it to do verification internally

# Clean up

# delete namespaces
ip netns delete "$IFACE0" &> /dev/null
ip netns delete "$IFACE1" &> /dev/null

# delete interfaces
ip link del dev "$IFACE0" &> /dev/null
ip link del dev "$IFACE1" &> /dev/null

echo pass
exit 0
