/*******************************************************************************
  acl control 
         These api can set acl rules on RTL865X_ACL_USER_USED and RTL865X_ACL_DEFAULT chain
         In static acl mode 	: all pkts are forwarded in 8676 swich (hw-acc) 
                                       	, the user can set some filter rules let them dropped or trapped to cpu
         In dynamic acl mode 	: all pkts are trapped to cpu first
         					, the user can set permit ruls let some specific flow be hw-acc in 8676 swich 
********************************************************************************/
#include "rtl865x_acl_control.h"
#include "../AsicDriver/rtl865xc_asicregs.h"
#include "../AsicDriver/rtl865x_asicCom.h"
#include "../common/rtl_errno.h"
#include "../common/rtl865x_netif_local.h"
#include "../common/rtl865x_eventMgr.h" /*call back function....*/
#include "../l3Driver/rtl865x_ppp_local.h"
#include "../l3Driver/rtl865x_nexthop.h"
#include <net/rtl/rtl865x_fdb_api.h>
#include <linux/seq_file.h>
#include <net/rtl/rtl865x_netif.h>

static unsigned int	ACL_MODE 			= RTL865X_ACL_Mode_Normal;
#ifdef CONFIG_RTL8676_Static_ACL
static unsigned int	MAC_FILTER_MODE 	= RTL865X_ACL_MAC_FILTER_ALL_PERMIT;
#endif

static struct list_head ChainList[RTL865X_CHAINLIST_NUMBER_PER_TBL];  
extern struct proc_dir_entry *rtl865x_proc_dir;
#ifdef CONFIG_RTL8676_Static_ACL
static struct proc_dir_entry *rtl865x_proc_acl_mac_filter_mode;
#endif

static struct file_operations acl_mode_fops;
static int _rtl865x_acl_control_rewrite_chain_to_acl(void);
static int _rtl865x_acl_control_change_default_action(void);
static int _rtl865x_acl_control_free_chain(struct list_head *listHead);
static int _rtl865x_acl_control_cleanup(void);
static int 	_rtl865x_acl_control_proc_acl_mode_read(struct seq_file *s, void *v);
static ssize_t 	_rtl865x_acl_control_proc_acl_mode_write( struct file *, const char __user *,size_t, loff_t *);
#ifdef CONFIG_RTL8676_Static_ACL
static int 	_rtl865x_acl_control_proc_acl_mac_filter_mode_read(char *page, char **start, off_t off, int count, int *eof, void *data);
static int 	_rtl865x_acl_control_proc_acl_mac_filter_mode_write( struct file *filp, const char *buff,unsigned long len, void *data );
#endif

int rtl865x_acl_control_set_mode(int mode);
#ifdef CONFIG_RTL8676_Static_ACL
static int _rtl865x_acl_control_set_mac_filter_mode(int mode);
#endif

extern int vlan_passthru_enable;

static int32 rtl865x_acl_control_eventHandle_swnetif_change(void *para)
{
	int32 retval = EVENT_CONTINUE_EXECUTE;

	ACL_CONTROL_DEBUG_PRK("Enter %s\n",__func__);

	/* re-sync all sw_rule to hw acl rules */
	_rtl865x_acl_control_change_default_action();
	
	return retval;
}


static int32 _rtl865x_acl_control_register_event(void)
{
	rtl865x_event_Param_t eventParam;
	eventParam.eventLayerId=DEFAULT_COMMON_EVENT_LIST_ID;
	eventParam.eventId=EVENT_ADD_NETIF;
	eventParam.eventPriority=0;
	eventParam.event_action_fn=rtl865x_acl_control_eventHandle_swnetif_change;
	rtl865x_registerEvent(&eventParam);

	memset(&eventParam,0,sizeof(rtl865x_event_Param_t));	
	eventParam.eventLayerId=DEFAULT_COMMON_EVENT_LIST_ID;
	eventParam.eventId=EVENT_DEL_NETIF;
	eventParam.eventPriority=0;
	eventParam.event_action_fn=rtl865x_acl_control_eventHandle_swnetif_change;
	rtl865x_registerEvent(&eventParam);
	
	return SUCCESS;

}
static int32 _rtl865x_acl_control_unRegister_event(void)
{
	rtl865x_event_Param_t eventParam;
	eventParam.eventLayerId=DEFAULT_COMMON_EVENT_LIST_ID;
	eventParam.eventId=EVENT_ADD_NETIF;
	eventParam.eventPriority=0;
	eventParam.event_action_fn=rtl865x_acl_control_eventHandle_swnetif_change;
	rtl865x_unRegisterEvent(&eventParam);

	memset(&eventParam,0,sizeof(rtl865x_event_Param_t));	
	eventParam.eventLayerId=DEFAULT_COMMON_EVENT_LIST_ID;
	eventParam.eventId=EVENT_DEL_NETIF;
	eventParam.eventPriority=0;
	eventParam.event_action_fn=rtl865x_acl_control_eventHandle_swnetif_change;
	rtl865x_unRegisterEvent(&eventParam);
	
	return SUCCESS;
}
static int __init rtl865x_acl_control_init(void)
{
	int i;
	int retval=SUCCESS;
	struct proc_dir_entry *entry;
	_rtl865x_acl_control_register_event();
	
	for(i = 0; i < RTL865X_CHAINLIST_NUMBER_PER_TBL; i++)
	{
		INIT_LIST_HEAD(&ChainList[i]);
	}

	if(rtl865x_proc_dir==NULL)
		rtl865x_proc_dir = proc_mkdir(RTL865X_PROC_DIR_NAME,NULL);
	
	if(rtl865x_proc_dir)
	{	
		/*  rtl865x_proc_acl_mode  */
		{
			entry = proc_create_data("acl_mode", 0, rtl865x_proc_dir, &acl_mode_fops, NULL);
			if (!entry)	{
				rtlglue_printf("can't create proc entry for rtl865x_proc_acl_mode");
				retval = FAILED;
				goto out;
			}
		}

		#ifdef CONFIG_RTL8676_Static_ACL
		/*  rtl865x_proc_acl_mac_filter_mode  */
		{
			rtl865x_proc_acl_mac_filter_mode = create_proc_entry("acl_mac_filter_mode",0,rtl865x_proc_dir);
			if(rtl865x_proc_acl_mac_filter_mode != NULL)
			{
				rtl865x_proc_acl_mac_filter_mode->read_proc = _rtl865x_acl_control_proc_acl_mac_filter_mode_read;
				rtl865x_proc_acl_mac_filter_mode->write_proc= _rtl865x_acl_control_proc_acl_mac_filter_mode_write;

				retval = SUCCESS;				
			}
			else
			{
				rtlglue_printf("can't create proc entry for rtl865x_proc_acl_mode");
				retval = FAILED;
				goto out;
			}
		}
		#endif

	}
	else	
		retval = FAILED;
	
out:
	if(retval == FAILED)
		_rtl865x_acl_control_cleanup();
	
	return retval;	

} 

static int _rtl865x_acl_control_cleanup(void)
{
	
	if(rtl865x_proc_dir)
	{	

        remove_proc_entry("acl_mode", rtl865x_proc_dir);

		#ifdef CONFIG_RTL8676_Static_ACL
		if(rtl865x_proc_acl_mac_filter_mode!=NULL)
		{
			remove_proc_entry("rtl865x_proc_acl_mac_filter_mode", rtl865x_proc_dir);
		}
		#endif
		
		remove_proc_entry(RTL865X_PROC_DIR_NAME, NULL);
		rtl865x_proc_dir = NULL;				
	}
	
	return SUCCESS;	
}


static void __exit rtl865x_acl_control_exit(void)
{
	_rtl865x_acl_control_unRegister_event();
	_rtl865x_acl_control_cleanup();
} 


static int _rtl865x_acl_control_proc_acl_mode_read(struct seq_file *s, void *v)
{	  
	  switch(ACL_MODE)
	  {
	  	case RTL865X_ACL_Mode_Always_Permit:
			seq_printf(s, "0412 asic ACL mode : always PERMIT \n");
			break;
		case RTL865X_ACL_Mode_Always_Trap:
			seq_printf(s, "0412 asic ACL mode : always TRAP \n");
			break;
		case RTL865X_ACL_Mode_Normal:
			seq_printf(s, "0412 asic ACL mode : NORMAL \n");
			break;
		default:
			seq_printf(s, "0412 asic ACL mode : Unknown (BUG!!) \n");
	  }      

      return 0;

}
static ssize_t _rtl865x_acl_control_proc_acl_mode_write( struct file *filp, const char __user *buff,size_t len, loff_t *data)
{
	char 	tmpbuf[512];
	char		*strptr;	
	int32 retval = FAILED;
	if (buff && !copy_from_user(tmpbuf, buff, len))
	{
		tmpbuf[len] = '\0';
		
		strptr=tmpbuf;

		if(strlen(strptr)==0)
		{
			goto errout;
		}
		
		/*parse command*/
		if(strncmp(strptr, "permit",6) == 0)
		{
			retval = rtl865x_acl_control_set_mode(RTL865X_ACL_Mode_Always_Permit);
		}
		#if defined(CONFIG_TRAP_PTM_TCP_US_TRAFFIC)
		else if(strncmp(strptr, "trap_ptm_tcpus",14) == 0)
		{
			retval = rtl865x_acl_control_set_mode(RTL865X_ACL_Mode_PTM_TCPUS_Trap);
		}
		else if(strncmp(strptr, "forward_ptm_tcpus",17) == 0)
		{
			retval = rtl865x_acl_control_set_mode(RTL865X_ACL_Mode_PTM_TCPUS_Permit);
		}
		#endif
		else if(strncmp(strptr, "trap",4) == 0)
		{
			retval = rtl865x_acl_control_set_mode(RTL865X_ACL_Mode_Always_Trap);
		}
		else if(strncmp(strptr, "normal",6) == 0)
		{
			retval = rtl865x_acl_control_set_mode(RTL865X_ACL_Mode_Normal);
		}
		#ifdef CONFIG_RTL8676_Dynamic_ACL
		else if(strncmp(strptr, "clean_L2",8) == 0)
		{
			retval = rtl865x_acl_control_L2_permit_clean();
		}
		else if(strncmp(strptr, "clean_L3",8) == 0)
		{
			retval = rtl865x_acl_control_L34_permit_clean();
		}
		#endif
		else
		{
			goto errout;
		}

		if(retval==SUCCESS)
			printk("write success ! \n");
		else
			printk("error : change mode fail ! \n");
		
	}
	else
	{
errout:
		#ifdef CONFIG_RTL8676_Dynamic_ACL
		#if defined(CONFIG_TRAP_PTM_TCP_US_TRAFFIC)
		printk("error input  (permit/trap/normal/clean_L2/clean_L3/trap_ptm_tcpus/forward_ptm_tcpus)\n");
		#else
		printk("error input  (permit/trap/normal/clean_L2/clean_L3)\n");
		#endif
		#else
		printk("error input  (permit/trap/normal)\n");
		#endif
	}

	return len;
}

static int acl_mode_proc_single_open(struct inode *inode, struct file *file)
{
	return (single_open(file, _rtl865x_acl_control_proc_acl_mode_read, NULL));
}

static ssize_t acl_mode_proc_single_write(struct file * file, const char __user * userbuf,
					size_t count, loff_t * off)
{
	return _rtl865x_acl_control_proc_acl_mode_write(file, userbuf, count, off);
}

static struct file_operations acl_mode_fops = {
	.open           = acl_mode_proc_single_open,
	.write          = acl_mode_proc_single_write,
	.read           = seq_read,
	.llseek         = seq_lseek,
	.release        = single_release,
};


#ifdef CONFIG_RTL8676_Static_ACL
static int _rtl865x_acl_control_proc_acl_mac_filter_mode_read(char *page, char **start, off_t off, 
		int count, int *eof, void *data)
{	  
      int len;	  
	  
	  switch(MAC_FILTER_MODE)
	  {
	  	case RTL865X_ACL_MAC_FILTER_ALL_PERMIT:
			len = sprintf(page, "0412 asic ACL mac filter mode :	In: PERMIT	Out: PERMIT\n");
			break;
		case RTL865X_ACL_MAC_FILTER_IN_DROP_OUT_PERMIT:
			len = sprintf(page, "0412 asic ACL mac filter mode :	In: DROP		Out: PERMIT\n");
			break;
		case RTL865X_ACL_MAC_FILTER_IN_PERMIT_OUT_DROP:
			len = sprintf(page, "0412 asic ACL mac filter mode :	In: PERMIT	Out: DROP\n");
			break;
		case RTL865X_ACL_MAC_FILTER_ALL_DROP:
			len = sprintf(page, "0412 asic ACL mac filter mode :	In: DROP		Out: DROP\n");
			break;	
		default:
			len = sprintf(page, "0412 asic ACL mac filter mode : 	Unknown (BUG!!) \n");
	  }  

      if (len <= off+count) *eof = 1;
      *start = page + off;
      len -= off;
      if (len>count) len = count;
      if (len<0) len = 0;
      return len;

}

static int32 _rtl865x_acl_control_proc_acl_mac_filter_mode_write( struct file *filp, const char *buff,unsigned long len, void *data )
{
	char 	tmpbuf[512];
	char		*strptr;	
	int32 retval = FAILED;
	if (buff && !copy_from_user(tmpbuf, buff, len))
	{
		tmpbuf[len] = '\0';
		
		strptr=tmpbuf;

		if(strlen(strptr)==0)
		{
			goto errout;
		}
		
		/*parse command*/
		if(strncmp(strptr, "all_permit",10) == 0)
		{
			retval = _rtl865x_acl_control_set_mac_filter_mode(RTL865X_ACL_MAC_FILTER_ALL_PERMIT);
		}
		else if(strncmp(strptr, "in_drop_out_permit",18) == 0)
		{
			retval = _rtl865x_acl_control_set_mac_filter_mode(RTL865X_ACL_MAC_FILTER_IN_DROP_OUT_PERMIT);
		}
		else if(strncmp(strptr, "in_permit_out_drop",18) == 0)
		{
			retval = _rtl865x_acl_control_set_mac_filter_mode(RTL865X_ACL_MAC_FILTER_IN_PERMIT_OUT_DROP);
		}
		else if(strncmp(strptr, "all_drop",8) == 0)
		{
			retval = _rtl865x_acl_control_set_mac_filter_mode(RTL865X_ACL_MAC_FILTER_ALL_DROP);
		}
		else
		{
			goto errout;
		}

		if(retval==SUCCESS)
			printk("write success ! \n");
		else
			printk("error : change mode fail ! \n");
		
	}
	else
	{
errout:
		printk("error input  (all_permit/in_drop_out_permit/in_permit_out_drop/all_drop)\n");
	}

	return len;
}
#endif

static int _rtl865x_acl_control_rewrite_chain_to_acl(void)
{

	xt_rule_to_acl_t *match2acl;
	int32 retval = SUCCESS;
	int32 temp_retval = SUCCESS;

	/* delete all user used acl in 8676 asic */
	rtl865x_flush_allAcl_fromChain(NULL,RTL865X_ACL_USER_USED);


	switch(ACL_MODE)
	{
		case RTL865X_ACL_Mode_Always_Permit:
		case RTL865X_ACL_Mode_Always_Trap:
			break;
		case RTL865X_ACL_Mode_Normal:
			#ifdef CONFIG_RTL8676_Static_ACL
			list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_FORCE_TRAP],list)
			{			
				temp_retval = rtl865x_add_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED);
				if( temp_retval!=SUCCESS && temp_retval!=RTL_EENTRYALREADYEXIST)				
					retval += temp_retval; 				
			}
			list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_NORMAL],list)
			{			
				temp_retval = rtl865x_add_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED);
				if( temp_retval!=SUCCESS && temp_retval!=RTL_EENTRYALREADYEXIST)				
					retval += temp_retval; 	
			}
			#else  /* CONFIG_RTL8676_Dynamic_ACL  */
			list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT],list)
			{							
				temp_retval = rtl865x_add_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED,1,0);
				if( temp_retval!=SUCCESS && temp_retval!=RTL_EENTRYALREADYEXIST)				
					retval += temp_retval; 	
			}
			list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_REDIRECT],list)
			{			
				temp_retval = rtl865x_add_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED,1,0);
				if( temp_retval!=SUCCESS && temp_retval!=RTL_EENTRYALREADYEXIST)				
					retval += temp_retval; 	
			}
	
			list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_PERMIT],list)
			{				
				temp_retval = rtl865x_add_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED,1,0);
				if( temp_retval!=SUCCESS && temp_retval!=RTL_EENTRYALREADYEXIST)				
					retval += temp_retval; 	
			}
			#endif			
			break;			
		default: //unrecognized acl mode
			return FAILED;				
	}

	/* Kevin, when there are same rules in acl chain (rtl865x_add_acl return RTL_EENTRYALREADYEXIST) , it is a acceptable case
		ex.    when drop the specified outgoing pkts in macfilter		
   		CMD: /bin/iptables -A macfilter -i eth0.2 -m mac --mac-source 00:50:fc:65:8d:39 -j RETURN
		CMD: /bin/iptables -A macfilter -i eth0.3 -m mac --mac-source 00:50:fc:65:8d:39 -j RETURN
		CMD: /bin/iptables -A macfilter -i eth0.4 -m mac --mac-source 00:50:fc:65:8d:39 -j RETURN
		CMD: /bin/iptables -A macfilter -i eth0.5 -m mac --mac-source 00:50:fc:65:8d:39 -j RETURN	

		these iptables rules will be translated in "br0" , so they are same in the acl rule
	*/	
	if(retval!=SUCCESS)		
	{
		ACL_CONTROL_DEBUG_PRK("warning ! %s return failed (error no.:%d) at line %d\n",__func__,retval,__LINE__);
		return FAILED;	
	}
	else
		return SUCCESS;	
}

static int _rtl865x_acl_control_change_default_action(void)
{	

	rtl865x_AclRule_t *rule;	
	int32 retval = SUCCESS;

	ACL_CONTROL_DEBUG_PRK("Enter %s (%d)\n",__FUNCTION__,__LINE__);
	#if defined(CONFIG_TRAP_PTM_TCP_US_TRAFFIC)
	if(ACL_MODE == RTL865X_ACL_Mode_PTM_TCPUS_Permit || ACL_MODE == RTL865X_ACL_Mode_PTM_TCPUS_Trap)
	{
		retval = rtl865x_acl_control_upstream_tcp_set_action(ACL_MODE);
		goto FINISH;
	}
	#endif

	/* delete default acl in 8676 asic */
	rtl865x_flush_allAcl_fromChain(NULL,RTL865X_ACL_DEFAULT);	
	
	/*  ingress */	
	if(ACL_MODE != RTL865X_ACL_Mode_Normal)
	{
		rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
		if(!rule)
		{				
			ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
			retval = FAILED;
			goto FINISH;
		}		
		memset(rule, 0,sizeof(rtl865x_AclRule_t));		
		rule->ruleType_	= RTL865X_ACL_MAC;	

		if(ACL_MODE == RTL865X_ACL_Mode_Always_Permit)
		{
			if(vlan_passthru_enable)
				rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL);
			else
				rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU);
			WRITE_MEM32(MSCR,READ_MEM32(MSCR)|(EN_L3|EN_L4));
			rule->actionType_	= RTL865X_ACL_PERMIT;	
		}
		else  // ACL_MODE == RTL865X_ACL_Mode_Always_Trap
		{
			rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL);
			WRITE_MEM32(MSCR,READ_MEM32(MSCR)&~(EN_L3|EN_L4));
			rule->actionType_	= RTL865X_ACL_TOCPU;	
		}
		rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
		rule->direction_ = RTL865X_ACL_INGRESS;	
		retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT,1,0);
		kfree(rule);
		if(retval!=SUCCESS)
			goto FINISH;
	}
	else // ACL_MODE == RTL865X_ACL_Mode_Normal
	{		
		#ifdef CONFIG_RTL8676_Static_ACL
		switch(MAC_FILTER_MODE)
		{
			case RTL865X_ACL_MAC_FILTER_ALL_PERMIT:
				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_	= RTL865X_ACL_MAC;
				rule->actionType_	= RTL865X_ACL_PERMIT;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;	
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;
				break;
			case RTL865X_ACL_MAC_FILTER_IN_DROP_OUT_PERMIT:
				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_ = RTL865X_ACL_SRCFILTER;
				rule->srcFilterIgnoreL3L4_ = 1;
				rule->srcFilterIgnoreL4_ = 1;							
				rule->srcFilterPort_ = 0x1E;  /*   port 1, 2, 3, 4  */
				rule->actionType_	= RTL865X_ACL_PERMIT;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;
				
				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));

				rule->ruleType_	= RTL865X_ACL_MAC;
				rule->typeLen_ 		= 0x0800;
				rule->typeLenMask_ 	= 0xFFFF;
				rule->actionType_	= RTL865X_ACL_DROP;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;				
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;

				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_	= RTL865X_ACL_MAC;
				rule->actionType_	= RTL865X_ACL_PERMIT;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;	
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;				
				break;
			case RTL865X_ACL_MAC_FILTER_IN_PERMIT_OUT_DROP:
				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_ = RTL865X_ACL_SRCFILTER;
				rule->srcFilterIgnoreL3L4_ = 1;
				rule->srcFilterIgnoreL4_ = 1;							
				rule->srcFilterPort_ = 0x01;  /*   port 0  */
				rule->actionType_	= RTL865X_ACL_PERMIT;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;
				
				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_	= RTL865X_ACL_MAC;
				rule->typeLen_ 		= 0x0800;
				rule->typeLenMask_ 	= 0xFFFF;
				rule->actionType_	= RTL865X_ACL_DROP;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;	
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;

				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_	= RTL865X_ACL_MAC;
				rule->actionType_	= RTL865X_ACL_PERMIT;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;	
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;	
				break;
			case RTL865X_ACL_MAC_FILTER_ALL_DROP:
				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_	= RTL865X_ACL_MAC;
				rule->typeLen_ 		= 0x0800;
				rule->typeLenMask_ 	= 0xFFFF;
				rule->actionType_	= RTL865X_ACL_DROP;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;	
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;

				rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
				if(!rule)
				{				
					ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
					retval = FAILED;
					goto FINISH;
				}		
				memset(rule, 0,sizeof(rtl865x_AclRule_t));		
				rule->ruleType_	= RTL865X_ACL_MAC;
				rule->actionType_	= RTL865X_ACL_PERMIT;		
				rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
				rule->direction_ = RTL865X_ACL_INGRESS;	
				retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT);
				kfree(rule);
				if(retval!=SUCCESS)
					goto FINISH;	
				break;
				
		}
		#else /* CONFIG_RTL8676_Dynamic_ACL */
			if(vlan_passthru_enable)
				rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL);
			else
				rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU);
			WRITE_MEM32(MSCR,READ_MEM32(MSCR)|(EN_L3|EN_L4));
			rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
			if(!rule)
			{				
				ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
				retval = FAILED;
				goto FINISH;
			}		
			memset(rule, 0,sizeof(rtl865x_AclRule_t));		
			rule->ruleType_	= RTL865X_ACL_MAC;			
			rule->actionType_	= RTL865X_ACL_TOCPU;			
			rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
			rule->direction_ = RTL865X_ACL_INGRESS;	
			retval = rtl865x_add_acl(rule, NULL, RTL865X_ACL_DEFAULT,1,0);
			kfree(rule);
			if(retval!=SUCCESS)
				goto FINISH;
		#endif
	}

	retval = _rtl865x_acl_control_rewrite_chain_to_acl();
	
FINISH:
	return retval;	
}

int rtl865x_acl_control_set_mode(int mode)
{
	if(	mode!=RTL865X_ACL_Mode_Always_Permit 
		#if defined(CONFIG_TRAP_PTM_TCP_US_TRAFFIC)
		&& mode!=RTL865X_ACL_Mode_PTM_TCPUS_Permit
		&& mode!=RTL865X_ACL_Mode_PTM_TCPUS_Trap
		#endif
		&& mode!=RTL865X_ACL_Mode_Always_Trap
		&& mode!=RTL865X_ACL_Mode_Normal)
		return FAILED;	

	ACL_MODE = mode;

	return _rtl865x_acl_control_change_default_action();
}

#ifdef CONFIG_RTL8676_Static_ACL
int _rtl865x_acl_control_set_mac_filter_mode(int mode)
{
	if(		mode!=RTL865X_ACL_MAC_FILTER_ALL_PERMIT 
		&& mode!=RTL865X_ACL_MAC_FILTER_IN_DROP_OUT_PERMIT
		&& mode!=RTL865X_ACL_MAC_FILTER_IN_PERMIT_OUT_DROP
		&& mode!=RTL865X_ACL_MAC_FILTER_ALL_DROP)
		return FAILED;	

	MAC_FILTER_MODE = mode;

	return _rtl865x_acl_control_change_default_action();
}

int rtl865x_acl_control_filter_rule_add(xt_rule_to_acl_t* node,int force_trap)
{
	if(force_trap)
		list_add_tail(&node->list,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_FORCE_TRAP]);
	else
		list_add_tail(&node->list,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_NORMAL]);

	if(_rtl865x_acl_control_rewrite_chain_to_acl()!=SUCCESS)
		return FAILED;
	else
		return SUCCESS;
}
int rtl865x_acl_control_filter_rule_clean(void)
{
	int i;
	for(i = 0; i < RTL865X_CHAINLIST_NUMBER_PER_TBL; i++)
	{
		_rtl865x_acl_control_free_chain(&ChainList[i]);
	}
	return 0;
}
#else /* CONFIG_RTL8676_Dynamic_ACL */

#if defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB)
int rtl865x_acl_control_L2_vid_add(unsigned char* src_mac,unsigned char* dst_mac, char *acl_in_ifname, unsigned int vlan_id)
{
	xt_rule_to_acl_t *list_node=NULL;
	rtl865x_AclRule_t *rule=NULL;	
	xt_rule_to_acl_t *match2acl;


	ACL_CONTROL_DEBUG_PRK("Enter %s (src mac:%02X:%02X:%02X:%02X:%02X:%02X    dst mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
			,src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]
			,dst_mac[0],dst_mac[1],dst_mac[2],dst_mac[3],dst_mac[4],dst_mac[5]);


	//duplicate check
	list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT],list)
	{			
		if((match2acl->aclRule->vid_==vlan_id)&&(match2acl->aclRule->actionType_==RTL865X_ACL_VID)&&!memcmp(match2acl->aclRule->un_ty.srcMac_.octet,src_mac,ETH_ALEN) && !memcmp(match2acl->aclRule->un_ty.dstMac_.octet,dst_mac,ETH_ALEN))
		{
			ACL_CONTROL_DEBUG_PRK("[%s %d]duplicate check fail!  match2acl->aclRule->vid_ %d  vlan_id %d",__func__,__LINE__,match2acl->aclRule->vid_,vlan_id);		
			return SUCCESS;
		}
	}	

	list_node = kmalloc(sizeof(xt_rule_to_acl_t),GFP_KERNEL);	
	if(!list_node)
	{
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}		
	rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_KERNEL);
	if(!rule)
	{		
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}
	
	memset(list_node, 0,sizeof(xt_rule_to_acl_t));
	memset(rule, 0,sizeof(rtl865x_AclRule_t));
	rule->ruleType_ = RTL865X_ACL_MAC;
	rule->actionType_	= RTL865X_ACL_VID;		
	rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
	rule->direction_ = RTL865X_ACL_INGRESS;
	rule->vid_=vlan_id;
	memcpy(rule->un_ty.srcMac_.octet, src_mac, ETH_ALEN);
	memcpy(rule->un_ty.dstMac_.octet, dst_mac, ETH_ALEN);
	memset(rule->un_ty.srcMacMask_.octet, 0xff, ETH_ALEN);
	memset(rule->un_ty.dstMacMask_.octet, 0xff, ETH_ALEN);
	list_node->aclRule = rule;
	strcpy(list_node->iniface, acl_in_ifname);

	/* If acl mode is normal , take effect immediately */
	if(ACL_MODE == RTL865X_ACL_Mode_Normal)
	{
		if(rtl865x_add_acl(rule, list_node->iniface, RTL865X_ACL_USER_USED,1,0)!=SUCCESS)
		{
			ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
			goto FailToAdd;
		}
	}
	list_add_tail(&list_node->list,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT]);	


		ACL_CONTROL_DEBUG_PRK("(%s)add succcess! (src mac:%02X:%02X:%02X:%02X:%02X:%02X    dst mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
			,src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]
			,dst_mac[0],dst_mac[1],dst_mac[2],dst_mac[3],dst_mac[4],dst_mac[5]);	
		return SUCCESS;


FailToAdd:	
	if(list_node)
		kfree(list_node);
	if(rule)
		kfree(rule);
		
	return FAILED;

}
#endif

#ifdef CONFIG_RTL_HW_QOS_SUPPORT
int rtl865x_acl_control_L2_priority_add(unsigned char* src_mac,unsigned char* dst_mac, char *acl_in_ifname,int priority)
{
	xt_rule_to_acl_t *list_node=NULL;
	rtl865x_AclRule_t *rule=NULL;	
	xt_rule_to_acl_t *match2acl;


	ACL_CONTROL_DEBUG_PRK("Enter %s (src mac:%02X:%02X:%02X:%02X:%02X:%02X    dst mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
			,src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]
			,dst_mac[0],dst_mac[1],dst_mac[2],dst_mac[3],dst_mac[4],dst_mac[5]);


	//duplicate check
	list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT],list)
	{			
		if((match2acl->aclRule->actionType_==RTL865X_ACL_PRIORITY)&&!memcmp(match2acl->aclRule->un_ty.srcMac_.octet,src_mac,ETH_ALEN) && !memcmp(match2acl->aclRule->un_ty.dstMac_.octet,dst_mac,ETH_ALEN))
		{
			ACL_CONTROL_DEBUG_PRK("[%s %d]duplicate check fail!  match2acl->aclRule->priority_ %d  priority %d",__func__,__LINE__,match2acl->aclRule->priority_,priority);		
			return SUCCESS;
		}
	}	

	list_node = kmalloc(sizeof(xt_rule_to_acl_t),GFP_KERNEL);	
	if(!list_node)
	{
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}		
	rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_KERNEL);
	if(!rule)
	{		
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}
	
	memset(list_node, 0,sizeof(xt_rule_to_acl_t));
	memset(rule, 0,sizeof(rtl865x_AclRule_t));
	rule->ruleType_ = RTL865X_ACL_MAC;
	rule->actionType_	= RTL865X_ACL_PRIORITY;
	rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
	rule->direction_ = RTL865X_ACL_INGRESS;
	rule->priority_=priority;
	memcpy(rule->un_ty.srcMac_.octet, src_mac, ETH_ALEN);
	memcpy(rule->un_ty.dstMac_.octet, dst_mac, ETH_ALEN);
	memset(rule->un_ty.srcMacMask_.octet, 0xff, ETH_ALEN);
	memset(rule->un_ty.dstMacMask_.octet, 0xff, ETH_ALEN);
	list_node->aclRule = rule;
	strcpy(list_node->iniface, acl_in_ifname);

	/* If acl mode is normal , take effect immediately */
	if(ACL_MODE == RTL865X_ACL_Mode_Normal)
	{
		if(rtl865x_add_acl(rule, list_node->iniface, RTL865X_ACL_USER_USED,1,0)!=SUCCESS)
		{
			ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
			goto FailToAdd;
		}
	}
	list_add_tail(&list_node->list,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT]);	


		ACL_CONTROL_DEBUG_PRK("(%s)add succcess! (src mac:%02X:%02X:%02X:%02X:%02X:%02X    dst mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
			,src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]
			,dst_mac[0],dst_mac[1],dst_mac[2],dst_mac[3],dst_mac[4],dst_mac[5]);	
		return SUCCESS;


FailToAdd:	
	if(list_node)
		kfree(list_node);
	if(rule)
		kfree(rule);
		
	return FAILED;

}
#endif

int rtl865x_acl_control_L2_permit_add(unsigned char* src_mac,unsigned char* dst_mac, char *acl_in_ifname)
{
	xt_rule_to_acl_t *list_node=NULL;
	rtl865x_AclRule_t *rule=NULL;	
	xt_rule_to_acl_t *match2acl;


	ACL_CONTROL_DEBUG_PRK("Enter %s (src mac:%02X:%02X:%02X:%02X:%02X:%02X    dst mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
			,src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]
			,dst_mac[0],dst_mac[1],dst_mac[2],dst_mac[3],dst_mac[4],dst_mac[5]);


	//duplicate check
	list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT],list)
	{			
		if((match2acl->aclRule->actionType_==RTL865X_ACL_PERMIT) &&!memcmp(match2acl->aclRule->un_ty.srcMac_.octet,src_mac,ETH_ALEN) && !memcmp(match2acl->aclRule->un_ty.dstMac_.octet,dst_mac,ETH_ALEN))
		{
			ACL_CONTROL_DEBUG_PRK("[%s %d] duplicate check fail! \n",__func__,__LINE__);
			return SUCCESS;
		}
	}	

	list_node = kmalloc(sizeof(xt_rule_to_acl_t),GFP_KERNEL);	
	if(!list_node)
	{
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}		
	rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_KERNEL);
	if(!rule)
	{		
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}
	
	memset(list_node, 0,sizeof(xt_rule_to_acl_t));
	memset(rule, 0,sizeof(rtl865x_AclRule_t));
	rule->ruleType_ = RTL865X_ACL_MAC;
	rule->actionType_	= RTL865X_ACL_PERMIT;		
	rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
	rule->direction_ = RTL865X_ACL_INGRESS;
	memcpy(rule->un_ty.srcMac_.octet, src_mac, ETH_ALEN);
	memcpy(rule->un_ty.dstMac_.octet, dst_mac, ETH_ALEN);
	memset(rule->un_ty.srcMacMask_.octet, 0xff, ETH_ALEN);
	memset(rule->un_ty.dstMacMask_.octet, 0xff, ETH_ALEN);
	list_node->aclRule = rule;
	strcpy(list_node->iniface, acl_in_ifname);

	/* If acl mode is normal , take effect immediately */
	if(ACL_MODE == RTL865X_ACL_Mode_Normal)
	{
		if(rtl865x_add_acl(rule, list_node->iniface, RTL865X_ACL_USER_USED,1,0)!=SUCCESS)
			goto FailToAdd;
	}
	list_add_tail(&list_node->list,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT]);	


		ACL_CONTROL_DEBUG_PRK("(%s)add succcess! (src mac:%02X:%02X:%02X:%02X:%02X:%02X    dst mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
			,src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]
			,dst_mac[0],dst_mac[1],dst_mac[2],dst_mac[3],dst_mac[4],dst_mac[5]);	
		return SUCCESS;


FailToAdd:	
	if(list_node)
		kfree(list_node);
	if(rule)
		kfree(rule);
		
	return FAILED;

}
int rtl865x_acl_control_L2_permit_del(unsigned char* del_mac)
{

	xt_rule_to_acl_t *match2acl;
	xt_rule_to_acl_t *nxt;
	int findout=false;	


	list_for_each_entry_safe(match2acl,nxt,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT],list)
	{			
		if(!memcmp(match2acl->aclRule->un_ty.srcMac_.octet,del_mac,ETH_ALEN) || !memcmp(match2acl->aclRule->un_ty.dstMac_.octet,del_mac,ETH_ALEN))
		{
			findout=true;
			if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
				return FAILED;								
			list_del(&match2acl->list);
			kfree(match2acl->aclRule);
			kfree(match2acl);
		}
		
	}

	if(findout==true)
	{	
			ACL_CONTROL_DEBUG_PRK("(%s)del from acl L2 permit : (mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
				,del_mac[0],del_mac[1],del_mac[2],del_mac[3],del_mac[4],del_mac[5]);
		}

		return SUCCESS;

}
int rtl865x_acl_control_L2_permit_del_pair(unsigned char* src_mac,unsigned char* dst_mac)
{

	xt_rule_to_acl_t *match2acl;
	xt_rule_to_acl_t *nxt;
	int findout=false;	


	list_for_each_entry_safe(match2acl,nxt,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT],list)
	{			
		if(!memcmp(match2acl->aclRule->un_ty.srcMac_.octet,src_mac,ETH_ALEN) && !memcmp(match2acl->aclRule->un_ty.dstMac_.octet,dst_mac,ETH_ALEN))
		{
			findout=true;
			if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
				return FAILED;
			list_del(&match2acl->list);
			kfree(match2acl->aclRule);
			kfree(match2acl);
		}
		
	}

	if(findout==true)
	{	
			ACL_CONTROL_DEBUG_PRK("(%s)del from acl L2 permit : (src mac:%02X:%02X:%02X:%02X:%02X:%02X    dst mac:%02X:%02X:%02X:%02X:%02X:%02X)\n",__func__
				,src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]
				,dst_mac[0],dst_mac[1],dst_mac[2],dst_mac[3],dst_mac[4],dst_mac[5]);
		}

		return SUCCESS;

}

int rtl865x_acl_control_L2_permit_clean(void)
{

	_rtl865x_acl_control_free_chain(&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L2_UNI_PERMIT]);

	if(_rtl865x_acl_control_rewrite_chain_to_acl()!=SUCCESS)
		return FAILED;
	else
		return SUCCESS;
	
}

int rtl865x_acl_control_L34_permit_add(__u32 src_ip,__u16 src_port,__u32 dst_ip,__u16 dst_port,__u8 protocol,char* in_netifname)
{

	xt_rule_to_acl_t *list_node=NULL;
	rtl865x_AclRule_t *rule=NULL;
	xt_rule_to_acl_t *match2acl;

	ACL_CONTROL_DEBUG_PRK("Enter %s : add into acl L34 permit : (src ip:%pI4:%u  dst ip:%pI4:%u  in_if:%s  protocol:%s)\n",__func__
			,&(src_ip),src_port,&(dst_ip),dst_port,in_netifname,protocol==IPPROTO_TCP?"TCP":"UDP");

	if( protocol!=IPPROTO_TCP && protocol!=IPPROTO_UDP )	
		goto FailToAdd;
	

	if(!rtl865x_netifExist(in_netifname))  /*  the interface is in asic netif table  */		
		goto FailToAdd;


	//duplicate check
	list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_PERMIT],list)
	{
#if 0
		if(protocol==IPPROTO_TCP && match2acl->aclRule->ruleType_==RTL865X_ACL_TCP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&  match2acl->aclRule->un_ty.tcpSrcPortLB_==src_port && match2acl->aclRule->un_ty.tcpSrcPortUB_==src_port &&
			    match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&  match2acl->aclRule->un_ty.tcpDstPortLB_==dst_port && match2acl->aclRule->un_ty.tcpDstPortUB_==dst_port &&
			    !strcmp(match2acl->iniface,in_netifname))
				return SUCCESS;
		}

		if(protocol==IPPROTO_UDP && match2acl->aclRule->ruleType_==RTL865X_ACL_UDP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&  match2acl->aclRule->un_ty.udpSrcPortLB_==src_port && match2acl->aclRule->un_ty.udpSrcPortUB_==src_port &&
			    match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&  match2acl->aclRule->un_ty.udpDstPortLB_==dst_port && match2acl->aclRule->un_ty.udpDstPortUB_==dst_port &&
			    !strcmp(match2acl->iniface,in_netifname))
				return SUCCESS;
		}
#else
		if(match2acl->aclRule->ruleType_==RTL865X_ACL_IP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&
			    match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&
			    !strcmp(match2acl->iniface,in_netifname))
			{
				(match2acl->ref)++;//cxy 2015-7-3: <sip,dip>acl permit rule reference by several napt entries
				return SUCCESS;
			}
		}
#endif
	}

	list_node = kmalloc(sizeof(xt_rule_to_acl_t),GFP_KERNEL);	
	if(!list_node)
	{
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}
		
	rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_KERNEL);
	if(!rule)
	{		
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}

	
	memset(list_node, 0,sizeof(xt_rule_to_acl_t));
	memset(rule, 0,sizeof(rtl865x_AclRule_t));
	rule->actionType_	= RTL865X_ACL_PERMIT;		
	rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
	rule->direction_ = RTL865X_ACL_INGRESS;
#if 0	
	if(protocol==IPPROTO_TCP)
	{
		rule->ruleType_ = RTL865X_ACL_TCP;
		rule->un_ty.srcIpAddr_ 		= src_ip;
		rule->un_ty.srcIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.dstIpAddr_		= dst_ip;
		rule->un_ty.dstIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.tcpSrcPortLB_	= src_port;
		rule->un_ty.tcpSrcPortUB_	= src_port;
		rule->un_ty.tcpDstPortLB_	= dst_port;
		rule->un_ty.tcpDstPortUB_	= dst_port;	
	}
	else if (protocol==IPPROTO_UDP)
	{
		rule->ruleType_ = RTL865X_ACL_UDP;	
		rule->un_ty.srcIpAddr_ 		= src_ip;
		rule->un_ty.srcIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.dstIpAddr_		= dst_ip;
		rule->un_ty.dstIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.udpSrcPortLB_	= src_port;
		rule->un_ty.udpSrcPortUB_	= src_port;
		rule->un_ty.udpDstPortLB_	= dst_port;
		rule->un_ty.udpDstPortUB_	= dst_port;
	}
#else//cxy 2015-7-13: use <sip, dip> match to save acl rule num
	rule->ruleType_ = RTL865X_ACL_IP;
	rule->un_ty.srcIpAddr_ 		= src_ip;
	rule->un_ty.srcIpAddrMask_	= 0xFFFFFFFF;
	rule->un_ty.dstIpAddr_		= dst_ip;
	rule->un_ty.dstIpAddrMask_	= 0xFFFFFFFF;
#endif
	list_node->aclRule = rule;
	strcpy(list_node->iniface,in_netifname);
	/* If acl mode is normal , take effect immediately */
	if(ACL_MODE == RTL865X_ACL_Mode_Normal)
	{
		if(rtl865x_add_acl(rule, list_node->iniface, RTL865X_ACL_USER_USED,1,0)!=SUCCESS)
			goto FailToAdd;	
	}	
	list_node->ref=1;//cxy 2015-7-3: for new napt entry reference
	list_add_tail(&list_node->list,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_PERMIT]);	


	
		ACL_CONTROL_DEBUG_PRK("(%s)add into acl L34 permit : (src ip:%pI4:%u  dst ip:%pI4:%u  in_if:%s  protocol:%s)\n",__func__
			,&(src_ip),src_port,&(dst_ip),dst_port,in_netifname,protocol==IPPROTO_TCP?"TCP":"UDP");
		return SUCCESS;

FailToAdd:	
	if(list_node)
		kfree(list_node);
	if(rule)
		kfree(rule);	
	return FAILED;
}
int rtl865x_acl_control_L34_permit_del(__u32 src_ip,__u16 src_port,__u32 dst_ip,__u16 dst_port,__u8 protocol)
{
	xt_rule_to_acl_t *match2acl;
	xt_rule_to_acl_t *nxt;
	int findout=false;
	


	list_for_each_entry_safe(match2acl,nxt,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_PERMIT],list)
	{	
#if 0
		if(protocol==IPPROTO_TCP && match2acl->aclRule->ruleType_==RTL865X_ACL_TCP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&  match2acl->aclRule->un_ty.tcpSrcPortLB_==src_port && match2acl->aclRule->un_ty.tcpSrcPortUB_==src_port &&
			    match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&  match2acl->aclRule->un_ty.tcpDstPortLB_==dst_port && match2acl->aclRule->un_ty.tcpDstPortUB_==dst_port)
			{
				findout=true;
				if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
					return FAILED;
				list_del(&match2acl->list);
				kfree(match2acl->aclRule);
				kfree(match2acl);
			}
		}

		if(protocol==IPPROTO_UDP && match2acl->aclRule->ruleType_==RTL865X_ACL_UDP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&  match2acl->aclRule->un_ty.udpSrcPortLB_==src_port && match2acl->aclRule->un_ty.udpSrcPortUB_==src_port &&
			    match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&  match2acl->aclRule->un_ty.udpDstPortLB_==dst_port && match2acl->aclRule->un_ty.udpDstPortUB_==dst_port)
			{
				findout=true;
				if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
					return FAILED;
				list_del(&match2acl->list);
				kfree(match2acl->aclRule);
				kfree(match2acl);
			}
		}
#else
		if(match2acl->aclRule->ruleType_==RTL865X_ACL_IP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&
				match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&
				(--(match2acl->ref))==0)
			{
				findout=true;
				if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
					return FAILED;
				list_del(&match2acl->list);
				kfree(match2acl->aclRule);
				kfree(match2acl);
			}
		}
#endif
	}

	if(findout==true)
	{
			ACL_CONTROL_DEBUG_PRK("(%s)del from acl L34 permit : (src ip:%pI4:%u  dst ip:%pI4:%u   protocol:%s)\n",__func__
				,&(src_ip),src_port,&(dst_ip),dst_port,protocol==IPPROTO_TCP?"TCP":"UDP");	
		}
	
		return SUCCESS;
	
}
int rtl865x_acl_control_L34_permit_clean(void)
{

	_rtl865x_acl_control_free_chain(&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_PERMIT]);

	if(_rtl865x_acl_control_rewrite_chain_to_acl()!=SUCCESS)
		return FAILED;
	else
		return SUCCESS;
	
}


/* 
	ACL match field : int_ip , int_port , rem_ip , rem_port , protocol

	This acl rule will add at "acl_in_ifname" netif
	 
	The following fields is for redirecting
		redir_scr_ip 		: After napt and policy routing , the packet's scr ip
		redir_dst_ip 		: After napt and policy routing , the packet's nexthop's ip addr
		                                (Attention : this is not packet's dest ip, it is used for fill packet L2 dest mac addr)
		redir_out_netif	: After napt and policy routing,  the asic netif that packet will be routed to 
*/
int rtl865x_acl_control_L34_redirect_add(__u32 int_ip,__u16 int_port,__u32 rem_ip,__u16 rem_port,__u8 protocol,char *acl_in_ifname
													,__u32 redir_scr_ip,__u32 redir_dst_ip,char *redir_out_netif)
{

	xt_rule_to_acl_t *list_node=NULL;
	rtl865x_AclRule_t *rule=NULL;
	xt_rule_to_acl_t *match2acl;
	rtl865x_netif_local_t *redir_out_netIf;	
		


	ACL_CONTROL_DEBUG_PRK("Enter %s : add into acl L34 redirect : (int ip:%pI4:%u  rem ip:%pI4:%u  protocol:%s in_if:%s)\n\
						apt_scr_ip:%pI4   nexthop's ip:%pI4  redir_out_netif:%s",__func__
			,&(int_ip),int_port,&(rem_ip),rem_port,protocol==IPPROTO_TCP?"TCP":"UDP",acl_in_ifname,&(redir_scr_ip),&(redir_dst_ip),redir_out_netif); /* hp_hack */
	

	if( protocol!=IPPROTO_TCP && protocol!=IPPROTO_UDP )
		goto FailToAdd;
	

	//duplicate check
	list_for_each_entry(match2acl,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_REDIRECT],list)
	{
#if 0
		if(protocol==IPPROTO_TCP && match2acl->aclRule->ruleType_==RTL865X_ACL_TCP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==int_ip &&  match2acl->aclRule->un_ty.tcpSrcPortLB_==int_port && match2acl->aclRule->un_ty.tcpSrcPortUB_==int_port &&
				match2acl->aclRule->un_ty.dstIpAddr_==rem_ip &&  match2acl->aclRule->un_ty.tcpDstPortLB_==rem_port && match2acl->aclRule->un_ty.tcpDstPortUB_==rem_port)
				return SUCCESS;
		}

		if(protocol==IPPROTO_UDP && match2acl->aclRule->ruleType_==RTL865X_ACL_UDP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==int_ip &&  match2acl->aclRule->un_ty.udpSrcPortLB_==int_port && match2acl->aclRule->un_ty.udpSrcPortUB_==int_port &&
				match2acl->aclRule->un_ty.dstIpAddr_==rem_ip &&  match2acl->aclRule->un_ty.udpDstPortLB_==rem_port && match2acl->aclRule->un_ty.udpDstPortUB_==rem_port)
				return SUCCESS;
		}
#else
		if(match2acl->aclRule->ruleType_==RTL865X_ACL_IP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==int_ip &&
			    match2acl->aclRule->un_ty.dstIpAddr_==rem_ip)
			{
				(match2acl->ref)++;//cxy 2015-7-3: L34 redirect acl rule is referenced multiple napt entries
				return SUCCESS;
			}
		}
#endif
	}

	list_node = kmalloc(sizeof(xt_rule_to_acl_t),GFP_KERNEL);
	if(!list_node)
	{
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}
	
	rule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_KERNEL);
	if(!rule)
	{
		ACL_CONTROL_DEBUG_PRK("\n!!!!!!%s(%d): No memory freed for kmalloc!!!",__FUNCTION__,__LINE__);
		goto FailToAdd;
	}
	memset(list_node, 0,sizeof(xt_rule_to_acl_t));
	memset(rule, 0,sizeof(rtl865x_AclRule_t));

	
	/* redirect to ethernet or ppppoe will cause SNAT fail. redirect to nextgop table instead */
	#if 0 /* the original */
	if (out_ifname[0] == 'p')//pppoe interface
		rule->actionType_ = RTL865X_ACL_REDIRECT_PPPOE;
	else
		rule->actionType_ = RTL865X_ACL_REDIRECT_ETHER;
	#else
	rule->actionType_ = RTL865X_ACL_DEFAULT_REDIRECT;
	#endif


	rule->pktOpApp_ 	= RTL865X_ACL_ALL_LAYER;
	rule->direction_ = RTL865X_ACL_INGRESS;
#if 0	
	if(protocol==IPPROTO_TCP)
	{
		rule->ruleType_ = RTL865X_ACL_TCP;
		rule->un_ty.srcIpAddr_		= int_ip;
		rule->un_ty.srcIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.dstIpAddr_		= rem_ip;
		rule->un_ty.dstIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.tcpSrcPortLB_ = int_port;
		rule->un_ty.tcpSrcPortUB_ = int_port;
		rule->un_ty.tcpDstPortLB_ = rem_port;
		rule->un_ty.tcpDstPortUB_ = rem_port; 
	}
	else
	{
		rule->ruleType_ = RTL865X_ACL_UDP;	
		rule->un_ty.srcIpAddr_		= int_ip;
		rule->un_ty.srcIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.dstIpAddr_		= rem_ip;
		rule->un_ty.dstIpAddrMask_	= 0xFFFFFFFF;
		rule->un_ty.udpSrcPortLB_ = int_port;
		rule->un_ty.udpSrcPortUB_ = int_port;
		rule->un_ty.udpDstPortLB_ = rem_port;
		rule->un_ty.udpDstPortUB_ = rem_port;
	}
#else//cxy 2015-7-13: use <sip, dip> match to save acl rule num
	rule->ruleType_ = RTL865X_ACL_IP;
	rule->un_ty.srcIpAddr_ 		= int_ip;
	rule->un_ty.srcIpAddrMask_	= 0xFFFFFFFF;
	rule->un_ty.dstIpAddr_		= rem_ip;
	rule->un_ty.dstIpAddrMask_	= 0xFFFFFFFF;
#endif	

	redir_out_netIf = _rtl865x_getSWNetifByName(redir_out_netif);
	if(!redir_out_netIf)
		goto FailToAdd;



	/*  case 1.  pppoe wan 
				redirect to the nexthop table that pppoe table use directly  
	*/
	if(redir_out_netIf->if_type == IF_PPPOE)
	{
		int nextHopIdx;
		rtl865x_ppp_t *ppp;		
	
		/* get session ID from pppoe table */
		ppp = rtl865x_getPppByNetifName(redir_out_netIf->name);		
		if (!ppp)	
			goto FailToAdd;	
		
		nextHopIdx=rtl865x_getNxtHopIdx(NEXTHOP_DEFREDIRECT_ACL, redir_out_netIf->name, ppp->sessionId,redir_scr_ip);

		if(nextHopIdx==-1)
			goto FailToAdd;	

		rule->nexthopIdx_ = nextHopIdx;			
	}
	/*  case 2.  ether wan 
				we have to create a new nexthop table for redirecting by myself  
	*/
	else if(redir_out_netIf->if_type == IF_ETHER)
	{
		int nextHopIdx;
	


		if ((nextHopIdx = rtl865x_getNxtHopIdx(NEXTHOP_DEFREDIRECT_ACL, redir_out_netIf->name, redir_dst_ip,redir_scr_ip)) == -1)
		{
			if ( rtl865x_addNxtHop(NEXTHOP_DEFREDIRECT_ACL, NULL, redir_out_netIf->name, redir_dst_ip, redir_scr_ip) != SUCCESS) 
				goto FailToAdd;		
		}			

		nextHopIdx = rtl865x_getNxtHopIdx(NEXTHOP_DEFREDIRECT_ACL, redir_out_netIf->name, redir_dst_ip,redir_scr_ip);

		if(nextHopIdx==-1)
			goto FailToAdd;		

		rule->nexthopIdx_ = nextHopIdx;
	}
	else
		goto FailToAdd;	
	



	list_node->aclRule = rule;
	strcpy(list_node->iniface,acl_in_ifname);
	/* If acl mode is normal , take effect immediately */
	if(ACL_MODE == RTL865X_ACL_Mode_Normal)
	{
		if(rtl865x_add_acl(rule, list_node->iniface, RTL865X_ACL_USER_USED,1,0)!=SUCCESS)
			goto FailToAdd;	
	}
	list_node->ref=1;//cxy 2015-7-3: for new napt entry reference
	list_add_tail(&list_node->list,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_REDIRECT]); 


		ACL_CONTROL_DEBUG_PRK("(%s)add into acl L34 redirect : (src ip:%pI4:%u  dst ip:%pI4:%u  in_if:%s  protocol:%s)\n",__func__
			,&(int_ip),int_port,&(rem_ip),rem_port,acl_in_ifname,protocol==IPPROTO_TCP?"TCP":"UDP");		
		return SUCCESS;
	

FailToAdd:	
	if(list_node)
		kfree(list_node);
	if(rule)
		kfree(rule);	
	return FAILED;
}


int rtl865x_acl_control_L34_redirect_del(__u32 src_ip,__u16 src_port,__u32 dst_ip,__u16 dst_port,__u8 protocol)
{
	xt_rule_to_acl_t *match2acl;
	xt_rule_to_acl_t *nxt;
	int findout=false;



	list_for_each_entry_safe(match2acl,nxt,&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_REDIRECT],list)
	{
#if 0
		if(protocol==IPPROTO_TCP && match2acl->aclRule->ruleType_==RTL865X_ACL_TCP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&  match2acl->aclRule->un_ty.tcpSrcPortLB_==src_port && match2acl->aclRule->un_ty.tcpSrcPortUB_==src_port &&
				match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&  match2acl->aclRule->un_ty.tcpDstPortLB_==dst_port && match2acl->aclRule->un_ty.tcpDstPortUB_==dst_port)
			{
				findout=true;
				if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
					return FAILED;
				list_del(&match2acl->list);
				kfree(match2acl->aclRule);
				kfree(match2acl);
								
			}
		}

		if(protocol==IPPROTO_UDP && match2acl->aclRule->ruleType_==RTL865X_ACL_UDP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&  match2acl->aclRule->un_ty.udpSrcPortLB_==src_port && match2acl->aclRule->un_ty.udpSrcPortUB_==src_port &&
				match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&  match2acl->aclRule->un_ty.udpDstPortLB_==dst_port && match2acl->aclRule->un_ty.udpDstPortUB_==dst_port)
			{
				findout=true;
				if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
					return FAILED;
				list_del(&match2acl->list);
				kfree(match2acl->aclRule);
				kfree(match2acl);
			}
		}
#else
		if(match2acl->aclRule->ruleType_==RTL865X_ACL_IP)
		{
			if( match2acl->aclRule->un_ty.srcIpAddr_==src_ip &&
				match2acl->aclRule->un_ty.dstIpAddr_==dst_ip &&
				(--(match2acl->ref))==0)
			{
				findout=true;
				if(rtl865x_del_acl(match2acl->aclRule, match2acl->iniface, RTL865X_ACL_USER_USED)!=SUCCESS)
					return FAILED;
				list_del(&match2acl->list);
				kfree(match2acl->aclRule);
				kfree(match2acl);
			}
		}
#endif
	}

	if(findout==true)
	{
			ACL_CONTROL_DEBUG_PRK("(%s)del from acl L34 redirect : (src ip:%pI4:%u  dst ip:%pI4:%u   protocol:%s)\n",__func__
				,&(src_ip),src_port,&(dst_ip),dst_port,protocol==IPPROTO_TCP?"TCP":"UDP");
		}

		return SUCCESS;
}

#if defined(CONFIG_TRAP_PTM_TCP_US_TRAFFIC)
#include <linux/if_smux.h>
extern struct smux_group *__find_smux_group(const char *ifname);
extern struct smux_dev_info *list_entry_smuxdev(const struct list_head *le);
int rtl865x_acl_control_upstream_tcp_set_action(int mode)
{
	rtl865x_AclRule_t rule;
	int action,vid=-1;
	int32 retval = SUCCESS;
	struct smux_group *grp = NULL;
	struct smux_dev_info *vdev_info = NULL;
	struct list_head *lh;

	ACL_CONTROL_DEBUG_PRK("Enter %s (%d)\n",__FUNCTION__,__LINE__);

	grp = __find_smux_group(ALIASNAME_PTM0);
	if (!grp)
		return -EADDRNOTAVAIL;
	if (list_empty(&grp->virtual_devs))
	{
		return -EADDRNOTAVAIL;
	}
	list_for_each(lh, &grp->virtual_devs)
	{
		vdev_info = list_entry_smuxdev(lh);
		printk("netif:%s vid:%d proto:%d(SMUX_PROTO_BRIDGE:%d)",vdev_info->vdev->name,vdev_info->vid,vdev_info->proto,SMUX_PROTO_BRIDGE);
		if(vdev_info->proto == SMUX_PROTO_BRIDGE)
			break;
	}
	if(vdev_info==NULL)
	{
		ACL_CONTROL_DEBUG_PRK("Error leave %s @ %d \n",__FUNCTION__,__LINE__);
		return FAILED;
	}

	rtl865x_getNetifVid(vdev_info->vdev->name,&vid);
	if(vid==-1)
	{
		ACL_CONTROL_DEBUG_PRK("Error leave %s @ %d \n",__FUNCTION__,__LINE__);
		return FAILED;
	}

	if(mode == RTL865X_ACL_Mode_PTM_TCPUS_Trap)
		action = RTL865X_ACL_TOCPU;
	else if(mode == RTL865X_ACL_Mode_PTM_TCPUS_Permit)
		action = RTL865X_ACL_PERMIT;
	else
	{
		ACL_CONTROL_DEBUG_PRK("Error input! Leave %s @ %d \n",__FUNCTION__,__LINE__);
		return FAILED;
	}

	/* Trap Bridge PTM WAN egress TCP traffic */
	memset(&rule,0,sizeof(rtl865x_AclRule_t));
	rule.ruleType_ = RTL865X_ACL_DSTFILTER;
	rule.pktOpApp_ = RTL865X_ACL_ALL_LAYER;
	rule.aclIdx = RTL865X_ACLTBL_EGRESS_PTM_TCPUS;
	rule.direction_ = RTL865X_ACL_EGRESS;
	rule.actionType_ = action;
	rule.comb = 1;
	rule.un_ty.DSTFILTER._vlanIdx = vid;
	rule.un_ty.DSTFILTER._vlanIdxMask = 0xfff;
	rule.un_ty.DSTFILTER._dstPortUpperBound = 0xffff;
	rule.un_ty.DSTFILTER._dstPortLowerBound = 0;
	retval = _rtl865x_setAclToAsic(rule.aclIdx, &rule);
	if(retval!=SUCCESS)
	{
		ACL_CONTROL_DEBUG_PRK("Error leave %s @ %d \n",__FUNCTION__,__LINE__);
		return retval;
	}
	memset(&rule,0,sizeof(rtl865x_AclRule_t));
	rule.ruleType_ = RTL865X_ACL_TCP;
	rule.pktOpApp_ = RTL865X_ACL_ALL_LAYER;
	rule.aclIdx = RTL865X_ACLTBL_EGRESS_PTM_TCPUS+1;
	rule.direction_ = RTL865X_ACL_EGRESS;
	rule.actionType_ = action;
	rule.comb = 0;
	rule.un_ty.tcpSrcPortUB_ = 0xffff;
	rule.un_ty.tcpSrcPortLB_ = 0;
	rule.un_ty.tcpDstPortUB_ = 0xffff;
	rule.un_ty.tcpDstPortLB_ = 0;
	retval = _rtl865x_setAclToAsic(rule.aclIdx, &rule);
	if(retval!=SUCCESS)
	{
		ACL_CONTROL_DEBUG_PRK("Error leave %s @ %d \n",__FUNCTION__,__LINE__);
		return retval;
	}
	return SUCCESS;
}
#endif

int rtl865x_acl_control_L34_redirect_clean(void)
{

	_rtl865x_acl_control_free_chain(&ChainList[RTL865x_CHAINLIST_PRIORITY_LEVEL_L3_REDIRECT]);

	if(_rtl865x_acl_control_rewrite_chain_to_acl()!=SUCCESS)
		return FAILED;
	else
		return SUCCESS;
}


#endif




static int _rtl865x_acl_control_free_chain(struct list_head *listHead)
{
	/*free all xtmatch rule*/
	xt_rule_to_acl_t *match2acl,*nxt;
	list_for_each_entry_safe(match2acl,nxt,listHead,list)
	{
		list_del(&match2acl->list);
		kfree(match2acl->aclRule);
		kfree(match2acl);			
	}

	return 0;
}



module_init(rtl865x_acl_control_init);
module_exit(rtl865x_acl_control_exit);