
#include 	"../include/gprs.h"

static int 								pps_radius_sap_id;

extern 	int 							subSystemID;
extern _state_machine					*smshm_ptr;
extern 	PPSParamStruct					ppsParam;
extern	_ram_data						*ramshm_ptr;
extern 	struct	timeval 				tvnow;

extern PPS_COS_RADIUS_ITEM *getCosRadiusItem(_account_info *ptr);
extern 	int intiateGprsReportedInfo(_state_data 		*shmp);

/****************************	vas		*******************************/
extern	BOOL isVas(WORD pid,RADIUS_ACCESS_REQUEST *access_request);
extern 	int vas_process_access_request(WORD pid,RADIUS_ACCESS_REQUEST *access_request);

/****************************	vas		*******************************/


static ulong getNasVolumeMeterUnit(DWORD ip)
{
	int						 		i;
	PPS_RADIUS_NAS_INFO				*nasItem;

	return 1;
	for(i=0;i < RADIUS_MAX_NUM_OF_NAS;i++)
	{
		nasItem = &ppsParam.ppsTables.gsn[i];
		if(ip == nasItem->nasInfo.nas_ip)
		{
			if(nasItem->volumeMeterUnit > 0)
				return nasItem->volumeMeterUnit;
		}
	}
	return 1;
}

static ulong getNasDurationMeterUnit(DWORD ip)
{
	int						 		i;
	PPS_RADIUS_NAS_INFO				*nasItem;

	return 1;
	for(i=0;i < RADIUS_MAX_NUM_OF_NAS;i++)
	{
		nasItem = &ppsParam.ppsTables.gsn[i];
		if(ip == nasItem->nasInfo.nas_ip)
		{
			if(nasItem->durationMeterUnit > 0)
				return nasItem->durationMeterUnit;
		}
	}
	return 1;
}

static int checkSupportedCapability(WORD pid,const RADIUS_VENDOR_SPECIFIC *vendorSpecific)
{
	int 									i;
	RADIUS_VENDOR_ATTRIBUTE_3GPP2_PPAC 		*ppac;
	RADIUS_ATTRIBUTE_VENDOR_SPECIFIC		*specific;
		
	for(i=0;i<vendorSpecific->num;i++)
	{		
		specific = (RADIUS_ATTRIBUTE_VENDOR_SPECIFIC *)&vendorSpecific->vendor_specific[i];
		if(specific->flag == 0)
		{
			pps_print_error(0,"Radius pid[%5ld],No vendor spefific content in %d",pid,i);
			continue;
		}
	
		if(specific->vendor_type == RADIUS_VENDOR_TYPE_3GPP2_PPAC)
		{
			ppac = &specific->attribute_3gpp2.ppac;			
			if(ppac->aic.aic & 0x03)
			{
				return 1;
			}
			break;
		}
	}	
	pps_print_error(0,"Radius pid[%5ld],Peer not support Volume Meter and Duration Meter",pid);
	return -1;
}

static RADIUS_VENDOR_ATTRIBUTE_3GPP_SGSN_ADDR *getSGSNAddr(WORD pid,const RADIUS_VENDOR_SPECIFIC *vendorSpecific)
{
	int 								i;
	RADIUS_ATTRIBUTE_VENDOR_SPECIFIC	*specific;
		
	for(i=0;i<vendorSpecific->num;i++)
	{		
		specific = (RADIUS_ATTRIBUTE_VENDOR_SPECIFIC *)&vendorSpecific->vendor_specific[i];
		if(specific->flag == 0)
		{
			pps_print_error(0,"Radius pid[%5ld],No vendor spefific content in %d",pid,i);
			continue;
		}
	
		if(specific->vendor_type == RADIUS_VENDOR_TYPE_3GPP_SGSN_ADDR)
		{
			return &specific->attribute_3gpp.sgsn_addr;
		}
	}	
	pps_print_error(0,"Radius pid[%5ld],Peer no sgsn address",pid);
	return NULL;
}

 RADIUS_ATTRIBUTE_NAS_IP_ADDRESS *getNASIP(WORD pid,const RADIUS_ACCESS_REQUEST *accessRequest)
{		
	if(accessRequest->nas_ip_addr.flag != 0)
	{
		return (RADIUS_ATTRIBUTE_NAS_IP_ADDRESS *)&accessRequest->nas_ip_addr;
	}
	pps_print_error(0,"Radius pid[%5ld],Peer no nas ip",pid);
	return NULL;
}

static void updateReason(u8 reasonID,u_char *reason)
{
	switch(reasonID)
	{
		case RADIUS_PPAQ_UR_PRE_INIT:
			strcpy((char *)reason,"PRE INIT");
		break;
		case RADIUS_PPAQ_UR_INIT_REQUEST:
			strcpy((char *)reason,"INIT REQUEST");
		break;
		case RADIUS_PPAQ_UR_THRESHOLD_REACHED:
			strcpy((char *)reason,"THRESHOLD REACHED");
		break;
		case RADIUS_PPAQ_UR_QUOTA_REACHED:
			strcpy((char *)reason,"QUOTA REACHED");
		break;
		case RADIUS_PPAQ_UR_TITSU_APPROACHING:
			strcpy((char *)reason,"TITSU APPROACHING");
		break;
		case RADIUS_PPAQ_UR_REMOTE_DISC:
			strcpy((char *)reason,"REMOTE DISC");
		break;
		case RADIUS_PPAQ_UR_CLIENT_SERVICE_TER:
			strcpy((char *)reason,"CLIENT SERVICE TER");
		break;
		case RADIUS_PPAQ_UR_ACCESS_SERVICE_TER:
			strcpy((char *)reason,"ACCESS SERVICE TER");
		break;
		case RADIUS_PPAQ_UR_SERVICE_NOT_EST:
			strcpy((char *)reason,"SERVICE NOT EST");
		break;
		case RADIUS_PPAQ_UR_ONE_TIME_CHARGING:
			strcpy((char *)reason,"ONE TIME CHARGING");
		break;
		default:
			strcpy((char *)reason,"Unknown Reason");
		break;			
	}		
}

static int process_PPAQ(WORD pid, RADIUS_VENDOR_ATTRIBUTE_3GPP2_PPAQ *ppaq, RADIUS_ATTRIBUTE_ACCT_SESSION_ID *pAsi)
{
	u_short 						pps_port,qid;
	_gprs_process					*gprsProc;
	_RADIUS_REPORTED_INFO			*radiusReportedInfo;
	_state_data 					*shmp;
	u_char							reason[256]={0};
	
	PutLogFunID("process_PPAQ");
	
	if(ppaq->qid.flag != 0)
	{
		if(ppaq->qid.qid.length != 2*sizeof(u_short))
		{
			pps_print_error(0,"Incorrect QID,length error");
			return -1;
		}

		// get pps port
		pps_port = ppaq->qid.qid.value >> 16;
		if(subSystemID != 0)
		{
			if ((pps_port < QID_DUAL_PLAT_POIND) || (pps_port >= (MAX_FSM_PROCESS + QID_DUAL_PLAT_POIND)))
			{
				pps_print_error(0,"Incorrect pps port[%d]", pps_port);
				return -1;
			}
			pps_port = pps_port - QID_DUAL_PLAT_POIND;
		}
		if (pps_port >= MAX_FSM_PROCESS)
		{
			pps_print_error(0,"Incorrect pps port[%d]", pps_port);
			return -1;
		}
		//

		// get qid, initial is 2
		qid = ppaq->qid.qid.value & 0xffff;
		if (qid < 2)
		{
			pps_print_error(0,"Incorrect qid[%d]", qid);
			return -1;
		}
		//
	}
	else
	{
		pps_print_error(0,"No QID");
		return -1;	
	}
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += pps_port;
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	if(gprsProc->qid != qid)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]Incorrect QID:%d,not same as pps allocated:%d",pps_port,qid,gprsProc->qid);
		return -1;
	}

	/* match acct-session-id
	if ((pAsi == NULL) || (pAsi->flag == 0))
	{
		pps_print_error(0, "No accounting session id");
		return -1;
	}

	if ((pAsi->acct_session_id.length < 2) || (pAsi->acct_session_id.length != gprsProc->acct_session_id.length) \
	    || (memcmp(pAsi->acct_session_id.value, gprsProc->acct_session_id.value, pAsi->acct_session_id.length-2) != 0))
	{
		pps_print_error(0, "accounting session id not match, discard");
		return -1;
	}
	*/

	gprsProc->accessRequestTimer = 0;

	if(ppaq->ur.flag != 0)
	{		
		updateReason(ppaq->ur.ur.value,reason);
		pps_print_event(shmp->process_info.logMsisdn,"[%05d] ppaq updated reason:%s",pps_port,reason);
		switch(ppaq->ur.ur.value)
		{
			case RADIUS_PPAQ_UR_PRE_INIT:
				pps_print_error(shmp->process_info.logMsisdn,"Pre-initialization is unsupported");
				return -1;
			break;
			case RADIUS_PPAQ_UR_INIT_REQUEST:
				pps_print_error(shmp->process_info.logMsisdn,"Initial Request is unsupported");
				return -1;
			break;
			case RADIUS_PPAQ_UR_THRESHOLD_REACHED:
				gprsProc->radiusCommand = PPS_RADIUS_THRESHOLD_REACHED;
			break;
			case RADIUS_PPAQ_UR_QUOTA_REACHED:
				gprsProc->radiusCommand = PPS_RADIUS_QUOTA_REACHED;
			break;
			case RADIUS_PPAQ_UR_TITSU_APPROACHING:
				pps_print_error(shmp->process_info.logMsisdn,"TITSU Approaching is unsupported");
				return -1;
			break;
			case RADIUS_PPAQ_UR_REMOTE_DISC:
				gprsProc->radiusCommand = PPS_RADIUS_REMOTE_DISC;
			break;
			case RADIUS_PPAQ_UR_CLIENT_SERVICE_TER:
				gprsProc->radiusCommand = PPS_RADIUS_CLIENT_SERVICE_TER;
			break;
			case RADIUS_PPAQ_UR_ACCESS_SERVICE_TER:
				gprsProc->radiusCommand = PPS_RADIUS_ACCESS_SERVICE_TER;
			break;
			case RADIUS_PPAQ_UR_SERVICE_NOT_EST:
				gprsProc->radiusCommand = PPS_RADIUS_SERVICE_NOT_EST;
			break;
			case RADIUS_PPAQ_UR_ONE_TIME_CHARGING:
				pps_print_error(shmp->process_info.logMsisdn,"One-Time Charging is unsupported");
				return -1;
			break;
			default:
				pps_print_error(shmp->process_info.logMsisdn,"Unknow update reason:%d", ppaq->ur.ur.value);
				return -1;
			break;			
		}				
	}
	else
	{
		pps_print_error(shmp->process_info.logMsisdn,"No update reason");
		return -1;
	}

	radiusReportedInfo = &gprsProc->radiusReportedInfo;
	if(ppaq->vq.flag != 0)
	{
		memcpy(&radiusReportedInfo->vq,&ppaq->vq,sizeof(RADIUS_VENDOR_3GPP2_PPAQ_SUB_ATTR_VQ));
		pps_print_event(shmp->process_info.logMsisdn,"[%05d] ppaq reported vq is:(integer:%lld,exponent:%d)",
						pps_port,radiusReportedInfo->vq.vq.integer,radiusReportedInfo->vq.vq.exponent);
	}
	if(ppaq->vt.flag != 0)
	{
		memcpy(&radiusReportedInfo->vt,&ppaq->vt,sizeof(RADIUS_VENDOR_3GPP2_PPAQ_SUB_ATTR_VT));
		pps_print_event(shmp->process_info.logMsisdn,"[%05d] ppaq reported vth is:(integer:%lld,exponent:%d)",
						pps_port,radiusReportedInfo->vt.vt.integer,radiusReportedInfo->vq.vq.exponent);
	}
	if(ppaq->dq.flag != 0)
	{
		memcpy(&radiusReportedInfo->dq,&ppaq->dq,sizeof(RADIUS_VENDOR_3GPP2_PPAQ_SUB_ATTR_DQ));
	}
	if(ppaq->dt.flag != 0)
	{
		memcpy(&radiusReportedInfo->dt,&ppaq->dt,sizeof(RADIUS_VENDOR_3GPP2_PPAQ_SUB_ATTR_DT));
	}
	return pps_port;	
}

static int getPPAQ(WORD pid,const RADIUS_VENDOR_SPECIFIC *vendorSpecific, RADIUS_ATTRIBUTE_ACCT_SESSION_ID *pAsi)
{
	int 								i;
	RADIUS_VENDOR_ATTRIBUTE_3GPP2_PPAQ *ppaq;
	RADIUS_ATTRIBUTE_VENDOR_SPECIFIC	*specific;
		
	for(i=0;i<vendorSpecific->num;i++)
	{		
		specific = (RADIUS_ATTRIBUTE_VENDOR_SPECIFIC *)&vendorSpecific->vendor_specific[i];
		if(specific->flag == 0)
		{
			pps_print_error(0,"Radius pid[%5ld],No vendor spefific content in %d",pid,i);
			continue;
		}
	
		if(specific->vendor_type == RADIUS_VENDOR_TYPE_3GPP2_PPAQ)
		{
			ppaq = &specific->attribute_3gpp2.ppaq;			
			return process_PPAQ(pid, ppaq, pAsi);
		}
	}	
	return -2;
}


static int allocate_GPRS_PORT(WORD pid,RADIUS_TLV_TS *csi,RADIUS_TLV_TS *asi)
{
	int					gprs_port;
	_state_data 		*shmp;
	_gprs_process		*gprsProc;
	u_char				msisdn[MAX_ASCII_NUMBER_LEN];
	
	gprs_port = AssignProcID(1);// gprs and gy has the same port range
	if(gprs_port < 0)
	{
		pps_print_error(0,"System busy ,no idle port");
		return -2;
	}		
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += gprs_port;
	shmp->context_info.mtype  = GPRS_O_OPEN;
	shmp->context_info.new_flag = YES;
	memset(msisdn,0,MAX_ASCII_NUMBER_LEN);
	if(csi->value[0] == '+')
		memcpy(msisdn,&csi->value[1],csi->length-1);	
	else
		memcpy(msisdn,&csi->value[0],csi->length);
	strcpy((char *)shmp->caller_info.number, (char *)msisdn);
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	memcpy(&gprsProc->acct_session_id,asi,sizeof(RADIUS_TLV_TS));
	gprsProc->radiusCommand = PPS_RADIUS_NEW_SERVICE;
	gprsProc->radiusPid = pid;
	gprsProc->qid = INIT_QID;
	
	memcpy(&gprsProc->calling_station_id,csi,sizeof(RADIUS_TLV_TS));
	if(logMsgFlag & 0x08)
	{
		if(strstr((char *)shmp->caller_info.number,logMsisdn))
			shmp->process_info.logMsisdn = 0x01;
	}
	return gprs_port;
}


static int process_access_request(WORD pid,RADIUS_ACCESS_REQUEST *access_request)
{
	RADIUS_TLV_TS							*csi,*asi;	
	RADIUS_VENDOR_ATTRIBUTE_3GPP_SGSN_ADDR	*sgsnAddr;
	RADIUS_ATTRIBUTE_NAS_IP_ADDRESS			*nasAddr;
	int										ppsPort=-1;
	
	if(access_request->vendor_specific.num == 0)
	{
		pps_print_error(0,"No vendor spefific");
		return -1;
	}


	sgsnAddr = getSGSNAddr(pid,&access_request->vendor_specific);
	if(sgsnAddr == NULL)
	{
		return -1;
	}
	nasAddr = getNASIP(pid,access_request);
	if(nasAddr == NULL)
	{
		return -1;
	}

	ppsPort = getPPAQ(pid, &access_request->vendor_specific, &access_request->acct_session_id);
	if(ppsPort == -1)
	{
		return -1;
	}
	
	if(ppsPort == -2)/*	First Access Request	*/
	{
		if(checkSupportedCapability(pid,&access_request->vendor_specific) < 0)
		{
			return -1;
		}

		csi = &access_request->calling_station_id.calling_station_id;
		asi = &access_request->acct_session_id.acct_session_id;
		if(access_request->calling_station_id.flag != 0)
		{						
			if(csi->length > MAX_ASCII_NUMBER_LEN)
			{
				pps_print_error(0,"calling station id is too long:%d",csi->length);
				return -1;	
			}
		}	
		else
		{
			pps_print_error(0,"No calling station id");
			return -1;
		}
		ppsPort = allocate_GPRS_PORT(pid,csi,asi);
	}
	
	if(ppsPort >= 0)
	{
		_state_data 					*shmp;
		_gprs_process					*gprsProc;
		_RADIUS_REPORTED_INFO			*radiusReportedInfo;
		
		shmp = (_state_data *)&smshm_ptr->state_data;
		shmp += ppsPort;
		
		shmp->process_info.portid = ppsPort;
		radiusReportedInfo = &shmp->process_info.ppsService.gprs_process.radiusReportedInfo;
		memcpy(&radiusReportedInfo->nasIP,nasAddr,sizeof(RADIUS_ATTRIBUTE_NAS_IP_ADDRESS));
		memcpy(&radiusReportedInfo->sgsnIP,sgsnAddr,sizeof(RADIUS_VENDOR_ATTRIBUTE_3GPP_SGSN_ADDR));

		gprsProc = &shmp->process_info.ppsService.gprs_process;
		gprsProc->volumeMeterUnit = getNasVolumeMeterUnit(radiusReportedInfo->nasIP.nas_ip_addr.value);
		gprsProc->accessRequestTimer = 0;
		gprsProc->gsnIP = nasAddr->nas_ip_addr.value;
		gprsProc->sgsnIP = sgsnAddr->sgsn_addr;
		gprsProc->radiusPid = pid;
		
		return ppsPort;
	}
	return -1;
}

static int process_accounting_request(WORD pid,RADIUS_ACCOUNTING_REQUEST *accounting_request)
{
	int								ppsPort=-1;
	_state_data 					*shmp;
	_gprs_process					*gprsProc;
	
	ppsPort = getPPAQ(pid,&accounting_request->vendor_specific, &accounting_request->acct_session_id);
	if(ppsPort < 0)
	{
		return -1;
	}
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += ppsPort;
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	memcpy(&gprsProc->acct_session_id,&accounting_request->acct_session_id.acct_session_id,
			sizeof(RADIUS_TLV_TS));
	gprsProc->radiusCommand = PPS_RADIUS_NO_CMD;/*	upper layer does not has any interest of this message,just ignore it here	*/
	return ppsPort;
}

int	 pps_process_radius_ind(WORD *usr_pid, WORD pid, RADIUS_MSG *msg_pdu)
{			
	int		ppsPort;
	
	switch(msg_pdu->msg_type)
	{	
		case RADIUS_MSG_TYPE_ACCESS_REQUEST:
			if(isVas(pid,&msg_pdu->msg.access_request)== TRUE)
			{
				pps_print_event(0,"PPS receive radius msg:Access Request,for VAS prepaid");
				ppsPort = vas_process_access_request(pid,&msg_pdu->msg.access_request);				
			}
			else
			{
				pps_print_event(0,"PPS receive radius msg:Access Request");
				ppsPort = process_access_request(pid,&msg_pdu->msg.access_request);		
			}
			if(ppsPort < 0)
				*usr_pid = 0;
			else
				*usr_pid = ppsPort;
			return ppsPort;	
		break;		
		case RADIUS_MSG_TYPE_ACCOUNTING_REQUEST:			
			pps_print_event(0,"PPS receive radius msg:Accounting Request but ignore it");
			return 0;	
		break;
		default:
			pps_print_error(0,"PPS not support radius msg type:%d ",msg_pdu->msg_type);
		break;
	}
	return -1;
}


int	pps_process_radius_cnf(WORD usr_pid, WORD pid, RADIUS_MSG *msg_pdu)
{
	int					ppsPort;
	_state_data 		*shmp;
	_gprs_process		*gprsProc;
	
	
	ppsPort = usr_pid;
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += ppsPort;
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	switch(msg_pdu->msg_type)
	{	
		case RADIUS_MSG_TYPE_DISCONNECT_ACK:
			gprsProc->radiusCommand = PPS_RADIUS_DISCONNECT_ACK;	
			pps_print_event(0,"PPS receive radius msg:disconnect ACK for pid %d",ppsPort);
		break;
		case RADIUS_MSG_TYPE_DISCONNECT_NAK:
			gprsProc->radiusCommand = PPS_RADIUS_DISCONNECT_NAK;
			pps_print_error(0,"PPS receive radius msg:disconnect NAK for pid %d",ppsPort);
		break;
		default:
			pps_print_error(0,"PPS not support radius conf");
		break;
	}	
	return 1;
}

int pps_bind_radius()
{
	RADIUS_SAP sap;

	strcpy(sap.name,"PPS");
	sap.radius_ind = pps_process_radius_ind;
	sap.radius_cnf = pps_process_radius_cnf;
	pps_radius_sap_id = radius_bind(&sap);
	if(pps_radius_sap_id< 0)
		return -1;
	return 1;
}

int pps_set_nas(BYTE id,RADIUS_NAS_INFORMATION *nasInfo)
{	
	return radius_set_nas_info(pps_radius_sap_id,id,nasInfo);
}

int pps_del_nas(BYTE id)
{	
	return radius_delet_nas_info(pps_radius_sap_id,id);
}

void init_radius_gsn()
{
	int 						i;
	PPS_RADIUS_NAS_INFO			*gsnItem;

	pps_bind_radius();
	
	for(i=0;i<MaxRadiusGsnItem;i++)
	{
		gsnItem = &ppsParam.ppsTables.gsn[i];
		if(gsnItem->existFlag == TRUE)
		{
			pps_set_nas(i,&gsnItem->nasInfo);
		}
	}
}



int sendRadiusAccessReject(u_short portid,u_char *reason)
{
	_state_data 			*shmp;
	_gprs_process			*gprsProc;
	RADIUS_MSG				radiusMsg;
	RADIUS_ACCESS_REJECT	*accessReject;
	int						len;
		
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	len = strlen((char *)reason)%RADIUS_TS_VALUE_LEN;
	radiusMsg.msg_type = RADIUS_MSG_TYPE_ACCESS_REJECT;
	
	accessReject = &radiusMsg.msg.access_reject;
	memset(accessReject,0,sizeof(RADIUS_ACCESS_REJECT));	
	if(len > 0)
	{
		accessReject->reply_message.num = 1;
		accessReject->reply_message.reply_message[0].flag = 1;
		accessReject->reply_message.reply_message[0].reply_message.length = len;
		memcpy(accessReject->reply_message.reply_message[0].reply_message.value,reason,len);	
	}
	pps_print_event(shmp->process_info.logMsisdn,"[%05d] send radius access reject,reason:%s",portid,reason);
	return radius_send_access_reject(pps_radius_sap_id,gprsProc->radiusPid,portid,&radiusMsg);
}

static int allocateQID(_state_data 	*shmp)
{
	_gprs_process					*gprsProc;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	gprsProc->qid++;
	return 1;
}

int sendRadiusAccessAccept(u_short portid,BOOL volumeFlag,BOOL durationFlag)
{
	_state_data 							*shmp;
	_gprs_process							*gprsProc;
	RADIUS_MSG								radiusMsg;
	RADIUS_ACCESS_ACCEPT					*accessAccept;
	RADIUS_ATTRIBUTE_VENDOR_SPECIFIC		*vendorSpecific;
	RADIUS_VENDOR_ATTRIBUTE_3GPP2_PPAQ		*ppaq;
	DWORD									qid=0;
	ulong									meterUnit;
	//PPS_COS_RADIUS_ITEM						*radiusItem;
	
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	radiusMsg.msg_type = RADIUS_MSG_TYPE_ACCESS_ACCEPT;
	
	accessAccept = &radiusMsg.msg.access_accept;
	memset(accessAccept,0,sizeof(RADIUS_ACCESS_ACCEPT));	
	accessAccept->vendor_specific.num = 1;	
	
	vendorSpecific = &accessAccept->vendor_specific.vendor_specific[0];
	vendorSpecific->vendor_type = RADIUS_VENDOR_TYPE_3GPP2_PPAC;
	vendorSpecific->attribute_3gpp2.ppac.sfs.flag = 1;
	vendorSpecific->attribute_3gpp2.ppac.sfs.sfs= 0x01;


	if(volumeFlag != FALSE || durationFlag != FALSE)
	{	
		allocateQID(shmp);
		accessAccept->vendor_specific.num++;
		vendorSpecific = &accessAccept->vendor_specific.vendor_specific[1];
		vendorSpecific->vendor_type = RADIUS_VENDOR_TYPE_3GPP2_PPAQ;	
		ppaq = &vendorSpecific->attribute_3gpp2.ppaq;

		ppaq->qid.flag = 1;
		if(subSystemID == 0)
		{
			qid = portid;
			qid <<=16;
			
		}
		else
		{
			qid = portid + QID_DUAL_PLAT_POIND;
			qid <<=16;
		}
		ppaq->qid.qid.value = qid | (gprsProc->qid & 0xffff);

		//radiusItem = getCosRadiusItem(&shmp->caller_info);
		
		
		if(volumeFlag == TRUE)
		{
			meterUnit = getNasVolumeMeterUnit(gprsProc->radiusReportedInfo.nasIP.nas_ip_addr.value);
			if(gprsProc->vqAllocated.integer> 0)
			{
				ppaq->vq.flag = 1;
				ppaq->vq.vq.integer = gprsProc->vqAllocated.integer/meterUnit;
				ppaq->vq.vq.exponent = gprsProc->vqAllocated.exponent;
			}
			if(gprsProc->vthAllocated.integer> 0)
			{
				ppaq->vt.flag = 1;
				ppaq->vt.vt.integer = (gprsProc->vthAllocated.integer)/meterUnit;
				ppaq->vt.vt.exponent = gprsProc->vthAllocated.exponent;	
			}
		}

		if(durationFlag == TRUE)
		{
			meterUnit = getNasDurationMeterUnit(gprsProc->radiusReportedInfo.nasIP.nas_ip_addr.value);
			ppaq->dq.flag = 1;
			ppaq->dq.dq.value = gprsProc->dqAllocated/meterUnit;

			ppaq->dt.flag = 1;
			ppaq->dt.dt.value = gprsProc->dthAllocated/meterUnit;
		}
	}
	intiateGprsReportedInfo(shmp);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d] send radius access accept",portid);
	return radius_send_access_accept(pps_radius_sap_id,gprsProc->radiusPid,portid,&radiusMsg);
}


int sendRadiusDisconnectReq(u_short portid)
{
	_state_data 							*shmp;
	_gprs_process							*gprsProc;
	RADIUS_MSG								radiusMsg;
	RADIUS_DISCONNECT_REQUEST				*disconnectReq;
	RADIUS_ADDR								addr;
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	radiusMsg.msg_type = RADIUS_MSG_TYPE_DISCONNECT_REQUEST;
	
	disconnectReq = &radiusMsg.msg.disconnect_request;
	memset(disconnectReq,0,sizeof(RADIUS_DISCONNECT_REQUEST));

	addr.addr.ipV4 = gprsProc->gsnIP;
	disconnectReq->calling_station_id.flag = 1;
	memcpy(&disconnectReq->calling_station_id.calling_station_id,&gprsProc->calling_station_id,sizeof(RADIUS_TLV_TS));
	disconnectReq->acct_session_id.flag = 1;
	memcpy(&disconnectReq->acct_session_id.acct_session_id,&gprsProc->acct_session_id,sizeof(RADIUS_TLV_TS));
		
	return radius_send_disconnect_request(pps_radius_sap_id,portid,&radiusMsg,&addr);
}



