#ifdef __KERNEL__
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter/nf_conntrack_h323_asn1.h>
#endif

#include <rtk_rg_h323.h>
#include <rtk_rg_alg_tool.h>
#include <rtk_rg_internal.h>


int _rtk_rg_ras_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo);

int _rtk_rg_q931_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo);

int _rtk_rg_h245_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo);

int rtk_rg_algRegFunc_TCP_h245(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo);

static int _rtk_rg_TPKT_parse(int direct,unsigned char ** ppData, int remainDataLen, 
										int * pDataLen,rtk_rg_alg_connection_t * pConn)
{
	uint16 tpktLen = 0;
	unsigned char * pData = *ppData;

#if 0
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{	
		int i;
		printk("\nTPKT >>>>>\n");
		for(i=0;i<32;i++)
			printk("0x%1x:",*(pData+i));
		printk("\n<<<<<<<<<<<<\n");
	}
#endif
	if((*pData == H323_TPKT_STR) && (pData[1] ==  H323_TPKT_RESERVE) )
	{
		tpktLen = *((unsigned short*)(pData+2));
		// netmeeting will send TPKT header seperately in a tcp fragment packet before Q931 data
		if( remainDataLen == 4)
		{	    
			//save this TPKT data length
		    pConn->app.h323.tpktLen[direct] = tpktLen-4;
			pConn->app.h323.receiveData[direct] =0;
		    return 0;
		}
		else 
		{
			*ppData = pData + 4;
			*pDataLen =tpktLen-4;
			pConn->app.h323.tpktLen[direct] = 0;
			pConn->app.h323.receiveData[direct] =1;
			return 1;
		}
	}
	//check if previous TPTK header is received
	else if(pConn->app.h323.receiveData[direct] == 0 && pConn->app.h323.tpktLen[direct] > 0 ) 
	{	
		*pDataLen = pConn->app.h323.tpktLen[direct];
		pConn->app.h323.tpktLen[direct] = 0;
		pConn->app.h323.receiveData[direct] =1;
		return 1;
	}
	//check if any next?!
	{
		int i;
		uint16 len;
		if((remainDataLen-*pDataLen)>4)	//still next?!
		{
			for(i=0;i<(remainDataLen-*pDataLen);i++)
			{
				if(pData[i] == H323_TPKT_STR && (pData[i+1] ==  H323_TPKT_RESERVE))
				{
					memcpy(&len,pData+i+2,sizeof(short));
					ALG("\n lock len=%d;remainDataLen=%d;pDataLen=%d\n",len,remainDataLen,*pDataLen);
					if(len > 0 && (len<=(remainDataLen-*pDataLen)))					{
						*ppData = pData + i + 4;
						*pDataLen =len-4;
						pConn->app.h323.tpktLen[direct] = 0;
						pConn->app.h323.receiveData[direct] =1;
#if 0
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{	
		pData = *ppData;
		int i;
		printk("\nTPKT ******\n");
		for(i=0;i<32;i++)
			printk("0x%1x:",*(pData+i));
		printk("\n*******\n");
	}
#endif
						return 1;
					}
				}
			}
			//else no next entry...goodbye
		}
	}
	pConn->app.h323.tpktLen[direct] = 0;
	return 0;
}

static int _rtk_rg_expect_t120(int direct, rtk_rg_alg_connection_t * pConn, rtk_rg_alg_newPort_t * pNewPort)
{
	rtk_rg_alg_tuple_t tuple;
	memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));
	
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{							
		//t120 should use udp
		tuple.isTcp = 0;
		tuple.isIp6 = pConn->tuple.isIp6;
		if(pConn->tuple.isIp6 == 0)
		{		
			tuple.internalIp.ip = pConn->tuple.internalIp.ip;
			tuple.internalPort = pNewPort->newPort;
			tuple.extIp.ip = pConn->tuple.extIp.ip;
			tuple.extPort = pNewPort->newExtPort;
			tuple.remoteIp.ip = pConn->tuple.remoteIp.ip;		
		}
		_rtk_rg_alg_expect_add(direct, &tuple, NULL);
	}
	
	return H323_SUCCESS;
}

//rtp and rtcp are udp packets
static int _rtk_rg_expect_rtp_rtcp(int direct, rtk_rg_alg_connection_t * pConn, rtk_rg_alg_newPort_t * pNewPort)
{
//	rtk_rg_alg_tuple_t tuple;
//	memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));
	
	
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{							
		//rtp or rtcp should use udp
		pConn->tuple.isTcp = 0;
		if(pConn->tuple.isIp6 == 0)
		{
			pConn->tuple.internalPort = pNewPort->newPort;
			//pConn->tuple.extIp.ip = pConn->tuple.extIp.ip;
			//pConn->tuple.extPort = pNewPort->newPort;			//pNewPort->newExtPort;
			pConn->tuple.extPort = pNewPort->newExtPort;
			//pConn->tuple.remoteIp.ip = pConn->tuple.remoteIp.ip;	
		}
		{
			
			int ret;
			rtk_rg_upnpConnection_t upnpConn;
			rtk_rg_pktHdr_t *pPktHdr;	
			pPktHdr = (rtk_rg_pktHdr_t *)pConn->pPktHdr;
			
			upnpConn.is_tcp=pConn->tuple.isTcp;
			upnpConn.wan_intf_idx=pPktHdr->netifIdx;
			//upnpConn.gateway_port=pConn->tuple.internalPort;			//no change port
			upnpConn.gateway_port=pConn->tuple.extPort;
			upnpConn.local_port=pConn->tuple.internalPort;
			upnpConn.local_ip=pConn->tuple.internalIp.ip;
			upnpConn.limit_remote_ip=1;
			upnpConn.limit_remote_port=0;		//can't restrict client using which port to connect
			upnpConn.remote_ip=pConn->tuple.remoteIp.ip;
			upnpConn.remote_port=0;
			upnpConn.type=UPNP_TYPE_ONESHOT;
			upnpConn.timeout=rg_db.algUserDefinedTimeout[RTK_RG_ALG_H323_TCP]; //auto time out if the client do not connect by this WAN
			
ALG("add upnp intf_idx=%d; upnp remot ip=0x%x port=%d; internal ip=0x%x port=%d",upnpConn.wan_intf_idx,pConn->tuple.remoteIp.ip,upnpConn.gateway_port,upnpConn.local_ip,upnpConn.local_port);
			assert_ok((pf.rtk_rg_upnpConnection_add)(&upnpConn,&ret));
		}
		_rtk_rg_alg_expect_add(direct, &pConn->tuple, NULL);
	}
	
	return H323_SUCCESS;
}

//return host byte order port and ipv4 ip, ipv6 ip keeps the network byte order
static int _rtk_rg_get_h245_address(unsigned char * pData, rtk_rg_alg_connection_t * pConn, H245_TransportAddress *taddr, 
										union rtk_rg_alg_addr * pAddr, uint16 * pPort, int * pOffset)
{
	int offset, len;
	if (taddr->choice != eH245_TransportAddress_unicastAddress)
		return 0;

	switch (taddr->unicastAddress.choice) 
	{
	case eUnicastAddress_iPAddress:
		if(pConn->tuple.isIp6)
			return 0;
		offset= taddr->unicastAddress.iPAddress.network;
		len = 4;
		break;
	case eUnicastAddress_iP6Address:
		if(!pConn->tuple.isIp6)
			return 0;
		offset = taddr->unicastAddress.iP6Address.network;
		len = 16;
		break;
	default:
		return 0;
	}
	if(pConn->tuple.isIp6 == 0)
		pAddr->ip = ntohl(*(uint32*)(&pData[offset]));
	else
		memcpy(pAddr, pData+offset, len);
	
	*pPort = ntohs(*(uint16*)(&pData[offset+len]));
	*pOffset = offset;
	return 1;
}

//return host byte order port and ipv4 ip, ipv6 ip keeps the network byte order
static int _rtk_rg_get_h225_address(unsigned char * pData, rtk_rg_alg_connection_t * pConn, TransportAddress  *taddr,
									union rtk_rg_alg_addr * pAddr, uint16 * pPort, int * pOffset)
{
	int offset, len;
	switch (taddr->choice) 
	{
	case eTransportAddress_ipAddress:
		if(pConn->tuple.isIp6)
			return 0;
		offset = taddr->ipAddress.ip;
		len = 4;
		break;
	case eTransportAddress_ip6Address:
		if(!pConn->tuple.isIp6)
			return 0;
		offset = taddr->ip6Address.ip;
		len = 16;
		break;
	default:
		return 0;
	}
	if(pConn->tuple.isIp6 == 0)
		pAddr->ip = ntohl(*(uint32*)(&pData[offset]));
	else
		memcpy(pAddr, pData+offset, len);
	
	*pPort = ntohs(*(uint16*)(&pData[offset+len]));
#if 1	//
if(*pPort != rg_db.algUserDefinedPort[RTK_RG_ALG_H323_TCP])
{	
	_rtk_rg_algDynamicPort_set(rtk_rg_algRegFunc_TCP_h245,0,0, *pPort,1,rg_db.systemGlobal.tcp_long_timeout);
}
#endif
	*pOffset = offset;
	return 1;
}
static int _rtk_rg_set_address_nocheck(unsigned char * pData, rtk_rg_alg_connection_t * pConn, 
								union rtk_rg_alg_addr * pAddr, uint16 port, int offset)
{
	if(pConn->tuple.isIp6 == 0)
	{
		*(uint32*)(&pData[offset]) = htonl(pAddr->ip);
		*(uint16*)(&pData[offset+4])= htons(port);
	}
	return 1;
}
static int _rtk_rg_set_address(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, 
								union rtk_rg_alg_addr * pAddr, uint16 port, int offset)
{
	if((direct == NAPT_DIRECTION_OUTBOUND) && 
	(rtk_rg_alg_addr_cmp(pAddr,&pConn->tuple.internalIp)) && (port == pConn->tuple.internalPort))
	{	    				
		if(pConn->tuple.isIp6 == 0)
		{
#if 0		//
			uint32 tmpIp;
			uint16 tmpPort;
			tmpIp = htonl(pConn->tuple.extIp.ip);
			tmpPort = htons(pConn->tuple.extPort);
			memcpy(pData+offset,&tmpIp,4);
			memcpy(pData+offset+4,&tmpPort,2);
#else
			*(uint32*)(&pData[offset]) = htonl(pConn->tuple.extIp.ip);
			*(uint16*)(&pData[offset+4])= htons(pConn->tuple.extPort);
#endif
		}
		return 1;				
	}
	else if(direct == NAPT_DIRECTION_INBOUND && 
		rtk_rg_alg_addr_cmp(pAddr, &pConn->tuple.extIp) && port == pConn->tuple.extPort)
	{
		if(pConn->tuple.isIp6 == 0)
		{
#if 0		//
			uint32 tmpIp;
			uint16 tmpPort;
			tmpIp = htonl(pConn->tuple.internalIp.ip);
			tmpPort = htons(pConn->tuple.internalPort);
			memcpy(pData+offset,&tmpIp,4);
			memcpy(pData+offset+4,&tmpPort,2);
#else
			*(uint32*)(&pData[offset]) = htonl(pConn->tuple.internalIp.ip);
			*(uint16*)(&pData[offset+4])= htons(pConn->tuple.internalPort);
#endif
		}
		return 1;
	}
	return 0;
}


static int _rtk_rg_set_ras_addr(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, 
								TransportAddress *taddr, int count)
{
	int i, offset;
	uint16 port;
	union rtk_rg_alg_addr addr;

	for (i = 0; i < count; i++) {
		if(_rtk_rg_get_h225_address(pData, pConn, &taddr[i], &addr, &port, &offset))
	   	{
			if( _rtk_rg_set_address(direct, pData, pConn, &addr, port, offset))
				break;
		}
	}
	return 0;
}

static int _rtk_rg_set_signal_addr(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, 
								TransportAddress *taddr, int count)
{
	int i, offset, tmpOffset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	
	for (i = 0; i < count; i++) 
	{
		if(_rtk_rg_get_h225_address(pData, pConn, &taddr[i], &addr, &port, &offset))
	   	{		
			if(direct == NAPT_DIRECTION_OUTBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.internalIp) &&
				port == pConn->app.h323.signal_port[direct])
			{
				/* Fix for Gnomemeeting */
				if (i > 0 && _rtk_rg_get_h225_address(pData, pConn, &taddr[0],
							  &addr, &port, &tmpOffset) &&
					(ntohl(addr.ip) & 0xff000000) == 0x7f000000)
				{
					offset = tmpOffset;
				}
				_rtk_rg_set_address_nocheck(pData, pConn, &pConn->tuple.extIp, pConn->app.h323.signal_port[!direct], offset);
				break;
			}
			else if(direct == NAPT_DIRECTION_INBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.extIp) &&
					port == pConn->app.h323.signal_port[direct])
			{
				_rtk_rg_set_address_nocheck(pData, pConn, &pConn->tuple.internalIp, pConn->app.h323.signal_port[!direct], offset);
			}
		}
	}
	
	return 0;
}
static int _rtk_rg_rtp_rtcp_process(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, 
									H245_TransportAddress *taddr)
{
	int offset = 0;
	uint16 port;
	union rtk_rg_alg_addr addr;
	rtk_rg_alg_newPort_t newPort;
	uint16 rtpPort, rtcpPort, rtpExtPort = 0,rtcpExtPort = 0;
	if(!_rtk_rg_get_h245_address(pData, pConn, taddr, &addr, &port, &offset)) 
	{		
		return 0;
	}
	memset(&newPort, 0 , sizeof(rtk_rg_alg_newPort_t));
	if(direct==NAPT_DIRECTION_OUTBOUND)
	{
		if(rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.internalIp) == 0 || port == 0)
		{
			return 0;
		}
		if(port != pConn->tuple.internalPort )
		{
#if 0
//rtp-rtcp format is ip-port-0x00-ip-port
			//do not change rtp/rtcp port
			rtpPort = port;
			rtpExtPort = port;
			if(pConn->tuple.isIp6 == 0)
			{
				int tmpIP;
				tmpIP = htonl(pConn->tuple.extIp.ip);
				*(uint32*)(&pData[offset]) = tmpIP;	//rtp ip changed
				//*(uint16*)(&pData[offset+4])= htons(rtpExtPort);	//do not change rtp port			
				memcpy(&pData[offset+7],(void *)&tmpIP,4);	//change rtcp ip				
				memcpy(&rtcpPort,&pData[offset+7+4],2);	//get rtcp port				
				rtcpExtPort	= rtcpPort;
				
ALG("_rtk_rg_rtp_rtcp_process get rtp ip/port=%x/%d; rtcp ip/port=%x:%d\n",tmpIP,rtcpPort);
			}
#else			
			if((port%2)==0)	/* RTP port is even */
			{
				rtpPort = port;
				rtcpPort = port + 1;		
				rtpExtPort = _rtk_rg_extPortPair_get(FALSE, port);
				if(rtpExtPort == FAIL)
				{
					return -1;
				}
				rtcpExtPort = rtpExtPort+1;
				//DEBUG("rtpPort:%d, rtpExtPort=%d, rtcpPort=%d, rtcpExtPort=%d", rtpPort, rtpExtPort, rtcpPort, rtcpExtPort);
				if(pConn->tuple.isIp6 == 0)
				{
					int tmpIP;
					tmpIP = htonl(pConn->tuple.extIp.ip);
					*(uint32*)(&pData[offset]) = tmpIP;	//rtp ip changed
					*(uint16*)(&pData[offset+4])= htons(rtpExtPort);	//rtp is changed
					//open rtp
					newPort.newPort = rtpPort;
					newPort.newExtPort = rtpExtPort;
					_rtk_rg_expect_rtp_rtcp(direct, pConn, &newPort);
								
					memcpy(&pData[offset+7],(void *)&tmpIP,4);	//change rtcp ip				
					memcpy(&pData[offset+7+4],&rtcpExtPort,2);	//get rtcp ext port
					//open rtcp
					newPort.newPort = rtcpPort;
					newPort.newExtPort = rtcpExtPort;
					_rtk_rg_expect_rtp_rtcp(direct, pConn, &newPort);
				}
			}
			else	/* only RTCP ...this should be odd*/
			{
				//since re-assign port, need to modify port also
				rtcpExtPort = _rtk_rg_extPort_get(FALSE, port);
				if(pConn->tuple.isIp6 == 0)
				{
					*(uint32*)(&pData[offset]) = htonl(pConn->tuple.extIp.ip);
					*(uint16*)(&pData[offset+4])= htons(rtcpExtPort);	//rtp is changed
				}
			}
			
#endif		
		}
		else
		{
			//since re-assign port, need to modify port also
			rtcpExtPort = _rtk_rg_extPort_get(FALSE, port);
			if(pConn->tuple.isIp6 == 0)
			{
				*(uint32*)(&pData[offset]) = htonl(pConn->tuple.extIp.ip);
				*(uint16*)(&pData[offset+4])= htons(rtcpExtPort);	//rtp is changed
			}
		}
	}
	
	return 0;
}

static int _rtk_rg_t120_process(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, 
								H245_TransportAddress *taddr)
{
	int offset = 0;
	uint16 port;
	union rtk_rg_alg_addr addr;
	uint16 extPort;
	rtk_rg_alg_newPort_t newPort;
		
	if(!_rtk_rg_get_h245_address(pData, pConn, taddr, &addr, &port, &offset)) 
	{
		return 0;
	}
	memset(&newPort, 0 , sizeof(rtk_rg_alg_newPort_t));
	if(direct==NAPT_DIRECTION_OUTBOUND)
	{
		if(rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.internalIp) == 0 || port == 0)
			return 0;
					
		if(port != pConn->tuple.internalPort) 
		{		
			extPort=_rtk_rg_extPort_get(FALSE,port);
			if(extPort==FAIL) return 0;	

			if(pConn->tuple.isIp6 == 0)
			{
				*(uint32*)(&pData[offset]) = htonl(pConn->tuple.extIp.ip);
				*(uint16*)(&pData[offset+4])= htons(extPort);
			}
			newPort.newPort = port;						
			newPort.newExtPort = extPort;	
					
			_rtk_rg_expect_t120(direct,pConn, &newPort);
		}				
		
	}
	
	return 0;
}

static int _rtk_rg_h245_channel_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, 
											H2250LogicalChannelParameters * channel)
{
	int ret;

	if (channel->options & eH2250LogicalChannelParameters_mediaChannel) {
		/* RTP */
		//ALG("RTP\n");
		ret = _rtk_rg_rtp_rtcp_process(direct, pData, pConn, &channel->mediaChannel);
		if (ret < 0)
			return -1;
	
	}

	
	if (channel->options & eH2250LogicalChannelParameters_mediaControlChannel) 
		{
		/* RTCP */
		//ALG("RTTP\n");
		ret = _rtk_rg_rtp_rtcp_process(direct, pData, pConn, &channel->mediaControlChannel);
		if (ret < 0)
			return -1;
	
	}

	
	return 0;
}

//open logical channel process
static int _rtk_rg_h245_olc_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, OpenLogicalChannel *olc)
{	
	int ret;
	if (olc->forwardLogicalChannelParameters.multiplexParameters.choice ==
		eOpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)
	{
		ret = _rtk_rg_h245_channel_process(direct, pData, pConn,
				&olc->forwardLogicalChannelParameters.multiplexParameters.h2250LogicalChannelParameters);
		if (ret < 0)
				return -1;
	}
	
	if ((olc->options & eOpenLogicalChannel_reverseLogicalChannelParameters) &&
			(olc->reverseLogicalChannelParameters.options &
			 eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters)
			&& (olc->reverseLogicalChannelParameters.multiplexParameters.choice ==
			eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters))
	{
		ret = _rtk_rg_h245_channel_process(direct, pData, pConn,
						 &olc->
						 reverseLogicalChannelParameters.
						 multiplexParameters.
						 h2250LogicalChannelParameters);
		if (ret < 0)
				return -1;
	}
	
	if ((olc->options & eOpenLogicalChannel_separateStack) &&
		 olc->forwardLogicalChannelParameters.dataType.choice == eDataType_data &&
		 olc->forwardLogicalChannelParameters.dataType.data.application.choice == 
		 eDataApplicationCapability_application_t120 &&
		 olc->forwardLogicalChannelParameters.dataType.data.application.t120.choice == 
		 eDataProtocolCapability_separateLANStack &&
		 olc->separateStack.networkAddress.choice ==
			eNetworkAccessParameters_networkAddress_localAreaAddress) 
	{
		ret = _rtk_rg_t120_process(direct, pData, pConn,
					  &olc->separateStack.networkAddress.localAreaAddress);
		if (ret < 0)
				return -1;
	}
	
	return H323_SUCCESS;
}

//open logical channel ack process
static int _rtk_rg_h245_olca_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, OpenLogicalChannelAck *olca)
{
	H2250LogicalChannelAckParameters *ack;
	int ret;

	if ((olca->options & eOpenLogicalChannelAck_reverseLogicalChannelParameters) &&

	    (olca->reverseLogicalChannelParameters.options & eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters) &&
	    (olca->reverseLogicalChannelParameters.multiplexParameters.choice == eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)
	    )
	
	    {
		ret = _rtk_rg_h245_channel_process(direct, pData, pConn, &olca->reverseLogicalChannelParameters.multiplexParameters.h2250LogicalChannelParameters);
	    	if (ret < 0)
	    	return -1;
	
	    }

	
	if ((olca->options & eOpenLogicalChannelAck_forwardMultiplexAckParameters) &&
	    (olca->forwardMultiplexAckParameters.choice == eOpenLogicalChannelAck_forwardMultiplexAckParameters_h2250LogicalChannelAckParameters)
	    )
	
	    {
		
	    	ack = &olca->forwardMultiplexAckParameters.h2250LogicalChannelAckParameters;
			if (ack->options & eH2250LogicalChannelAckParameters_mediaChannel) 
			{
			/* RTP */
			
			ret = _rtk_rg_rtp_rtcp_process(direct, pData, pConn, &ack->mediaChannel);
			if (ret < 0)
				return -1;
		
			}

		
			if (ack->options & eH2250LogicalChannelAckParameters_mediaControlChannel) 
			{
			/* RTCP */
			
			ret = _rtk_rg_rtp_rtcp_process(direct, pData, pConn,&ack->mediaControlChannel);
			if (ret < 0)
				return -1;
			}
		}

	
		if ((olca->options & eOpenLogicalChannelAck_separateStack) &&
			olca->separateStack.networkAddress.choice == eNetworkAccessParameters_networkAddress_localAreaAddress) 
			{
		
				ret = _rtk_rg_t120_process(direct, pData, pConn,&olca->separateStack.networkAddress.localAreaAddress);
				if (ret < 0)
					return -1;
	
			}

	
			return 0;
}

static int _rtk_rg_MultimediaSystemControlMessage_process(int direct, unsigned char * pData, 
									rtk_rg_alg_connection_t * pConn, MultimediaSystemControlMessage *mscm)
{	
	int ret = 0;
	
	switch (mscm->choice) {
	case eMultimediaSystemControlMessage_request:
		
		if (mscm->request.choice == eRequestMessage_openLogicalChannel) 
		{		
			ALG("eRequestMessage_openLogicalChannel\n");
			ret = _rtk_rg_h245_olc_process(direct, pData, pConn, &mscm->request.openLogicalChannel);
		}
		break;
	case eMultimediaSystemControlMessage_response:
		if (mscm->response.choice == eResponseMessage_openLogicalChannelAck) 
		{	
			ALG("eResponseMessage_openLogicalChannelAck\n");
			ret = _rtk_rg_h245_olca_process(direct, pData, pConn, &mscm->response.openLogicalChannelAck);
		}
		break;
	default:
		ret = -1;
		break;
	}
	return ret;
}

static int _rtk_rg_h245_process(int direct,unsigned char * pAppData, int appLen,rtk_rg_alg_connection_t * pConn)
{
	static MultimediaSystemControlMessage mscm;
	int ret,remainDataLen, h245DataLen;
	unsigned char  * pData = pAppData;
	unsigned char  * pDataEnd = pAppData + appLen;
	
	while(((remainDataLen = pDataEnd- pData) >=4) && _rtk_rg_TPKT_parse(direct, &pData, remainDataLen, &h245DataLen, pConn))
	{
		ret = DecodeMultimediaSystemControlMessage(pData, h245DataLen, &mscm);
		if (ret < 0) 
		{
			ALG("rtk_rg_h323: h245 decoding error: %s\n", ret == H323_ERROR_BOUND ?
						 "out of bound" : "out of range");
			break;
		}
		ret = _rtk_rg_MultimediaSystemControlMessage_process(direct, pData, pConn, &mscm);

		pData += h245DataLen;
	}
	return H323_SUCCESS;
}

int _rtk_rg_h245_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo)
{
	int ret = SUCCESS;
#ifdef __KERNEL__
	char *pData, *pAppData;
	int appLen=0,dataOff=0;
	rtk_rg_pktHdr_t *pPktHdr;
	struct sk_buff *skb;
	rtk_rg_alg_connection_t * pConn;
	
	
	if(pSkb == NULL || pPktInfo == NULL)
		return -1;
	
	pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;
	pConn = (rtk_rg_alg_connection_t *)pConnInfo;
	skb= (struct sk_buff *)pSkb;
	
	pData=skb->data;	
	if(pPktHdr->tagif&TCP_TAGIF)
		dataOff = pPktHdr->l4Offset + pPktHdr->headerLen;
	else
		dataOff = pPktHdr->l4Offset + 8; /*udp header length is 8 bytes*/	
#if 1
	appLen = pPktHdr->l3Len + pPktHdr->l3Offset - dataOff;
	ALG("_rtk_rg_h245_handler appLen(%d-%d-%d) = %d\n",pPktHdr->l3Len,pPktHdr->l3Offset,dataOff,appLen);
#else
	appLen = skb->len - dataOff;
#endif
	pAppData = pData + dataOff;
	//not valid H245 packet, just return
	if(appLen < 4)
		return FAIL;
	
	if(after == 0)
	{
		//do nothing
	}
	else
	{
		if(direct == NAPT_DIRECTION_OUTBOUND)
		{
			if(pConn->tuple.isIp6 == 0)
			{
				pConn->tuple.extIp.ip = ntohl(*pPktHdr->pIpv4Sip);
				pConn->tuple.extPort = ntohs(*pPktHdr->pSport);
			}			
			ret = _rtk_rg_h245_process(direct,pAppData,appLen,pConn);
		}
		else
		{
			ret = _rtk_rg_h245_process(direct,pAppData,appLen,pConn);	
		}
	}
#endif
	return ret;
}

static rtk_rg_alg_expect_t * _rtk_rg_init_expect(int direct, rtk_rg_alg_connection_t * pConn, rtk_rg_alg_newPort_t * pNewPort)
{
	rtk_rg_alg_tuple_t tuple;
	rtk_rg_alg_expect_t * pEntry=NULL;
	memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));

	tuple.isTcp = pConn->tuple.isTcp;
	tuple.isIp6 = pConn->tuple.isIp6;

	if(direct == NAPT_DIRECTION_OUTBOUND)
	{								
		if(pConn->tuple.isIp6 == 0)
		{			
			tuple.internalIp.ip = pConn->tuple.internalIp.ip;
			tuple.internalPort = pNewPort->newPort;
			tuple.extIp.ip = pConn->tuple.extIp.ip;
			tuple.extPort = pNewPort->newExtPort;
			tuple.remoteIp.ip = pConn->tuple.remoteIp.ip;	
		}
		else
		{	
			//ipv6
			memcpy(&tuple.internalIp, &pConn->tuple.internalIp, sizeof(union rtk_rg_alg_addr));
			tuple.internalPort = pNewPort->newPort;
			memcpy(&tuple.remoteIp, &pConn->tuple.remoteIp, sizeof(union rtk_rg_alg_addr));
		}
		pEntry = _rtk_rg_alg_expect_add(direct, &tuple, NULL);
	}
	else
	{			
		if(pConn->tuple.isIp6 == 0)
		{
			tuple.internalIp.ip = pConn->tuple.internalIp.ip;
			tuple.remoteIp.ip = pConn->tuple.remoteIp.ip;
			tuple.remotePort = pNewPort->newPort;
		}
		else
		{	
			//ipv6
			memcpy(&tuple.internalIp, &pConn->tuple.internalIp, sizeof(union rtk_rg_alg_addr));
			memcpy(&tuple.remoteIp, &pConn->tuple.remoteIp, sizeof(union rtk_rg_alg_addr));
			tuple.remotePort = pNewPort->newPort;
		}
		pEntry = _rtk_rg_alg_expect_add(direct, &tuple, NULL);
	}
	return pEntry;
}

static int _rtk_rg_expect_h245(int direct, rtk_rg_alg_connection_t * pConn, rtk_rg_alg_newPort_t * pNewPort)
{
	rtk_rg_alg_expect_t * pExpect;
	pExpect = _rtk_rg_init_expect(direct, pConn, pNewPort);
	if(pExpect != NULL)
	{
		pExpect->appHandler = _rtk_rg_h245_handler;
	}
	return H323_SUCCESS;
}

int _rtk_rg_ras_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo);

int _rtk_rg_q931_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo);


static int _rtk_rg_expect_ras(int direct, rtk_rg_alg_connection_t * pConn, rtk_rg_alg_newPort_t * pNewPort)
{
	rtk_rg_alg_expect_t * pExpect;
	pExpect = _rtk_rg_init_expect(direct, pConn, pNewPort);
	
	if(pExpect != NULL)
	{
		pExpect->appHandler = _rtk_rg_ras_handler;
	}
	return H323_SUCCESS;
}

static int _rtk_rg_expect_q931(int direct, rtk_rg_alg_connection_t * pConn, rtk_rg_alg_newPort_t * pNewPort)
{
	rtk_rg_alg_expect_t * pExpect;
	pExpect = _rtk_rg_init_expect(direct, pConn, pNewPort);
	if(pExpect != NULL)
	{
		pExpect->appHandler = _rtk_rg_q931_handler;
	}
	return H323_SUCCESS;
}

static int _rtk_rg_h245Address_process(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, union rtk_rg_alg_addr * pAddr, uint16 port, int offset)
{
	uint16  extPort;
	rtk_rg_alg_newPort_t newPort;

	memset(&newPort, 0 , sizeof(rtk_rg_alg_newPort_t));		
	
	if(direct==NAPT_DIRECTION_OUTBOUND)
	{
		if(rtk_rg_alg_addr_cmp(pAddr, &pConn->tuple.internalIp) && port != pConn->tuple.internalPort)
		{				
			if(pConn->tuple.isIp6 == 0)
			{
				extPort=_rtk_rg_extPort_get(pConn->tuple.isTcp, port);
				if(extPort==FAIL) return 0;	

				*(uint32*)(&pData[offset]) = htonl(pConn->tuple.extIp.ip);
				*(uint16*)(&pData[offset+4])= htons(extPort);
				
				newPort.newPort = port;						
				newPort.newExtPort = extPort;
			}
				
			_rtk_rg_expect_h245(direct,pConn, &newPort);
		}				
		
	}
	else
	{
		if (rtk_rg_alg_addr_cmp(pAddr, &pConn->tuple.remoteIp) && (port != pConn->tuple.remotePort))
		{
			newPort.newPort = port;		
			_rtk_rg_expect_h245(direct,pConn, &newPort);
		}
	}
	return 0;

}

static int _rtk_rg_h225_callforwarding(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, union rtk_rg_alg_addr * pAddr, uint16 port, int offset)
{
	//may use a different ip address, so need to create a new napt connection, not support now
	return H323_SUCCESS;
}

static int _rtk_rg_h225_setup_process(int direct, unsigned char * pData, rtk_rg_alg_connection_t * pConn, Setup_UUIE *setup)
{
	int ret, i, offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	
	ALG("_rtk_rg_h225_setup_process\n");
	if (setup->options & eSetup_UUIE_h245Address) {

		if(_rtk_rg_get_h225_address(pData, pConn, &setup->h245Address, &addr, &port, &offset))
	   	{
	   		ret = _rtk_rg_h245Address_process(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
		}	
	}

	if (setup->options & eSetup_UUIE_destCallSignalAddress)
	{
	   	if(_rtk_rg_get_h225_address(pData, pConn, &setup->destCallSignalAddress, &addr, &port, &offset))
	   	{
			ret = _rtk_rg_set_address(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
	   	}		
	}

	if (setup->options & eSetup_UUIE_sourceCallSignalAddress) 
	{
		if(_rtk_rg_get_h225_address(pData, pConn, &setup->sourceCallSignalAddress, &addr, &port, &offset))
	   	{
			ret = _rtk_rg_set_address(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
	   	}
	}

	if (setup->options & eSetup_UUIE_fastStart) {
		for (i = 0; i < setup->fastStart.count; i++) {
			ret = _rtk_rg_h245_olc_process(direct, pData, pConn,
					  &setup->fastStart.item[i]);
			if (ret < 0)
				return -1;
		}
	}

	return 0;
}

static int _rtk_rg_h225_callproceeding_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, CallProceeding_UUIE *callproc)
{
	int ret, i, offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	if (callproc->options & eCallProceeding_UUIE_h245Address) {
		

		if(_rtk_rg_get_h225_address(pData, pConn, &callproc->h245Address, &addr, &port, &offset))
	   	{
	   		ret = _rtk_rg_h245Address_process(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
		}	
	}

	if (callproc->options & eCallProceeding_UUIE_fastStart) {
		for (i = 0; i < callproc->fastStart.count; i++) {
			ret = _rtk_rg_h245_olc_process(direct, pData, pConn,
					  &callproc->fastStart.item[i]);
			if (ret < 0)
				return -1;
		}
	}

	return 0;
}

static int _rtk_rg_h225_connect_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, Connect_UUIE *connect)
{
	int ret, i, offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	
	if (connect->options & eConnect_UUIE_h245Address) {
		
		if(_rtk_rg_get_h225_address(pData, pConn, &connect->h245Address, &addr, &port, &offset))
	   	{
	   		ret = _rtk_rg_h245Address_process(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
		}	
	}

	if (connect->options & eConnect_UUIE_fastStart) {
		for (i = 0; i < connect->fastStart.count; i++) {
			ret = _rtk_rg_h245_olc_process(direct, pData, pConn,
					  &connect->fastStart.item[i]);
			if (ret < 0)
				return -1;
		}

	}

	return 0;
}

static int _rtk_rg_h225_alerting_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, Alerting_UUIE *alert)
{
	int ret, i, offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	
	if (alert->options & eAlerting_UUIE_h245Address) {
		

		if(_rtk_rg_get_h225_address(pData, pConn, &alert->h245Address, &addr, &port, &offset))
	   	{
	   		ret = _rtk_rg_h245Address_process(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
		}
	}

	if (alert->options & eAlerting_UUIE_fastStart) {
		for (i = 0; i < alert->fastStart.count; i++) {
			ret = _rtk_rg_h245_olc_process(direct, pData, pConn,
					  &alert->fastStart.item[i]);
			if (ret < 0)
				return -1;
		}
	}

	return 0;
}

static int _rtk_rg_h225_facility_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, Facility_UUIE *facility)
{
	int ret, i, offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	
	if ((facility->reason.choice == eFacilityReason_callForwarded) &&
		(facility->options & eFacility_UUIE_alternativeAddress))
	{
		if(_rtk_rg_get_h225_address(pData, pConn, &facility->alternativeAddress, &addr, &port, &offset))
		{
			//use another ip:port to open a new h225 connection
			_rtk_rg_h225_callforwarding(direct, pData, pConn, &addr, port, offset);
		}
	}

	

	if (facility->options & eFacility_UUIE_h245Address) {

		if(_rtk_rg_get_h225_address(pData, pConn, &facility->h245Address, &addr, &port, &offset))
	   	{
	   		ret = _rtk_rg_h245Address_process(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
		}
	}

	if (facility->options & eFacility_UUIE_fastStart) {
		for (i = 0; i < facility->fastStart.count; i++) {
			ret = _rtk_rg_h245_olc_process(direct, pData, pConn,
					  &facility->fastStart.item[i]);
			if (ret < 0)
				return -1;
		}
	}

	return 0;
}

static int _rtk_rg_h225_progress_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, Progress_UUIE *progress)
{
	int ret, i, offset;
	uint16 port;
	union rtk_rg_alg_addr addr;


	if (progress->options & eProgress_UUIE_h245Address) {
		
		
		if(_rtk_rg_get_h225_address(pData, pConn, &progress->h245Address, &addr, &port, &offset))
	   	{
	   		ret = _rtk_rg_h245Address_process(direct, pData, pConn, &addr, port, offset);
			if (ret < 0)
				return -1;
		}
	}

	if (progress->options & eProgress_UUIE_fastStart) {
		for (i = 0; i < progress->fastStart.count; i++) {
			ret = _rtk_rg_h245_olc_process(direct, pData, pConn,
					  &progress->fastStart.item[i]);
			if (ret < 0)
				return -1;
		}
	}

	return 0;
}

static int _rtk_rg_h225_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, Q931 * pQ931)
{
	H323_UU_PDU * pdu = &pQ931->UUIE.h323_uu_pdu;
	int i;
	int ret = 0;


	ALG("_rtk_rg_h225_process choice=%d(%d)\n",pdu->h323_message_body.choice,eH323_UU_PDU_h323_message_body_setup);
	switch (pdu->h323_message_body.choice) {
	
		case eH323_UU_PDU_h323_message_body_setup:
		
		ret = _rtk_rg_h225_setup_process(direct, pData, pConn,&pdu->h323_message_body.setup);
		break;
	
		case eH323_UU_PDU_h323_message_body_callProceeding:
		
		ret = _rtk_rg_h225_callproceeding_process(direct, pData, pConn,&pdu->h323_message_body.callProceeding);
		break;
		case eH323_UU_PDU_h323_message_body_connect:
		
		ret = _rtk_rg_h225_connect_process(direct, pData, pConn,&pdu->h323_message_body.connect);
		break;
	
		case eH323_UU_PDU_h323_message_body_alerting:
		
		ret = _rtk_rg_h225_alerting_process(direct, pData, pConn,&pdu->h323_message_body.alerting);
		
		break;
	
		case eH323_UU_PDU_h323_message_body_facility:
		
		ret = _rtk_rg_h225_facility_process(direct, pData, pConn,&pdu->h323_message_body.facility);
		
		break;
	
		case eH323_UU_PDU_h323_message_body_progress:
		
		ret = _rtk_rg_h225_progress_process(direct, pData, pConn,&pdu->h323_message_body.progress);
		
		break;
	
		default:
		break;
	
	}

	
	if (ret < 0)
		return -1;

	if (pdu->options & eH323_UU_PDU_h245Control) 
	{
		
		for (i = 0; i < pdu->h245Control.count; i++) {
			
			ret = _rtk_rg_MultimediaSystemControlMessage_process(direct, pData, pConn,&pdu->h245Control.item[i]);
			if (ret < 0) return -1;
		}
	
	}
	return 0;	
}

static int _rtk_rg_q931_process(int direct,unsigned char * pAppData, int appLen,rtk_rg_alg_connection_t * pConn)
{
	int ret, remainDataLen, q931DataLen = 0;
	unsigned char  * pData = pAppData;
	unsigned char  * pDataEnd = pAppData + appLen;
	static Q931 q931;
	ret = H323_SUCCESS;//
	
	while(((remainDataLen = pDataEnd- pData) >=4) && _rtk_rg_TPKT_parse(direct, &pData, remainDataLen, &q931DataLen, pConn))
	{
		/* Decode Q.931 signal */
		ret = DecodeQ931(pData, q931DataLen, &q931);
		if (ret < 0) {
			ALG("rtk_rg_h323 : q931 decoding error: %s\n",
				 ret == H323_ERROR_BOUND ?
				 "out of bound" : "out of range");
			ret = H323_FORMAT_ERROR;//
			break;
		}
		_rtk_rg_h225_process(direct, pData, pConn, &q931);
		pData += q931DataLen;
	}
	return ret;	//
}

static int _rtk_rg_ras_grq_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, GatekeeperRequest *grq)
{
	return _rtk_rg_set_ras_addr(direct, pData, pConn, &grq->rasAddress, 1);
}

static int _rtk_rg_ras_gcf_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, GatekeeperConfirm *gcf)
{
	int offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	rtk_rg_alg_newPort_t newPort;
	
	if (!_rtk_rg_get_h225_address(pData, pConn, &gcf->rasAddress, &addr, &port, &offset))
		return 0;	
	
	//gatekeeper should be in public network, so direct should be NAPT_DIRECTION_INBOUND
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{
		return 0;	
	}
	else if(direct == NAPT_DIRECTION_INBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.remoteIp) )
	{	
		/* Registration port is the same as discovery port */
		if(port == pConn->tuple.remotePort)
			return 0;
		
		newPort.newPort = port;
		_rtk_rg_expect_ras(direct, pConn, &newPort);
	}	
	return 1;
}

static int _rtk_rg_ras_rrq_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, RegistrationRequest *rrq)
{
	int i, offset, count;
	TransportAddress *taddr;
	uint16 port, extPort;
	union rtk_rg_alg_addr addr;
	rtk_rg_alg_newPort_t newPort;
	
	taddr = rrq->callSignalAddress.item;
	count = rrq->callSignalAddress.count;
	for(i = 0; i< count; i++)
	{	
		if(_rtk_rg_get_h225_address(pData, pConn, &taddr[i], &addr, &port, &offset))
	   	{
	   		if(direct == NAPT_DIRECTION_OUTBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.internalIp) 
				&& port > 0 && port != pConn->tuple.internalPort)
				break;
		}
	}
	if (i >= count)		/* Not found */
		return 0;

	//gatekeeper should be in public network, so direct should be NAPT_DIRECTION_OUTBOUND
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{
		extPort=_rtk_rg_extPort_get(FALSE, port);
		if(extPort==FAIL) return 0;	
				
		newPort.newPort = port;
		newPort.newExtPort = extPort;
		_rtk_rg_expect_q931(direct, pConn, &newPort);
		
		if(_rtk_rg_set_address_nocheck(pData, pConn, &pConn->tuple.extIp, extPort, offset))
		{
			/* Save ports */
			pConn->app.h323.signal_port[direct] = port;
			pConn->app.h323.signal_port[!direct] = extPort;
			
			/* Fix for Gnomemeeting */
			if (i > 0 && _rtk_rg_get_h225_address(pData, pConn, &taddr[0], &addr, &port, &offset) &&
			    (ntohl(addr.ip) & 0xff000000) == 0x7f000000) 
			{
				_rtk_rg_set_address_nocheck(pData, pConn, &pConn->tuple.extIp, extPort, offset);
			}
		}
	}

	return _rtk_rg_set_ras_addr(direct, pData, pConn, rrq->rasAddress.item, rrq->rasAddress.count);
	
}

static int _rtk_rg_ras_rcf_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, RegistrationConfirm *rcf)
{
	return _rtk_rg_set_signal_addr(direct, pData, pConn,
					rcf->callSignalAddress.item, rcf->callSignalAddress.count);	
}

static int _rtk_rg_ras_urq_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, UnregistrationRequest *urq)
{
	 _rtk_rg_set_signal_addr(direct, pData, pConn,urq->callSignalAddress.item,
				   			urq->callSignalAddress.count);
	 //need to remove  expects related to this connection
	 return 1;
}

static int _rtk_rg_ras_arq_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, AdmissionRequest *arq)
{
	int offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	
	if ((arq->options & eAdmissionRequest_destCallSignalAddress) &&
	    _rtk_rg_get_h225_address(pData, pConn, &arq->destCallSignalAddress, &addr, &port, &offset) &&
	    port == pConn->app.h323.signal_port[direct]) 
	{
		/* Answering ARQ */
		if(direct == NAPT_DIRECTION_OUTBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.internalIp))
			return _rtk_rg_set_address_nocheck(pData, pConn, &pConn->tuple.extIp, pConn->app.h323.signal_port[!direct], offset);
		else if(direct == NAPT_DIRECTION_INBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.extIp))
			return _rtk_rg_set_address_nocheck(pData, pConn, &pConn->tuple.internalIp, pConn->app.h323.signal_port[!direct], offset);
	}

	if ((arq->options & eAdmissionRequest_srcCallSignalAddress) &&
	    _rtk_rg_get_h225_address(pData, pConn, &arq->srcCallSignalAddress, &addr, &port, &offset)) 
	{
		/* Calling ARQ */
		return _rtk_rg_set_address(direct, pData, pConn, &addr, port, offset);
	}
	return 0;
}

static int _rtk_rg_ras_acf_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, AdmissionConfirm *acf)
{
	int offset;
	uint16 port;
	union rtk_rg_alg_addr addr;
	
	if (!_rtk_rg_get_h225_address(pData, pConn, &acf->destCallSignalAddress,
			   &addr, &port, &offset))
		return 0;
	
	/* Answering ACF */
	if(direct == NAPT_DIRECTION_OUTBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.remoteIp))
	{
		return 0;	
	}
	else if(direct == NAPT_DIRECTION_INBOUND && rtk_rg_alg_addr_cmp(&addr, &pConn->tuple.extIp) )
	{	
		return  _rtk_rg_set_signal_addr(direct, pData, pConn, &acf->destCallSignalAddress, 1);
	}

	//new ip address, need to add a expect
	return 0;
}

static int _rtk_rg_ras_result_process(int direct,unsigned char * pData, rtk_rg_alg_connection_t * pConn, RasMessage *ras)
{
	switch (ras->choice) {
	case eRasMessage_gatekeeperRequest:
		return _rtk_rg_ras_grq_process(direct, pData, pConn, &ras->gatekeeperRequest);
	case eRasMessage_gatekeeperConfirm:
		return _rtk_rg_ras_gcf_process(direct, pData, pConn, &ras->gatekeeperConfirm);
	case eRasMessage_registrationRequest:
		return _rtk_rg_ras_rrq_process(direct, pData, pConn, &ras->registrationRequest);
	case eRasMessage_registrationConfirm:
		return _rtk_rg_ras_rcf_process(direct, pData, pConn, &ras->registrationConfirm);
	case eRasMessage_unregistrationRequest:
		return _rtk_rg_ras_urq_process(direct, pData, pConn, &ras->unregistrationRequest);
	case eRasMessage_admissionRequest:
		return _rtk_rg_ras_arq_process(direct, pData, pConn, &ras->admissionRequest);
	case eRasMessage_admissionConfirm:
		return _rtk_rg_ras_acf_process(direct, pData, pConn, &ras->admissionConfirm);
	default:
		break;
	}
	
	return 0;
}


static int _rtk_rg_ras_process(int direct,unsigned char * pAppData, int appLen,rtk_rg_alg_connection_t * pConn)
{
	int ret;
	RasMessage ras;
	unsigned char  * pData = pAppData;
	
	/* Decode RAS message */
	ret = DecodeRasMessage(pAppData, appLen, &ras);
	if (ret < 0) {
		DEBUG("rtk_rg_h323: ras message decoding error: %s\n",
			 ret == H323_ERROR_BOUND ?
			 "out of bound" : "out of range");
		return H323_STOP;
	}
	/* Process RAS message */
	if (_rtk_rg_ras_result_process(direct, pData, pConn, &ras) < 0)
		return H323_FAIL;
	
	return H323_SUCCESS;
}

int _rtk_rg_ras_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo)
{
	int ret = SUCCESS;
#ifdef __KERNEL__
	unsigned char *pData, *pAppData;
	int appLen=0,dataOff=0;
	rtk_rg_pktHdr_t *pPktHdr;
	struct sk_buff *skb;
	rtk_rg_alg_connection_t * pConn;
	
	pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;
	pConn = (rtk_rg_alg_connection_t *)pConnInfo;
	skb= (struct sk_buff *)pSkb;
	
	pData=skb->data;	
	//udp header length is 8 bytes
	dataOff = pPktHdr->l4Offset + 8;	
#if 1	//
	appLen = pPktHdr->l3Len + pPktHdr->l3Offset - dataOff;
	ALG("_rtk_rg_ras_handler appLen(%d-%d-%d) = %d\n",pPktHdr->l3Len,pPktHdr->l3Offset,dataOff,appLen);
#else
	appLen = skb->len - dataOff;
#endif
	pAppData = pData + dataOff;
	if(after==0)
	{
		//do nothing
	}
	else
	{
		//Post function
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{		
			if(pConn->tuple.isIp6 == 0)
			{
				pConn->tuple.extIp.ip = ntohl(*pPktHdr->pIpv4Sip);
				pConn->tuple.extPort = ntohs(*pPktHdr->pSport);
			}
			ret = _rtk_rg_ras_process(direct, pAppData, appLen, pConn);				
		}
		else
		{
			ret = _rtk_rg_ras_process(direct, pAppData, appLen, pConn);
		}
	}
#endif
	return ret;
}

int _rtk_rg_q931_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo)
{
	int ret = SUCCESS;
#ifdef __KERNEL__
	unsigned char *pData, *pAppData;
	int appLen=0,dataOff=0;
	rtk_rg_pktHdr_t *pPktHdr;
	struct sk_buff *skb;
	rtk_rg_alg_connection_t * pConn;
	
	pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;
	pConn = (rtk_rg_alg_connection_t *)pConnInfo;
	skb= (struct sk_buff *)pSkb;
	pData=skb->data;
	if(pPktHdr->tagif&TCP_TAGIF)
		dataOff = pPktHdr->l4Offset + pPktHdr->headerLen;
	else
		dataOff = pPktHdr->l4Offset + 8; /*udp header length is 8 bytes*/	
	
#if 1	//
	appLen = pPktHdr->l3Len + pPktHdr->l3Offset - dataOff;
	ALG("_rtk_rg_q931_handler appLen(%d-%d-%d) = %d\n",pPktHdr->l3Len,pPktHdr->l3Offset,dataOff,appLen);
#else
	appLen = skb->len - dataOff;
#endif
	pAppData = pData + dataOff;
	//not valid q931 packet, just return
	if(appLen < 4)
		return H323_FAIL;
	if(after==0)
	{
		//do nothing
	}
	else
	{
		//Post function
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{		
			if(pConn->tuple.isIp6 == 0)
			{
				pConn->tuple.extIp.ip = ntohl(*pPktHdr->pIpv4Sip);
				pConn->tuple.extPort = ntohs(*pPktHdr->pSport);
			}
			ret = _rtk_rg_q931_process(direct, pAppData, appLen, pConn);
		}
		else
		{
			ret = _rtk_rg_q931_process(direct, pAppData, appLen, pConn);
		}
	}
#endif
	return ret;
}

int rtk_rg_algRegFunc_TCP_h245(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo)
{
//Attention: caller function needs to make sure it needs to do napt modification
//ipv6 address and port doesn't need to do napt modification now
#ifdef __KERNEL__
	rtk_rg_pktHdr_t *pPktHdr;
	rtk_rg_alg_connection_t * pConn;
	rtk_rg_alg_tuple_t tuple;
	pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;
	
	memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));
	if(after==0)
	{
		//Pre function
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
			
			pConn = _rtk_rg_alg_connection_find(&tuple);
			
			if(pConn == NULL)
			{
				pConn = _rtk_rg_alg_connection_add(&tuple);	
			}
			pConn->pPktHdr = pPktInfo;			
		}
	}
	else
	{
		//Post function
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{		
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);

			pConn = _rtk_rg_alg_connection_find(&tuple);
			//entrance
			if(pConn == NULL)
				return FAIL;
			
			pConn->pPktHdr = pPktInfo;
			_rtk_rg_h245_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
		}
		else
		{
			//Attention, pPktHdr->ipv4Dip is not the original external ip after napt modification, it is the internal ip
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
			pConn = _rtk_rg_alg_connection_find(&tuple);
			
			if(pConn == NULL)
				return FAIL;
			pConn->pPktHdr = pPktInfo;
			_rtk_rg_h245_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
		}

	}
#endif
#if defined(CONFIG_RTL9600_SERIES)
	//20160331LUKE: checksum by sw offload
	if(pPktHdr->egressTagif&PPPOE_TAGIF)
	{
		struct sk_buff *skb;
		skb= (struct sk_buff *)pSkb;
			//re-cal l3 checksum
			*pPktHdr->pIpv4Checksum=0;
			*pPktHdr->pIpv4Checksum=htons(inet_chksum(skb->data+pPktHdr->l3Offset,pPktHdr->l4Offset-pPktHdr->l3Offset));
		
			//re-cal l4 checksum
			*pPktHdr->pL4Checksum=0;
			*pPktHdr->pL4Checksum=htons(inet_chksum_pseudo(skb->data+pPktHdr->l4Offset,pPktHdr->l3Offset+(*pPktHdr->pL3Len)-pPktHdr->l4Offset,ntohl(*pPktHdr->pIpv4Sip),ntohl(*pPktHdr->pIpv4Dip),pPktHdr->ipProtocol));
	}
#endif
  return SUCCESS;
}

//tcp port 1720
rtk_rg_fwdEngineAlgReturn_t rtk_rg_algRegFunc_TCP_h323(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo)
{
//Attention: caller function needs to make sure it needs to do napt modification
//ipv6 address and port doesn't need to do napt modification now
#ifdef __KERNEL__
	int ret=0;
	rtk_rg_pktHdr_t *pPktHdr;
	rtk_rg_alg_connection_t * pConn;
	rtk_rg_alg_tuple_t tuple;
	pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;
	
	memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));
	
	if(after==0)
	{
		//Pre function
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
			
			pConn = _rtk_rg_alg_connection_find(&tuple);
			
			if(pConn == NULL)
			{
				pConn = _rtk_rg_alg_connection_add(&tuple);	
			}
			pConn->pPktHdr = pPktInfo;			
		}
	}
	else
	{
		//Post function
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{		
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);

			pConn = _rtk_rg_alg_connection_find(&tuple);
			//entrance
			if(pConn == NULL)
				return RG_FWDENGINE_ALG_RET_FAIL;
			
			pConn->pPktHdr = pPktInfo;
			ret = _rtk_rg_q931_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
			//if(ret == H323_FORMAT_ERROR)	//not q931
				//_rtk_rg_h245_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
		}
		else
		{
			//Attention, pPktHdr->ipv4Dip is not the original external ip after napt modification, it is the internal ip
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
			pConn = _rtk_rg_alg_connection_find(&tuple);
			
			if(pConn == NULL)
				return RG_FWDENGINE_ALG_RET_FAIL;
			pConn->pPktHdr = pPktInfo;
			ret = _rtk_rg_q931_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
			//if(ret == H323_FORMAT_ERROR)	//not q931
				//_rtk_rg_h245_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
		}

	}
#endif
#if defined(CONFIG_RTL9600_SERIES)
	//20160331LUKE: checksum by sw offload
	if(pPktHdr->egressTagif&PPPOE_TAGIF)
	{
		struct sk_buff *skb;
		skb= (struct sk_buff *)pSkb;
			//re-cal l3 checksum
			*pPktHdr->pIpv4Checksum=0;
			*pPktHdr->pIpv4Checksum=htons(inet_chksum(skb->data+pPktHdr->l3Offset,pPktHdr->l4Offset-pPktHdr->l3Offset));
		
			//re-cal l4 checksum
			*pPktHdr->pL4Checksum=0;
			*pPktHdr->pL4Checksum=htons(inet_chksum_pseudo(skb->data+pPktHdr->l4Offset,pPktHdr->l3Offset+(*pPktHdr->pL3Len)-pPktHdr->l4Offset,ntohl(*pPktHdr->pIpv4Sip),ntohl(*pPktHdr->pIpv4Dip),pPktHdr->ipProtocol));
	}
#endif
  return RG_FWDENGINE_ALG_RET_SUCCESS;
}

//udp port 1719
rtk_rg_fwdEngineAlgReturn_t rtk_rg_algRegFunc_UDP_ras(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo)
{
//Attention: caller function needs to make sure it needs to do napt modification
//ipv6 address and port doesn't need to do napt modification now
#ifdef __KERNEL__
	int ret=0;
	rtk_rg_pktHdr_t *pPktHdr;
	rtk_rg_alg_connection_t *pConn;
	rtk_rg_alg_tuple_t tuple;
	pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;

	memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));
	
	if(after==0)
	{
		//Pre function
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
			
			pConn = _rtk_rg_alg_connection_find(&tuple);
			if(pConn == NULL)
			{
				
				pConn = _rtk_rg_alg_connection_add(&tuple);	
			}			
			pConn->pPktHdr = pPktInfo;
		}
	}
	else
	{
		//Post function		
		if(direct==NAPT_DIRECTION_OUTBOUND)
		{		
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);

			pConn = _rtk_rg_alg_connection_find(&tuple);		
			if(pConn == NULL)
				return RG_FWDENGINE_ALG_RET_FAIL;
			
			pConn->pPktHdr = pPktInfo;
			ret = _rtk_rg_ras_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);			
		}
		else
		{
			//Attention, pPktHdr->ipv4Dip is not the original external ip after napt modification, it is the internal ip
			_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
			
			pConn = _rtk_rg_alg_connection_find(&tuple);		
			if(pConn == NULL)
				return RG_FWDENGINE_ALG_RET_FAIL;
			
			pConn->pPktHdr = pPktInfo;
			ret = _rtk_rg_ras_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
		}

	}
#endif
#if defined(CONFIG_RTL9600_SERIES)
	//20160331LUKE: checksum by sw offload
	if(pPktHdr->egressTagif&PPPOE_TAGIF)
	{
		struct sk_buff *skb;
		skb= (struct sk_buff *)pSkb;
			//re-cal l3 checksum
			*pPktHdr->pIpv4Checksum=0;
			*pPktHdr->pIpv4Checksum=htons(inet_chksum(skb->data+pPktHdr->l3Offset,pPktHdr->l4Offset-pPktHdr->l3Offset));
		
			//re-cal l4 checksum
			*pPktHdr->pL4Checksum=0;
			*pPktHdr->pL4Checksum=htons(inet_chksum_pseudo(skb->data+pPktHdr->l4Offset,pPktHdr->l3Offset+(*pPktHdr->pL3Len)-pPktHdr->l4Offset,ntohl(*pPktHdr->pIpv4Sip),ntohl(*pPktHdr->pIpv4Dip),pPktHdr->ipProtocol));
	}
#endif
  return RG_FWDENGINE_ALG_RET_SUCCESS;
}

#ifdef __KERNEL__
static int __init init(void)
{
	return SUCCESS;
}


static void __exit exit(void)
{
}

module_init(init);
module_exit(exit);

#endif