/*
	Linux Kernel Hacking:
	net/atm/br2684.c	// MC_FastPath_Enter()
	net/ipv4/ipmr.c		// ipmr_cache_find() and mcast forward
*/

#include <linux/ip.h>
#include <linux/mroute.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include "mc_fastpath_core.h"

#define	MODULE_NAME	"Realtek MCast FastPath"
struct mfc_cache *ipmr_cache_find(struct net *net, __be32 origin, __be32 mcastgrp);
int ip_mr_forward_fast(struct sk_buff *skb, struct mfc_cache *cache);
static int mc_fp_on=1;

int MC_FastPath_Enter(struct sk_buff *skb)	/* Ethertype = 0x0800 (IP Packet) */
{
	struct iphdr *iph;
	struct mfc_cache *mcache;
	
	if (!mc_fp_on)
		return 0;
	iph = (struct iphdr *)skb_network_header(skb);
	if(iph->frag_off & htons(0x3fff)){ // fragmentted packets go normal path
		return 0;
	}
	
	mcache = ipmr_cache_find(&init_net, 0, iph->daddr);
	if (mcache) {
		//printk("find 0x%x\n", iph->daddr);
		return ip_mr_forward_fast(skb, mcache);
	}
	return 0;
}

static struct proc_dir_entry *FP_Proc_File;
#define PROCFS_NAME 		"mc_FastPath"
static int fp_proc_read(struct seq_file* s, void* v)
{
	if(mc_fp_on==1)
		seq_printf(s, "mcast fastpath ON!\n");
	if(mc_fp_on==0)
		seq_printf(s, "mcast fastpath OFF!\n");
	return 0;
}

static int fp_proc_open(struct inode *inode, struct file *file)
{
	return(single_open(file, fp_proc_read, NULL));
}


static ssize_t fp_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
	char proc_buffer[count];
	
	/* write data to the buffer */
	memset(proc_buffer, 0, sizeof(proc_buffer));
	if ( copy_from_user(proc_buffer, buffer, count) ) {
		return -EFAULT;
	}
	switch(proc_buffer[0]) {
	case '0':
		mc_fp_on = 0;
		break;
	case '1':
		mc_fp_on = 1;
		break;
	default:
		printk("Error setting!\n");
	}
	return -1;
}

static const struct file_operations fp_proc_fops = {
	.open           = fp_proc_open,
	.write          = fp_proc_write,
	.read           = seq_read,
	.llseek         = seq_lseek,
	.release        = single_release,
};


static int __init mc_fastpath_init(void)
{
	printk("%s\n",MODULE_NAME);
	//create proc
	FP_Proc_File = proc_create_data(PROCFS_NAME, 0644, NULL, &fp_proc_fops, NULL);

	if (FP_Proc_File == NULL) {
		printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
			PROCFS_NAME);
		return -ENOMEM;
	}
	
	printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME);
	return 0;
}

static void __exit mc_fastpath_exit(void)
{
	printk("%s removed!\n", MODULE_NAME);
}

module_init(mc_fastpath_init);
module_exit(mc_fastpath_exit);