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

#include <linux/ipv6.h>
#include <linux/mroute.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include "ip6mc_fastpath_core.h"

#define	MODULE_NAME			"Realtek IPv6 MCast FastPath"
#define PROCFS_NAME 		"ip6mc_FastPath"

extern struct mfc6_cache *ip6mr_cache_find(struct net *net,
					   struct in6_addr *origin,
					   struct in6_addr *mcastgrp);
extern int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache);


static int ip6mc_fp_on=1;
static struct proc_dir_entry *FP_Proc_File;

int Ip6_MC_FastPath_Enter(struct sk_buff *skb)	/* Ethertype = 0x0800 (IP Packet) */
{
	struct mfc6_cache *cache;
	struct net *net = dev_net(skb->dev);
	
	if (!ip6mc_fp_on)
		return 0;
	
	cache = ip6mr_cache_find(net,
				 &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
	if (cache) {
		return ip6_mr_forward(skb, cache);
	}

	return 0;
}

static int ip6_fp_proc_read(struct seq_file*s, void *v)
{
	if(ip6mc_fp_on == 1)
		seq_printf(s, "ipv6 mcast fastpath ON!\n");
	if(ip6mc_fp_on == 0)
		seq_printf(s, "ipv6 mcast fastpath OFF!\n");
	return 0;
}


static int ip6_fp_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, ip6_fp_proc_read, NULL);
}

static ssize_t ip6_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':
		ip6mc_fp_on = 0;
		break;
	case '1':
		ip6mc_fp_on = 1;
		break;
	default:
		printk("Error setting!\n");
	}
	return -1;
}

static const struct file_operations ip6_fp_proc_fops = {
	.open       = ip6_fp_proc_open,
	.write		= ip6_fp_proc_write,
	.read       = seq_read,
	.llseek     = seq_lseek,
	.release    = single_release,
};

static int __init ip6mc_fastpath_init(void)
{
	printk("%s\n",MODULE_NAME);
	//create proc
	FP_Proc_File= proc_create_data(PROCFS_NAME, 0644, NULL, &ip6_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 ip6mc_fastpath_exit(void)
{
	printk("%s removed!\n", MODULE_NAME);
}

module_init(ip6mc_fastpath_init);
module_exit(ip6mc_fastpath_exit);