/* 
 * ourmon probe - main module.
 *
 * insmod ourmon.o [optional params] 
 *
 * input parameters may include:
 *
 * debug=1: turn debugging on 
 * 
 * device_name="device", default: eth0, so set if you want something else
 *
 * timer_val=N, default 30 seconds.   
 *
 * config_file="./config.bin", default is ./config.bin.  This is
 *     the load time config file.
 */

#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif

#include "includes.h"

#define TRUE   1
#define FALSE  0

/* module parameters ************************************************/
MODULE_LICENSE("BSD/GPL");

static int debug = 0; 
MODULE_PARM(debug, "i");  /* debug=1 */

static char *config_file="ourmon.bin";	/* compiled config file */
static int timer_val = 30;		/* default to 30 seconds */
static char *device_name = "eth0";	/* default interface name */

MODULE_PARM(timer_val, "i");
MODULE_PARM(device_name, "s");
MODULE_PARM(config_file, "s");

/* end module parameters ************************************************/

/* global variables.  TBD put in om global structure
 * to reduce name space problems.
*/
extern struct net_device_stats *stats;
extern unsigned long init_caught, init_drops;

/* local global variables
*/

/* module exit print this out
*/
static unsigned long packets_out, packets_in, packets_other;	
static unsigned long packets_mcast, packets_bcast;

/* eth0 turned into device pointer
*/
static struct net_device *device;

/* ourmon protocol structure
*/
static struct packet_type ourmon_proto;

/* ourmon_timer - copies current proc_file to fixed mon.lite output
*/
static struct timer_list ourmon_timer;

/* ourmon -- bottom-half packet receive function.
 *
*/
int 
ourmon_rcv(struct sk_buff *skb, struct net_device *dv, struct packet_type *pt) 
{

	spin_lock(&our_lock);

	/* push back to ll header
	*/
	skb_push(skb, skb->data - skb->mac.raw);

	/* setup h hdr to point to L4.  L3 plus size of L3.
	*/
        skb->h.raw = skb->nh.raw + skb->nh.iph->ihl*4;

	/* switch on packet type.  keep inputs
	*/
        switch (skb->pkt_type) {
	/* we don't care about packets going out
	*/
	case PACKET_OUTGOING:
               	packets_out++;
        	kfree_skb(skb);
        	return 0;
		break;
	case PACKET_BROADCAST:
		packets_bcast++;
		cur_monlite.fixed_count.count++; 
		cur_monlite.fixed_cast.bcast_bytes += skb->len;
		break;
	case PACKET_MULTICAST:
		packets_mcast++;
		cur_monlite.fixed_count.count++; 
		cur_monlite.fixed_cast.mcast_bytes += skb->len;
		break;
	/* packets to us
	*/
	case PACKET_HOST:
		packets_in++;
		cur_monlite.fixed_count.count++; 
		cur_monlite.fixed_cast.ucast_bytes += skb->len;
		break;
	/* packets picked up due to promiscuous mode
	*/
        case PACKET_OTHERHOST:
               	packets_other++;
		cur_monlite.fixed_count.count++; 
		cur_monlite.fixed_cast.ucast_bytes += skb->len;
		break;
	default:
		goto out;
	}

	/* packets at this point are only input packets
	*/

	/* analyze the packet and do the filtering.
	*/
	analyze(skb, debug);

	/* free the skb buffer
	*/
out:
        kfree_skb(skb);
	spin_unlock(&our_lock);
        return 0;
}

void  create_proc(void) 
{
        proc_mkdir("ourmon", NULL);
        create_proc_read_entry("ourmon/config", 0, NULL, 
		read_proc_config, NULL);
        create_proc_read_entry("ourmon/mon.lite", 0, NULL, 
		read_proc_preMonlite, NULL);
        create_proc_read_entry("ourmon/cur.mon.lite", 0, NULL, 
		read_proc_curMonlite, NULL);
}

void clean_proc(void) 
{
        remove_proc_entry("ourmon/config", 0);
        remove_proc_entry("ourmon/preMonlite", 0);
        remove_proc_entry("ourmon/curMonlite", 0);
        remove_proc_entry("ourmon", 0);
}      

/* put this in *.h file 
*/
void ourmon_timeout(unsigned long ptr);
        
void set_ourmontimer(void)
{
	init_timer(&ourmon_timer);  /*init a timer structure */
	ourmon_timer.expires = jiffies + timer_val*HZ;  /* set 30 secs alarm */
	ourmon_timer.function = ourmon_timeout;
	ourmon_timer.data = 0;
	/* insert my timer into the global list of active timers*/
	add_timer(&ourmon_timer); 
	if (debug)
		printk("timer begins.\n");
}

void ourmon_timeout(unsigned long ptr)
{
	spin_lock(&our_lock);
	copy_monlite();
	spin_unlock(&our_lock);
	set_ourmontimer();
}


/* init_module */
int 
init_module(void) 
{
	int rc = 0;

	printk("ourmon loaded: debug %d\n", debug);
	packets_in = packets_out = packets_other = 0;

	rc = config_init(config_file);
	if (rc < 0) 
		goto fail;

	create_proc();

	device = dev_get_by_name(device_name);
	if (!device) {
		printk("device <%s> does not exist\n", device_name);
		goto fail;
	}
	else {
		printk("device %s, ifindex: %i\n", device_name, 
			device->ifindex);
		ourmon_proto.dev = device;
	}
	if (!device->get_stats) {
		printk("don't know how to get stats infomation\n");
		goto fail;
	}
	stats = device->get_stats(device);
	init_caught = stats->rx_packets;
	init_drops = stats->rx_dropped;

	/* setup proto structure
	*/
	ourmon_proto.type = htons(ETH_P_ALL);
        ourmon_proto.func = ourmon_rcv;
        dev_add_pack(&ourmon_proto);

	set_ourmontimer();

	/* turn on promiscuous mode
	*/
	dev_set_promiscuity(device, 1);

	return(0);
fail:
	return(-EINVAL);
}

void 
cleanup_module(void) 
{
	del_timer_sync(&ourmon_timer);

	/* turn promiscuous mode off or at least
	* decrement its reference count
	*/
	dev_set_promiscuity(device, -1);

	/* remove the packet capture function
	*/
	dev_remove_pack(&ourmon_proto);

	/* remove the /proc entries
	*/
	clean_proc();

	printk("ourmon: unloaded\n");
	printk("ourmon: final stats: [in: %li] [out: %li] [mcast: %li] [bcast: %li] [other: %li]\n", 
		packets_in, packets_out, packets_mcast, packets_bcast, packets_other); 	
}
