#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/uaccess.h> 
#include <linux/sysctl.h>
#include <linux/ip.h>
#include <linux/ipv6.h>


/*
 *	三元组超时时间设置为120s
 */
int dpi_cache_node_timeout = 120;

/*
 *	三元组数据结构
*/
struct dpi_cache_node {
	struct list_head list;

	union {
		struct in6_addr  ip6;
		struct in_addr   ip4;
		
	}ip;

	__u16 proto;
	__u16 port;
	__u32 appid;
	uint64_t uptime;
};


/*
 *	初始化cache链表
 */
#define TRIPLE_CAHCE_HASH_SIZE (2048)
struct list_head dpi_cache_head[TRIPLE_CAHCE_HASH_SIZE] = {0};

typedef struct pkt_info{
    unsigned int srcip;
    unsigned short sport;
    unsigned int dstip;
    unsigned short dport;
    unsigned short proto;
}pkt_info_t;

#define POLY 0x01101 // CRC20生成多项式x^20+x^12+x^8+1即:01101 CRC32:04C11DB7L
static unsigned int crc_table[256] = {0};

unsigned int get_sum_poly(unsigned char data)
{
    unsigned int sum_poly = data;
    int j;
    sum_poly <<= 24;
    for(j = 0; j < 8; j++)
    {
        int hi = sum_poly&0x80000000; // 取得reg的最高位
        sum_poly <<= 1;
        if(hi) sum_poly = sum_poly^POLY;
    }
    return sum_poly;
}

void create_crc_table(void)  //在使用CRC20_key函数应该先建立crc表
{
    int i;
    for(i = 0; i < 256; i++)
    {
    	crc_table[i] = get_sum_poly(i&0xFF);
    }
}

unsigned int CRC20_key(unsigned char* data, int len)
{
    int i;
    unsigned int reg = 0xFFFFFFFF;// 0xFFFFFFFF,见后面解释
    for(i = 0; i < len; i++)
    {
        reg = (reg<<8) ^ crc_table[((reg>>24)&0xFF) ^ data[i]];
    }
    return (reg&0XFFFFF);//得到的reg取后20作为key值
}

/*
 * 初始化dpi三元组hash链表
 */
void dpi_cache_list_init(void)
{
	int i = 0;
	for (i = 0; i < TRIPLE_CAHCE_HASH_SIZE; i++){
		INIT_LIST_HEAD(&dpi_cache_head[i]);
	}
	
	return;
}


/*
 * 三元组hash值计算
 */
int dpi_cache_node_hash(struct dpi_cache_node node)
{
	int i = 0;
	int dip = 0;

	pkt_info_t info;
	info.dport = node.port;
	info.sport = 0;
	info.srcip = 0;
	info.proto = node.proto;
	if (node.ip.ip4.s_addr !=0 ) {
		info.dstip = node.ip.ip4.s_addr;
	} else {
		for (i = 0; i < 4; ++i) {
			dip ^=  node.ip.ip6.s6_addr32[i];
		}
		info.dstip = dip;
	}
	
	return CRC20_key((unsigned char *)&info, sizeof(pkt_info_t))&(TRIPLE_CAHCE_HASH_SIZE - 1);
}


/*
 *   src 和dst比较,并更新dst的appid,update_appid=true,update
*/
bool dpi_cache_node_compare(struct dpi_cache_node src,struct dpi_cache_node dst,bool update_appid)
{
	int i = 0;
	if (src.port != dst.port){
		return false;
	}

	if (src.proto != dst.proto){
		return false;
	}

	if(src.ip.ip4.s_addr != 0){
		if(src.ip.ip4.s_addr != dst.ip.ip4.s_addr){
			return false;
		}
	}else{
		for (i = 0; i < 4; i++){
			if (src.ip.ip6.s6_addr32[i] != dst.ip.ip6.s6_addr32[i]){
				return  false;
			}
		}

	}

	if (update_appid){
			dst.appid = src.appid;
			dst.uptime = get_jiffies_64();
	}
	
	return true;
}


/*
 *遍历hash链表,查找node
 */
bool dpi_cache_node_search(struct dpi_cache_node node,int flag)
{
	struct list_head * pList;
	struct dpi_cache_node *pNode;
	int hash  = dpi_cache_node_hash(node);
	list_for_each(pList,&dpi_cache_head[hash]){
		pNode = list_entry(pList,struct dpi_cache_node,list);
		if (dpi_cache_node_compare(node,*pNode,flag)){
			return true;
		}
	}
	
	return false;
}


/*
 *  DPI 信息记录,外部接口调用
*/
bool dpi_cache_node_add(struct dpi_cache_node node)
{
	int hash = 0;
	struct dpi_cache_node *pNode =NULL;
	
	if (dpi_cache_node_search(node,true)) {
		return true;
	}
		
	pNode = kmalloc(sizeof(struct dpi_cache_node), GFP_KERNEL);
	if (NULL == pNode) {
		return false;
	}
	
	hash = dpi_cache_node_hash(node);
	pNode->uptime = get_jiffies_64();
	pNode->appid = node.appid;
	list_add_tail(&pNode->list,&dpi_cache_head[hash]);
	return true;
}


/*
 * 释放hash链表上所有node节点
*/
void dpi_cache_list_release(void)
{
	struct list_head * pList = NULL;
	struct dpi_cache_node *pNode = NULL;
	int i = 0;
	
	for (i = 0; i < TRIPLE_CAHCE_HASH_SIZE; i++){
		list_for_each(pList,&dpi_cache_head[i]){
			pNode = list_entry(pList,struct dpi_cache_node,list);
			if(pNode){
				list_del(&pNode->list);
			}

		}
	}
}


/*
 *定时器超时,删除超时的节点
 */
void dpi_cache_node_timeout_func(void)
{
	struct list_head * pList = NULL;
	struct dpi_cache_node *pNode = NULL;
	int i = 0;
	
	for (i = 0; i < TRIPLE_CAHCE_HASH_SIZE; i++){
		list_for_each(pList,&dpi_cache_head[i]){
			pNode = list_entry(pList,struct dpi_cache_node,list);
			if(pNode&&
				(get_jiffies_64() - pNode->uptime >= dpi_cache_node_timeout)){
				list_del(&pNode->list);
			}


		}
	}
}

/*
 *  定时器操作
 */
struct timer_list gTimer;

void dpi_cache_timer_handler(unsigned long data) {
    printk(KERN_INFO"timer pending:%d\n", timer_pending(&gTimer));
	dpi_cache_node_timeout_func();
    mod_timer(&gTimer, jiffies+msecs_to_jiffies(dpi_cache_node_timeout*1000));

}

int dpi_cache_timer_init(void) {
    printk(KERN_INFO"%s jiffies:%ld\n", __func__, jiffies);
    printk(KERN_INFO"ji:%d,HZ:%d\n", jiffies_to_msecs(250), HZ);
	init_timer(&gTimer);
    gTimer.expires = jiffies + dpi_cache_node_timeout*HZ;
	gTimer.function = dpi_cache_timer_handler;
    add_timer(&gTimer);
    printk(KERN_INFO"timer pending:%d\n", timer_pending(&gTimer));
    return 0;
}
 
void dpi_cache_timer_exit(void) {
    printk(KERN_INFO"%s jiffies:%ld\n", __func__, jiffies);
    del_timer(&gTimer);
}

int dpi_cahce_module_init(void)
{
	dpi_cache_timer_init();
	dpi_cache_list_init();
	return 1;
}

int dpi_cache_module_exit(void)
{
	dpi_cache_timer_exit();
	dpi_cache_list_release();
	return 1;
}

module_init(dpi_cahce_module_init);
module_exit(dpi_cache_timer_exit);
MODULE_LICENSE("GPL");