#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <stdlib.h> /* free, exit */


#include <string.h> /* strdup */

#include <pcap.h>

#include <net/ethernet.h> /* ETHERTYPE_, struct ethhdr */
#include <netinet/ether.h> /* ether_ntoa */

#include <sys/socket.h> /* inet_ntoa */
#include <netinet/in.h> /* IPPROTO_TCP ... */
#include <arpa/inet.h>

#include <linux/ip.h> /* struct iphdr */

#include "main.h"
#include "pcap_ip.h"

void pcap_packet_cb(u_char *args, const struct pcap_pkthdr *header,
		const u_char *packet)
{
	struct ethhdr *ethernet;
	unsigned short et;

	ethernet = (struct ethhdr*)packet;

	DEBUGP(D_NOTICE, "eth src: %s\n",
			ether_ntoa((struct ether_addr*)ethernet->h_dest));
	DEBUGP(D_NOTICE, "eth dst: %s\n",
			ether_ntoa((struct ether_addr*)ethernet->h_source));

	et = ethernet->h_proto<<8;
	switch (et) {
	case ETHERTYPE_IP:
		DEBUGP(D_NOTICE, "ip packet!\n");
		handle_ip(packet);
		break;
	case ETHERTYPE_ARP:
		DEBUGP(D_NOTICE, "arp packet!\n");
		break;
	default:
		DEBUGP(D_WARNING, "Unknown ethernet packet type: %d\n", et);
		break;
	}
}

int main(int argc, char *argv[])
{
	char errbuf[PCAP_ERRBUF_SIZE];
	char *dev=NULL;
	char *filter_string=NULL;
	int numpkt=10000, promisc=0, i;
	pcap_t *handle;
	struct bpf_program filter;	/* The compiled filter expression */
	bpf_u_int32 mask;		/* The netmask configured on device */
	bpf_u_int32 net;		/* The IP of our sniffing device */
#if 0
	struct pcap_pkthdr header;	/* The header that pcap gives us */
	const u_char *packet;		/* The actual packet */
#endif

	int c, option_index=0;
	static struct option long_options [] = {
		{
			.name = "device",
			.has_arg = 1,
			.flag = NULL,
			.val = 'd'
		},
		{
			.name = "filter",
			.has_arg = 1,
			.flag = NULL,
			.val = 'f'
		},
		{
			.name = "greeting",
			.has_arg = 1,
			.flag = NULL,
			.val = 'g'
		}
	};
	char *short_options = "d:f:g:";

	/* parse command line arguments */
	c = getopt_long(argc, argv, short_options, long_options, &option_index);
	while (c >= 0) {
		switch (c) {
		case 'd':
			dev = optarg;
		break;
		case 'g':
			DEBUGP(D_NOW, "%s\n", optarg);
		break;
		case 'f':
			filter_string = optarg;
		break;
		default:
			DEBUGP(D_WARNING, "Missing option: %c", c);
		break;
		}
		c = getopt_long(argc, argv, short_options, long_options, &option_index);
	}

	/* look up default settings */
	if (!dev) {
		dev = pcap_lookupdev(errbuf);
		if (!dev)
			exit(1);
	}
	DEBUGP(D_NOW, "Device: %s\n", dev);

	i = pcap_lookupnet(dev, &net, &mask, errbuf);
	if (i < 0) 
		exit(2);
	DEBUGP(D_NOTICE, "Network: %s\n", inet_ntoa(*(struct in_addr*)&net));
	DEBUGP(D_NOTICE, "Netmask: %s\n", inet_ntoa(*(struct in_addr*)&mask));

	/* open up device for sniffing */
	handle = pcap_open_live(dev, BUFSIZ, promisc, 0, errbuf);
	if (!handle) {
		DEBUGP(D_FATAL, "%s: %s\n", __FUNCTION__, errbuf);
		exit(3);
	}

	i = pcap_datalink(handle);
	if (i != DLT_EN10MB) {
		DEBUGP(D_FATAL, "I can only handle ethernet. (datalink=%d)\n", i);
		exit(4);
	}

	/* set filters */
	if (filter_string) {
		i = pcap_compile(handle, &filter, filter_string, 0, net);
		if (i != 0) {
			DEBUGP(D_WARNING, "filter '%s' did not compile, skipping.\n",
					filter_string);
		} else {
			pcap_setfilter(handle, &filter);
		}
	}

#if 0
	packet = pcap_next(handle, &header);
	printf("Jacked a packet with length of [%d]\n", header.len);
#endif

	/* main loop */
	pcap_loop(handle, numpkt, pcap_packet_cb, NULL);

	/* we are done... */
	pcap_close(handle);
	return 0;
}

