/*****************************************************************************************************
 * Software License Agreement (BSD License)
 * Author : Souheil Ben Ayed <souheil@tera.ics.keio.ac.jp>
 *
 * Copyright (c) 2009-2010, Souheil Ben Ayed, Teraoka Laboratory of Keio University, and the WIDE Project
 * All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Souheil Ben Ayed <souheil@tera.ics.keio.ac.jp>.
 *
 * 4. Neither the name of Souheil Ben Ayed, Teraoka Laboratory of Keio University or the WIDE Project nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************************************/

#include "diameap_common.h"

/* handler for DiamEAP server callback */
static struct disp_hdl * handle;

/* session handler for DiamEAP sessions state machine */
static struct session_handler * diameap_server_reg = NULL;


struct avp_max_occurences auth_avps[] =
{
{ "Service-Type", 1 },
{ "Callback-Number", 1 },
{ "Callback-Id", 1 },
{ "Idle-Timeout", 1 },
{ "Port-Limit", 1 },
{ "NAS-Filter-Rule" - 1 },
{ "Filter-Id", -1 },
{ "Configuration-Token", -1 },
{ "QoS-Filter-Rule", -1 },
{ "Framed-Protocol", 1 },
{ "Framed-Routing", 1 },
{ "Framed-MTU", 1 },
{ "Framed-Compression", -1 },
{ "Framed-IP-Address", 1 },
{ "Framed-IP-Netmask", 1 },
{ "Framed-Route", -1 },
{ "Framed-Pool", 1 },
{ "Framed-Interface-Id", 1 },
{ "Framed-IPv6-Prefix", -1 },
{ "Framed-IPv6-Pool", 1 },
{ "Framed-IPv6-Route", -1 },
{ "Framed-IPX-Network", 1 },
{ "Framed-Appletalk-Link", 1 },
{ "Framed-Appletalk-Network", -1 },
{ "Framed-Appletalk-Zone", 1 },
{ "NAS-IPv6-Address", 0 },
{ "NAS-Identifier", 0 },
{ "NAS-IP-Address", 0 },
{ "NAS-Port", 0 },
{ "NAS-Port-Id", 0 },
{ "NAS-Port-Type", 0 },
{ "Called-Station-Id", 0 },
{ "Calling-Station-Id", 0 },
{ "Connect-Info", 0 },
{ "Originating-Line-Info", 0 } };



void diameap_cli_sess_cleanup(struct sess_state * diameap_sess_data, os0_t sid, void * opaque)
{

	CHECK_PARAMS_DO( diameap_sess_data, return );

	if (diameap_sess_data != NULL)
	{
		if (diameap_sess_data->methodData != NULL)
		{
			struct plugin * cplugin;
			if (diameap_plugin_get(diameap_sess_data->currentVendor,
					diameap_sess_data->currentMethod, &cplugin))
			{
				TRACE_DEBUG(INFO,"%sUnable to access EAP Method plugin {Type=%d, Vendor=%d}.",DIAMEAP_EXTENSION,diameap_sess_data->currentMethod,diameap_sess_data->currentVendor);
			}

			if (cplugin->eap_method_free)
			{
				(*cplugin->eap_method_free)(diameap_sess_data->methodData);
				diameap_sess_data->methodData = NULL;
			}
			else
			{
				TRACE_DEBUG(FULL+1,"%s[%s plugin] datafree function not available.",DIAMEAP_EXTENSION,cplugin->methodname);
				if (diameap_sess_data->methodData != NULL)
				{
					free(diameap_sess_data->methodData);
					diameap_sess_data->methodData = NULL;
				}
			}
			if (diameap_sess_data->methodData)
			{
				TRACE_DEBUG(INFO,"%sSession state was not been freed correctly!!!",DIAMEAP_EXTENSION);
			}
		}

		if (diameap_sess_data->user.password != NULL)
		{
			free(diameap_sess_data->user.password);
			diameap_sess_data->user.password = NULL;
		}

		if (diameap_sess_data->user.userid != NULL)
		{
			free(diameap_sess_data->user.userid);
			diameap_sess_data->user.userid = NULL;
		}
		free(diameap_sess_data);
		diameap_sess_data = NULL;
	}
}

static int diameap_initialize_diameap_sm(
		struct diameap_state_machine * diameap_sm,
		struct sess_state * diameap_sess_data)
{
	TRACE_ENTRY("%p %p", diameap_sm, diameap_sess_data);

	int i;

	/* Initialize Long Term Variables */
	if (diameap_sess_data != NULL)
	{
		diameap_sm->invalid_eappackets = diameap_sess_data->invalid_eappackets;

		/* Initialize eap state machine variables */
		/*User*/
		diameap_sm->eap_sm.user.id = diameap_sess_data->user.id;

		if ((diameap_sess_data->user.userid != NULL)
				&& (diameap_sess_data->user.useridLength > 0))
		{
			diameap_sm->eap_sm.user.useridLength
					= diameap_sess_data->user.useridLength;
			CHECK_MALLOC(diameap_sm->eap_sm.user.userid= malloc(diameap_sm->eap_sm.user.useridLength+1));
			U8COPY(diameap_sm->eap_sm.user.userid,0,diameap_sm->eap_sm.user.useridLength+1,diameap_sess_data->user.userid);
			free(diameap_sess_data->user.userid);
			diameap_sess_data->user.userid = NULL;

		}
		else
		{
			TRACE_DEBUG(INFO,"%s user not identified yet.",DIAMEAP_EXTENSION);
			diameap_sm->eap_sm.user.useridLength = 0;
			diameap_sm->eap_sm.user.userid = NULL;
		}

		if ((diameap_sess_data->user.password != NULL)
				&& (diameap_sess_data->user.passwordLength > 0))
		{
			diameap_sm->eap_sm.user.passwordLength
					= diameap_sess_data->user.passwordLength;
			CHECK_MALLOC(diameap_sm->eap_sm.user.password = malloc(diameap_sm->eap_sm.user.passwordLength+1));
			U8COPY(diameap_sm->eap_sm.user.password,0,diameap_sm->eap_sm.user.passwordLength+1, diameap_sess_data->user.password);
			free(diameap_sess_data->user.password);
			diameap_sess_data->user.password = NULL;
		}
		else
		{
			diameap_sm->eap_sm.user.passwordLength = 0;
			diameap_sm->eap_sm.user.password = NULL;
		}

		diameap_sm->eap_sm.user.methodId = diameap_sess_data->user.methodId;
		for (i = 0; i < MAXMETHODS; i++)
		{
			diameap_sm->eap_sm.user.methods[i].method
					= diameap_sess_data->user.methods[i].method;
			diameap_sm->eap_sm.user.methods[i].vendor
					= diameap_sess_data->user.methods[i].vendor;
		}
		for (i = 0; i < MAXPROPOSEDMETHODS; i++)
		{
			diameap_sm->eap_sm.user.proposedmethods[i].method
					= diameap_sess_data->user.proposedmethods[i].method;
			diameap_sm->eap_sm.user.proposedmethods[i].vendor
					= diameap_sess_data->user.proposedmethods[i].vendor;
		}

		diameap_sm->eap_sm.user.pmethods = diameap_sess_data->user.pmethods;
		diameap_sm->eap_sm.user.proposed_eap_method
				= diameap_sess_data->user.proposed_eap_method;
		diameap_sm->eap_sm.user.proposed_eap_method_vendor
				= diameap_sess_data->user.proposed_eap_method_vendor;
		diameap_sm->eap_sm.user.success = diameap_sess_data->user.success;

		diameap_sm->eap_sm.currentId = diameap_sess_data->currentId;
		diameap_sm->eap_sm.currentVendor = diameap_sess_data->currentVendor;
		diameap_sm->eap_sm.lastId = diameap_sess_data->lastId;
		diameap_sm->eap_sm.methodState = diameap_sess_data->methodState;
		diameap_sm->eap_sm.currentMethod = diameap_sess_data->currentMethod;

		diameap_sm->eap_sm.methodData = diameap_sess_data->methodData;
		diameap_sess_data->methodData = NULL;

		if (diameap_sm->eap_sm.currentMethod != TYPE_NONE)
		{
			diameap_plugin_get(diameap_sm->eap_sm.currentVendor,
					diameap_sm->eap_sm.currentMethod,
					&diameap_sm->eap_sm.selectedMethod);
		}

		/* free session data*/
		free(diameap_sess_data);
		diameap_sess_data = NULL;

	}
	else
	{
		diameap_sm->invalid_eappackets = 0;

		/* Initialize eap state machine variables */
		/*User*/
		diameap_sm->eap_sm.user.id = 0;
		diameap_sm->eap_sm.user.userid = NULL;
		diameap_sm->eap_sm.user.useridLength = 0;
		diameap_sm->eap_sm.user.password = NULL;
		diameap_sm->eap_sm.user.passwordLength = 0;
		diameap_sm->eap_sm.user.methodId = -1;
		for (i = 0; i < MAXMETHODS; i++)
		{
			diameap_sm->eap_sm.user.methods[i].method = TYPE_NONE;
			diameap_sm->eap_sm.user.methods[i].vendor = VENDOR_IETF;
		}
		for (i = 0; i < MAXPROPOSEDMETHODS; i++)
		{
			diameap_sm->eap_sm.user.proposedmethods[i].method = TYPE_NONE;
			diameap_sm->eap_sm.user.proposedmethods[i].vendor = VENDOR_IETF;
		}

		diameap_sm->eap_sm.user.pmethods = -1;
		diameap_sm->eap_sm.user.proposed_eap_method = TYPE_NONE;
		diameap_sm->eap_sm.user.proposed_eap_method_vendor = VENDOR_IETF;
		diameap_sm->eap_sm.user.success = FALSE;

		diameap_sm->eap_sm.currentId = -1;
		diameap_sm->eap_sm.currentVendor = VENDOR_IETF;
		diameap_sm->eap_sm.lastId = -1;
		diameap_sm->eap_sm.methodState = EAP_M_PROPOSED;

		diameap_sm->eap_sm.currentMethod = TYPE_NONE;

		diameap_sm->eap_sm.methodData = NULL;

	}

	diameap_sm->result_code = 0;
	fd_list_init(&diameap_sm->attributes, NULL);
	fd_list_init(&diameap_sm->req_attributes, NULL);
	fd_list_init(&diameap_sm->ans_attributes, NULL);
	diameap_sm->failedavp = NULL;
	diameap_sm->auth_request_val = AUTHENTICATE_ONLY;
	diameap_sm->verify_authorization = FALSE;
	diameap_sm->authSuccess = FALSE;
	diameap_sm->authFailure = FALSE;
	diameap_sm->lastReqEAPavp = NULL;
	diameap_sm->privateUser = FALSE;
	diameap_sm->authorized = FALSE;

	diameap_sm->eap_sm.rxResp = FALSE;
	diameap_sm->eap_sm.respId = -1;
	diameap_sm->eap_sm.respMethod = TYPE_NONE;
	diameap_sm->eap_sm.respVendor = VENDOR_IETF;
	diameap_sm->eap_sm.respVendorMethod = TYPE_NONE;

	return 0;
}

static int diameap_initialize_diameap_eap_interface(
		struct diameap_eap_interface * eap_i)
{
	TRACE_ENTRY("%p", eap_i);

	//Initialize AAA-EAP Interface
	eap_i->aaaEapResp = FALSE;
	eap_i->aaaEapRespData.data = NULL;
	//Initialize EAP-AAA Interface
	eap_i->aaaEapReq = FALSE;
	eap_i->aaaEapNoReq = FALSE;
	eap_i->aaaSuccess = FALSE;
	eap_i->aaaFail = FALSE;
	eap_i->aaaEapReqData.data = NULL;
	eap_i->aaaEapMSKData = NULL;
	eap_i->aaaEapEMSKData = NULL;
	eap_i->aaaEapKeyAvailable = FALSE;
	eap_i->aaaMethodTimeout = 0;

	return 0;
}

static int diameap_failed_avp(struct diameap_state_machine * diameap_sm,
		struct avp * invalidavp)
{
	TRACE_ENTRY("%p %p",diameap_sm,invalidavp);
	if (!invalidavp)
		return EINVAL;

	if (!diameap_sm)
		return EINVAL;

	if (diameap_sm->failedavp == NULL)
	{
		CHECK_FCT( fd_msg_avp_new( dataobj_failed_avp, 0, &diameap_sm->failedavp) );

		CHECK_FCT( fd_msg_avp_add( diameap_sm->failedavp, MSG_BRW_LAST_CHILD, invalidavp ) );

	}
	else
	{
		//add multiple AVPs in Failed-AVP
	}
	return 0;
}

static int diameap_parse_eap_resp(struct eap_state_machine * eap_sm,
		struct eap_packet *eappacket)
{
	TRACE_ENTRY("%p %p",eap_sm, eappacket)

	eap_sm->rxResp = FALSE;
	eap_sm->respId = -1;
	eap_sm->respMethod = TYPE_NONE;
	eap_sm->respVendor = VENDOR_IETF;
	eap_sm->respVendorMethod = TYPE_NONE;

	if (eappacket->data == NULL)
	{
		TRACE_DEBUG(INFO,"%s Empty EAP packet",DIAMEAP_EXTENSION);
		return 0;
	}

	u16 plength;
	CHECK_FCT(diameap_eap_get_packetlength(eappacket,&plength));
	if ((int) plength < EAP_HEADER)
	{
		TRACE_DEBUG(INFO,"%s EAP packet length less than EAP header.",DIAMEAP_EXTENSION);
		return 0;
	}

	u16 length;
	CHECK_FCT(diameap_eap_get_length(eappacket,&length));
	if ((int) length < EAP_HEADER)
	{
		TRACE_DEBUG(INFO,"%sEAP packet length field less than EAP header.",DIAMEAP_EXTENSION);
		return 0;
	}

	if (plength < length)
	{
		TRACE_DEBUG(INFO,"%sLength of received EAP packet is less than the value of the length field.",DIAMEAP_EXTENSION);
		return 0;
	}

	eap_code code;
	CHECK_FCT(diameap_eap_get_code(eappacket,&code));
	if (code == EAP_REQUEST || code == EAP_SUCCESS || code == EAP_FAILURE)
	{
		TRACE_DEBUG(INFO,"%sOnly EAP Responses are accepted at EAP server side.",DIAMEAP_EXTENSION);
		return 0;
	}

	u8 id;
	CHECK_FCT(diameap_eap_get_identifier(eappacket,&id));
	eap_sm->respId = id;

	CHECK_FCT(diameap_eap_get_type(eappacket,&eap_sm->respMethod));
	if ((eap_sm->methodState != EAP_M_PROPOSED) && (eap_sm->respMethod
			== TYPE_NAK || eap_sm->respMethod == TYPE_EXPANDED_TYPES))
	{
		TRACE_DEBUG(INFO,"%sNAK or EXPANDED_NAK received after an EAP TYPE been selected",DIAMEAP_EXTENSION);
		return 0;
	}

	if ((eap_sm->respMethod == TYPE_EXPANDED_TYPES) && (length < 20))
	{
		TRACE_DEBUG(INFO,"%s Truncated EAP Packet received.",DIAMEAP_EXTENSION);
		return 0;
	}

	if ((eap_sm->respMethod == TYPE_NAK) && (eap_sm->currentMethod < 4))
	{
		TRACE_DEBUG(INFO,"%sNAK response not expected at this step (Only EAP type = 4 and above are accepted).",DIAMEAP_EXTENSION);
		return 0;
	}

	if (eap_sm->respMethod == TYPE_EXPANDED_TYPES)
	{
		u8 *data = (u8 *) eappacket->data;
		//int len = 0;
		//u32 respVendor, respVendorMethod;
		data += 5;
		eap_sm->respVendor = G24BIGE(data);
		data += 3;
		eap_sm->respVendorMethod = G32BIGE(data);
		data += 4;
		/*		while ((length - 12) > (len * 8))
		 {
		 if (((eap_type) G8(data)) != TYPE_EXPANDED_TYPES)
		 {
		 return FALSE;
		 }
		 data += 1;
		 respVendor = G24BIGE(data);
		 data += 3;
		 respVendorMethod = G32BIGE(data);
		 eap_sm->user.proposedmethods[len].method = respVendor;
		 eap_sm->user.proposedmethods[len].vendor = respVendorMethod;
		 len++;
		 data += 4;
		 }
		 eap_sm->user.methodId = 0;*/
	}

	if((eap_sm->respMethod == TYPE_IDENTITY) && (length < 6)){
		TRACE_DEBUG(INFO,"%sUser Identity missing",DIAMEAP_EXTENSION);
		return 0;
	}

	eap_sm->rxResp = TRUE;
	return 0;
}

static int diameap_eappacket_new(struct eap_packet * eappacket,
		struct avp_hdr * avpdata)
{
	TRACE_ENTRY("%p %p",eappacket,avpdata);
	eappacket->ulength = (u16) avpdata->avp_value->os.len;
	eappacket->data = (u8 *) avpdata->avp_value->os.data;
	diameap_eap_get_packetlength(eappacket, &eappacket->length);
	return 0;
}

static int diameap_parse_avps(struct diameap_state_machine * diameap_sm,
		struct msg * req, struct diameap_eap_interface * eap_i)
{
	TRACE_ENTRY("%p %p %p",diameap_sm,req,eap_i);
	struct avp * avp, *avp2;
	struct avp_hdr * avpdata;
	int ret;
	int depth;

	/* EAP-Payload data*/
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_eap_payload, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		CHECK_FCT(diameap_eappacket_new(&eap_i->aaaEapRespData, avpdata));
		eap_i->aaaEapResp = TRUE;
		u16 length;
		diameap_eap_get_length(&eap_i->aaaEapRespData, &length);
		if (length >= 4)
		{
			eap_code code;
			CHECK_FCT(diameap_eap_get_code(&eap_i->aaaEapRespData,&code));

			if (code != EAP_RESPONSE)
			{
				diameap_sm->result_code = 5004; /* DIAMETER_INVALID_AVP_VALUE 5004 */
				struct avp * invalidavp;
				union avp_value val;
				CHECK_FCT( fd_msg_avp_new ( dataobj_eap_payload, 0, &invalidavp));
				val.os.data = eap_i->aaaEapRespData.data;
				val.os.len = eap_i->aaaEapRespData.length;
				CHECK_FCT( fd_msg_avp_setvalue( invalidavp, &val ))
				CHECK_FCT( diameap_failed_avp(diameap_sm, invalidavp));
				TRACE_DEBUG(INFO,"%sIncorrect EAP Packet. EAP Code != Response.",DIAMEAP_EXTENSION);
				return 0;
			}
			else
			{
				CHECK_FCT(diameap_parse_eap_resp(&diameap_sm->eap_sm, &eap_i->aaaEapRespData));
				if (diameap_sm->eap_sm.rxResp == FALSE)
				{
					diameap_sm->result_code = 1001; /*DIAMETER_MULTI_ROUND_AUTH*/
					eap_i->aaaEapNoReq = TRUE;
					eap_i->aaaEapResp = FALSE;
				}
			}
		}
		else
		{
			if (diameap_sm->eap_sm.currentMethod != TYPE_NONE)
			{
				diameap_sm->result_code = 5004; /* DIAMETER_INVALID_AVP_VALUE 5004 */
				CHECK_FCT(diameap_failed_avp(diameap_sm, avp));
				TRACE_DEBUG(INFO,"%sEAP packet length < Minimum EAP packet length.",DIAMEAP_EXTENSION);
				return 1;
			}
			//EAP start received

		}
	}

	/* User-Name AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_user_name, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "User-Name";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* EAP-Key-Name AVP */

	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_eap_key_name, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "EAP-Key-Name";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Auth-Request-Type AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_auth_request_type, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		diameap_sm->auth_request_val = avpdata->avp_value->i32;
	}

	/* Authorization-Lifetime AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_authorization_lifetime, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Authorization-Lifetime";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Auth-Grace-Period AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_auth_grace_period, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Auth-Grace-Period";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Auth-Session-State AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_auth_session_state, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Auth-Session-State";
		attribute->value.i32 = avpdata->avp_value->i32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Origin-state-Id AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_origin_state_id, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Origin-state-Id AVP";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* NAS-Port AVP*/
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_nas_port, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "NAS-Port";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* NAS-Port-Id AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_nas_port_id, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "NAS-Port-Id";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* NAS-Port-Type AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_nas_port_type, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "NAS-Port-Type";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Called-Station-Id AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_called_station_id, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Called-Station-Id";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Calling-Station-Id AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_calling_station_id, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Calling-Station-Id";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Connect-Info AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_connect_info, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Connect-Info";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Originating-Line-Info AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_originating_line_info, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Originating-Line-Info";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Service-Type AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_service_type, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Service-Type";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Callback-Number AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_callback_number, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Callback-Number";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Port-Limit AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_port_limit, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Port-Limit";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Framed-Protocol AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_framed_protocol, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Framed-Protocol";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Framed-MTU AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_framed_mtu, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Framed-MTU";
		attribute->value.u32 = avpdata->avp_value->u32;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Framed-Compression AVP */
	avp = NULL;
	avp2 = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_framed_compression, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		u32 Pi_Code = avpdata->avp_code;
		int depth;
		do
		{
			struct avp_attribute * attribute;
			CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
			memset(attribute, 0, sizeof(struct avp_attribute));
			fd_list_init(&attribute->chain, attribute);
			attribute->attrib = "Framed-Compression";
			attribute->value.u32 = avpdata->avp_value->u32;
			fd_list_insert_before(&diameap_sm->req_attributes,
					&attribute->chain);
			ret = 0;
			depth = 0;
			ret = fd_msg_browse ( avp, MSG_BRW_NEXT, &avp2, &depth);
			if (avp2 != NULL)
			{
				CHECK_FCT(fd_msg_avp_hdr(avp2, &avpdata));
			}
			avp = avp2;
		} while ((avp2 != NULL) && (ret == 0) && (ret == 0)
				&& (avpdata->avp_code == Pi_Code));
	}

	/* Framed-IP-Address AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_framed_ip_address, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Framed-IP-Address";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Framed-IP-Netmask AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_framed_ip_netmask, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Framed-IP-Netmask";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Framed-Interface-Id AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_framed_interface_id, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "Framed-Interface-Id";
		attribute->value.u64 = avpdata->avp_value->u64;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* Framed-IPv6-Prefix AVP */
	avp = NULL;
	avp2 = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_framed_ipv6_prefix, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		u32 Pi_Code = avpdata->avp_code;
		do
		{
			struct avp_attribute * attribute;
			CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
			memset(attribute, 0, sizeof(struct avp_attribute));
			fd_list_init(&attribute->chain, attribute);
			attribute->attrib = "Framed-IPv6-Prefix";
			attribute->value.u32 = avpdata->avp_value->u32;
			fd_list_insert_before(&diameap_sm->req_attributes,
					&attribute->chain);
			ret = 0;
			depth = 0;
			ret = fd_msg_browse ( avp, MSG_BRW_NEXT, &avp2, &depth);
			if (avp2 != NULL)
			{
				CHECK_FCT(fd_msg_avp_hdr(avp2, &avpdata));
			}
			avp = avp2;
		} while ((avp2 != NULL) && (ret == 0) && (ret == 0)
				&& (avpdata->avp_code == Pi_Code));
	}

	/* Tunneling AVP */
	avp = NULL;
	avp2 = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_tunneling, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		u32 Pi_Code = avpdata->avp_code;
		int depth;
		do
		{
			struct avp_attribute * attribute;
			CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
			memset(attribute, 0, sizeof(struct avp_attribute));
			fd_list_init(&attribute->chain, attribute);
			attribute->attrib = "Tunneling";
			//grouped AVP
			fd_list_insert_before(&diameap_sm->req_attributes,
					&attribute->chain);
			ret = 0;
			depth = 0;
			ret = fd_msg_browse ( avp, MSG_BRW_NEXT, &avp2, &depth);
			if (avp2 != NULL)
			{
				CHECK_FCT(fd_msg_avp_hdr(avp2, &avpdata));
			}
			avp = avp2;
		} while ((avp2 != NULL) && (ret == 0) && (ret == 0)
				&& (avpdata->avp_code == Pi_Code));
	}

	/* NAS-Identifier AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_nas_identifier, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "NAS-Identifier";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* NAS-IP-Address AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_nas_ip_address, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "NAS-IP-Address";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* NAS-IPv6-Address AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_nas_ipv6_address, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "NAS-IPv6-Address";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	/* State AVP */
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_state, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr(avp, &avpdata));
		struct avp_attribute * attribute;
		CHECK_MALLOC(attribute = malloc(sizeof(struct avp_attribute)));
		memset(attribute, 0, sizeof(struct avp_attribute));
		fd_list_init(&attribute->chain, attribute);
		attribute->attrib = "State";
		attribute->value.os.data = avpdata->avp_value->os.data;
		attribute->value.os.len = avpdata->avp_value->os.len;
		fd_list_insert_before(&diameap_sm->req_attributes, &attribute->chain);
	}

	return 0;
}


static int diameap_sess_data_new(
		struct sess_state *diameap_sess_data,
		struct diameap_state_machine *diameap_sm)
{
	if (!diameap_sm)
		return EINVAL;

	int i;

	diameap_sess_data->invalid_eappackets = diameap_sm->invalid_eappackets;

	diameap_sess_data->user.id = diameap_sm->eap_sm.user.id;

	if ((diameap_sm->eap_sm.user.userid != NULL)
			&& (diameap_sm->eap_sm.user.useridLength > 0))
	{
		diameap_sess_data->user.useridLength
				= diameap_sm->eap_sm.user.useridLength;
		CHECK_MALLOC(diameap_sess_data->user.userid= malloc(diameap_sess_data->user.useridLength+1));
		U8COPY(diameap_sess_data->user.userid,0,diameap_sess_data->user.useridLength+1,diameap_sm->eap_sm.user.userid);
		free(diameap_sm->eap_sm.user.userid);
		diameap_sm->eap_sm.user.userid = NULL;

	}
	else
	{
		diameap_sess_data->user.useridLength = 0;
		diameap_sess_data->user.userid = NULL;
	}

	if ((diameap_sm->eap_sm.user.password != NULL)
			&& (diameap_sm->eap_sm.user.passwordLength > 0))
	{
		diameap_sess_data->user.passwordLength
				= diameap_sm->eap_sm.user.passwordLength;
		CHECK_MALLOC(diameap_sess_data->user.password = malloc(diameap_sess_data->user.passwordLength+1));
		U8COPY(diameap_sess_data->user.password,0,diameap_sess_data->user.passwordLength+1,diameap_sm->eap_sm.user.password);
		free(diameap_sm->eap_sm.user.password);
		diameap_sm->eap_sm.user.password = NULL;
	}
	else
	{
		diameap_sess_data->user.passwordLength = 0;
		diameap_sess_data->user.password = NULL;
	}

	diameap_sess_data->user.methodId = diameap_sm->eap_sm.user.methodId;
	for (i = 0; i < MAXMETHODS; i++)
	{
		diameap_sess_data->user.methods[i].method
				= diameap_sm->eap_sm.user.methods[i].method;
		diameap_sess_data->user.methods[i].vendor
				= diameap_sm->eap_sm.user.methods[i].vendor;
	}

	for (i = 0; i < MAXPROPOSEDMETHODS; i++)
	{
		diameap_sess_data->user.proposedmethods[i].method
				= diameap_sm->eap_sm.user.proposedmethods[i].method;
		diameap_sess_data->user.proposedmethods[i].vendor
				= diameap_sm->eap_sm.user.proposedmethods[i].vendor;
	}

	diameap_sess_data->user.pmethods = diameap_sm->eap_sm.user.pmethods;
	diameap_sess_data->user.proposed_eap_method
			= diameap_sm->eap_sm.user.proposed_eap_method;
	diameap_sess_data->user.proposed_eap_method_vendor
			= diameap_sm->eap_sm.user.proposed_eap_method_vendor;
	diameap_sess_data->user.success = diameap_sm->eap_sm.user.success;

	diameap_sess_data->currentId = diameap_sm->eap_sm.currentId;
	diameap_sess_data->currentMethod = diameap_sm->eap_sm.currentMethod;
	diameap_sess_data->currentVendor = diameap_sm->eap_sm.currentVendor;
	diameap_sess_data->lastId = diameap_sm->eap_sm.lastId;
	diameap_sess_data->methodState = diameap_sm->eap_sm.methodState;

	diameap_sess_data->methodData = diameap_sm->eap_sm.methodData;
	diameap_sm->eap_sm.methodData = NULL;

	return 0;
}

static void free_attrib(struct auth_attribute * auth_attrib)
{
	if (auth_attrib == NULL)
	{
		return;
	}
	if (auth_attrib->attrib != NULL)
	{
		free(auth_attrib->attrib);
		auth_attrib->attrib = NULL;
	}
	if (auth_attrib->op != NULL)
	{
		free(auth_attrib->op);
		auth_attrib->op = NULL;
	}
	if (auth_attrib->value != NULL)
	{
		free(auth_attrib->value);
		auth_attrib->value = NULL;
	}
	free(auth_attrib);
	auth_attrib = NULL;
}

static void free_avp_attrib(struct avp_attribute * avp_attrib)
{
	if(avp_attrib){
	free(avp_attrib);
	avp_attrib = NULL;
	}
}

static void free_ans_attrib(struct avp_attribute * ans_attrib)
{
	if(ans_attrib){
		if (ans_attrib->tofree == 1)
		{
			if(ans_attrib->value.os.data){
			free(ans_attrib->value.os.data);
			ans_attrib->value.os.data = NULL;
			}
		}
		free(ans_attrib);
		ans_attrib = NULL;
	}
}

static int diameap_unlink_attributes_lists(
		struct diameap_state_machine * diameap_sm)
{
	TRACE_ENTRY("%p ", diameap_sm);
	if (diameap_sm == NULL)
	{
		return EINVAL;
	}

	while (!FD_IS_LIST_EMPTY(&diameap_sm->attributes))
	{
		struct fd_list * item = (struct fd_list *) diameap_sm->attributes.next;
		struct auth_attribute * auth = (struct auth_attribute *) item;
		fd_list_unlink(item);
		free_attrib(auth);
	}

	while (!FD_IS_LIST_EMPTY(&diameap_sm->req_attributes))
	{
		struct fd_list * item =
				(struct fd_list *) diameap_sm->req_attributes.next;
		struct avp_attribute * avp = (struct avp_attribute *) item;
		fd_list_unlink(item);
		free_avp_attrib(avp);
	}

	while (!FD_IS_LIST_EMPTY(&diameap_sm->ans_attributes))
	{
		struct fd_list * item =
				(struct fd_list *) diameap_sm->ans_attributes.next;
		struct avp_attribute * avp_ans = (struct avp_attribute *) item;
		fd_list_unlink(item);
		free_ans_attrib(avp_ans);
	}

	return 0;
}

static void diameap_free(struct diameap_state_machine * diameap_sm)
{

	if (diameap_sm != NULL)
	{
		if (diameap_sm->eap_sm.user.userid != NULL)
		{
			free(diameap_sm->eap_sm.user.userid);
			diameap_sm->eap_sm.user.userid = NULL;
		}

		if (diameap_sm->eap_sm.user.password != NULL)
		{
			free(diameap_sm->eap_sm.user.password);
			diameap_sm->eap_sm.user.password = NULL;
		}

		diameap_sm->eap_sm.selectedMethod = NULL;

		if (diameap_sm->eap_sm.methodData != NULL)
		{

			struct plugin * cplugin;
			if (diameap_plugin_get(diameap_sm->eap_sm.currentVendor,
					diameap_sm->eap_sm.currentMethod, &cplugin))
			{
				TRACE_DEBUG(INFO,"%sUnable to access EAP Method plugin {Type=%d, Vendor=%d}.",DIAMEAP_EXTENSION,diameap_sm->eap_sm.currentMethod,diameap_sm->eap_sm.currentVendor);
			}

			if (cplugin->eap_method_free)
			{
				(*cplugin->eap_method_free)(diameap_sm->eap_sm.methodData);
				diameap_sm->eap_sm.methodData = NULL;
			}
			else
			{
				TRACE_DEBUG(INFO,"%s[%s plugin] datafree function not available.",DIAMEAP_EXTENSION,cplugin->methodname);
				if (diameap_sm->eap_sm.methodData != NULL)
				{
					free(diameap_sm->eap_sm.methodData);
					diameap_sm->eap_sm.methodData = NULL;
				}
			}
			if (diameap_sm->eap_sm.methodData)
			{
				TRACE_DEBUG(INFO,"%sSession state was not been freed correctly!!!",DIAMEAP_EXTENSION);
			}
		}

		if (diameap_sm->failedavp != NULL)
		{
			CHECK_FCT_DO(fd_msg_free(diameap_sm->failedavp), );
		}

		if (diameap_sm->lastReqEAPavp != NULL)
		{
			CHECK_FCT_DO(fd_msg_free(diameap_sm->lastReqEAPavp), );
		}

		CHECK_FCT_DO(diameap_unlink_attributes_lists(diameap_sm), );

		free(diameap_sm);
		diameap_sm = NULL;
	}

}

static int diameap_get_avp_attribute(struct fd_list * avp_attributes,
		char * attribute, struct avp_attribute ** avp_attrib, int unlink,
		int *ret)
{
	TRACE_ENTRY("%p %p %p %d %p", avp_attributes, attribute, avp_attrib, unlink, ret);
	if (avp_attributes == NULL)
	{
		return EINVAL;
	}
	if (attribute == NULL)
	{
		return EINVAL;
	}
	struct fd_list * attrib;
	for (attrib = avp_attributes->next; attrib != avp_attributes; attrib
			= attrib->next)
	{
		*avp_attrib = (struct avp_attribute *) attrib;
		if (strcmp((*avp_attrib)->attrib, attribute) == 0)
		{
			*ret = 0;
			if (unlink == 1)
			{
				fd_list_unlink(&(*avp_attrib)->chain);
			}
			return 0;
		}
	}
	*avp_attrib = NULL;
	*ret = 1;
	return 0;
}

static int diameap_get_auth_attribute(struct fd_list * auth_attributes,
		char * attribute, struct auth_attribute ** auth_attrib, int unlink,
		int *ret)
{

	TRACE_ENTRY("%p %p %p %d %p", auth_attributes, attribute, auth_attrib, unlink, ret);

	if (auth_attributes == NULL)
	{
		return EINVAL;
	}
	if (attribute == NULL)
	{
		return EINVAL;
	}

	struct fd_list * attrib;

	for (attrib = auth_attributes->next; attrib != auth_attributes; attrib
			= attrib->next)
	{
		*auth_attrib = (struct auth_attribute *) attrib;
		if (strcmp((*auth_attrib)->attrib, attribute) == 0)
		{
			*ret = 0;
			if (unlink == 1)
			{
				fd_list_unlink(&(*auth_attrib)->chain);
			}
			return 0;
		}
	}
	*auth_attrib = NULL;
	*ret = 1;
	return 0;
}

static int diameap_get_ans_attribute(struct fd_list * ans_attributes,
		char * attribute, struct avp_attribute ** ans_attrib, int unlink,
		int *ret)
{
	TRACE_ENTRY("%p %p %p %d %p", ans_attributes, attribute, ans_attrib, unlink, ret);
	if (ans_attributes == NULL)
	{
		return EINVAL;
	}
	if (attribute == NULL)
	{
		return EINVAL;
	}
	struct fd_list * attrib;
	for (attrib = ans_attributes->next; attrib != ans_attributes; attrib
			= attrib->next)
	{
		*ans_attrib = (struct avp_attribute *) attrib;
		if (strcmp((*ans_attrib)->attrib, attribute) == 0)
		{
			*ret = 0;
			if (unlink == 1)
			{
				fd_list_unlink(&(*ans_attrib)->chain);
			}
			return 0;
		}
	}
	*ans_attrib = NULL;
	*ret = 1;
	return 0;
}

static int diameap_answer_avp_attributes(
		struct diameap_state_machine * diameap_sm)
{
	TRACE_ENTRY("%p",diameap_sm);

	if (diameap_sm == NULL)
	{
		return EINVAL;
	}
	int ret1, ret2;
	struct avp_attribute * avp_attrib;
	struct avp_attribute * ans_attrib;
	struct auth_attribute * auth_attrib;

	/* Authorization-Lifetime */
	{
		CHECK_FCT(diameap_get_avp_attribute(&diameap_sm->req_attributes, "Authorization-Lifetime", &avp_attrib,1, &ret1));

		CHECK_FCT(diameap_get_auth_attribute(&diameap_sm->attributes, "Authorization-Lifetime", &auth_attrib,1, &ret2));

		if ((ret1 == 1) && (ret2 == 0) && (auth_attrib != NULL))
		{

			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));

			memset(ans_attrib, 0, sizeof(struct avp_attribute));

			fd_list_init(&ans_attrib->chain, NULL);

			ans_attrib->attrib = "Authorization-Lifetime";

			ans_attrib->value.u32 = atoi(auth_attrib->value);

			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);

			free_attrib(auth_attrib);

		}
		if ((ret1 == 0) && (ret2 == 1) && (avp_attrib != NULL))
		{

			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));

			memset(ans_attrib, 0, sizeof(struct avp_attribute));

			fd_list_init(&ans_attrib->chain, NULL);

			ans_attrib->attrib = "Authorization-Lifetime";

			ans_attrib->value.u32 = avp_attrib->value.u32;

			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);

			free_avp_attrib(avp_attrib);

		}
		if ((ret1 == 0) && (ret2 == 0) && (auth_attrib != NULL) && (avp_attrib
				!= NULL))
		{

			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));

			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Authorization-Lifetime";
			if (avp_attrib->value.u32 < atoi(auth_attrib->value))
			{
				ans_attrib->value.u32 = avp_attrib->value.u32;
			}
			else
			{
				ans_attrib->value.u32 = atoi(auth_attrib->value);
			}
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_avp_attrib(avp_attrib);
			free_attrib(auth_attrib);
		}
	}

	/* Auth-Grace-Period */
	{
		CHECK_FCT(diameap_get_avp_attribute(&diameap_sm->req_attributes, "Auth-Grace-Period", &avp_attrib,1, &ret1));

		CHECK_FCT(diameap_get_auth_attribute(&diameap_sm->attributes, "Auth-Grace-Period", &auth_attrib,1, &ret2));

		if ((ret1 == 1) && (ret2 == 0) && (auth_attrib != NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Auth-Grace-Period";
			ans_attrib->value.u32 = atoi(auth_attrib->value);
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_attrib(auth_attrib);
		}
		if ((ret1 == 0) && (ret2 == 1) && (avp_attrib != NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Auth-Grace-Period";
			ans_attrib->value.u32 = avp_attrib->value.u32;
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_avp_attrib(avp_attrib);
		}
		if ((ret1 == 0) && (ret2 == 0) && (auth_attrib != NULL) && (avp_attrib
				!= NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Auth-Grace-Period";
			if (avp_attrib->value.u32 < atoi(auth_attrib->value))
			{
				ans_attrib->value.u32 = avp_attrib->value.u32;
			}
			else
			{
				ans_attrib->value.u32 = atoi(auth_attrib->value);
			}
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_attrib(auth_attrib);
			free_avp_attrib(avp_attrib);
		}
	}

	/* Auth-Session-State */
	{
		CHECK_FCT(diameap_get_auth_attribute(&diameap_sm->attributes, "Auth-Session-State", &auth_attrib,1, &ret2));
		if ((ret2 == 0) && (auth_attrib != NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Auth-Session-State";
			ans_attrib->value.i32 = atoi(auth_attrib->value);
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_attrib(auth_attrib);
		}
	}

	/* Re-Auth-Request-Type */
	{
		CHECK_FCT(diameap_get_auth_attribute(&diameap_sm->attributes, "Re-Auth-Request-Type", &auth_attrib,1, &ret2));
		if ((ret2 == 0) && (auth_attrib != NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Re-Auth-Request-Type";
			ans_attrib->value.i32 = atoi(auth_attrib->value);
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_attrib(auth_attrib);
		}
		else
		{
			ans_attrib = NULL;
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes, "Authorization-Lifetime", &ans_attrib,0, &ret1));
			if ((ret1 == 0) && (ans_attrib != NULL))
			{
				CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
				memset(ans_attrib, 0, sizeof(struct avp_attribute));
				fd_list_init(&ans_attrib->chain, NULL);
				ans_attrib->attrib = "Re-Auth-Request-Type";
				ans_attrib->value.i32 = 0;
				fd_list_insert_before(&diameap_sm->ans_attributes,
						&ans_attrib->chain);
			}
		}
	}

	/* Session-Timeout */
	{
		CHECK_FCT(diameap_get_auth_attribute(&diameap_sm->attributes, "Session-Timeout", &auth_attrib,1, &ret2));
		if ((ret2 == 0) && (auth_attrib != NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Session-Timeout";
			ans_attrib->value.u32 = atoi(auth_attrib->value);
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_attrib(auth_attrib);
		}
	}

	/* Multi-Round-Time-Out */
	{
		CHECK_FCT(diameap_get_auth_attribute(&diameap_sm->attributes, "Multi-Round-Time-Out", &auth_attrib,1, &ret2));
		if ((ret2 == 0) && (auth_attrib != NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Multi-Round-Time-Out";
			ans_attrib->value.u32 = atoi(auth_attrib->value);
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_attrib(auth_attrib);
		}
	}

	/* Acct-Interim-Interval */
	{
		CHECK_FCT(diameap_get_auth_attribute(&diameap_sm->attributes, "Acct-Interim-Interval", &auth_attrib,1, &ret2));
		if ((ret2 == 0) && (auth_attrib != NULL))
		{
			CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
			memset(ans_attrib, 0, sizeof(struct avp_attribute));
			fd_list_init(&ans_attrib->chain, NULL);
			ans_attrib->attrib = "Acct-Interim-Interval";
			ans_attrib->value.u32 = atoi(auth_attrib->value);
			fd_list_insert_before(&diameap_sm->ans_attributes,
					&ans_attrib->chain);
			free_attrib(auth_attrib);
		}
	}

	return 0;
}

#define	DIAMEAP_STR	1
#define	DIAMEAP_NUM	2

#define DIAMEAP_OP_NO	0	//Not supported operator
#define	DIAMEAP_OP_EQ	1	//==
#define	DIAMEAP_OP_GT	2	//>
#define	DIAMEAP_OP_LT	3	//<
#define	DIAMEAP_OP_GE	4	//>=
#define	DIAMEAP_OP_LE	5	//<=
#define	DIAMEAP_OP_NE	6	//!=
#define	DIAMEAP_OP_EX	7	//~=
#define EQ(A,B) A==B ? TRUE : FALSE
#define GT(A,B) A>B ? TRUE : FALSE
#define GE(A,B) A>=B ? TRUE : FALSE
#define LT(A,B) A<B ? TRUE : FALSE
#define LE(A,B) A<=B ? TRUE : FALSE
#define NE(A,B) A!=B ? TRUE : FALSE

int diameap_get_operator(char *operator)
{
	TRACE_ENTRY("%p",operator);
	if (strcmp(operator, "==") == 0)
	{
		return DIAMEAP_OP_EQ;
	}
	if (strcmp(operator, ">") == 0)
	{
		return DIAMEAP_OP_GT;
	}
	if (strcmp(operator, "<") == 0)
	{
		return DIAMEAP_OP_LT;
	}
	if (strcmp(operator, ">=") == 0)
	{
		return DIAMEAP_OP_GE;
	}
	if (strcmp(operator, "<=") == 0)
	{
		return DIAMEAP_OP_LE;
	}
	if (strcmp(operator, "!=") == 0)
	{
		return DIAMEAP_OP_NE;
	}
	if (strcmp(operator, "~=") == 0)
	{
		return DIAMEAP_OP_EX;
	}
	return DIAMEAP_OP_NO;
}

boolean is_operator(int format_type, char * operator)
{
	TRACE_ENTRY("%d %p",format_type,operator);
	if ((format_type == DIAMEAP_STR) && (strcmp(operator, "==") == 0 || strcmp(
			operator, "~=") == 0 || strcmp(operator, "!=") == 0))
	{
		return TRUE;
	}
	if ((format_type == DIAMEAP_NUM) && (strcmp(operator, "~=") != 0))
	{
		return TRUE;
	}
	return FALSE;
}

union avp_value diameap_get_num(char * num, enum dict_avp_basetype datatype)
{
	TRACE_ENTRY("%p %d",num,datatype);
	union avp_value val;
	switch (datatype)
	{
	case AVP_TYPE_INTEGER32://i32
		val.i32 = atoi(num);
		break;
	case AVP_TYPE_INTEGER64://i64
		val.i64 = atoll(num);
		break;
	case AVP_TYPE_UNSIGNED32://u32
		val.u32 = strtoul(num, NULL, 10);
		break;
	case AVP_TYPE_UNSIGNED64://u64
		val.u64 = strtoull(num, NULL, 10);
		break;
	case AVP_TYPE_FLOAT32://f32
		val.f32 = atof(num);
		break;
	case AVP_TYPE_FLOAT64://f64
		val.f64 = strtod(num, NULL);
		break;
	default:
		TRACE_DEBUG(INFO, "%sUnknown AVP Base Type.",DIAMEAP_EXTENSION)
		memset(&val, 0, sizeof(val));
		;
	}
	return val;
}

boolean diameap_check(union avp_value *A, char * B, char * operator,
		enum dict_avp_basetype datatype)
{
	TRACE_ENTRY("%p %p %p %d",A,B,operator,datatype);
	if (((datatype == AVP_TYPE_OCTETSTRING) && (is_operator(DIAMEAP_STR,
			operator) == TRUE)) || ((datatype != AVP_TYPE_OCTETSTRING)
			&& (datatype != AVP_TYPE_GROUPED) && (is_operator(DIAMEAP_NUM,
			operator) == TRUE)))
	{
		switch (diameap_get_operator(operator))
		{
		case DIAMEAP_OP_EQ:
			if ((datatype == AVP_TYPE_OCTETSTRING) && (is_operator(DIAMEAP_STR,
					operator) == TRUE))
			{
				if (strcmp((char *)A->os.data, B) == 0)
					return TRUE;
				else
					return FALSE;
			}
			else if ((datatype != AVP_TYPE_OCTETSTRING) && (datatype
					!= AVP_TYPE_GROUPED) && (is_operator(DIAMEAP_NUM, operator)
					== TRUE))
			{

				switch (datatype)
				{
				case AVP_TYPE_INTEGER32://i32
					return EQ(A->i32,diameap_get_num(B, datatype).i32);
					break;
				case AVP_TYPE_INTEGER64://i64
					return EQ(A->i64,diameap_get_num(B, datatype).i64);
					break;
				case AVP_TYPE_UNSIGNED32://u32
					return EQ(A->u32,diameap_get_num(B, datatype).u32);
					break;
				case AVP_TYPE_UNSIGNED64://u64
					return EQ(A->u64,diameap_get_num(B, datatype).u64);
					break;
				case AVP_TYPE_FLOAT32://f32
					return EQ(A->f32,diameap_get_num(B, datatype).f32);
					break;
				case AVP_TYPE_FLOAT64://f64
					return EQ(A->f64,diameap_get_num(B, datatype).f64);
					break;
				default:
					return FALSE;
				}
			}
			else
			{
				return FALSE;
			}
			break;
		case DIAMEAP_OP_EX:
		{
			//string only
			boolean authorized = FALSE;
			if ((datatype == AVP_TYPE_OCTETSTRING) && (is_operator(DIAMEAP_STR,
					operator) == TRUE))
			{
				regex_t rule_regexp;
				regcomp(&rule_regexp, B, REG_EXTENDED | REG_NOSUB | REG_ICASE);
				if (regexec(&rule_regexp, (char *)A->os.data, 0, NULL, 0) != 0)
				{
					authorized = FALSE;
				}
				else
				{
					authorized = TRUE;
				}
				regfree(&rule_regexp);
			}
			return authorized;
		}
		case DIAMEAP_OP_GT:

			if ((datatype != AVP_TYPE_OCTETSTRING) && (datatype
					!= AVP_TYPE_GROUPED) && (is_operator(DIAMEAP_NUM, operator)
					== TRUE))
			{
				switch (datatype)
				{
				case AVP_TYPE_INTEGER32://i32
					return GT(A->i32, diameap_get_num(B, datatype).i32);
					break;
				case AVP_TYPE_INTEGER64://i64
					return GT(A->i64, diameap_get_num(B, datatype).i64);
					break;
				case AVP_TYPE_UNSIGNED32://u32
					return GT(A->u32, diameap_get_num(B, datatype).u32);
					break;
				case AVP_TYPE_UNSIGNED64://u64
					return GT(A->u64, diameap_get_num(B, datatype).u64);
					break;
				case AVP_TYPE_FLOAT32://f32
					return GT(A->f32, diameap_get_num(B, datatype).f32);
					break;
				case AVP_TYPE_FLOAT64://f64
					return GT(A->f64, diameap_get_num(B, datatype).f64);
					break;
				default:
					return FALSE;
				}
			}
			else
			{
				return FALSE;
			}
			break;
		case DIAMEAP_OP_GE:
			if ((datatype != AVP_TYPE_OCTETSTRING) && (datatype
					!= AVP_TYPE_GROUPED) && (is_operator(DIAMEAP_NUM, operator)
					== TRUE))
			{
				switch (datatype)
				{
				case AVP_TYPE_INTEGER32://i32
					return GE(A->i32,diameap_get_num(B, datatype).i32);
					break;
				case AVP_TYPE_INTEGER64://i64
					return GE(A->i64,diameap_get_num(B, datatype).i64);
					break;
				case AVP_TYPE_UNSIGNED32://u32
					return GE(A->u32,diameap_get_num(B, datatype).u32);
					break;
				case AVP_TYPE_UNSIGNED64://u64
					return GE(A->u64,diameap_get_num(B, datatype).u64);
					break;
				case AVP_TYPE_FLOAT32://f32
					return GE(A->f32,diameap_get_num(B, datatype).f32);
					break;
				case AVP_TYPE_FLOAT64://f64
					return GE(A->f64,diameap_get_num(B, datatype).f64);
					break;
				default:
					return FALSE;
				}
			}
			else
			{
				return FALSE;
			}
			break;
		case DIAMEAP_OP_LT:
			if ((datatype != AVP_TYPE_OCTETSTRING) && (datatype
					!= AVP_TYPE_GROUPED) && (is_operator(DIAMEAP_NUM, operator)
					== TRUE))
			{
				switch (datatype)
				{
				case AVP_TYPE_INTEGER32://i32
					return LT(A->i32, diameap_get_num(B, datatype).i32);
					break;
				case AVP_TYPE_INTEGER64://i64
					return LT(A->i64, diameap_get_num(B, datatype).i64);
					break;
				case AVP_TYPE_UNSIGNED32://u32
					return LT(A->u32, diameap_get_num(B, datatype).u32);
					break;
				case AVP_TYPE_UNSIGNED64://u64
					return LT(A->u64, diameap_get_num(B, datatype).u64);
					break;
				case AVP_TYPE_FLOAT32://f32
					return LT(A->f32, diameap_get_num(B, datatype).f32);
					break;
				case AVP_TYPE_FLOAT64://f64
					return LT(A->f64, diameap_get_num(B, datatype).f64);
					break;
				default:
					return FALSE;
				}
			}
			else
			{
				return FALSE;
			}
			break;
		case DIAMEAP_OP_LE:
			if ((datatype != AVP_TYPE_OCTETSTRING) && (datatype
					!= AVP_TYPE_GROUPED) && (is_operator(DIAMEAP_NUM, operator)
					== TRUE))
			{
				switch (datatype)
				{
				case AVP_TYPE_INTEGER32://i32
					return LE(A->i32, diameap_get_num(B, datatype).i32);
					break;
				case AVP_TYPE_INTEGER64://i64
					return LE(A->i64, diameap_get_num(B, datatype).i64);
					break;
				case AVP_TYPE_UNSIGNED32://u32
					return LE(A->u32, diameap_get_num(B, datatype).u32);
					break;
				case AVP_TYPE_UNSIGNED64://u64
					return LE(A->u64, diameap_get_num(B, datatype).u64);
					break;
				case AVP_TYPE_FLOAT32://f32
					return LE(A->f32, diameap_get_num(B, datatype).f32);
					break;
				case AVP_TYPE_FLOAT64://f64
					return LE(A->f64, diameap_get_num(B, datatype).f64);
					break;
				default:
					return FALSE;
				}
			}
			else
			{
				return FALSE;
			}
			break;
		case DIAMEAP_OP_NE:
			if ((datatype == AVP_TYPE_OCTETSTRING) && (is_operator(DIAMEAP_STR,
					operator) == TRUE))
			{
				if (strcmp((char *)A->os.data, B) != 0)
					return TRUE;
				else
					return FALSE;
			}
			else if ((datatype != AVP_TYPE_OCTETSTRING) && (datatype
					!= AVP_TYPE_GROUPED) && (is_operator(DIAMEAP_NUM, operator)
					== TRUE))
			{
				switch (datatype)
				{
				case AVP_TYPE_INTEGER32://i32
					return NE(A->i32, diameap_get_num(B, datatype).i32);
					break;
				case AVP_TYPE_INTEGER64://i64
					return NE(A->i64, diameap_get_num(B, datatype).i64);
					break;
				case AVP_TYPE_UNSIGNED32://u32
					return NE(A->u32, diameap_get_num(B, datatype).u32);
					break;
				case AVP_TYPE_UNSIGNED64://u64
					return NE(A->u64, diameap_get_num(B, datatype).u64);
					break;
				case AVP_TYPE_FLOAT32://f32
					return NE(A->f32, diameap_get_num(B, datatype).f32);
					break;
				case AVP_TYPE_FLOAT64://f64
					return NE(A->f64, diameap_get_num(B, datatype).f64);
					break;
				default:
					return FALSE;
				}
			}
			else
			{
				return FALSE;
			}
			break;
		}
	}
	return FALSE;
}

char * diameap_attribute_operator(char * op, int * toadd, boolean *isrule)
{
	TRACE_ENTRY("%p %p %p",op,toadd,isrule);
	char * attribute_op;

	if (op[0] == '+')
	{
		*toadd = 1;
	}
	else if (op[strlen(op) - 1] == '+')
	{
		*toadd = 2;
	}
	else
	{
		*toadd = 0;
	}

	switch (*toadd)
	{
	case 1:
		attribute_op = calloc(strlen(op), sizeof(char *));
		memcpy(attribute_op, op + 1, strlen(op) - 1);
		break;
	case 2:
		attribute_op = calloc(strlen(op), sizeof(char *));
		memcpy(attribute_op, op, strlen(op) - 1);
		break;
	default:
		attribute_op = calloc(strlen(op) + 1, sizeof(char *));
		memcpy(attribute_op, op, strlen(op));
	}
	if (strcmp(attribute_op, "=") == 0)
	{
		*isrule = FALSE;
		*toadd = 2;
	}
	else
	{
		*isrule = TRUE;
	}

	return attribute_op;
}

int diameap_answer_set_attribute_valueA(union avp_value *A, int *tofree,
		enum dict_avp_basetype datatype, union avp_value * rval)
{
	TRACE_ENTRY("%p %p %d %p",A,tofree,datatype,rval);
	if (datatype == AVP_TYPE_OCTETSTRING)
	{
		CHECK_MALLOC(rval->os.data=malloc(A->os.len));
		memcpy(rval->os.data,A->os.data,A->os.len);
		rval->os.len = A->os.len;
		*tofree = 1;
	}
	else
	{
		*rval = *A;
	}
	return 0;
}
int diameap_answer_set_attribute_valueB(char * B, int *tofree,
		enum dict_avp_basetype datatype, union avp_value * rval)
{
	TRACE_ENTRY("%p %p %d %p",B,tofree,datatype,rval);
	if (datatype == AVP_TYPE_OCTETSTRING)
	{
		CHECK_MALLOC(rval->os.data=malloc(strlen(B)));
		memcpy(rval->os.data,B,strlen(B));
		rval->os.len = strlen(B);

		*tofree = 1;
	}
	else
	{

		*rval = diameap_get_num(B, datatype);
	}

	return 0;
}

static int diameap_attribute_limits(char * attrib, int * max, int *ret)
{
	TRACE_ENTRY("%p %p %p",attrib,max,ret);
	if (attrib == NULL)
	{
		return EINVAL;
	}
	int i;
	for (i = 0; i < sizeof(auth_avps); i++)
	{
		if (strcmp(auth_avps[i].avp_attribute, attrib) == 0)
		{
			*max = auth_avps[i].max;
			*ret = 0;
			return 0;
		}
	}
	*max = 0;
	*ret = 1;
	return 0;
}

static int diameap_answer_authorization_attributes(
		struct diameap_state_machine * diameap_sm)
{
	TRACE_ENTRY("%p",diameap_sm);

	if (diameap_sm == NULL)
	{
		return EINVAL;
	}
	boolean checked = TRUE;

	struct fd_list * attrib;
	struct auth_attribute * auth_attrib;
	struct avp_attribute * avp_attrib;
	int ret;

	for (attrib = (&diameap_sm->attributes)->next; attrib
			!= (&diameap_sm->attributes); attrib = attrib->next)
	{
		avp_attrib = NULL;
		auth_attrib = (struct auth_attribute *) attrib;

		int toadd = 0;
		boolean isrule = FALSE;
		char * op;

		op = diameap_attribute_operator(auth_attrib->op, &toadd, &isrule);

		struct dict_object * dobj;
		struct dict_avp_data avpdata;
		fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME,
				auth_attrib->attrib, &dobj, ENOENT);
		fd_dict_getval(dobj, &avpdata);

		checked = TRUE;
		if (isrule == TRUE)
		{
			CHECK_FCT(diameap_get_avp_attribute(&diameap_sm->req_attributes,auth_attrib->attrib,&avp_attrib,0,&ret));
			if (ret == 0)
			{
				checked = diameap_check(&avp_attrib->value, auth_attrib->value,
						op, avpdata.avp_basetype);
			}
		}
		if (checked == TRUE && toadd != 0)
		{

			struct avp_attribute * ans_attrib;
			int max = 0;
			diameap_attribute_limits(auth_attrib->attrib, &max, &ret);
			if ((ret == 0) && (max != 0))
			{
				if (max == 1)//only one
				{
					int ret = 0;
					diameap_get_ans_attribute(&diameap_sm->ans_attributes,
							auth_attrib->attrib, &ans_attrib, 0, &ret);
					if (ret == 1)
					{
						ans_attrib = NULL;
						CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
						memset(ans_attrib, 0, sizeof(struct avp_attribute));
						fd_list_init(&ans_attrib->chain, NULL);
						ans_attrib->attrib = strdup(auth_attrib->attrib);
						if (toadd == 1)
						{
							diameap_answer_set_attribute_valueA(
									&avp_attrib->value, &ans_attrib->tofree,
									avpdata.avp_basetype, &ans_attrib->value);
						}
						else
						{
							diameap_answer_set_attribute_valueB(
									auth_attrib->value, &ans_attrib->tofree,
									avpdata.avp_basetype, &ans_attrib->value);
						}
						fd_list_insert_before(&diameap_sm->ans_attributes,
								&ans_attrib->chain);

					}
					else
					{
						//an answer avp is already added
					}
				}
				else
				{
					ans_attrib = NULL;
					CHECK_MALLOC(ans_attrib = malloc(sizeof(struct avp_attribute)));
					memset(ans_attrib, 0, sizeof(struct avp_attribute));
					fd_list_init(&ans_attrib->chain, NULL);
					ans_attrib->attrib = auth_attrib->attrib;
					if (toadd == 1)
					{
						diameap_answer_set_attribute_valueA(&avp_attrib->value,
								&ans_attrib->tofree, avpdata.avp_basetype,
								&ans_attrib->value);
					}
					else
					{
						diameap_answer_set_attribute_valueB(auth_attrib->value,
								&ans_attrib->tofree, avpdata.avp_basetype,
								&ans_attrib->value);
					}
					fd_list_insert_before(&diameap_sm->ans_attributes,
							&ans_attrib->chain);
				}
			}
		}
		if (checked == FALSE)
		{

			diameap_sm->authorized = FALSE;
			return 0;
		}
	}
	diameap_sm->authorized = checked;
	return 0;

	return 0;
}



static int diameap_policy_decision(struct diameap_state_machine * diameap_sm,
		struct diameap_eap_interface *eap_i)
{
	TRACE_ENTRY("%p %p",diameap_sm,eap_i);

	if ((eap_i->aaaFail == TRUE) && (eap_i->aaaSuccess == TRUE))
	{
		TRACE_DEBUG(INFO,"%s Incorrect EAP decision. EAP process should not return both success and failure for the same session.(please report this problem.)",DIAMEAP_EXTENSION);
		return -1;
	}

	if (eap_i->aaaFail == TRUE)
	{
		diameap_sm->result_code = 4001; /* DIAMETER_AUTHENTICATION_REJECTED 4001 */
		diameap_sm->authFailure = TRUE;
		TRACE_DEBUG(FULL+1,"%s Auth failure: Authentication Rejected ",DIAMEAP_EXTENSION);
		return 0;
	}

	if (eap_i->aaaSuccess == FALSE)
	{
		diameap_sm->result_code = 1001; /* DIAMETER_MULTI_ROUND_AUTH 1001 */
		return 0;
	}

	if (eap_i->aaaSuccess == TRUE)
	{
		if (diameap_sm->auth_request_val == AUTHORIZE_AUTHENTICATE)
		{
			if ((diameap_sm->verify_authorization == TRUE)
					&& (diameap_sm->result_code == 0))
			{
				diameap_sm->result_code = 2001; /* DIAMETER_SUCCESS 2001 */
				diameap_sm->authSuccess = TRUE;
				TRACE_DEBUG(FULL+1,"%s Auth success: Authorization and Authentication ",DIAMEAP_EXTENSION);
				return 0;
			}
			else
			{
				//
			}
		}
		if (diameap_sm->auth_request_val == AUTHENTICATE_ONLY)
		{
			diameap_sm->result_code = 2001; /* DIAMETER_SUCCESS 2001 */
			diameap_sm->authSuccess = TRUE;
			TRACE_DEBUG(FULL+1,"%s Auth success: Authentication Only ",DIAMEAP_EXTENSION);
			return 0;
		}
	}

	return 0;
}

static int diameap_add_avps(struct diameap_state_machine * diameap_sm,
		struct msg * ans, struct msg * req)
{
	TRACE_ENTRY("%p %p %p",diameap_sm,ans,req);

	struct avp * avp, *avp2;
	struct avp_hdr * avpdata;
	union avp_value avp_val;
	int ret = 0;

	/* Origin-Host AVP and Origin-Realm AVP */
	{
		CHECK_FCT( fd_msg_add_origin ( ans, 0 ) );
	}

	/* Auth-Application-Id AVP */
	{
		CHECK_FCT(fd_msg_avp_new(dataobj_auth_application_id, 0, &avp));
		avp_val.u32 = diameap_config->application_id;
		CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
		CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp));
	}

	/* Auth-Request-Type AVP
	 * Enumerated values:
	 * 	AUTHENTICATE_ONLY 1
	 * 	AUTHORIZE_ONLY 2
	 * 	AUTHORIZE_AUTHENTICATE 3 */
	{
		CHECK_FCT(fd_msg_avp_new(dataobj_auth_request_type, 0, &avp));
		if (!diameap_config->authorize)
		{
			//AUTHENTICATE ONLY
			avp_val.i32 = AUTHENTICATE_ONLY;
		}
		else
		{
			if (diameap_sm->auth_request_val == 3)
			{
				//AUTHORIZE_AUTHENTICATE
				avp_val.i32 = AUTHORIZE_AUTHENTICATE;
			}
			else
			{
				//AUTHENTICATE_ONLY
				avp_val.i32 = AUTHENTICATE_ONLY;
			}
		}
		CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
		CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp));
	}

	/* Proxy-Info AVP */
	CHECK_FCT(fd_msg_search_avp(req, dataobj_proxy_info, &avp));
	if (avp != NULL)
	{
		CHECK_FCT(fd_msg_avp_hdr ( avp, &avpdata ) );
		u32 proxy_info_code = avpdata->avp_code;
		int depth;
		do
		{
			ret = 0;
			depth = 0;
			CHECK_FCT(fd_msg_avp_hdr ( avp, &avpdata ) );
			if (avpdata->avp_code == proxy_info_code)
			{
				CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp));
			}
			ret = fd_msg_browse ( avp, MSG_BRW_NEXT, &avp2, &depth);
			avp = avp2;
		} while ((avp != NULL) && (ret == 0) && (ret == 0));
	}

	if (diameap_sm->eap_sm.user.userid)
	{
		/* User-Name AVP */
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_user_name, 0, &avp));

			if (diameap_sm->privateUser == FALSE)
			{

				avp_val.os.data = diameap_sm->eap_sm.user.userid;
				avp_val.os.len = diameap_sm->eap_sm.user.useridLength;
				CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
				CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			}
			else
			{

			}

		}
	}
	return 0;
}

static int diameap_add_user_sessions_avps(
		struct diameap_state_machine * diameap_sm, struct msg * ans)
{
	TRACE_ENTRY("%p %p",diameap_sm,ans);

	struct avp * avp;
	union avp_value avp_val;
	int ret;

	/* Authorization-Lifetime AVP */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Authorization-Lifetime",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_authorization_lifetime, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Auth-Grace-Period AVP */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Auth-Grace-Period",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_auth_grace_period, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Auth-Session-State AVP */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Auth-Session-State",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_auth_session_state, 0, &avp));
			avp_val.i32 = ans_attrib->value.i32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Re-Auth-Request-Type AVP */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Re-Auth-Request-Type",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_re_auth_request_type, 0, &avp));
			avp_val.i32 = ans_attrib->value.i32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Session-Timeout AVP */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Session-Timeout",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_session_timeout, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Acct-Interim-Interval AVP */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Acct-Interim-Interval",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_acct_interim_interval, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}
	return 0;
}

static int diameap_add_authorization_avps(
		struct diameap_state_machine * diameap_sm, struct msg * ans)
{

	TRACE_ENTRY("%p %p",diameap_sm, ans);

	struct avp * avp;
	union avp_value avp_val;
	int ret;

	if (diameap_sm == NULL)
	{
		return EINVAL;
	}
	if (ans == NULL)
	{
		return EINVAL;
	}

	/* Reply-Message */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Reply-Message",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_reply_message, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Reply-Message",&ans_attrib,1,&ret));
		}

	}

	/* Service-Type */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Service-Type",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_service_type, 0, &avp));
			avp_val.i32 = ans_attrib->value.i32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Callback-Number */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Callback-Number",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_callback_number, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Callback-Id */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Callback-Id",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_callback_id, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Idle-Timeout */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Idle-Timeout",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_idle_timeout, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* NAS-Filter-Rule */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"NAS-Filter-Rule",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_nas_filter_rule, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Filter-Id */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Filter-Id",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_filter_id, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Filter-Id",&ans_attrib,1,&ret));
		}

	}

	/* Configuration-Token */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Configuration-Token",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_configuration_token, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Configuration-Token",&ans_attrib,1,&ret));
		}
	}

	/* QoS-Filter-Rule */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"QoS-Filter-Rule",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_qos_filter_rule, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"QoS-Filter-Rule",&ans_attrib,1,&ret));
		}

	}

	/* Framed-Protocol */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Protocol",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_protocol, 0, &avp));
			avp_val.i32 = ans_attrib->value.i32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Framed-Routing */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Routing",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_routing, 0, &avp));
			avp_val.i32 = ans_attrib->value.i32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}

	}

	/* Framed-MTU */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-MTU",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_mtu, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Framed-Compression */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Compression",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_compression, 0, &avp));
			avp_val.i32 = ans_attrib->value.i32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Compression",&ans_attrib,1,&ret));
		}

	}
	/* Framed-IP-Address */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IP-Address",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_ip_address, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Framed-IP-Netmask */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IP-Netmask",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_ip_netmask, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Framed-Route */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Route",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_route, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Route",&ans_attrib,1,&ret));
		}
	}

	/* Framed-Pool */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Pool",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_pool, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Framed-Interface-Id */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-Interface-Id",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_interface_id, 0, &avp));
			avp_val.u64 = ans_attrib->value.u64;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Framed-IPv6-Prefix */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IPv6-Prefix",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_ipv6_prefix, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IPv6-Prefix",&ans_attrib,1,&ret));
		}
	}

	/* Framed-IPv6-Route */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IPv6-Route",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_ipv6_route, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IPv6-Route",&ans_attrib,1,&ret));
		}
	}

	/* Framed-IPv6-Pool */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IPv6-Pool",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_ipv6_pool, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Framed-IPX-Network */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-IPX-Network",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_ipx_network, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}

	}
	/* Framed-AppleTalk-Link */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-AppleTalk-Link",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_appletalk_link, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}

	}

	/* Framed-AppleTalk-Network */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-AppleTalk-Network",&ans_attrib,1,&ret));
		while ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_appletalk_network, 0, &avp));
			avp_val.u32 = ans_attrib->value.u32;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
			CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-AppleTalk-Network",&ans_attrib,1,&ret));
		}

	}

	/* Framed-AppleTalk-Zone */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-AppleTalk-Zone",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_framed_appletalk_zone, 0, &avp));
			avp_val.os.data = ans_attrib->value.os.data;
			avp_val.os.len = ans_attrib->value.os.len;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_ans_attrib(ans_attrib);
		}
	}

	/* Tunneling */
	//

	/* State */
	//
	return 0;
}

static int diameap_add_result_code(struct diameap_state_machine * diameap_sm,
		struct msg * ans, struct session * sess)
{
	TRACE_ENTRY("%p %p",diameap_sm,ans);
	struct avp * avp;
	union avp_value avp_val;
	int ret;

	/* Result-Code AVP */
	CHECK_FCT(fd_msg_avp_new(dataobj_result_code, 0, &avp));
	avp_val.u32 = diameap_sm->result_code;
	CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
	/* Add Result-Code AVP to the message */
	CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );

	/* Multi_Round_Time_Out AVP */
	if (diameap_sm->result_code == 1001)
	{
		struct timespec sess_timeout;
		CHECK_FCT(fd_msg_avp_new(dataobj_multi_round_time_out, 0, &avp));
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Multi-Round-Time-Out",&ans_attrib,1,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			avp_val.u32 = ans_attrib->value.u32;
			/* Update the session timeout with multi-round-time-out value */
			CHECK_SYS(clock_gettime(CLOCK_REALTIME,&sess_timeout));
			sess_timeout.tv_sec += diameap_config->multi_round_time_out;
			CHECK_FCT(fd_sess_settimeout(sess, &sess_timeout));
			free_ans_attrib(ans_attrib);
		}
		else
		{
			avp_val.u32 = diameap_config->multi_round_time_out;
			/* Update the session timeout with multi-round-time-out value */
			CHECK_SYS(clock_gettime(CLOCK_REALTIME,&sess_timeout));
			sess_timeout.tv_sec += diameap_config->multi_round_time_out;
			CHECK_FCT(fd_sess_settimeout(sess, &sess_timeout));
		}
		CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
	}
	return 0;
}

static int diameap_add_eap_payload(struct diameap_state_machine * diameap_sm,
		struct msg * ans, struct diameap_eap_interface *eap_i)
{
	TRACE_ENTRY("%p %p",diameap_sm,ans);
	struct avp * avp;
	union avp_value avp_val;
	int ret;
	u32 Framed_MTU = 1500; //1500 default value
	u32 NAS_Port_Type_HeaderLength = 4;
	int EAP_Max_Length = 0;

	/* get Framed-MTU AVP value */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"Framed-MTU",&ans_attrib,0,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			Framed_MTU = ans_attrib->value.u32;
		}
	}

	/* get NAS-Port-Type AVP value */
	{
		struct avp_attribute * ans_attrib;
		CHECK_FCT(diameap_get_ans_attribute(&diameap_sm->ans_attributes,"NAS-Port-Type",&ans_attrib,0,&ret));
		if ((ret == 0) && (ans_attrib != NULL))
		{
			// = ans_attrib->value.i32;
		}
	}

	//TD take the link type into consideration when calculating EAP_MAX_Length
	EAP_Max_Length = Framed_MTU - NAS_Port_Type_HeaderLength;

	if (eap_i->aaaEapReqData.length <= EAP_Max_Length)
	{

		/* EAP-Payload AVP */
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_eap_payload, 0, &avp));
			avp_val.os.data = eap_i->aaaEapReqData.data;
			avp_val.os.len = eap_i->aaaEapReqData.length;
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );

			if (diameap_sm->lastReqEAPavp != NULL)
			{
				fd_msg_free(diameap_sm->lastReqEAPavp);
				diameap_sm->lastReqEAPavp = NULL;
			}

			CHECK_FCT(fd_msg_avp_new(dataobj_eap_payload, 0, &diameap_sm->lastReqEAPavp));
			avp_val.os.data = eap_i->aaaEapReqData.data;
			avp_val.os.len = eap_i->aaaEapReqData.length;
			CHECK_FCT(fd_msg_avp_setvalue(diameap_sm->lastReqEAPavp, &avp_val));

		}
	}
	else
	{
		//if EAP Packet length > EAP_Max_Length
	}
	return 0;
}

static int diameap_send(struct msg ** rmsg)
{
	TRACE_ENTRY("%p",rmsg);
	CHECK_FCT( fd_msg_send( rmsg, NULL, NULL));
	return 0;
}

static int diameap_add_eap_success_avps(
		struct diameap_state_machine * diameap_sm, struct msg * ans,
		struct diameap_eap_interface *eap_i)
{
	TRACE_ENTRY("%p %p %p",diameap_sm,ans,eap_i);
	struct avp * avp;
	union avp_value avp_val;
	int ret;

	/* EAP-Master-Session-Key AVP */
	if (eap_i->aaaEapKeyAvailable == TRUE)
	{
		CHECK_FCT(fd_msg_avp_new(dataobj_eap_master_session_key, 0, &avp));
		avp_val.os.data = eap_i->aaaEapMSKData;
		avp_val.os.len = eap_i->aaaEapMSKLength;
		CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );

	}

	/* EAP-Key-Name AVP */
	struct avp_attribute * avp_attrib = NULL;

	CHECK_FCT(diameap_get_avp_attribute(&diameap_sm->req_attributes,"EAP-Key-Name",&avp_attrib,1,&ret))
	if ((avp_attrib != NULL) && (ret != 1))
	{
		if (avp_attrib->value.os.len == 0)
		{
			CHECK_FCT(fd_msg_avp_new(dataobj_eap_key_name, 0, &avp));
			avp_val.os.data = NULL;//
			avp_val.os.len = 0;//
			CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
			CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
			free_avp_attrib(avp_attrib);
		}
	}

	return 0;
}

int diameap_authorize(struct diameap_state_machine * diameap_sm)
{
	TRACE_ENTRY("%p",diameap_sm);

	CHECK_FCT(diameap_authorization_get_attribs(&diameap_sm->eap_sm.user, &diameap_sm->attributes));

	diameap_sm->authorized = TRUE;

	CHECK_FCT_DO(diameap_answer_authorization_attributes(diameap_sm),
	);
	if (diameap_sm->authorized == FALSE)
	{
		diameap_sm->result_code = 4001; /* DIAMETER_AUTHENTICATION_REJECTED 4001 */
	}
	return 0;
}

static int diameap_add_accounting_eap_auth_method(
		struct diameap_state_machine * diameap_sm, struct msg * ans)
{
	TRACE_ENTRY("%p %p",diameap_sm,ans);
	int i = 0;
	struct avp * avp;
	union avp_value avp_val;
	/* Accounting-EAP-Auth-Method AVP */
	while (i < diameap_sm->eap_sm.user.methodId)
	{
		CHECK_FCT(fd_msg_avp_new(dataobj_accounting_eap_auth_method, 0, &avp));

		avp_val.u64 = (u64) (((diameap_sm->eap_sm.user.methods[i].vendor)
				* pow((double) 2, (double) 32))
				+ diameap_sm->eap_sm.user.methods[i].method);
		CHECK_FCT(fd_msg_avp_setvalue(avp, &avp_val));
		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
		i++;
	}
	return 0;
}

static int diameap_add_eap_reissued_payload(struct msg * ans, struct msg * req)
{
	TRACE_ENTRY("%p %p", ans, req);
	struct avp * avp, *re_avp;
	union avp_value avp_val;
	struct avp_hdr * avphdr;

	if ((ans == NULL) || (req == NULL))
	{
		return EINVAL;
	}
	avp = NULL;
	CHECK_FCT(fd_msg_search_avp(req, dataobj_eap_payload, &avp));
	if (avp != NULL)
	{
		CHECK_FCT( fd_msg_avp_hdr(avp, &avphdr));
		CHECK_FCT( fd_msg_avp_new(dataobj_eap_reissued_payload, 0, &re_avp));
		CHECK_MALLOC(avp_val.os.data=malloc(avphdr->avp_value->os.len));
		memcpy(avp_val.os.data,avphdr->avp_value->os.data,avphdr->avp_value->os.len);
		avp_val.os.len = avphdr->avp_value->os.len;
		CHECK_FCT(fd_msg_avp_setvalue(re_avp, &avp_val));
		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, re_avp ) );
	}
	else
	{
		TRACE_DEBUG(INFO,"%sUnable to find EAP-Payload AVP in received Diameter-EAP-Request.",DIAMEAP_EXTENSION);
		return 1;
	}

	return 0;
}




static int diameap_server_callback(struct msg ** rmsg, struct avp * ravp,
		struct session * sess, void * opaque, enum disp_action * action)
{
	TRACE_ENTRY("%p %p %p %p", rmsg, ravp, sess, action);

	struct sess_state * diameap_sess_data = NULL;
	struct diameap_state_machine * diameap_sm = NULL;
	struct diameap_eap_interface eap_i;
	struct msg *req, *ans = NULL;
	boolean non_fatal_error = FALSE;

	if (rmsg == NULL)
		return EINVAL;

	req = *rmsg;

	CHECK_FCT_DO(fd_sess_state_retrieve(diameap_server_reg, sess, &diameap_sess_data),
			{	TRACE_DEBUG(INFO,"%s retrieving session state failed.",DIAMEAP_EXTENSION); goto s_end;});

	CHECK_MALLOC_DO(diameap_sm = malloc(sizeof(struct diameap_state_machine)),
			goto s_end);
	memset(diameap_sm, 0, sizeof(struct diameap_state_machine));

	if (diameap_sess_data)
	{
		diameap_sm->state = DIAMEAP_RECEIVED;
		diameap_sm->eap_sm.eap_state = EAP_IDLE;
	}
	else
	{
		diameap_sm->state = DIAMEAP_DISABLED;
		diameap_sm->eap_sm.eap_state = EAP_INITIALIZE;
	}

	while (diameap_sm->state != DIAMEAP_IDLE && diameap_sm->state
			!= DIAMEAP_END)
	{
		switch (diameap_sm->state)
		{
		case DIAMEAP_DISABLED:
			if (rmsg)
			{
				diameap_sm->state = DIAMEAP_INITIALIZE;
			}
			else
			{
				TRACE_DEBUG(INFO,"%sReceived empty Diameter EAP Request message.",DIAMEAP_EXTENSION);
				goto s_end;
			}
			break;

		case DIAMEAP_INITIALIZE:

			CHECK_FCT_DO(diameap_initialize_diameap_sm(diameap_sm,diameap_sess_data),
					{	TRACE_DEBUG(INFO,"%s Initializing DiamEAP state machine failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			CHECK_FCT_DO(diameap_initialize_diameap_eap_interface(&eap_i),
					{	TRACE_DEBUG(INFO,"%s Initializing DiamEAP-EAP Interface failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			TRACE_DEBUG(FULL+1,"%sParsing AVPs",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO(diameap_parse_avps(diameap_sm, req, &eap_i), TRACE_DEBUG(INFO,"%s Unable to parse Diameter-EAP-Request AVPs.",DIAMEAP_EXTENSION))
			;

			if ((diameap_sm->result_code != 0))
			{
				diameap_sm->state = DIAMEAP_SEND_ERROR_MSG;
			}
			else
			{
				diameap_sm->state = DIAMEAP_AUTHENTICATION_VERIFY;
			}
			break;

		case DIAMEAP_RECEIVED:

			CHECK_FCT_DO(diameap_initialize_diameap_sm(diameap_sm,diameap_sess_data),
					{	TRACE_DEBUG(INFO,"%s Initializing DiamEAP state machine failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			CHECK_FCT_DO(diameap_initialize_diameap_eap_interface(&eap_i),
					{	TRACE_DEBUG(INFO,"%s Initializing DiamEAP-EAP Interface failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			TRACE_DEBUG(FULL+1,"%sParsing AVPs",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO(diameap_parse_avps(diameap_sm, req, &eap_i), TRACE_DEBUG(INFO,"%s Unable to parse Diameter-EAP-Request AVPs.",DIAMEAP_EXTENSION))
			;

			if (diameap_sm->result_code != 0)
			{
				diameap_sm->state = DIAMEAP_SEND_ERROR_MSG;
			}
			else
			{
				diameap_sm->state = DIAMEAP_AUTHENTICATION_VERIFY;
			}
			break;

		case DIAMEAP_AUTHENTICATION_VERIFY:
		{

			TRACE_DEBUG(FULL+1,"%sVerify authentication",DIAMEAP_EXTENSION);
			CHECK_FCT_DO(diameap_eap_statemachine(&diameap_sm->eap_sm, &eap_i,&non_fatal_error),
					{	TRACE_DEBUG(INFO,"%s EAP process failed.",DIAMEAP_EXTENSION); goto s_end;});

			if (non_fatal_error == TRUE)
			{
				TRACE_DEBUG(FULL+1,"%sAuthentication verify finished with a non-fatal-error.",DIAMEAP_EXTENSION);
				diameap_sm->state = DIAMEAP_SEND_ERROR_MSG;
			}
			else
			{
				diameap_sm->state = DIAMEAP_SELECT_DECISION;

			}
		}
			break;

		case DIAMEAP_SELECT_DECISION:

			CHECK_FCT_DO( diameap_policy_decision(diameap_sm,&eap_i),
					goto s_end)
			;

			if ((eap_i.aaaSuccess == TRUE) && (diameap_sm->auth_request_val
					== AUTHORIZE_AUTHENTICATE)
					&& (diameap_sm->verify_authorization == FALSE))
			{
				diameap_sm->state = DIAMEAP_AUTHORIZATION_VERIFY;
			}
			else
			{
				diameap_sm->state = DIAMEAP_DIAMETER_EAP_ANSWER;
			}
			break;

		case DIAMEAP_AUTHORIZATION_VERIFY:
			diameap_sm->verify_authorization = TRUE;
			TRACE_DEBUG(FULL+1,"%sVerify authorization",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO(diameap_authorize(diameap_sm),
					{	TRACE_DEBUG(INFO,"%s Authorization check process failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			diameap_sm->state = DIAMEAP_SELECT_DECISION;

			break;

		case DIAMEAP_DIAMETER_EAP_ANSWER:
			TRACE_DEBUG(FULL+1,"%screate Diameter EAP Answer",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO(fd_msg_new_answer_from_req(fd_g_config->cnf_dict, rmsg, 0),
					goto s_end)
			;
			ans = *rmsg;
			TRACE_DEBUG(FULL+1,"%sAdding AVPs to Diameter EAP Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_avps(diameap_sm, ans,req),
					{	TRACE_DEBUG(INFO,"%s Unable to add AVPs to Diameter-EAP-Answer message.",DIAMEAP_EXTENSION);goto s_end;})
			;
			if (diameap_sm->authFailure == FALSE)
			{
				if (diameap_sm->eap_sm.user.id != 0)
				{
					TRACE_DEBUG(FULL+1,"%sSelect authentication attributes.",DIAMEAP_EXTENSION);
					CHECK_FCT_DO(diameap_authentication_get_attribs(&diameap_sm->eap_sm.user, &diameap_sm->attributes),
							{	TRACE_DEBUG(INFO,"%s Unable to get user's session attributes.",DIAMEAP_EXTENSION); goto s_end;});
					TRACE_DEBUG(FULL+1,"%sCreate answer authentication attributes.",DIAMEAP_EXTENSION);
					CHECK_FCT_DO(diameap_answer_avp_attributes(diameap_sm),
							{	TRACE_DEBUG(INFO,"%s Unable to generate answer attributes.",DIAMEAP_EXTENSION); goto s_end;});
				}

				if (diameap_sm->authSuccess == FALSE)
				{
					diameap_sm->state = DIAMEAP_SEND_REQUEST;
				}
				else
				{

					diameap_sm->state = DIAMEAP_SEND_SUCCESS;
				}
			}
			else
			{
				diameap_sm->state = DIAMEAP_SEND_FAILURE;
			}
			break;

		case DIAMEAP_SEND_REQUEST:
			TRACE_DEBUG(FULL+1,"%sAdding Result Code AVP to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_result_code(diameap_sm, ans, sess),
					{	TRACE_DEBUG(INFO,"%s Adding Result-Code AVP failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			TRACE_DEBUG(FULL+1,"%sAdding EAP-Payload to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_eap_payload(diameap_sm, ans,&eap_i),
					{	TRACE_DEBUG(INFO,"%s Adding EAP-Payload AVP failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			TRACE_DEBUG(FULL+1,"%sStoring DiamEAP session data.",DIAMEAP_EXTENSION)
			;
			CHECK_MALLOC(diameap_sess_data = malloc(sizeof(struct sess_state)))
			;
			memset(diameap_sess_data, 0, sizeof(struct sess_state));
			diameap_sess_data_new(diameap_sess_data, diameap_sm);

			CHECK_FCT_DO(fd_sess_state_store(diameap_server_reg, sess, &diameap_sess_data),
					{	TRACE_DEBUG(INFO,"%s Storing session state failed.",DIAMEAP_EXTENSION); goto s_end;})
			;

			CHECK_FCT_DO( diameap_send(rmsg),
					goto s_end)
			;

			diameap_sm->state = DIAMEAP_IDLE;
			break;

		case DIAMEAP_SEND_FAILURE:
			TRACE_DEBUG(FULL+1,"%sAdding Result Code AVP to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_result_code(diameap_sm, ans, sess),
					{	TRACE_DEBUG(INFO,"%s Adding Result-Code AVP failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			TRACE_DEBUG(FULL+1,"%sAdding EAP-Payload to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_eap_payload(diameap_sm, ans,&eap_i),
					{	TRACE_DEBUG(INFO,"%s Adding EAP-Payload AVP failed.",DIAMEAP_EXTENSION); goto s_end;})
			;

			LOG_N("%s Auth FAIL: %.*s",DIAMEAP_EXTENSION, diameap_sm->eap_sm.user.useridLength, diameap_sm->eap_sm.user.userid);
			
			CHECK_FCT_DO( diameap_send(rmsg),
					goto s_end)
			;
			diameap_sm->state = DIAMEAP_END;
			break;

		case DIAMEAP_SEND_SUCCESS:
			TRACE_DEBUG(FULL+1,"%sAdding User session AVPs to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO(diameap_add_user_sessions_avps(diameap_sm,ans),
					{	TRACE_DEBUG(INFO,"%s Adding user's session AVPs failed.",DIAMEAP_EXTENSION); goto s_end;})
			;

			if (diameap_sm->auth_request_val == AUTHORIZE_AUTHENTICATE)
			{
				TRACE_DEBUG(FULL+1,"%sAdding Authorization AVPs to Diameter-EAP-Answer.",DIAMEAP_EXTENSION);
				CHECK_FCT_DO(diameap_add_authorization_avps(diameap_sm,ans),
						{	TRACE_DEBUG(INFO,"%s Adding Authorization AVPs failed.",DIAMEAP_EXTENSION); goto s_end;});
			}
			TRACE_DEBUG(FULL+1,"%sAdding Result Code AVP to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_result_code(diameap_sm, ans, sess),
					{	TRACE_DEBUG(INFO,"%s Adding Result-Code AVP failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			TRACE_DEBUG(FULL+1,"%sAdding EAP-Payload to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_eap_payload(diameap_sm, ans,&eap_i),
					{	TRACE_DEBUG(INFO,"%s Adding EAP-Payload AVP failed.",DIAMEAP_EXTENSION); goto s_end;})
			;
			TRACE_DEBUG(FULL+1,"%sAdding EAP success AVPs AVPs to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO( diameap_add_eap_success_avps(diameap_sm, ans, &eap_i),
					goto s_end)
			;
			TRACE_DEBUG(FULL+1,"%sAdding Accounting-EAP-Auth-Method AVPs to Diameter-EAP-Answer.",DIAMEAP_EXTENSION)
			;
			CHECK_FCT_DO(diameap_add_accounting_eap_auth_method(diameap_sm, ans),
					{	TRACE_DEBUG(INFO,"%s Adding accounting AVP failed",DIAMEAP_EXTENSION); goto s_end;})
			;
			
			LOG_N("%s Auth Success: %.*s",DIAMEAP_EXTENSION, diameap_sm->eap_sm.user.useridLength, diameap_sm->eap_sm.user.userid);
			
			CHECK_FCT_DO( diameap_send(rmsg),
					goto s_end)
			;
			diameap_sm->state = DIAMEAP_END;
			break;

		case DIAMEAP_SEND_ERROR_MSG:
			diameap_sm->invalid_eappackets++;
			if (diameap_sm->invalid_eappackets
					== diameap_config->max_invalid_eap_packet)
			{
				diameap_sm->result_code = 4001;//DIAMETER_AUTHENTICATION_REJECTED
				TRACE_DEBUG(FULL,"%s Maximum permitted invalid EAP Packet reached. Diameter Authentication Rejected.",DIAMEAP_EXTENSION);
			}

			CHECK_FCT_DO(fd_msg_new_answer_from_req(fd_g_config->cnf_dict, rmsg, 0),
					goto s_end)
			;

			ans = *rmsg;
			CHECK_FCT_DO( diameap_add_avps(diameap_sm, ans,req),
					{	TRACE_DEBUG(INFO,"%s Adding AVPs to Diameter-EAP-Answer message failed.",DIAMEAP_EXTENSION);goto s_end;})
			;
			if ((non_fatal_error == TRUE) && (diameap_sm->result_code == 0))
			{
				diameap_sm->result_code = 1001;
			}

			if (diameap_sm->result_code == 1001)
			{
				CHECK_FCT_DO( diameap_add_eap_reissued_payload(ans,req), goto s_end);
			}

			if (diameap_sm->result_code == 5004)
			{
				CHECK_FCT_DO( fd_msg_avp_add( ans , MSG_BRW_LAST_CHILD, diameap_sm->failedavp ),goto s_end );
			}

			CHECK_FCT_DO( diameap_add_result_code(diameap_sm, ans, sess), goto s_end)
			;

			CHECK_FCT_DO( diameap_send(rmsg), goto s_end)
			;
			diameap_sm->state = DIAMEAP_IDLE;
			break;

		case DIAMEAP_END:
			break;

		case DIAMEAP_IDLE:
			break;
		}
	}

	diameap_free(diameap_sm);

	s_end: return 0;
}

int diameap_start_server(void)
{
	struct disp_when when;

	/*create handler for sessions */
	CHECK_FCT(fd_sess_handler_create(&diameap_server_reg, diameap_cli_sess_cleanup, NULL, NULL));

	/* Register the callback */
	memset(&when, 0, sizeof(when));
	when.command = dataobj_diameap_cmd;
	when.app = dataobj_diameap_app;

	/* Register the callback for EAP Application */
	CHECK_FCT(fd_disp_register(diameap_server_callback, DISP_HOW_CC, &when, NULL,
					&handle));

	if (handle == NULL)
	{
		TRACE_DEBUG(INFO, "%sCannot register the callback !!!",DIAMEAP_EXTENSION);
		return 1;
	}
	return 0;
}

int diameap_stop_server(void)
{
	CHECK_FCT(fd_sess_handler_destroy(&diameap_server_reg, NULL));
	CHECK_FCT(fd_disp_unregister(&handle, NULL));

	return 0;
}
