#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
#include <uapi/linux/netfilter_ipv4.h>
#include <uapi/linux/ip.h>
#include <net/netlink.h>
#include <net/net_namespace.h>

#include "../netlink_api/libnetlink_k.h"
#include "../../../Common/commuapinl.h"



struct netlinkk_cfg g_upmnlcfg;
struct commnl_msgtype_process cfgnl_msg_handlers[COMMCFG_NLMSG_MAX_TYPE];

int cfgnl_unicast(struct sk_buff *skb, u32 portid)
{
	return(commnl_unicast(g_upmnlcfg.sk, skb, portid));
}


#if 0
int nl_upm_data_ready(struct sk_buff *skb, struct nlmsghdr *nlh)
{
    void *payload;
    struct sk_buff *out_skb;
    void *out_payload;
    struct nlmsghdr *out_nlh;
    int payload_len; // with padding, but ok for echo 
    
	printk(KERN_DEBUG "nl_upm_data_ready() begin.\n");

    switch(nlh->nlmsg_type)
    {
        case COMMNMSG_POLICYCONF:/**/

			payload = nlmsg_data(nlh);
			payload_len = nlmsg_len(nlh);
			printk(KERN_INFO "Recievid: %s, From: %d\n", (char *)payload, nlh->nlmsg_pid);
			
			out_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); //分配足以存放默认大小的sk_buff
			if (!out_skb) goto failure;
			
			out_nlh = nlmsg_put(out_skb, 0, 0, COMMNMSG_POLICYCONF, payload_len, 0); //填充协议头数据
			if (!out_nlh) goto failure;
			
			//out_payload = nlmsg_data(out_nlh);
			//nla_put_u32(out_skb,PDELIVERY_DUMP_PKTNUM_ATTR,g_nlcfg->pkt_delev_num);
			
			commnl_unicast(g_upmnlcfg.sk, out_skb, nlh->nlmsg_pid);
            break;

        default:
            printk(KERN_INFO "libnetlink Unknow msgtype recieved!\n");
			return 0;
    }
		
	printk(KERN_DEBUG "nl_upm_data_ready() end.\n");
	
    return 0;
failure:
    printk(KERN_INFO " failed in fun dataready!\n");

	return 0;
}
#endif

int nl_cfg_data_ready(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack)
{
	int ret = 0;
     
	printk(KERN_DEBUG "nl_cfg_data_ready() nlmsg_type = %d begin.\n",nlh->nlmsg_type);
	g_upmnlcfg.dfs.rev_total++;
	
	if(NULL != cfgnl_msg_handlers[nlh->nlmsg_type].doit)
	{
		cfgnl_msg_handlers[nlh->nlmsg_type].dfs.rev_total++;
		ret = cfgnl_msg_handlers[nlh->nlmsg_type].doit(skb, nlh,extack);
		if(ret >= 0)
		{
			g_upmnlcfg.dfs.rev_cb_sucess++;
			cfgnl_msg_handlers[nlh->nlmsg_type].dfs.rev_cb_sucess++;
		}
		else
		{
			g_upmnlcfg.dfs.rev_cb_fail++;
			cfgnl_msg_handlers[nlh->nlmsg_type].dfs.rev_cb_fail++;
		}
		
	}
	else
	{
		cfgnl_msg_handlers[nlh->nlmsg_type].dfs.rev_drop_total++;
		cfgnl_msg_handlers[nlh->nlmsg_type].dfs.rev_drop_nodoit++;
		g_upmnlcfg.dfs.rev_drop_total++;		
		g_upmnlcfg.dfs.rev_drop_nodoit++;
		
		printk(KERN_WARNING "no doit fun register with nlmsg_type = %d .\n",nlh->nlmsg_type);
	}
			
	printk(KERN_DEBUG "nl_cfg_data_ready() nlmsg_type = %d end.\n",nlh->nlmsg_type);
	
	return ret;
}


static void libcfgnl_rcv(struct sk_buff *skb)
{
	printk(KERN_DEBUG "libcfgnl_rcv:\n");

	netlink_rcv_skb(skb, &nl_cfg_data_ready);
	
}


int cfgrcv_debugfs(struct sk_buff *skb, struct nlmsghdr *nlh,struct netlink_ext_ack *extack)
{
	int ret = 0;

    printk(KERN_INFO "cfgrcv_debugfs, From pid: %d\n", nlh->nlmsg_pid);

#ifdef NLDEBUG_ACK_COOKIES
	ret = debugfs_pkt_num_stati(&g_upmnlcfg, nlh,extack);
#else
	ret = debugfs_pkt_num_stati_witisend(&g_upmnlcfg, nlh,cfgnl_msg_handlers);
#endif

	printk("*****************biduichengong***************\n");
	return ret;

}

int cfg_msgtype_register( int msgtype,commnl_doit_func doit, 
			commnl_dumpit_func dumpit,commnl_calcit_func calcit)
{	
	if(msgtype >= COMMCFG_NLMSG_MAX_TYPE )
	{
		printk(KERN_ERR"netlink.ko-msgtype register invalid msgtype %d,protocl comcfg.\r\n",msgtype);
		return -1;
	}

	commnl_register(cfgnl_msg_handlers, msgtype,doit, dumpit,calcit);
	
	 printk(KERN_INFO"netlink.ko-msgtype register sucess msgtype %d,protocl comcfg.\r\n",msgtype);
	 
	 return 0;
}

int cfg_msgtype_unregister(int msgtype)
{
	if(msgtype >= COMMCFG_NLMSG_MAX_TYPE )
	{
		printk(KERN_ERR"commnl_unregister invalid msgtype %d,protocl conncfg.\r\n",msgtype);
		return -1;
	}
	
	commnl_unregister(cfgnl_msg_handlers,msgtype);

	printk(KERN_DEBUG"commnl_unregister sucess msgtype %d,protocl comcfg.\r\n",msgtype);
	
	return 0;	
}


int __init cfgrcv_init(void)
{
	int ret = -1;
	
	printk(KERN_INFO "cfgrcv.ko-initialed!\n");

	/*init for pdelivery module*/
	g_upmnlcfg.groups = 0;
	g_upmnlcfg.subscriptions = NETLINK_COMMCFG;/*创建配置处理通道*/	
	g_upmnlcfg.cfg.input = libcfgnl_rcv;
	
	ret = libnetlinkk_init_byproto(&g_upmnlcfg);
	if(ret < 0)
	{
		return ret;
	}	

	/*do msg process register*/
	ret = cfg_msgtype_register(COMMNMSG_CFG_DEBUGFS,cfgrcv_debugfs,NULL,NULL);
	
	return ret;
}

void __exit cfgrcv_exit(void)
{
    printk(KERN_CRIT "nl_upm existing...\n");
    libnetlinkk_exit(&g_upmnlcfg);

	return;
}

EXPORT_SYMBOL_GPL(cfgnl_unicast);
EXPORT_SYMBOL_GPL(cfg_msgtype_register);
EXPORT_SYMBOL_GPL(cfg_msgtype_unregister);



module_init(cfgrcv_init);
module_exit(cfgrcv_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("a simple example for upm(user policy manage) netlink protocal family");
MODULE_AUTHOR("RSLjdkt");