Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/app_radgw/rgwx_auth.c b/extensions/app_radgw/rgwx_auth.c
new file mode 100644
index 0000000..f78b97e
--- /dev/null
+++ b/extensions/app_radgw/rgwx_auth.c
@@ -0,0 +1,1961 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* 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:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * 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.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* 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.								 *
+*********************************************************************************************************/
+
+/* RADIUS Access-Request messages translation plugin */
+
+#include "rgw_common.h"
+
+/* Attributes missing from radius.h */
+#define RADIUS_ATTR_CHAP_PASSWORD	3
+#define RADIUS_ATTR_ARAP_PASSWORD	70
+
+/* Other constants we use */
+#define AI_NASREQ		1	/* Diameter NASREQ */
+#define AI_EAP			5	/* Diameter EAP application */
+#define CC_AA			265	/* AAR */
+#define CC_DIAMETER_EAP		268	/* DER */
+#define ACV_ART_AUTHORIZE_AUTHENTICATE	3	/* AUTHORIZE_AUTHENTICATE */
+#define ACV_OAP_RADIUS			1	/* RADIUS */
+#define ACV_ASS_STATE_MAINTAINED	0	/* STATE_MAINTAINED */
+#define ACV_ASS_NO_STATE_MAINTAINED	1	/* NO_STATE_MAINTAINED */
+
+/* The state we keep for this plugin */
+struct rgwp_config {
+	struct {
+		struct dict_object * ARAP_Password;		/* ARAP-Password */
+		struct dict_object * ARAP_Security;		/* ARAP-Security */
+		struct dict_object * ARAP_Security_Data;	/* ARAP-Security-Data */
+		struct dict_object * Auth_Application_Id;	/* Auth-Application-Id */
+		struct dict_object * Auth_Request_Type;		/* Auth-Request-Type */
+		struct dict_object * Authorization_Lifetime;	/* Authorization-Lifetime */
+		struct dict_object * Callback_Number;		/* Callback-Number */
+		struct dict_object * Called_Station_Id;		/* Called-Station-Id */
+		struct dict_object * Calling_Station_Id;	/* Calling-Station-Id */
+		struct dict_object * CHAP_Algorithm;		/* CHAP-Algorithm */
+		struct dict_object * CHAP_Auth;			/* CHAP-Auth */
+		struct dict_object * CHAP_Challenge;		/* CHAP-Challenge */
+		struct dict_object * CHAP_Ident;		/* CHAP-Ident */
+		struct dict_object * CHAP_Response;		/* CHAP-Response */
+		struct dict_object * Destination_Host;		/* Destination-Host */
+		struct dict_object * Destination_Realm;		/* Destination-Realm */
+		struct dict_object * Connect_Info;		/* Connect-Info */
+		struct dict_object * EAP_Payload;		/* EAP-Payload */
+		struct dict_object * Error_Message;		/* Error-Message */
+		struct dict_object * Error_Reporting_Host;	/* Error-Reporting-Host */
+		struct dict_object * Failed_AVP;		/* Failed-AVP */
+		struct dict_object * Framed_Compression;	/* Framed-Compression */
+		struct dict_object * Framed_IP_Address;		/* Framed-IP-Address */
+		struct dict_object * Framed_IP_Netmask;		/* Framed-IP-Netmask */
+		struct dict_object * Framed_Interface_Id;	/* Framed-Interface-Id */
+		struct dict_object * Framed_IPv6_Prefix;	/* Framed-IPv6-Prefix */
+		struct dict_object * Framed_MTU;		/* Framed-MTU */
+		struct dict_object * Framed_Protocol;		/* Framed-Protocol */
+		struct dict_object * Login_IP_Host;		/* Login-IP-Host */
+		struct dict_object * Login_IPv6_Host;		/* Login-IPv6-Host */
+		struct dict_object * Login_LAT_Group;		/* Login-LAT-Group */
+		struct dict_object * Login_LAT_Node;		/* Login-LAT-Node */
+		struct dict_object * Login_LAT_Port;		/* Login-LAT-Port */
+		struct dict_object * Login_LAT_Service;		/* Login-LAT-Service */
+		struct dict_object * NAS_Identifier;		/* NAS-Identifier */
+		struct dict_object * NAS_IP_Address;		/* NAS-IP-Address */
+		struct dict_object * NAS_IPv6_Address;		/* NAS-IPv6-Address */
+		struct dict_object * NAS_Port;			/* NAS-Port */
+		struct dict_object * NAS_Port_Id;		/* NAS-Port-Id */
+		struct dict_object * NAS_Port_Type;		/* NAS-Port-Type */
+		struct dict_object * Origin_AAA_Protocol;	/* Origin-AAA-Protocol */
+		struct dict_object * Origin_Host;		/* Origin-Host */
+		struct dict_object * Origin_Realm;		/* Origin-Realm */
+		struct dict_object * Originating_Line_Info;	/* Originating-Line-Info */
+		struct dict_object * Port_Limit;		/* Port-Limit */
+		struct dict_object * Re_Auth_Request_Type;	/* Re-Auth-Request-Type */
+		struct dict_object * Result_Code;		/* Result-Code */
+		struct dict_object * Service_Type;		/* Service-Type */
+		struct dict_object * Session_Id;		/* Session-Id */
+		struct dict_object * Session_Timeout;		/* Session-Timeout */
+		struct dict_object * State;			/* State */
+		struct dict_object * Tunneling;			/* Tunneling */
+		struct dict_object * Tunnel_Type;		/* Tunnel-Type */
+		struct dict_object * Tunnel_Medium_Type;	/* Tunnel-Medium-Type */
+		struct dict_object * Tunnel_Client_Endpoint;	/* Tunnel-Client-Endpoint */
+		struct dict_object * Tunnel_Server_Endpoint;	/* Tunnel-Server-Endpoint */
+		struct dict_object * Tunnel_Private_Group_Id;	/* Tunnel-Private-Group-Id */
+		struct dict_object * Tunnel_Preference;		/* Tunnel-Preference */
+		struct dict_object * Tunnel_Client_Auth_Id;	/* Tunnel-Client-Auth-Id */
+		struct dict_object * Tunnel_Server_Auth_Id;	/* Tunnel-Server-Auth-Id */
+		struct dict_object * User_Name;			/* User-Name */
+		struct dict_object * User_Password;		/* User-Password */
+		
+	} dict; /* cache of the dictionary objects we use */
+	struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */
+	char * confstr;
+	
+	int ignore_nai;
+};
+
+struct sess_state {
+	uint8_t req_auth[16];
+};
+
+
+/* Initialize the plugin */
+static int auth_conf_parse(char * confstr, struct rgwp_config ** state)
+{
+	struct rgwp_config * new;
+	struct dict_object * app;
+	
+	TRACE_ENTRY("%p %p", confstr, state);
+	CHECK_PARAMS( state );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
+	memset(new, 0, sizeof(struct rgwp_config));
+	
+	CHECK_FCT( fd_sess_handler_create( &new->sess_hdl, (void *)free, NULL, NULL ) );
+	new->confstr = confstr;
+	
+	if (confstr && strstr(confstr, "nonai"))
+		new->ignore_nai = 1;
+	
+	/* Resolve all dictionary objects we use */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Password", &new->dict.ARAP_Password, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security", &new->dict.ARAP_Security, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security-Data", &new->dict.ARAP_Security_Data, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Request-Type", &new->dict.Auth_Request_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Authorization-Lifetime", &new->dict.Authorization_Lifetime, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Number", &new->dict.Callback_Number, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Called-Station-Id", &new->dict.Called_Station_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Calling-Station-Id", &new->dict.Calling_Station_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Algorithm", &new->dict.CHAP_Algorithm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Auth", &new->dict.CHAP_Auth, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Challenge", &new->dict.CHAP_Challenge, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Ident", &new->dict.CHAP_Ident, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Response", &new->dict.CHAP_Response, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Connect-Info", &new->dict.Connect_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "EAP-Payload", &new->dict.EAP_Payload, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &new->dict.Error_Message, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &new->dict.Error_Reporting_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &new->dict.Failed_AVP, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Compression", &new->dict.Framed_Compression, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Address", &new->dict.Framed_IP_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Netmask", &new->dict.Framed_IP_Netmask, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Interface-Id", &new->dict.Framed_Interface_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Prefix", &new->dict.Framed_IPv6_Prefix, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-MTU", &new->dict.Framed_MTU, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Protocol", &new->dict.Framed_Protocol, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IP-Host", &new->dict.Login_IP_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IPv6-Host", &new->dict.Login_IPv6_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Group", &new->dict.Login_LAT_Group, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Node", &new->dict.Login_LAT_Node, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Port", &new->dict.Login_LAT_Port, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Service", &new->dict.Login_LAT_Service, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Identifier", &new->dict.NAS_Identifier, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IP-Address", &new->dict.NAS_IP_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IPv6-Address", &new->dict.NAS_IPv6_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port", &new->dict.NAS_Port, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Id", &new->dict.NAS_Port_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Type", &new->dict.NAS_Port_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-AAA-Protocol", &new->dict.Origin_AAA_Protocol, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Originating-Line-Info", &new->dict.Originating_Line_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Port-Limit", &new->dict.Port_Limit, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Re-Auth-Request-Type", &new->dict.Re_Auth_Request_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Service-Type", &new->dict.Service_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Timeout", &new->dict.Session_Timeout, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "State", &new->dict.State, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunneling", &new->dict.Tunneling, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Type", &new->dict.Tunnel_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Medium-Type", &new->dict.Tunnel_Medium_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Endpoint", &new->dict.Tunnel_Client_Endpoint, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Endpoint", &new->dict.Tunnel_Server_Endpoint, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Private-Group-Id", &new->dict.Tunnel_Private_Group_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Preference", &new->dict.Tunnel_Preference, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Auth-Id", &new->dict.Tunnel_Client_Auth_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Auth-Id", &new->dict.Tunnel_Server_Auth_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Password", &new->dict.User_Password, ENOENT) );
+	
+	/* This plugin provides the following Diameter authentication applications support: */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Network Access Server Application", &app, ENOENT) );
+	CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
+
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Extensible Authentication Protocol (EAP) Application", &app, ENOENT) );
+	CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
+	
+	*state = new;
+	return 0;
+}
+
+/* deinitialize */
+static void auth_conf_free(struct rgwp_config * state)
+{
+	TRACE_ENTRY("%p", state);
+	CHECK_PARAMS_DO( state, return );
+	CHECK_FCT_DO( fd_sess_handler_destroy( &state->sess_hdl, NULL ),  );
+	free(state);
+	return;
+}
+
+/* Handle an incoming RADIUS request */
+static int auth_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+{
+	int idx;
+	int got_id = 0;
+	int got_mac = 0;
+	int got_passwd = 0;
+	int got_eap = 0;
+	int got_empty_eap = 0;
+	const char * prefix = "Diameter/";
+	size_t pref_len;
+	os0_t dh = NULL;
+	size_t dh_len = 0;
+	os0_t dr = NULL;
+	size_t dr_len = 0;
+	os0_t si = NULL;
+	size_t si_len = 0;
+	os0_t un = NULL;
+	size_t un_len = 0;
+	size_t nattr_used = 0;
+	struct avp ** avp_tun = NULL, *avp = NULL;
+	union avp_value value;
+	struct session * sess;
+	
+	TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
+	CHECK_PARAMS(cs && rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
+	
+	pref_len = strlen(prefix);
+	
+	/*
+	   Guidelines:
+	     http://tools.ietf.org/html/rfc4005#section-9.1
+	     http://tools.ietf.org/html/rfc4072#section-6.1
+
+	   When a Translation Agent receives a RADIUS message, the following
+	   steps should be taken:
+
+	      -  If a Message-Authenticator attribute is present, the value MUST
+        	 be checked but not included in the Diameter message.  If it is
+        	 incorrect, the RADIUS message should be silently discarded.
+        	 The gateway system SHOULD generate and include a Message-
+        	 Authenticator in returned RADIUS responses.
+	             -> done in rgw_msg_auth_check
+
+	      -  The transport address of the sender MUST be checked against the
+        	 NAS identifying attributes.  See the description of NAS-
+        	 Identifier and NAS-IP-Address below.
+		     -> done in rgw_clients_check_origin
+
+	      -  The Translation Agent must maintain transaction state
+        	 information relevant to the RADIUS request, such as the
+        	 Identifier field in the RADIUS header, any existing RADIUS
+        	 Proxy-State attribute, and the source IP address and port
+        	 number of the UDP packet.  These may be maintained locally in a
+        	 state table or saved in a Proxy-Info AVP group.  A Diameter
+        	 Session-Id AVP value must be created using a session state
+        	 mapping mechanism.
+		     -> Identifier, source and port are saved along with the request,
+		        and associated with the session state.
+		     -> sub_echo_drop should handle the Proxy-State attribute (conf issue)
+
+	      -  The Diameter Origin-Host and Origin-Realm AVPs MUST be created
+        	 and added by using the information from an FQDN corresponding
+        	 to the NAS-IP-Address attribute (preferred if available),
+        	 and/or to the NAS-Identifier attribute.  (Note that the RADIUS
+        	 NAS-Identifier is not required to be an FQDN.)
+		     -> done in rgw_clients_create_origin.
+
+	      -  The response MUST have an Origin-AAA-Protocol AVP added,
+        	 indicating the protocol of origin of the message.
+		     -> what "response" ??? Added to the AAR / DER in this function.
+
+	      -  The Proxy-Info group SHOULD be added, with the local server's
+        	 identity specified in the Proxy-Host AVP.  This should ensure
+        	 that the response is returned to this system.
+		     -> We don't need this, answer is always routed here anyway.
+		     
+	      For EAP:
+	      
+	      o  RADIUS EAP-Message attribute(s) are translated to a Diameter
+		 EAP-Payload AVP.  If multiple RADIUS EAP-Message attributes are
+		 present, they are concatenated and translated to a single Diameter
+		 EAP-Payload AVP.
+		     -> concatenation done by radius_msg_get_eap
+
+	      -> the remaining is specific conversion rules
+	*/
+	
+	/* Check basic information is there, and also retrieve some attribute information */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+		uint8_t * attr_val = (uint8_t *)(attr + 1);
+		size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
+		
+		switch (attr->type) {
+			case RADIUS_ATTR_NAS_IP_ADDRESS:
+			case RADIUS_ATTR_NAS_IDENTIFIER:
+			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
+				got_id = 1;
+				break;
+			case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
+				got_mac = 1;
+				break;
+			case RADIUS_ATTR_EAP_MESSAGE:
+				got_eap = 1;
+				if (attr->length == 2)
+					got_empty_eap = 1;
+				break;
+			case RADIUS_ATTR_USER_PASSWORD:
+			case RADIUS_ATTR_CHAP_PASSWORD:
+			case RADIUS_ATTR_ARAP_PASSWORD:
+				got_passwd += 1;
+				break;
+				
+			/* Is there a State attribute with prefix "Diameter/" in the message? (in that case: Diameter/Destination-Host/Destination-Realm/Session-Id) */
+			/* NOTE: RFC4005 says "Origin-Host" here, but it's not coherent with the rules for answers. Destination-Host makes more sense */
+			case RADIUS_ATTR_STATE:
+				if ((attr_len > pref_len + 5 /* for the '/'s and non empty strings */ ) 
+					&& ! memcmp(attr_val, prefix, pref_len)) {
+					int i, start;
+
+					TRACE_DEBUG(ANNOYING, "Found a State attribute with '%s' prefix (attr #%d).", prefix, idx);
+
+					/* Now parse the value and check its content is valid. Unfortunately we cannot use strchr here since strings are not \0-terminated */
+
+					i = start = pref_len;
+					dh = attr_val + i;
+					for (; (i < attr_len - 2) && (attr_val[i] != '/'); i++) /* loop */;
+					if ( i >= attr_len - 2 ) continue; /* the attribute format is not good */
+					dh_len = i - start;
+
+					start = ++i;
+					dr = attr_val + i;
+					for (; (i < attr_len - 1) && (attr_val[i] != '/'); i++) /* loop */;
+					if ( i >= attr_len - 1 ) continue; /* the attribute format is not good */
+					dr_len = i - start;
+
+					i++;
+					si = attr_val + i;
+					si_len = attr_len - i;
+
+					TRACE_DEBUG(ANNOYING, "Attribute parsed successfully: DH:'%.*s' DR:'%.*s' SI:'%.*s'.", (int)dh_len, dh, (int)dr_len, dr, (int)si_len, si);
+					/* Remove from the message */
+					for (i = idx + 1; i < rad_req->attr_used; i++)
+						rad_req->attr_pos[i - 1] = rad_req->attr_pos[i];
+					rad_req->attr_used -= 1;
+					idx--;
+				}
+				break;
+		
+			case RADIUS_ATTR_USER_NAME:
+				TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", (int)attr_len, attr_len ? (char *)attr_val : "");
+				un = attr_val;
+				un_len = attr_len;
+				break;
+			
+		}
+	}
+	if (!got_id) {
+		TRACE_DEBUG(INFO, "RADIUS Access-Request did not contain a NAS IP or Identifier attribute, reject.");
+		return EINVAL;
+	}
+	/* [Note 1] An Access-Request that contains either a User-Password or
+	   CHAP-Password or ARAP-Password or one or more EAP-Message attributes
+	   MUST NOT contain more than one type of those four attributes.  If it
+	   does not contain any of those four attributes, it SHOULD contain a
+	   Message-Authenticator.  If any packet type contains an EAP-Message
+	   attribute it MUST also contain a Message-Authenticator.  A RADIUS
+	   server receiving an Access-Request not containing any of those four
+	   attributes and also not containing a Message-Authenticator attribute
+	   SHOULD silently discard it.  */
+	if (((got_eap + got_passwd) > 1) || (got_eap && !got_mac) || (!got_eap && !got_passwd && !got_mac)) {
+		TRACE_DEBUG(INFO, "RADIUS Access-Request not conform to RFC3579 sec 3.3 note 1, discard.");
+		return EINVAL;
+	}
+	
+	
+	
+	/*
+	      -  If the RADIUS request contained a State attribute and the
+        	 prefix of the data is "Diameter/", the data following the
+        	 prefix contains the Diameter Origin-Host/Origin-Realm/Session-
+        	 Id.  If no such attributes are present and the RADIUS command
+        	 is an Access-Request, a new Session-Id is created.  The
+        	 Session-Id is included in the Session-Id AVP.
+	*/
+	
+	/* Add the Destination-Realm AVP */
+	CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
+	if (dr) {
+		value.os.data = (unsigned char *)dr;
+		value.os.len = dr_len;
+	} else {
+		int i = 0;
+		if (un && ! cs->ignore_nai) {
+			/* Is there an '@' in the user name? We don't care for decorated NAI here */
+			for (i = un_len - 2; i > 0; i--) {
+				if (un[i] == '@') {
+					i++;
+					break;
+				}
+			}
+		}
+		if (i <= 0) {
+			/* Not found in the User-Name => we use the local domain of this gateway */
+			value.os.data = (uint8_t *)fd_g_config->cnf_diamrlm;
+			value.os.len  = fd_g_config->cnf_diamrlm_len;
+		} else {
+			value.os.data = un + i;
+			value.os.len  = un_len - i;
+		}
+	}
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	
+	/* Add the Destination-Host if found */
+	if (dh) {
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Host, 0, &avp ) );
+		value.os.data = (unsigned char *)dh;
+		value.os.len = dh_len;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	}
+	
+	/* Create the session if it is not already done */
+	{
+		os0_t sess_str = NULL;
+		size_t sess_strlen;
+		
+		if (si_len) {
+			/* We already have the Session-Id, just use it */
+			CHECK_FCT( fd_sess_fromsid_msg ( si, si_len, &sess, NULL) );
+		} else {
+			/* Create a new Session-Id string */
+			
+			DiamId_t fqdn;
+			size_t fqdnlen;
+			DiamId_t realm;
+			size_t realmlen;
+			
+			/* Get information on the RADIUS client */
+			CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdnlen, &realm, &realmlen) );
+			
+			/* If we have a user name, create the new session with it */
+			if (un) {
+				int len;
+				/* If not found, create a new Session-Id. Our format is: {fqdn;hi32;lo32;username;diamid} */
+				CHECK_MALLOC( sess_str = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) );
+				len = sprintf((char *)sess_str, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
+				CHECK_FCT( fd_sess_new(&sess, fqdn, fqdnlen, sess_str, len) );
+				free(sess_str);
+			} else {
+				/* We don't have enough information to create the Session-Id, the RADIUS message is probably invalid */
+				TRACE_DEBUG(INFO, "RADIUS Access-Request does not contain a User-Name attribute, rejecting.");
+				return EINVAL;
+			}	
+		}
+		
+		/* Now, add the Session-Id AVP at beginning of Diameter message */
+		CHECK_FCT( fd_sess_getsid(sess, &sess_str, &sess_strlen) );
+		TRACE_DEBUG(FULL, "[auth.rgwx] Translating new message for session '%s'...", sess_str);
+		
+		/* Now add this session in the message */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
+		value.os.data = sess_str;
+		value.os.len = sess_strlen;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+		CHECK_FCT( fd_msg_sess_set(*diam_fw, sess) );
+	}
+	
+	
+	/* Add the appropriate command code & Auth-Application-Id */
+	{
+		struct msg_hdr * header = NULL;
+		CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) );
+		header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
+		if (got_eap) {
+			header->msg_code = CC_DIAMETER_EAP;
+			header->msg_appl = AI_EAP;
+		} else {
+			header->msg_code = CC_AA;
+			header->msg_appl = AI_NASREQ;
+		}
+		
+		/* Add the Auth-Application-Id */
+		{
+			CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) );
+			value.i32 = header->msg_appl;
+			CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+			CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+		}
+	}
+	
+	/*  	The type of request is identified through the Auth-Request-Type AVP
+		[BASE].  The recommended value for most RADIUS interoperabily
+		situations is AUTHORIZE_AUTHENTICATE. */
+	
+	/* Add Auth-Request-Type AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Request_Type, 0, &avp ) );
+		value.i32 = ACV_ART_AUTHORIZE_AUTHENTICATE;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Add Origin-AAA-Protocol AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_AAA_Protocol, 0, &avp ) );
+		value.i32 = ACV_OAP_RADIUS;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Convert the EAP payload (concat RADIUS attributes) */
+	if (got_eap) {
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.EAP_Payload, 0, &avp ) );
+		
+		/*    o  An empty RADIUS EAP-Message attribute (with length 2) signifies
+			 EAP-Start, and it is translated to an empty EAP-Payload AVP. */
+		if (got_empty_eap) {
+			value.os.len = 0;
+			value.os.data = (uint8_t *)"";
+		} else {
+			CHECK_MALLOC( value.os.data = radius_msg_get_eap(rad_req, &value.os.len) );
+		}
+		
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Tunnel AVPs need some preparation */
+	/* Convert the attributes one by one */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+
+		switch (attr->type) {
+			
+			/* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
+			#define CONV2DIAM_STR( _dictobj_ )	\
+				CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) );			\
+				/* Create the AVP with the specified dictionary model */			\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				value.os.len = attr->length - sizeof(struct radius_attr_hdr);			\
+				value.os.data = (os0_t)(attr + 1);						\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				/* Add the AVP in the Diameter message. */					\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+			/* Same thing, for scalar AVPs of 32 bits */
+			#define CONV2DIAM_32B( _dictobj_ )	\
+				CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u32  = (v[0] << 24)						\
+					           | (v[1] << 16)						\
+					           | (v[2] <<  8)						\
+					           |  v[3] ;							\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+			/* And the 64b version */
+			#define CONV2DIAM_64B( _dictobj_ )	\
+				CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint64_t) );\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u64  = ((uint64_t)(v[0]) << 56)					\
+					           | ((uint64_t)(v[1]) << 48)					\
+					           | ((uint64_t)(v[2]) << 40)					\
+					           | ((uint64_t)(v[3]) << 32)					\
+					           | ((uint64_t)(v[4]) << 24)					\
+					           | ((uint64_t)(v[5]) << 16)					\
+					           | ((uint64_t)(v[6]) <<  8)					\
+					           |  (uint64_t)(v[7]) ;					\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+		/* RFC 2865 */	
+			/*
+			      -  The Destination-Realm AVP is created from the information found
+        			 in the RADIUS User-Name attribute.
+				 	-> done in rgw_clients_create_origin
+			*/
+			case RADIUS_ATTR_USER_NAME:
+				CONV2DIAM_STR( User_Name );
+				break;
+				
+			/*
+			      -  If the RADIUS User-Password attribute is present, the password
+        			 must be unencrypted by using the link's RADIUS shared secret.
+        			 The unencrypted value must be forwarded in a User-Password AVP
+        			 using Diameter security.
+			*/
+			case RADIUS_ATTR_USER_PASSWORD:
+				if ((attr->length - 2) % 16) {
+					TRACE_DEBUG(INFO, "Invalid length of User-Password attribute: %hhd", attr->length);
+					return EINVAL;
+				}
+				{
+					/* Decipher following this logic (refers to rfc2865#section-5.2 )
+					   b1 = MD5(S + RA)	p1 = c(1) xor b1
+					   b2 = MD5(S + c(1))   p2 = c(2) xor b2
+					   ...
+					*/
+					
+					uint8_t *ciph = (uint8_t *)(attr+1); 	/* c(i) */
+					size_t ciph_len = attr->length - 2;
+					uint8_t deciph[128];			/* pi */
+					size_t deciph_len = 0;
+					uint8_t * secret;			/* S */
+					size_t secret_len;
+					uint8_t hash[16];			/* b(i) */
+					const uint8_t *addr[2];
+					size_t len[2];
+					
+					/* Retrieve the shared secret */
+					CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
+					
+					/* Initial b1 = MD5(S + RA) */
+					addr[0] = secret;
+					len[0] = secret_len;
+					addr[1] = rad_req->hdr->authenticator;
+					len[1] = 16;
+					md5_vector(2, addr, len, hash);
+					
+					/* loop */
+					while (deciph_len < ciph_len) {
+						int i;
+						/* pi = c(i) xor bi */
+						for (i = 0; i < 16; i++)
+							deciph[deciph_len + i] = ciph[deciph_len + i] ^ hash[i];
+							/* do we have to remove the padding '\0's ? */
+						
+						/* b(i+1) = MD5(S + c(i) */
+						addr[1] = ciph + deciph_len;
+						md5_vector(2, addr, len, hash);
+						
+						deciph_len += 16;
+					}
+					
+					/* Now save this value in the AVP */
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.User_Password, 0, &avp ) );
+					value.os.data = &deciph[0];
+					value.os.len  = deciph_len;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				}
+				break;
+				
+
+			/*
+			      -  If the RADIUS CHAP-Password attribute is present, the Ident and
+        			 Data portion of the attribute are used to create the CHAP-Auth
+        			 grouped AVP.
+			*/
+			case RADIUS_ATTR_CHAP_PASSWORD:
+				CHECK_PARAMS( attr->length == 19 /* RFC 2865 */);
+				{
+					uint8_t * c = (uint8_t *)(attr + 1);
+					struct avp * chap_auth;
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Auth, 0, &chap_auth ) );
+					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, chap_auth) );
+
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Algorithm, 0, &avp ) );
+					value.u32 = 5; /* The only value defined currently... */
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
+					
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Ident, 0, &avp ) );
+					value.os.data = c;
+					value.os.len = 1;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
+					
+					c++;
+					
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Response, 0, &avp ) );
+					value.os.data = c;
+					value.os.len = attr->length - 3;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
+				}
+				break;
+				
+			case RADIUS_ATTR_NAS_IP_ADDRESS:
+				CONV2DIAM_STR( NAS_IP_Address );
+				break;
+				
+			case RADIUS_ATTR_NAS_PORT:
+				CONV2DIAM_32B( NAS_Port );
+				break;
+				
+			case RADIUS_ATTR_SERVICE_TYPE:
+				CONV2DIAM_32B( Service_Type );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_PROTOCOL:
+				CONV2DIAM_32B( Framed_Protocol );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IP_ADDRESS:
+				CONV2DIAM_STR( Framed_IP_Address );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IP_NETMASK:
+				CONV2DIAM_STR( Framed_IP_Netmask );
+				break;
+				
+			/* Framed-Routing never present in an Access-Request */
+			/* Filter-Id never present in an Access-Request */
+				
+			case RADIUS_ATTR_FRAMED_MTU:
+				CONV2DIAM_32B( Framed_MTU );
+				break;
+			
+			case RADIUS_ATTR_FRAMED_COMPRESSION:
+				CONV2DIAM_32B( Framed_Compression );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_IP_HOST:
+				CONV2DIAM_STR( Login_IP_Host );
+				break;
+				
+			/* Login-Service never present in an Access-Request */
+			/* Login-TCP-Port never present in an Access-Request */
+			/* Reply-Message never present in an Access-Request */
+			
+			case RADIUS_ATTR_CALLBACK_NUMBER:
+				CONV2DIAM_STR( Callback_Number );
+				break;
+				
+			/* Callback-Id never present in an Access-Request */
+			/* Framed-Route never present in an Access-Request */
+			/* Framed-IPX-Network never present in an Access-Request */
+				
+			case RADIUS_ATTR_STATE:
+				CONV2DIAM_STR( State );
+				break;
+			
+			/* Class never present in an Access-Request */
+				
+			case RADIUS_ATTR_VENDOR_SPECIFIC:
+				/* RFC 4005, Section 9.6 : 
+					   Systems that don't have vendor format knowledge MAY discard such
+					   attributes without knowing a suitable translation.
+					   
+					   [conversion rule in 9.6.2]
+				 */
+				if (attr->length >= 6) {
+					uint32_t vendor_id;
+					uint8_t * c = (uint8_t *)(attr + 1);
+					
+					vendor_id = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
+					c += 4;
+					
+					switch (vendor_id) {
+						
+						/* For the vendors we KNOW they follow the VSA recommended format, we convert following the rules of RFC4005 (9.6.2) */
+						case RADIUS_VENDOR_ID_MICROSOFT : /* RFC 2548 */
+						/* other vendors ? */
+						{
+							size_t left;
+							struct radius_attr_vendor *vtlv;
+							
+							left = attr->length - 6;
+							vtlv = (struct radius_attr_vendor *)c;
+						
+							while ((left >= 2) && (vtlv->vendor_length <= left)) {
+								/* Search our dictionary for corresponding Vendor's AVP */
+								struct dict_avp_request req;
+								struct dict_object * avp_model = NULL;
+								memset(&req, 0, sizeof(struct dict_avp_request));
+								req.avp_vendor = vendor_id;
+								req.avp_code = vtlv->vendor_type;
+								
+								CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &req, &avp_model, 0) );
+								if (!avp_model) {
+									TRACE_DEBUG(FULL, "Unknown attribute (vendor 0x%x, code 0x%x) ignored.", req.avp_vendor, req.avp_code);
+								} else {
+									CHECK_FCT( fd_msg_avp_new ( avp_model, 0, &avp ) );
+									value.os.len = vtlv->vendor_length - 2;
+									value.os.data = (unsigned char *)(vtlv + 1);
+									CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+									CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+								}
+								c += vtlv->vendor_length;
+								left -= vtlv->vendor_length;
+								vtlv = (struct radius_attr_vendor *)c;
+							}
+						}
+						break;
+						
+						/* Other vendors we KNOw how to convert the attributes would be added here... */
+						/* case RADIUS_VENDOR_ID_CISCO :
+							break; */
+						/* case RADIUS_VENDOR_ID_IETF : (extended RADIUS attributes)
+							break; */
+						
+						/* When we don't know, just discard the attribute... VSA are optional with regards to RADIUS anyway */
+						default:
+							/* do nothing */
+							TRACE_DEBUG(FULL, "VSA attribute from vendor %d discarded", vendor_id);
+							
+					}
+				}
+				break;
+				
+			/* Session-Timeout never present in an Access-Request */
+			/* Idle-Timeout never present in an Access-Request */
+			/* Termination-Action never present in an Access-Request */
+				
+			case RADIUS_ATTR_CALLED_STATION_ID:
+				CONV2DIAM_STR( Called_Station_Id );
+				break;
+			
+			case RADIUS_ATTR_CALLING_STATION_ID:
+				CONV2DIAM_STR( Calling_Station_Id );
+				break;
+			
+			case RADIUS_ATTR_NAS_IDENTIFIER:
+				CONV2DIAM_STR( NAS_Identifier );
+				break;
+			
+			/* Proxy-State is handled by echo_drop.rgwx plugin, we ignore it here */
+			
+			case RADIUS_ATTR_LOGIN_LAT_SERVICE:
+				CONV2DIAM_STR( Login_LAT_Service );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_NODE:
+				CONV2DIAM_STR( Login_LAT_Node );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_GROUP:
+				CONV2DIAM_STR( Login_LAT_Group );
+				break;
+			
+			/* Framed-AppleTalk-Link never present in an Access-Request */
+			/* Framed-AppleTalk-Network never present in an Access-Request */
+			/* Framed-AppleTalk-Zone never present in an Access-Request */
+			
+			case RADIUS_ATTR_CHAP_CHALLENGE:
+				CONV2DIAM_STR( CHAP_Challenge );
+				break;
+			
+			case RADIUS_ATTR_NAS_PORT_TYPE:
+				CONV2DIAM_32B( NAS_Port_Type );
+				break;
+			
+			case RADIUS_ATTR_PORT_LIMIT:
+				CONV2DIAM_32B( Port_Limit );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_PORT:
+				CONV2DIAM_STR( Login_LAT_Port );
+				break;
+			
+			
+		/* RFC 3162 */	
+			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
+				CONV2DIAM_STR( NAS_IPv6_Address );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_INTERFACE_ID:
+				CONV2DIAM_64B( Framed_Interface_Id );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IPV6_PREFIX:
+				CONV2DIAM_STR( Framed_IPv6_Prefix );
+				break;
+				
+			case RADIUS_ATTR_LOGIN_IPV6_HOST:
+				CONV2DIAM_STR( Login_IPv6_Host );
+				break;
+				
+			/* Framed-IPv6-Route never present in an Access-Request */
+			/* Framed-IPv6-Pool never present in an Access-Request */
+
+
+		/* RFC 2868 */
+			/* Prepare the top-level Tunneling AVP for each tag values, as needed, and add to the Diameter message.
+				This macro is called when an AVP is added inside the group, so we will not have empty grouped AVPs */
+			#define AVP_TUN_PREPARE() {										\
+						if (avp_tun == NULL) {								\
+							CHECK_MALLOC( avp_tun = calloc(sizeof(struct avp *), 32 ) );		\
+						}										\
+						tag = *(uint8_t *)(attr + 1);							\
+						if (tag > 0x1F) tag = 0;							\
+						if (avp_tun[tag] == NULL) {							\
+							CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) );	\
+							CHECK_FCT( fd_msg_avp_add (*diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]));\
+						}										\
+					}
+			
+			/* Convert an attribute to an OctetString AVP and add inside the Tunneling AVP corresponding to the tag */
+			#define CONV2DIAM_TUN_STR( _dictobj_ ) {						\
+				uint8_t tag;									\
+				CHECK_PARAMS( attr->length >= 3);						\
+				AVP_TUN_PREPARE();								\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				value.os.len = attr->length - (tag ? 3 : 2);					\
+				value.os.data = ((unsigned char *)(attr + 1)) + (tag ? 1 : 0);			\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
+				}
+				
+			/* Convert an attribute to a scalar AVP and add inside the Tunneling AVP corresponding to the tag */
+			#define CONV2DIAM_TUN_24B( _dictobj_ ) {						\
+				uint8_t tag;									\
+				CHECK_PARAMS( attr->length == 6);						\
+				AVP_TUN_PREPARE();								\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u32 = (v[1] << 16) | (v[2] <<8) | v[3] ;				\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
+				}
+
+			/*
+			      -  If the RADIUS message contains Tunnel information [RADTunnels],
+        			 the attributes or tagged groups should each be converted to a
+        			 Diameter Tunneling Grouped AVP set.  If the tunnel information
+        			 contains a Tunnel-Password attribute, the RADIUS encryption
+        			 must be resolved, and the password forwarded, by using Diameter
+        			 security methods.
+				    -> If the RADIUS message does not use properly the Tag info, result is unpredictable here.. 
+			*/
+			case RADIUS_ATTR_TUNNEL_TYPE:
+				CONV2DIAM_TUN_24B( Tunnel_Type );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
+				CONV2DIAM_TUN_24B( Tunnel_Medium_Type );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT:
+				CONV2DIAM_TUN_STR( Tunnel_Client_Endpoint );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT:
+				CONV2DIAM_TUN_STR( Tunnel_Server_Endpoint );
+				break;
+			
+			/* Tunnel-Password never present in an Access-Request */
+
+			case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Private_Group_Id );
+				break;
+			
+			/* Tunnel-Assignment-ID never present in an Access-Request */
+			
+			case RADIUS_ATTR_TUNNEL_PREFERENCE:
+				CONV2DIAM_TUN_24B( Tunnel_Preference );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Client_Auth_Id );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Server_Auth_Id );
+				break;
+			
+			
+		/* RFC 2869 */	
+			case RADIUS_ATTR_ARAP_PASSWORD:
+				CONV2DIAM_STR( ARAP_Password );
+				break;
+				
+			/* ARAP-Features never present in an Access-Request */
+			/* ARAP-Zone-Access never present in an Access-Request */
+			
+			case RADIUS_ATTR_ARAP_SECURITY:
+				CONV2DIAM_32B( ARAP_Security );
+				break;
+			
+			case RADIUS_ATTR_ARAP_SECURITY_DATA:
+				CONV2DIAM_STR( ARAP_Security_Data );
+				break;
+			
+			/* Password-Retry never present in an Access-Request */
+			/* Prompt never present in an Access-Request */
+			
+			case RADIUS_ATTR_CONNECT_INFO:
+				CONV2DIAM_STR( Connect_Info );
+				break;
+			
+			/* Configuration-Token never present in an Access-Request */
+			/* ARAP-Challenge-Response never present in an Access-Request */
+			/* Acct-Interim-Interval never present in an Access-Request */
+			
+			case RADIUS_ATTR_NAS_PORT_ID:
+				CONV2DIAM_STR( NAS_Port_Id );
+				break;
+			
+			/* Framed-Pool never present in an Access-Request */
+			
+				
+		/* RFC 2869 / 3579 */	
+			case RADIUS_ATTR_ORIGINATING_LINE_INFO:
+				CONV2DIAM_STR( Originating_Line_Info );
+				break;
+				
+			case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
+			case RADIUS_ATTR_EAP_MESSAGE:
+				/* It was already handled, just remove the attribute */
+				break;
+				
+		/* Default */		
+			default: /* unknown attribute */
+				/* We just keep the attribute in the RADIUS message */
+				rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx];
+		}
+	}
+	
+	/* Destroy tunnel pointers (if we used it) */
+	free(avp_tun);
+	
+	/* Update the radius message to remove all handled attributes */
+	rad_req->attr_used = nattr_used;
+
+	/* Store the request identifier in the session (if provided) */
+	{
+		struct sess_state  *st;
+		CHECK_MALLOC(st = malloc(sizeof(struct sess_state)));
+		memcpy(st->req_auth, &rad_req->hdr->authenticator[0], 16);
+		
+		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, sess, &st ) );
+	}
+	
+	return 0;
+}
+
+static int auth_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+{
+	struct msg_hdr * hdr;
+	struct avp *avp, *next, *avp_x, *avp_y, *aoh;
+	struct avp_hdr *ahdr, *oh;
+	uint8_t buf[254]; /* to store some attributes values (with final '\0') */
+	size_t sz;
+	int ta_set = 0;
+	int no_str = 0; /* indicate if an STR is required for this server */
+	uint8_t	tuntag = 0;
+	struct sess_state  *st;
+	int error_cause = 0;
+	struct session * sess;
+	os0_t sid = NULL;
+	size_t sidlen;
+	
+	TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
+	CHECK_PARAMS(cs && diam_ans && *diam_ans && rad_fw && *rad_fw);
+	
+	/* Retrieve the request identified which was stored in the session */
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
+	if (sess) {
+		CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, sess, &st ) );
+		CHECK_FCT( fd_sess_getsid(sess, &sid, &sidlen) );
+	} /* else ? */
+	
+	/*	
+	      -  If the Diameter Command-Code is set to AA-Answer and the
+        	 Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH, the
+        	 gateway must send a RADIUS Access-Challenge.  This must have
+        	 the Origin-Host, Origin-Realm, and Diameter Session-Id AVPs
+        	 encapsulated in the RADIUS State attribute, with the prefix
+        	 "Diameter/", concatenated in the above order separated with "/"
+        	 characters, in UTF-8 [UTF-8].  This is necessary to ensure that
+        	 the Translation Agent receiving the subsequent RADIUS Access-
+        	 Request will have access to the Session Identifier and be able
+        	 to set the Destination-Host to the correct value.
+		 	-> done here below
+		 
+	      -  If the Command-Code is set to AA-Answer, the Diameter Session-
+        	 Id AVP is saved in a new RADIUS Class attribute whose format
+        	 consists of the string "Diameter/" followed by the Diameter
+        	 Session Identifier.  This will ensure that the subsequent
+        	 Accounting messages, which could be received by any Translation
+        	 Agent, would have access to the original Diameter Session
+        	 Identifier.
+		 	-> done here but only for Access-Accept messages (Result-Code = success)
+	 */
+	
+	/* MACROS to help in the process: convert AVP data to RADIUS attributes. */
+	/* Control large attributes:  _trunc_ = 0 => error; _trunc_ = 1 => truncate; _trunc = 2 => create several attributes */
+	#define CONV2RAD_STR( _attr_, _data_, _len_, _trunc_)	{					\
+		size_t __l = (size_t)(_len_);								\
+		size_t __off = 0;									\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		if ((_trunc_) == 0) {									\
+			CHECK_PARAMS( __l <= 253 );							\
+		}											\
+		if ((__l > 253) && (_trunc_ == 1)) {							\
+			TRACE_DEBUG(INFO, "[auth.rgwx] AVP truncated in "#_attr_);			\
+			__l = 253;									\
+		}											\
+		do {											\
+			size_t __w = (__l > 253) ? 253 : __l;						\
+			CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w));	\
+			__off += __w;									\
+			__l   -= __w;									\
+		} while (__l);										\
+	}
+
+	#define CONV2RAD_32B( _attr_, _data_)	{							\
+		uint32_t __v = htonl((uint32_t)(_data_));						\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+	}
+
+	#define CONV2RAD_64B( _attr_, _data_)	{							\
+		uint64_t __v = htonll((uint64_t)(_data_));						\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+	}
+
+	/* Search the different AVPs we handle here */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Host, &aoh) );
+	CHECK_FCT( fd_msg_avp_hdr ( aoh, &oh ) );
+
+	/* Check the Diameter error code */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) );
+	ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+	switch (ahdr->avp_value->u32) {
+		case ER_DIAMETER_MULTI_ROUND_AUTH:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_CHALLENGE;
+			break;
+		case ER_DIAMETER_SUCCESS:
+		case ER_DIAMETER_LIMITED_SUCCESS:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_ACCEPT;
+			break;
+		
+		default:
+			/* Can we convert the value to a natural Error-Cause ? */
+			switch (ahdr->avp_value->u32) {
+				case ER_DIAMETER_AVP_UNSUPPORTED:
+					error_cause = 401; /* Unsupported Attribute */
+					break;
+					
+				case ER_DIAMETER_MISSING_AVP:
+					error_cause = 402; /* Missing Attribute */
+					break;
+					
+				case ER_DIAMETER_UNABLE_TO_COMPLY:
+					error_cause = 404; /* Invalid Request */
+					break;
+					
+				case ER_DIAMETER_APPLICATION_UNSUPPORTED:
+					error_cause = 405; /* Unsupported Service */
+					break;
+					
+				case ER_DIAMETER_COMMAND_UNSUPPORTED:
+					error_cause = 406; /* Unsupported Extension */
+					break;
+					
+				case ER_DIAMETER_INVALID_AVP_VALUE:
+					error_cause = 407; /* Invalid Attribute Value */
+					break;
+					
+				case ER_DIAMETER_AVP_NOT_ALLOWED:
+					error_cause = 501; /* Administratively Prohibited */
+					break;
+					
+				case ER_DIAMETER_REALM_NOT_SERVED:
+				case ER_DIAMETER_LOOP_DETECTED:
+				case ER_DIAMETER_UNKNOWN_PEER:
+				case ER_DIAMETER_UNABLE_TO_DELIVER:
+					error_cause = 502; /* Request Not Routable (Proxy) */
+					break;
+					
+				case ER_DIAMETER_UNKNOWN_SESSION_ID:
+					error_cause = 503; /* Session Context Not Found */
+					break;
+					
+				case ER_DIAMETER_TOO_BUSY:
+				case ER_DIAMETER_OUT_OF_SPACE:
+					error_cause = 506; /* Resources Unavailable */
+					break;
+					
+#if 0
+			/* remaining Diameter Result-Code & RADIUS Error-Cause */
+				case ER_DIAMETER_REDIRECT_INDICATION:
+				case ER_DIAMETER_INVALID_HDR_BITS:
+				case ER_DIAMETER_INVALID_AVP_BITS:
+				case ER_DIAMETER_AUTHENTICATION_REJECTED:
+				case ER_ELECTION_LOST:
+				case ER_DIAMETER_AUTHORIZATION_REJECTED:
+				case ER_DIAMETER_RESOURCES_EXCEEDED:
+				case ER_DIAMETER_CONTRADICTING_AVPS:
+				case ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES
+				case ER_DIAMETER_NO_COMMON_APPLICATION:
+				case ER_DIAMETER_UNSUPPORTED_VERSION:
+				case ER_DIAMETER_INVALID_BIT_IN_HEADER:
+				case ER_DIAMETER_INVALID_AVP_LENGTH:
+				case ER_DIAMETER_INVALID_MESSAGE_LENGTH:
+				case ER_DIAMETER_INVALID_AVP_BIT_COMBO:
+				case ER_DIAMETER_NO_COMMON_SECURITY:
+					error_cause = 403; /* NAS Identification Mismatch */
+					error_cause = 504; /* Session Context Not Removable */
+					error_cause = 505; /* Other Proxy Processing Error */
+					error_cause = 507; /* Request Initiated */
+					error_cause = 508; /* Multiple Session Selection Unsupported */
+#endif /* 0 */
+			}
+			/* In any case, the following is processed: */
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_REJECT;
+			fd_log_debug("[auth.rgwx] Received Diameter answer with error code '%d' from server '%.*s', session %.*s, translating into Access-Reject",
+					ahdr->avp_value->u32, 
+					(int)oh->avp_value->os.len, oh->avp_value->os.data,
+					(int)sidlen, sid);
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Message, &avp_x) );
+			if (avp_x) {
+				CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+				fd_log_debug("[auth.rgwx]   Error-Message content: '%.*s'",
+						(int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
+			}
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Reporting_Host, &avp_x) );
+			if (avp_x) {
+				CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+				fd_log_debug("[auth.rgwx]   Error-Reporting-Host: '%.*s'",
+						(int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
+			}
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Failed_AVP, &avp_x) );
+			if (avp_x) {
+				fd_log_debug("[auth.rgwx]   Failed-AVP was included in the message.");
+				/* Dump its content ? */
+			}
+	}
+	/* Remove this Result-Code avp */
+	CHECK_FCT( fd_msg_free( avp ) );
+	
+	/* Creation of the State or Class attribute with session information */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Realm, &avp) );
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+	
+	/* Now, save the session-id and eventually server info in a STATE or CLASS attribute */
+	if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_CHALLENGE) {
+		if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), "Diameter/%.*s/%.*s/%.*s", 
+				(int)oh->avp_value->os.len,  (char *)oh->avp_value->os.data,
+				(int)ahdr->avp_value->os.len,  (char *)ahdr->avp_value->os.data,
+				(int)sidlen, (char *)sid))) {
+			TRACE_DEBUG(INFO, "Data truncated in State attribute: %s", buf);
+		}
+		CONV2RAD_STR(RADIUS_ATTR_STATE, buf, sz, 0);
+	}
+
+	if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		/* Add the Session-Id */
+		if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), "Diameter/%.*s", 
+				(int)sidlen, sid))) {
+			TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf);
+		}
+		CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, sz, 0);
+	}
+	
+	/* Unlink the Origin-Realm now; the others are unlinked at the end of this function */
+	CHECK_FCT( fd_msg_free( avp ) );
+	
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Session_Timeout, &avp) );
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Authorization_Lifetime, &avp_x) );
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Re_Auth_Request_Type, &avp_y) );
+	/*	
+	   When translating a Diameter AA-Answer (with successful result code)
+	   to RADIUS Access-Accept that contains a Session-Timeout or
+	   Authorization-Lifetime AVP, take the following steps:
+	   
+	      -  If the Diameter message contains a Session-Timeout AVP but no
+        	 Authorization-Lifetime AVP, translate it to a Session-Timeout
+        	 attribute (not a Termination-Action).
+	*/
+	if ((avp != NULL) && (avp_x == NULL)) {
+		CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+		CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
+	}
+	
+	/*	
+	      -  If the Diameter message contains an Authorization-Lifetime AVP
+        	 but no Session-Timeout AVP, translate it to a Session-Timeout
+        	 attribute and a Termination-Action set to AA-REQUEST.  (Remove
+        	 Authorization-Lifetime and Re-Auth-Request-Type.)
+	*/
+	if ((avp == NULL) && (avp_x != NULL)) {
+		CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+		CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
+		CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
+		ta_set = 1;
+	}
+	
+	/*	
+	      -  If the Diameter message has both, the Session-Timeout must be
+        	 greater than or equal to the Authorization-Lifetime (required
+        	 by [BASE]).  Translate it to a Session-Timeout value (with
+        	 value from Authorization-Lifetime AVP, the smaller one) and
+        	 with the Termination-Action set to AA-REQUEST.  (Remove the
+        	 Authorization-Lifetime and Re-Auth-Request-Type.)
+	*/
+	if ((avp != NULL) && (avp_x != NULL)) {
+		CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+		CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
+		CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
+		ta_set = 1;
+	}
+	
+	/*  -> Not too sure about Auth-Grace-Period... we'll just discard it for now */
+	
+	if (avp) {
+		CHECK_FCT( fd_msg_free( avp ) );
+	}
+	if (avp_x) {
+		CHECK_FCT( fd_msg_free( avp_x ) );
+	}
+	if (avp_y) {
+		CHECK_FCT( fd_msg_free( avp_y ) );
+	}
+	
+	
+	/*
+	      -  If a Proxy-State attribute was present in the RADIUS request,
+        	 the same attribute is added in the response.  This information
+        	 may be found in the Proxy-Info AVP group, or in a local state
+        	 table.
+		 	-> handled by sub_echo_drop
+
+	      -  If state information regarding the RADIUS request was saved in
+        	 a Proxy-Info AVP or local state table, the RADIUS Identifier
+        	 and UDP IP Address and port number are extracted and used in
+        	 issuing the RADIUS reply.
+		 	-> was saved with the full request
+	*/
+	
+	
+	/* Now loop in the list of AVPs and convert those that we know how */
+	CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
+	
+	while (next) {
+		int handled = 1;
+		avp = next;
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL) );
+		
+		CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+		
+		if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+			switch (ahdr->avp_code) {
+		/* In case of Diameter error, include the Reply-Message attribute */
+				case DIAM_ATTR_ERROR_MESSAGE:
+					CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_ERROR_REPORTING_HOST:
+					{
+						char buf[254];
+						int bsz = snprintf(buf, sizeof(buf), "Error-Reporting-Host: %*s", (int)(ahdr->avp_value->os.len), ahdr->avp_value->os.data);
+						CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, (uint8_t *)buf, bsz, 2);
+					}
+					break;
+				
+				case DIAM_ATTR_FAILED_AVP:
+					{
+						struct avp * favp;
+						CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &favp, NULL) );
+						if (favp) {
+							char buf[254];
+							int bsz;
+							struct dict_object * favp_model;
+							
+							CHECK_FCT( fd_msg_model(favp, &favp_model) );
+							if (favp_model) {
+								struct dict_avp_data fadata;
+								CHECK_FCT( fd_dict_getval(favp_model, &fadata) );
+								bsz = snprintf(buf, sizeof(buf), "Failed-AVP: %s", fadata.avp_name);
+							} else {
+								struct avp_hdr * favp_hdr;
+								CHECK_FCT( fd_msg_avp_hdr ( favp, &favp_hdr ) );
+								bsz = snprintf(buf, sizeof(buf), "Failed-AVP: code %u, vendor %u", favp_hdr->avp_code, favp_hdr->avp_vendor);
+							}
+							CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, (uint8_t *)buf, bsz, 2);
+						}
+					}
+					break;
+					
+		/* RFC 4005 (AVP in the order of the AA-Request/Answer AVP Table) */
+				case DIAM_ATTR_ACCT_INTERIM_INTERVAL:
+					CONV2RAD_32B(RADIUS_ATTR_ACCT_INTERIM_INTERVAL, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_ARAP_CHALLENGE_RESPONSE:
+					CONV2RAD_STR(RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_ARAP_FEATURES:
+					CONV2RAD_STR(RADIUS_ATTR_ARAP_FEATURES, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				/* ARAP-Password is not present in answers */
+					
+				case DIAM_ATTR_ARAP_SECURITY:
+					CONV2RAD_32B(RADIUS_ATTR_ARAP_SECURITY, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_ARAP_SECURITY_DATA:
+					CONV2RAD_STR(RADIUS_ATTR_ARAP_SECURITY_DATA, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_ARAP_ZONE_ACCESS:
+					CONV2RAD_32B(RADIUS_ATTR_ARAP_ZONE_ACCESS, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_AUTH_APPLICATION_ID:
+					/* We just remove this AVP */
+					break;
+					
+				case DIAM_ATTR_AUTH_GRACE_PERIOD:
+					/* We just remove this AVP (?) */
+					break;
+				
+				case DIAM_ATTR_AUTH_REQUEST_TYPE:
+					/* We only check the value */
+					if (ahdr->avp_value->u32 != 3) {
+						fd_log_debug("[auth.rgwx] Received Diameter answer with Auth-Request-Type set to %d (%s) from server %.*s, session %.*s."
+								"  This may cause interoperability problems with RADIUS.",
+								ahdr->avp_value->u32,
+								(ahdr->avp_value->u32 == 1) ? "AUTHENTICATE_ONLY" :
+									((ahdr->avp_value->u32 == 2) ? "AUTHORIZE_ONLY" : "???"),
+								(int)oh->avp_value->os.len, oh->avp_value->os.data, 
+								(int)sidlen, sid);
+					}
+					break;
+				
+				case DIAM_ATTR_AUTH_SESSION_STATE:
+					if ((!ta_set) && (ahdr->avp_value->u32 == ACV_ASS_STATE_MAINTAINED)) {
+						CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
+					}
+					
+					if (ahdr->avp_value->u32 == ACV_ASS_NO_STATE_MAINTAINED) {
+						no_str = 1;
+					}
+					break;
+					
+				/* Authorization-Lifetime already handled */
+				
+				case DIAM_ATTR_CALLBACK_ID:
+					CONV2RAD_STR(RADIUS_ATTR_CALLBACK_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+				case DIAM_ATTR_CALLBACK_NUMBER:
+					CONV2RAD_STR(RADIUS_ATTR_CALLBACK_NUMBER, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+				/* Called-Station-Id is not present in answers */
+				/* Calling-Station-Id is not present in answers */
+				/* CHAP-Auth is not present in answers */
+				/* CHAP-Challenge is not present in answers */
+					
+				case DIAM_ATTR_CLASS:
+					CONV2RAD_STR(RADIUS_ATTR_CLASS, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+				
+				case DIAM_ATTR_CONFIGURATION_TOKEN:
+					/* We might as well remove it since it's not supposed to be sent to the NAS... */
+					CONV2RAD_STR(RADIUS_ATTR_CONFIGURATION_TOKEN, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+				
+				/* Connect-Info is not present in answers */
+				
+				case DIAM_ATTR_FILTER_ID:
+					CONV2RAD_STR(RADIUS_ATTR_FILTER_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_LINK:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_APPLETALK_LINK, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_NETWORK:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_APPLETALK_NETWORK, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_ZONE:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_APPLETALK_ZONE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_COMPRESSION:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_COMPRESSION,  ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_INTERFACE_ID:
+					CONV2RAD_64B(RADIUS_ATTR_FRAMED_INTERFACE_ID,  ahdr->avp_value->u64);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IP_ADDRESS:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IP_ADDRESS,  ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IP_NETMASK:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IP_NETMASK, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_PREFIX:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_PREFIX, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_POOL:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_POOL, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_ROUTE:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_ROUTE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPX_NETWORK:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_IPX_NETWORK, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_MTU:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_MTU, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_POOL:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_POOL, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_PROTOCOL:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_PROTOCOL, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_ROUTE:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_ROUTE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_ROUTING:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_ROUTING, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_IDLE_TIMEOUT:
+					CONV2RAD_32B(RADIUS_ATTR_IDLE_TIMEOUT, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_LOGIN_IP_HOST:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_IP_HOST, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_LOGIN_IPV6_HOST:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_IPV6_HOST, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_GROUP:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_GROUP, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_NODE:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_NODE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_PORT:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_PORT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_SERVICE:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_SERVICE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_SERVICE:
+					CONV2RAD_32B(RADIUS_ATTR_LOGIN_SERVICE, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_LOGIN_TCP_PORT:
+					CONV2RAD_32B(RADIUS_ATTR_LOGIN_TCP_PORT, ahdr->avp_value->u32);
+					break;
+					
+			/*
+			      -							        If the
+        			 Multi-Round-Time-Out AVP is present, the value of the AVP MUST
+        			 be inserted in the RADIUS Session-Timeout AVP.
+
+			      o  As described in [NASREQ], if the Result-Code AVP set to
+				 DIAMETER_MULTI_ROUND_AUTH and the Multi-Round-Time-Out AVP is
+				 present, it is translated to the RADIUS Session-Timeout attribute.
+			*/
+				case DIAM_ATTR_MULTI_ROUND_TIMEOUT:
+					CONV2RAD_32B(RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_NAS_FILTER_RULE:
+					/* This is not translatable to RADIUS */
+					fd_log_debug("[auth.rgwx] Received Diameter answer with non-translatable NAS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.",
+							(int)oh->avp_value->os.len, oh->avp_value->os.data,
+							(int)sidlen, sid);
+					handled = 0;
+					break;
+					
+				/* NAS-Identifier is not present in answers */
+				/* NAS-IP-Address is not present in answers */
+				/* NAS-IPv6-Address is not present in answers */
+				/* NAS-Port is not present in answers */
+				/* NAS-Port-Id is not present in answers */
+				/* NAS-Port-Type is not present in answers */
+				
+				case DIAM_ATTR_ORIGIN_AAA_PROTOCOL:
+					/* We just remove this AVP */
+					break;
+					
+				/* Originating-Line-Info is not present in answers */
+				
+				case DIAM_ATTR_PASSWORD_RETRY:
+					CONV2RAD_32B(RADIUS_ATTR_PASSWORD_RETRY, ahdr->avp_value->u32);
+					break;
+				
+				case DIAM_ATTR_PORT_LIMIT:
+					CONV2RAD_32B(RADIUS_ATTR_PORT_LIMIT, ahdr->avp_value->u32);
+					break;
+				
+				case DIAM_ATTR_PROMPT:
+					CONV2RAD_32B(RADIUS_ATTR_PROMPT, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_QOS_FILTER_RULE:
+					/* This is not translatable to RADIUS */
+					fd_log_debug("[auth.rgwx] Received Diameter answer with non-translatable QoS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.",
+							(int)oh->avp_value->os.len, oh->avp_value->os.data,
+							(int)sidlen, sid);
+					handled = 0;
+					break;
+					
+				/* Re-Auth-Request-Type already handled */
+				
+				case DIAM_ATTR_REPLY_MESSAGE:
+					CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_SERVICE_TYPE:
+					CONV2RAD_32B(RADIUS_ATTR_SERVICE_TYPE, ahdr->avp_value->u32);
+					break;
+				
+				case DIAM_ATTR_STATE:
+					CONV2RAD_STR(RADIUS_ATTR_STATE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_TUNNELING:
+					{
+#define CONV2RAD_TUN_STR( _attr_, _data_, _len_, _trunc_)	{				\
+	size_t __l = (size_t)(_len_);								\
+	size_t __w = (__l > 252) ? 252 : __l;							\
+	size_t __off = 0;									\
+	if ((_trunc_) == 0) {									\
+		CHECK_PARAMS( __l <= 252 );							\
+	}											\
+	if ((__l > 252) && (_trunc_ == 1)) {							\
+		TRACE_DEBUG(FULL, "Attribute truncated!");					\
+		__l = 252;									\
+	}											\
+	buf[0] = tuntag;									\
+	memcpy(&buf[1], (_data_), __w);								\
+	CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), &buf[0], __w + 1));			\
+	while (__l -= __w) {									\
+		__off += __w;									\
+		__w = (__l > 253) ? 253 : __l;							\
+		CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w));	\
+	}											\
+}
+
+#define CONV2RAD_TUN_32B( _attr_, _data_)	{						\
+	uint32_t __v = htonl((uint32_t)(_data_) | (tuntag << 24));				\
+	CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+}
+						struct avp *inavp, *innext;
+						tuntag++;
+						CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &innext, NULL) );
+						while (innext) {
+							inavp = innext;
+							CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &innext, NULL) );
+							CHECK_FCT( fd_msg_avp_hdr ( inavp, &ahdr ) );
+							
+							if ( ! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+								switch (ahdr->avp_code) {
+									case DIAM_ATTR_TUNNEL_TYPE:
+										CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_TYPE, ahdr->avp_value->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_MEDIUM_TYPE:
+										CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, ahdr->avp_value->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_CLIENT_ENDPOINT:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_SERVER_ENDPOINT:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PREFERENCE:
+										CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_PREFERENCE, ahdr->avp_value->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_SERVER_AUTH_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_ASSIGNMENT_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PASSWORD:
+										{
+											/* This AVP must be encoded for RADIUS (similar to radius_msg_add_attr_user_password)
+											    0                   1                   2                   3
+											    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+											   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+											   |     Type      |    Length     |     Tag       |   Salt
+											   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+											      Salt (cont)  |   String ...
+											   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+											*/
+											size_t pos;
+											int i;
+											uint8_t * secret;	/* S */
+											size_t secret_len;
+											uint8_t hash[16];	/* b(i) */
+											const uint8_t *addr[3];
+											size_t len[3];
+											
+											/* We need the request authenticator */
+											CHECK_PARAMS(st);
+
+											/* Retrieve the shared secret */
+											CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
+											
+											/* Beginning of the buffer */
+											buf[0] = tuntag;
+											buf[1] = (uint8_t)(lrand48()); /* A (hi bits) */
+											buf[2] = (uint8_t)(lrand48()); /* A (low bits) */
+											
+											/* The plain text string P */
+											CHECK_PARAMS(ahdr->avp_value->os.len < 240);
+											buf[3] = ahdr->avp_value->os.len;
+											memcpy(&buf[4], ahdr->avp_value->os.data, ahdr->avp_value->os.len);
+											memset(&buf[4 + ahdr->avp_value->os.len], 0, sizeof(buf) - 4 - ahdr->avp_value->os.len);
+											
+											/* Initial b1 = MD5(S + R + A) */
+											addr[0] = secret;
+											len[0] = secret_len;
+											addr[1] = st->req_auth;
+											len[1] = 16;
+											addr[2] = &buf[1];
+											len[2] = 2;
+											md5_vector(3, addr, len, hash);
+											
+											/* Initial c(1) = p(1) xor b(1) */
+											for (i = 0; i < 16; i++) {
+												buf[i + 3] ^= hash[i];
+											}
+											pos = 16;
+											
+											/* loop */
+											while (pos < ahdr->avp_value->os.len + 1) {
+												addr[0] = secret;
+												len[0] = secret_len;
+												addr[1] = &buf[pos - 13];
+												len[1] = 16;
+												/* b(i) = MD5( S + c(i-1) */
+												md5_vector(2, addr, len, hash);
+												
+												/* c(i) = p(i) xor b(i) */
+												for (i = 0; i < 16; i++)
+													buf[pos + i + 3] ^= hash[i];
+
+												pos += 16;
+											}
+											
+											CONV2RAD_STR(RADIUS_ATTR_TUNNEL_PASSWORD, &buf[0], pos + 3, 0);
+										}
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+									
+									default:
+										TRACE_DEBUG(FULL, "Ignored unknown AVP inside Tunneling AVP (%d)", ahdr->avp_code);
+								}
+							} else {
+								TRACE_DEBUG(FULL, "Ignored unknown Vendor AVP inside Tunneling AVP (%d, %d)", ahdr->avp_vendor, ahdr->avp_code);
+							}
+						}
+					}
+					break;
+					
+				case DIAM_ATTR_USER_NAME:
+					CONV2RAD_STR(RADIUS_ATTR_USER_NAME, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+				/* User-Password never present in answers */
+					
+		/* RFC 4072 (AVP in the order of the EAP Command AVP Table) */
+			/*
+			      o  Diameter Accounting-EAP-Auth-Method AVPs, if present, are
+				 discarded.
+			*/
+				case DIAM_ATTR_ACCOUNTING_EAP_AUTH_METHOD:
+					break;
+					
+			/*
+			      o  Diameter EAP-Master-Session-Key AVP can be translated to the
+				 vendor-specific RADIUS MS-MPPE-Recv-Key and MS-MPPE-Send-Key
+				 attributes [RFC2548].  The first up to 32 octets of the key is
+				 stored into MS-MPPE-Recv-Key, and the next up to 32 octets (if
+				 present) are stored into MS-MPPE-Send-Key.  The encryption of this
+				 attribute is described in [RFC2548].
+			*/
+				case DIAM_ATTR_EAP_MASTER_SESSION_KEY:
+					{
+						uint8_t * secret;	/* S */
+						size_t secret_len;
+						size_t recv_len, send_len;
+
+						/* We need the request authenticator */
+						CHECK_PARAMS(st);
+
+						/* Retrieve the shared secret */
+						CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
+						
+						if (ahdr->avp_value->os.len != 64) {
+							TRACE_DEBUG(INFO, "Received EAP-Master-Session-Key attribute with length %zd != 64.", ahdr->avp_value->os.len)
+						}
+						
+						CHECK_PARAMS(ahdr->avp_value->os.len <= 64);
+						recv_len = ahdr->avp_value->os.len >= 32 ? 32 : ahdr->avp_value->os.len;
+						send_len = ahdr->avp_value->os.len - recv_len;
+						
+						if ( ! radius_msg_add_mppe_keys(*rad_fw, st->req_auth, secret, secret_len, 
+								ahdr->avp_value->os.data + recv_len, send_len,
+								ahdr->avp_value->os.data, recv_len) ) {
+							TRACE_DEBUG(INFO, "Error while converting EAP-Master-Session-Key to RADIUS message");
+							return ENOMEM;
+						}
+					}
+					break;
+				
+				case DIAM_ATTR_EAP_KEY_NAME:
+					CONV2RAD_STR(RADIUS_ATTR_EAP_KEY_NAME, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+			/*
+			      o  Diameter EAP-Payload AVP is translated to RADIUS EAP-Message
+				 attribute(s).  If necessary, the value is split into multiple
+				 RADIUS EAP-Message attributes.
+			*/
+				case DIAM_ATTR_EAP_PAYLOAD:
+					if ( ! radius_msg_add_eap(*rad_fw, ahdr->avp_value->os.data, ahdr->avp_value->os.len) ) {
+						TRACE_DEBUG(INFO, "Error while converting EAP payload to RADIUS message");
+						return ENOMEM;
+					}
+					break;
+					
+			/*
+			      o  Diameter EAP-Reissued-Payload AVP is translated to a message that
+				 contains RADIUS EAP-Message attribute(s), and a RADIUS Error-Cause
+				 attribute [RFC3576] with value 202 (decimal), "Invalid EAP Packet
+				 (Ignored)" [RFC3579].
+			*/
+				case DIAM_ATTR_EAP_REISSUED_PAYLOAD:
+					if ( ! radius_msg_add_eap(*rad_fw, ahdr->avp_value->os.data, ahdr->avp_value->os.len) ) {
+						TRACE_DEBUG(INFO, "Error while converting EAP reissued payload to RADIUS message");
+						return ENOMEM;
+					}
+					
+					error_cause = 202; /* Invalid EAP Packet */
+					break;
+			
+				default:
+					/* Leave the AVP in the message for further treatment */
+					handled = 0;
+			}
+		} else {
+			/* Vendor-specific AVPs */
+			switch (ahdr->avp_vendor) {
+				
+				default: /* unknown vendor */
+					handled = 0;
+			}
+		}
+		
+		if (handled) {
+			CHECK_FCT( fd_msg_free( avp ) );
+		}
+	}
+	
+	CHECK_FCT( fd_msg_free( aoh ) );
+	free(st);
+	
+	if (error_cause) {
+		if ( ! radius_msg_add_attr_int32(*rad_fw, RADIUS_ATTR_ERROR_CAUSE, error_cause) ) {
+			TRACE_DEBUG(INFO, "Error while adding Error-Cause attribute in RADIUS message");
+			return ENOMEM;
+		}
+	}
+
+	if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		/* Add the auth-application-id required for STR, or 0 if no STR is required */
+		CHECK_FCT( fd_msg_hdr( *diam_ans, &hdr ) );
+		if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), CLASS_AAI_PREFIX "%u", 
+				no_str ? 0 : hdr->msg_appl))) {
+			TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf);
+		}
+		CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, sz, 0);
+	}
+	
+	return 0;
+}
+
+/* The exported symbol */
+struct rgw_api rgwp_descriptor = {
+	.rgwp_name       = "auth",
+	.rgwp_conf_parse = auth_conf_parse,
+	.rgwp_conf_free  = auth_conf_free,
+	.rgwp_rad_req    = auth_rad_req,
+	.rgwp_diam_ans   = auth_diam_ans
+};