/* (C) Cambridge University Computer Laboratory, 2000
 *     All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Systems Research
 *      Group at Cambridge University Computer Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <signal.h>
#include <sys/time.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "ecn.h"

typedef unsigned int uint32_t;

#define MTU 1514

void
die (const char *s)
{
    perror (s);
    exit (1);
}


volatile int marks_seen = 0;

void
mark_printer (int sig)
{
    static int last_seen = 0;
    int m = marks_seen; /* sample the counter */

    fprintf (stderr, "Marks/sec = %2d\n", m - last_seen);
    last_seen = m;
}


void
usage (char *progname)
{
    fprintf (stderr, "usage: %s [ <port> ]\n"
	     "\tListen on <port> for UDP traffic, and print it to\n"
	     "\tstdout, with periodic ECN mark reports to stderr\n",
	     progname);
    exit (1);
}


int
main (int argc, char **argv)
{
    int fd;
    int ret;
    struct sockaddr_in laddr;
    int port = htons (5000);
    int tos;
    char rxbuf[MTU];

    if (argc > 2)
	usage (argv[0]);

    if (argc == 2 && (port = htons (atoi (argv[1]))) == 0)
    {
	fprintf (stderr, "%s: unable to parse port number\n", argv[0]);
	usage (argv[0]);
    }
    

    fd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (fd < 0)
	die("socket");

    laddr.sin_family = AF_INET;
    laddr.sin_port = port;
    laddr.sin_addr.s_addr = 0;  /* all interfaces */
    ret = bind (fd, (void*)&laddr, sizeof(laddr));
    if (ret < 0)
	die ("bind");

    if (socket_setrecvtos (fd, 1) < 0)
	die ("setrecvtos");

    /* setup mark printer signal handler and delivery */
    {
	struct sigaction sa;
	struct itimerval it;

	sa.sa_handler = mark_printer;
	sigemptyset (&sa.sa_mask);
	sa.sa_flags = SA_RESTART;  /* need to restart syscalls */

	ret = sigaction (SIGALRM, &sa, NULL);
	if (ret < 0)
	    die ("sigaction");

	/* trigger every second, after waiting an initial second */
	it.it_interval.tv_sec  = 1;
	it.it_interval.tv_usec = 0;
	it.it_value = it.it_interval;
	ret = setitimer (ITIMER_REAL, &it, NULL);
	if (ret < 0)
	    die ("setitimer");
    }

    /* listen for packets */
    while(1)
    {
	/* get the data & flip it out again */
	ret = recv_withtos (fd, rxbuf, sizeof(rxbuf), 0/*flags*/, &tos);
	if (ret < 0)
	    die ("recv_withtos");

	/* EOF, so exit */
	if (ret == 0)
	    exit (0);

	if (tos < 0)
	    die ("bad tos");

	/* Mark seen iff have ECN Capable Transport and packet
         * experienced congestion. */
	if ((tos & TOS_ECN_ECT) && (tos & TOS_ECN_CE))
	    marks_seen++;

	printf ("%.*s", ret, rxbuf);
    }
}
