/*
 * Copyright (c) 2002-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagfwddemo.c 12688 2010-03-31 21:27:06Z sfd $
 *
 * Takes network traffic traces of various formats, allows filtering and
 * packet manipulations to be performed and writes modified traces out in
 * various formats.
 */

/* Endace headers. */
#include "dagutil.h"
#include "dagapi.h"
#include "dag_platform.h"

/* Project headers. */
#include "inline_filter.h"

#if defined(_WIN32)

#include <getopt.h>
extern char *optarg;

#endif /* Platform-specific code. */


/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagfwddemo.c 12688 2010-03-31 21:27:06Z sfd $";
static const char* const kRevisionString = "$Revision: 12688 $";

#define DEFAULT_DAG_DEVICE "dag0"
static char dagname[DAGNAME_BUFSIZE] = DEFAULT_DAG_DEVICE;
static char txdagname[DAGNAME_BUFSIZE] = "";
static int rxstream = 0;
static int txstream = 1;

static const char usage_text[] = 
"dagfwddemo - utility for demonstrating forwarding using Endace DAG cards.\n"
"Usage: dagfwddemo [options] \"BPF filter\"\n"
"Options:\n"
"   -h,-?,--help,--usage    Display this page.\n"
"   -V,--version            Display the version information.\n"
"   -d <device>             Specify the device to use (default is " DEFAULT_DAG_DEVICE ").\n"
"   -i                      Do not change the interface number when forwarding.\n"
"   -t <seconds>            Runtime in seconds (default is to run forever).\n"
"   -R                      Low latency Receive mode, be aware enable this option will use all CPU\n"
"   -B <kBytes>             Maximum bytes allow to be cumulated before transmit. Default 64KB\n"
"                           Larger values will use less CPU, but will introduct longer latency\n"
"                           Smaller values will use more CPU, but will get better latency\n"
"   -c                      Copy transmit mode, work with normal memory hole setup.\n"
"                           By default dagfwddemo works with overlap memory hole setup.\n"
"   -C <device>             Optionally specify transmit device for copy mode, e.g. -C dag1:1\n"
"   -f <filename>           Save records to a file at the same time, by default, records being\n"
"                           saved are the same as records being forwarded. But an alternate filter\n"
"                           could be used to filter the records to be saved using the -F switch.\n"
"                           A buffer of 256MB will be used to ensure all packets are written to disk.\n"
"   -m <sizeinMB>           change the size of disk writing buffer\n"                        
"   -p <pollinterval>       poll interval in micro secs \n"
"                           Note that when the disk is full, the dagfwddemo will exit\n"
"   -F \"BPF filter\"       Alternate BPF filter for records saved to file"
"\n"
"  By default dagfwddemo will work on overlapped memory holes. \n"
"  If you want to make dagfwddemo working on normal memory holes  use -c \n"
"\n"
"  The filter expression can be any valid BPF filter.  See the tcpdump man pages for more information.\n"
"  Usage example: dagfwddemo \"icmp\"\n"
"  Usage example: dagfwddemo \"icmp and tcp\"\n"
"  Usage example: dagfwddemo -d 0 -f icmp.erf -F \"icmp\" \"tcp and udp\"\n"
"                  icmp will be safed to a file, tcp and udp will be forwarded"
"  Usage example: dagfwddemo \"\"\n"
"                 All packets will be forwarded\n"
"\n";

/* Internal routines. */
static void print_version(void);
static void print_usage(void);


/* Implementation of internal routines. */
static void
print_version(void)
{
	printf("dagfwddemo (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}


static void
print_usage(void)
{
	print_version();
	printf("%s", usage_text);
}


int
dagfwddemo_main(int argc, char* argv[])
{
	char opt = 0;
	int swap_iface = 1;
	int rx = 0;
	unsigned int runtime_seconds = 0;
	unsigned int poll_input = 1000;
	unsigned long mem_cache_size_mb=0;
	unsigned long tx_cumulate_bytes=(TX_ACCUM_BYTES_MAX/1024);

	dagutil_set_progname("dagfwddemo");

	/* Set up default DAG device. */
	if (-1 == dag_parse_name(DEFAULT_DAG_DEVICE, dagname, DAGNAME_BUFSIZE, &rxstream))
	{
		dagutil_panic("dag_parse_name(%s): %s\n", DEFAULT_DAG_DEVICE, strerror(errno));
	}

	while ((opt = getopt(argc, argv, "p:d:C:t:hiVRc?-:f:F:B:m:v")) != EOF)
	{
		switch (opt)
		{
		case 'V':
			print_version();
			return EXIT_SUCCESS;
			break;

		case 'h':
		case '?':
			print_usage();
			return EXIT_SUCCESS;
			break;

		case 'v':
			dagutil_inc_verbosity();
			set_dag_fwd_verbosity();
			break;

		case 'i':
			swap_iface = 0;
			break;

		case 't':
			runtime_seconds = strtoul(optarg, NULL, 0);
			break;
		case 'p':
			poll_input  = strtoul(optarg, NULL, 0);
				printf("poll input is %d\n", poll_input);
			break;


		case 'd':
			if (-1 == dag_parse_name(optarg, dagname, DAGNAME_BUFSIZE, &rxstream))
			{
				dagutil_panic("dag_parse_name(%s): %s\n", optarg, strerror(errno));
			}
			break;

		case 'C':
			if (-1 == dag_parse_name(optarg, txdagname, DAGNAME_BUFSIZE, &txstream))
			{
				dagutil_panic("dag_parse_name(%s): %s\n", optarg, strerror(errno));
			}
			break;

		case 'R':
			rx = 1;
			break;
		case 'c':
			set_copy_transmit();
			break;

		case 'f':
			printf("file name: %s\n",optarg);
			set_file_name(optarg);

			break;
		case 'F':
			printf("file filter: %s\n",optarg);
			set_file_filter_expr(optarg);
			break;
		case 'B':
			tx_cumulate_bytes = strtoul(optarg,NULL,10);
			break;
		case 'm':
			mem_cache_size_mb = strtoul(optarg,NULL,10);
			if(mem_cache_size_mb>2048)
			{
				printf("Size of disk buffer specified is too large: %lu\n",mem_cache_size_mb);
				print_usage();
				return EXIT_SUCCESS;
			}
			set_disk_cache_size(mem_cache_size_mb);
			break;
		case '-':
			if (strcmp(optarg, "help") == 0 || strcmp(optarg, "usage") == 0)
			{
				print_usage();
				return EXIT_SUCCESS;
			}
			else if (strcmp(optarg, "version") == 0)
			{
				print_version();
				return EXIT_SUCCESS;
			}
			else if (strcmp(optarg, "verbose") == 0)
			{
				dagutil_inc_verbosity();
			}
			break;

		default:
			print_usage();
			dagutil_panic("unknown option or argument to -%c\n", optopt);
			break;
		}
	}

	argc -= optind;
	argv += optind;

	printf("dagfwddemo: Starting up.\n");

	if(0 == txdagname[0])
		strncpy(txdagname, dagname, DAGNAME_BUFSIZE);

	set_device_name(dagname, rxstream);
	set_tx_device_name(txdagname, txstream);
	set_low_latency(rx);

	printf("tx_cumulate_bytes: %lu Kbytes\n",tx_cumulate_bytes);
	set_tx_accum_bytes(tx_cumulate_bytes);
	if (0 != swap_iface)
	{
		set_interface_swap();
	}
	
	if (argc > 0)
	{
		printf("forward filter %s\n",argv[0]);
		set_filter_expression(argv[0]);
	}

	if (init_filter() == -1)
	{
		printf("dagfwddemo: Invalid filter.\n");
		return -1;
	}

	run_inline_filter(runtime_seconds,poll_input);
	
	printf("dagfwddemo: Finished.\n");
	
	return EXIT_SUCCESS;
}


#ifndef ENDACE_UNIT_TEST
int
main(int argc, const char* const * argv)
{
	return dagfwddemo_main(argc, (char**) argv);
}
#endif /* ENDACE_UNIT_TEST */
