#!/bin/sh

packet=/tmp/udp

if [ -z "$2" ]; then
  echo "Usage: flood payload seconds [iface]">&2
  exit 1
fi

payload="$1"
ip="10.0.0.2"
sec="$2"
target="$3"
len=$(($payload + 14 + 20 + 8))
l2len=$(($len+8+4+12))

set -f
set `arp -n $ip`

case "$4" in
no|*incomplete*)
	echo "$ip: looking for MAC (not cached)"
	ping -W 1000 -c1 -q $ip >/dev/null 2>&1
	set `arp -n $ip`
	;;
esac

if [ "$4" = no ]; then
	echo "$ip: MAC not found, assume it's reachable through gateway"
	eval `route -n get $ip | awk '/interface:/ {printf "iface=%s\n", $2;}'`
else
	iface="$6"
fi

echo "$ip: reachable through interface: $iface"

fakeip=no
if [ "$4" = '(incomplete)' ]; then
  echo "$ip: MAC not found at $iface, using faked MAC"
  arp -S $ip 01:01:01:01:01:01 && fakeip=yes
fi

sysctl_rx() {
#  sysctl dev.$1 | awk '/\.rx_frames_/ { s+=$2 } END {print s}'
#  sysctl dev.$1 | awk '/\.mac_stats.good_pkts_recvd/ { s+=$2 } END {print s}'
#  sysctl dev.$1 | awk '/\.mac_stats.total_pkts_recvd/ { s+=$2 } END {print s}'
  sysctl dev.$1.mac_stats.total_pkts_recvd | awk '{ print $2 }'
}

sysctl_tx() {
  sysctl dev.$1.mac_stats.total_pkts_txd | awk '{ print $2 }'
}

mk_packet() {
  echo "$ip: preparing a packet for flood..."
  rm -f $packet.pcap
  [ -n "$target" ] && iface="$target"
  tcpdump -w $packet.pcap -i $iface -s0 -c1 -np dst host $ip and udp port 5001 >/dev/null 2>&1 &
  tpid=$!
  iperf -l $payload -u -t 1 -c $ip -b 10K >/dev/null 2>&1
  sleep 1
  kill $tpid >/dev/null 2>&1

  set -- `ls -l $packet.pcap`
  if [ -z "$5" -o "$5" -le 24 ]; then
    echo $ip: cannot prepare a packet, stop. >&2
    exit 1
  fi

  tail -c $len $packet.pcap > $packet.raw
}

mk_packet
[ $fakeip = yes ] && arp -d $ip

echo "$ip: flooding through $iface: payload=$payload iplen="$(($payload+20+8))" framelen=$len l2len=$l2len"

stop_and_show() {
  ngctl msg $iface:orphans stop
  eval `ngctl msg $iface:orphans getstats |\
    awk '/^Args:/ { print $3, $4, $16, $17 }'`
  sleep 2
  s_tx=$((`sysctl_tx igb.0`-$s_tx))
  s_rx=$((`sysctl_rx igb.1`-$s_rx))
  echo $ip: flood time was `printf "scale=3\n$tv_sec+${tv_usec}/1000000\n" | bc -l` sec, $s_tx frames sent
  echo $ip: flood L2 speed was `printf "scale=3\n$s_tx/($tv_sec+${tv_usec}/1000000)\n" | bc -l` frames/sec
  echo $ip: flood L2 speed was `printf "scale=3\n$l2len*$s_tx*8/($tv_sec+${tv_usec}/1000000)/1000000\n" | bc -l` Mbit/sec

  ngctl shutdown $iface:orphans
  lost=`printf "scale=3\n($s_tx-$s_rx)/($tv_sec+${tv_usec}/1000000)\n" | bc -l`
  echo $ip: "lost " $(($s_tx-$s_rx)) frames, $lost frames/sec, `printf "scale=3\n($s_tx-$s_rx)*100/$s_tx\n" | bc -l`\%
  echo $ip: transmitted $s_tx, received $s_rx
}

trap 'stop_and_show; exit 0' SIGHUP SIGINT SIGTERM

count=1000000000

if ngctl mkpeer $iface: source orphans output >/dev/null 2>&1; then
  sleep 2
  s_tx=`sysctl_tx igb.0`
  s_rx=`sysctl_rx igb.1`
  nghook $iface:orphans input < $packet.raw >/dev/null 2>&1
  ngctl msg $iface:orphans start $count
  sleep $sec
  stop_and_show
  exit 0
fi
echo $ip: cannot flood through interface $iface