changeset 419:fc0d723c1f8b

Added conversion of Diameter answers to RADIUS
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 23 Jun 2009 11:32:45 +0900
parents 8155408c6dc5
children 7de6e87d752e
files extensions/radius_gw/radius_gw.h extensions/radius_gw/rg_common.h extensions/radius_gw/rgw_clients.c extensions/radius_gw/rgw_extensions.c extensions/radius_gw/rgw_msg.c extensions/radius_gw/rgw_work.c extensions/radius_gw/sub_auth.c
diffstat 7 files changed, 922 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/radius_gw/radius_gw.h	Tue Jun 16 15:38:18 2009 +0900
+++ b/extensions/radius_gw/radius_gw.h	Tue Jun 23 11:32:45 2009 +0900
@@ -111,7 +111,7 @@
 int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref);
 int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli);
 int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli);
-int rgw_clients_get_origin(struct rgw_client *cli, char * oh, size_t oh_len, char * or, size_t or_len,  char **fqdn, char **realm, int strict);
+int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm);
 int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli);
 void rgw_clients_dispose(struct rgw_client ** ref);
 void rgw_clients_dump(void);
--- a/extensions/radius_gw/rg_common.h	Tue Jun 16 15:38:18 2009 +0900
+++ b/extensions/radius_gw/rg_common.h	Tue Jun 23 11:32:45 2009 +0900
@@ -128,7 +128,83 @@
        RADIUS_ATTR_FRAMED_IPV6_PREFIX = 97,
        RADIUS_ATTR_LOGIN_IPV6_HOST = 98,
        RADIUS_ATTR_FRAMED_IPV6_ROUTE = 99,
-       RADIUS_ATTR_FRAMED_IPV6_POOL = 100
+       RADIUS_ATTR_FRAMED_IPV6_POOL = 100,
+       RADIUS_ATTR_ERROR_CAUSE = 101,
+       RADIUS_ATTR_EAP_KEY_NAME = 102
+};
+
+enum {  DIAM_ATTR_USER_NAME = 1,
+	DIAM_ATTR_USER_PASSWORD = 2,
+	DIAM_ATTR_SERVICE_TYPE = 6,
+	DIAM_ATTR_FRAMED_PROTOCOL = 7,
+	DIAM_ATTR_FRAMED_IP_ADDRESS = 8,
+	DIAM_ATTR_FRAMED_IP_NETMASK = 9,
+	DIAM_ATTR_FRAMED_ROUTING = 10,
+	DIAM_ATTR_FILTER_ID = 11,
+	DIAM_ATTR_FRAMED_MTU = 12,
+	DIAM_ATTR_FRAMED_COMPRESSION = 13,
+	DIAM_ATTR_LOGIN_IP_HOST = 14,
+	DIAM_ATTR_LOGIN_SERVICE = 15,
+	DIAM_ATTR_LOGIN_TCP_PORT = 16,
+	DIAM_ATTR_REPLY_MESSAGE = 18,
+	DIAM_ATTR_CALLBACK_NUMBER = 19,
+	DIAM_ATTR_CALLBACK_ID = 20,
+	DIAM_ATTR_FRAMED_ROUTE = 22,
+	DIAM_ATTR_FRAMED_IPX_NETWORK = 23,
+	DIAM_ATTR_STATE = 24,
+	DIAM_ATTR_CLASS = 25,
+	DIAM_ATTR_IDLE_TIMEOUT = 28,
+	DIAM_ATTR_LOGIN_LAT_SERVICE = 34,
+	DIAM_ATTR_LOGIN_LAT_NODE = 35,
+	DIAM_ATTR_LOGIN_LAT_GROUP = 36,
+	DIAM_ATTR_FRAMED_APPLETALK_LINK = 37,
+	DIAM_ATTR_FRAMED_APPLETALK_NETWORK = 38,
+	DIAM_ATTR_FRAMED_APPLETALK_ZONE = 39,
+	DIAM_ATTR_PORT_LIMIT = 62,
+	DIAM_ATTR_LOGIN_LAT_PORT = 63,
+	DIAM_ATTR_TUNNEL_TYPE = 64,
+	DIAM_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+	DIAM_ATTR_TUNNEL_CLIENT_ENDPOINT = 66,
+	DIAM_ATTR_TUNNEL_SERVER_ENDPOINT = 67,
+	DIAM_ATTR_TUNNEL_PASSWORD = 69,
+	DIAM_ATTR_ARAP_FEATURES = 71,
+	DIAM_ATTR_ARAP_ZONE_ACCESS = 72,
+	DIAM_ATTR_ARAP_SECURITY = 73,
+	DIAM_ATTR_ARAP_SECURITY_DATA = 74,
+	DIAM_ATTR_PASSWORD_RETRY = 75,
+	DIAM_ATTR_PROMPT = 76,
+	DIAM_ATTR_CONFIGURATION_TOKEN = 78,
+	DIAM_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
+	DIAM_ATTR_TUNNEL_ASSIGNEMENT_ID = 82,
+	DIAM_ATTR_TUNNEL_PREFERENCE = 83,
+	DIAM_ATTR_ARAP_CHALLENGE_RESPONSE = 84,
+	DIAM_ATTR_ACCT_INTERIM_INTERVAL = 85,
+	DIAM_ATTR_FRAMED_POOL = 88,
+	DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID = 90,
+	DIAM_ATTR_TUNNEL_SERVER_AUTH_ID = 91,
+	DIAM_ATTR_FRAMED_INTERFACE_ID = 96,
+	DIAM_ATTR_FRAMED_IPV6_PREFIX = 97,
+	DIAM_ATTR_LOGIN_IPV6_HOST = 98,
+	DIAM_ATTR_FRAMED_IPV6_ROUTE = 99,
+	DIAM_ATTR_FRAMED_IPV6_POOL = 100,
+	DIAM_ATTR_EAP_KEY_NAME = 102,
+	DIAM_ATTR_AUTH_APPLICATION_ID = 258,
+	DIAM_ATTR_MULTI_ROUND_TIMEOUT = 272,
+	DIAM_ATTR_AUTH_REQUEST_TYPE = 274,
+	DIAM_ATTR_AUTH_GRACE_PERIOD = 276,
+	DIAM_ATTR_AUTH_SESSION_STATE = 277,
+	DIAM_ATTR_ORIGIN_STATE_ID = 278,
+	DIAM_ATTR_FAILED_AVP = 279,
+	DIAM_ATTR_ERROR_MESSAGE = 281,
+	DIAM_ATTR_ERROR_REPORTING_HOST = 294,
+	DIAM_ATTR_NAS_FILTER_RULE = 400,
+	DIAM_ATTR_TUNNELING = 401,
+	DIAM_ATTR_QOS_FILTER_RULE = 407,
+	DIAM_ATTR_ORIGIN_AAA_PROTOCOL = 408,
+	DIAM_ATTR_EAP_PAYLOAD = 462,
+	DIAM_ATTR_EAP_REISSUED_PAYLOAD = 463,
+	DIAM_ATTR_EAP_MASTER_SESSION_KEY = 464,
+	DIAM_ATTR_ACCOUNTING_EAP_AUTH_METHOD = 465
 };
 
 
--- a/extensions/radius_gw/rgw_clients.c	Tue Jun 16 15:38:18 2009 +0900
+++ b/extensions/radius_gw/rgw_clients.c	Tue Jun 23 11:32:45 2009 +0900
@@ -306,6 +306,7 @@
 
 /* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */
 /* Also update the client list of aliases if needed */
+/* NOTE: This function will require changes to allow RADIUS Proxy on the path... */
 int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli)
 {
 	int idx;
@@ -462,27 +463,11 @@
 	return 0;
 }
 
-int rgw_clients_get_origin(struct rgw_client *cli, char * oh, size_t oh_len, char * or, size_t or_len,  char **fqdn, char **realm, int strict)
+int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm)
 {
-	TRACE_ENTRY("%p %p %g %p %g %p %p", cli, oh, oh_len, or, or_len, fqdn, realm);
+	TRACE_ENTRY("%p %p %p", cli, fqdn, realm);
 	CHECK_PARAMS(cli && fqdn && realm);
 	
-	if (oh && oh_len) {
-		if (strncasecmp(oh, cli->fqdn, oh_len)) {
-			TRACE_DEBUG(INFO, "Received unexpected '%.*s' Origin-Host in RADIUS State attribute, replacing with '%s'", oh_len, oh, cli->fqdn);
-			if (strict)
-				return EINVAL;
-		}
-	}
-	
-	if (or && or_len) {
-		if (strncasecmp(or, cli->realm, or_len)) {
-			TRACE_DEBUG(INFO, "Received unexpected '%.*s' Origin-Realm in RADIUS State attribute, replacing with '%s'", or_len, or, cli->realm);
-			if (strict)
-				return EINVAL;
-		}
-	}
-	
 	*fqdn = cli->fqdn;
 	*realm= cli->realm;
 	return 0;
--- a/extensions/radius_gw/rgw_extensions.c	Tue Jun 16 15:38:18 2009 +0900
+++ b/extensions/radius_gw/rgw_extensions.c	Tue Jun 23 11:32:45 2009 +0900
@@ -443,7 +443,7 @@
 	
 	if (ret > 0) {
 		/* Critical error, log and exit */
-		log_error("An error occurred while handling a DIAMETER answer to a converted RADIUS request, turn on DEBUG for details: %s\n", strerror(ret));
+		log_error("(radius_gw) An error occurred while handling a DIAMETER answer to a converted RADIUS request, turn on DEBUG for details: %s\n", strerror(ret));
 		return ret;
 	}
 	
--- a/extensions/radius_gw/rgw_msg.c	Tue Jun 16 15:38:18 2009 +0900
+++ b/extensions/radius_gw/rgw_msg.c	Tue Jun 23 11:32:45 2009 +0900
@@ -140,6 +140,8 @@
 }
 
 static dict_object_t * rm_sess_id;
+static dict_object_t * rm_dest_host;
+static dict_object_t * rm_dest_realm;
 static dict_object_t * rm_orig_host;
 static dict_object_t * rm_orig_realm;
 
@@ -149,25 +151,26 @@
 	CHECK_FCT( dict_search(DICT_AVP, AVP_BY_NAME, "Session-Id", &rm_sess_id, ENOENT) );
 	CHECK_FCT( dict_search(DICT_AVP, AVP_BY_NAME, "Origin-Host", &rm_orig_host, ENOENT) );
 	CHECK_FCT( dict_search(DICT_AVP, AVP_BY_NAME, "Origin-Realm", &rm_orig_realm, ENOENT) );
+	CHECK_FCT( dict_search(DICT_AVP, AVP_BY_NAME, "Destination-Host", &rm_dest_host, ENOENT) );
+	CHECK_FCT( dict_search(DICT_AVP, AVP_BY_NAME, "Destination-Realm", &rm_dest_realm, ENOENT) );
 	return 0;
 }
 
 /* Create a msg with origin-host & realm, and session-id, and a session object from a RADIUS request message */
 int rgw_msg_create_base(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, sess_id_t ** session, msg_t ** diam)
 {
-	int idx;
+	int idx, i;
 	const char * prefix = "Diameter/";
 	size_t pref_len;
-	char * oh = NULL;
-	size_t oh_len = 0;
-	char * or = NULL;
-	size_t or_len = 0;
+	char * dh = NULL;
+	size_t dh_len = 0;
+	char * dr = NULL;
+	size_t dr_len = 0;
 	char * si = NULL;
 	size_t si_len = 0;
 	char * un = NULL;
 	size_t un_len = 0;
 	
-	
 	char * fqdn;
 	char * realm;
 	char * sess_str = NULL;
@@ -180,7 +183,8 @@
 	
 	pref_len = strlen(prefix);
 	
-	/* Is there a State attribute with prefix "Diameter/" in the message? (in that case: Diameter/Origin-Host/Origin-Realm/Session-Id) */
+	/* 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 */
 	/* Is there a Class attribute with prefix "Diameter/" in the message? (in that case: Diameter/Session-Id) */
 	for (idx = 0; idx < msg->radius.attr_used; idx++) {
 		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]);
@@ -205,22 +209,26 @@
 			/* 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;
-			oh = attr_val + i;
+			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 */
-			oh_len = i - 1 - start;
+			dh_len = i - 1 - start;
 
 			start = ++i;
-			or = attr_val + 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 */
-			or_len = i - 1 - start;
+			dr_len = i - 1 - start;
 
 			i++;
 			si = attr_val + i;
 			si_len = attr_len - i;
 
-			TRACE_DEBUG(ANNOYING, "Attribute parsed successfully: OH:'%.*s' OR:'%.*s' SI:'%.*s'.", oh_len, oh, or_len, or, si_len, si);
+			TRACE_DEBUG(ANNOYING, "Attribute parsed successfully: DH:'%.*s' DR:'%.*s' SI:'%.*s'.", dh_len, dh, dr_len, dr, si_len, si);
+			/* Remove from the message */
+			for (i = idx + 1; i < msg->radius.attr_used; i++)
+				msg->radius.attr_pos[i - 1] = msg->radius.attr_pos[i];
+			msg->radius.attr_used -= 1;
 			break;
 		}
 		
@@ -230,12 +238,17 @@
 			si = attr_val + pref_len;
 			si_len = attr_len - pref_len;
 			TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), SI:'%.*s'.", prefix, idx, si_len, si);
+			/* Remove from the message */
+			for (i = idx + 1; i < msg->radius.attr_used; i++)
+				msg->radius.attr_pos[i - 1] = msg->radius.attr_pos[i];
+			msg->radius.attr_used -= 1;
 			break;
 		}
+		
 	}
 	
 	/* Get information on this peer */
-	CHECK_FCT( rgw_clients_get_origin(cli, oh, oh_len, or, or_len, &fqdn, &realm, 0) );
+	CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) );
 	
 	/* Create the session object */
 	if (si_len) {
@@ -270,6 +283,45 @@
 		TRACE_DEBUG(FULL, "No session has been created for this message");
 	}
 	
+	/* Add the Destination-Realm as next AVP */
+	CHECK_FCT( msg_avp_new ( rm_dest_realm, 0, &avp ) );
+	memset(&avp_val, 0, sizeof(avp_val));
+	if (dr) {
+		avp_val.os.data = (unsigned char *)dr;
+		avp_val.os.len = dr_len;
+	} else {
+		int i = 0;
+		if (un) {
+			/* 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 => local domain (of this gateway) */
+			avp_val.os.data = g_pconf->diameter_realm;
+			avp_val.os.len  = strlen(g_pconf->diameter_realm);
+		} else {
+			avp_val.os.data = un + i;
+			avp_val.os.len  = un_len - i;
+		}
+	}
+	CHECK_FCT( msg_avp_setvalue ( avp, &avp_val ) );
+	CHECK_FCT( msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	
+	/* Add the Destination-Host as next AVP */
+	if (dh) {
+		CHECK_FCT( msg_avp_new ( rm_dest_host, 0, &avp ) );
+		memset(&avp_val, 0, sizeof(avp_val));
+		avp_val.os.data = (unsigned char *)dh;
+		avp_val.os.len = dh_len;
+		CHECK_FCT( msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
 	/* Add the Origin-Host as next AVP */
 	CHECK_FCT( msg_avp_new ( rm_orig_host, 0, &avp ) );
 	memset(&avp_val, 0, sizeof(avp_val));
--- a/extensions/radius_gw/rgw_work.c	Tue Jun 16 15:38:18 2009 +0900
+++ b/extensions/radius_gw/rgw_work.c	Tue Jun 23 11:32:45 2009 +0900
@@ -70,14 +70,15 @@
 	TRACE_ENTRY("%p %p", pa, ans);
 	CHECK_PARAMS_DO( pa && ans, return );
 	
-	TRACE_DEBUG(INFO, "Handling Diameter answer: Not implemented yet...");
-	
 	/* Create an empty RADIUS answer message */
-	CHECK_MALLOC_DO( rad_ans = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, pa->rad->radius.hdr->identifier), goto out );
+	CHECK_MALLOC_DO( rad_ans = radius_msg_new(0, pa->rad->radius.hdr->identifier), goto out );
 	
 	/* Pass the Diameter answer to the same extensions as the request */
 	CHECK_FCT_DO( rgw_extensions_loop_ans(pa->rad, pa->sess, ans, &rad_ans, pa->cli), goto out );
 	
+	/* Now check what AVPs remain in the diameter answer. If AVPs with the 'M' flag are here, we have a problem... */
+	ASSERT(0);
+	
 	/* Now try and send the RADIUS answer */
 	if (rad_ans) {
 		CHECK_FCT_DO( rgw_client_finish_send(&rad_ans, pa->rad, pa->cli), goto out);	
--- a/extensions/radius_gw/sub_auth.c	Tue Jun 16 15:38:18 2009 +0900
+++ b/extensions/radius_gw/sub_auth.c	Tue Jun 23 11:32:45 2009 +0900
@@ -55,6 +55,7 @@
 
 struct rga_conf_state {
 	char * conffile;
+	sess_reg_t * sess_hdl;
 	void * dl_handle;
 	int (*rgw_clients_getkey)(void * cli, unsigned char **key, size_t *key_len);
 };
@@ -68,6 +69,8 @@
 	CHECK_MALLOC_DO( cs = malloc(sizeof(struct rga_conf_state)), return NULL );
 	memset(cs, 0, sizeof(struct rga_conf_state));
 	
+	CHECK_FCT_DO( sess_regext( &cs->sess_hdl ), { free(cs); return NULL; } );
+	
 	cs->conffile = conffile;
 	
 	if (conffile) {
@@ -85,7 +88,8 @@
 static void auth_conf_free(struct rga_conf_state * cs)
 {
 	TRACE_ENTRY("%p", cs);
-	CHECK_PARAMS_DO( cs, );
+	CHECK_PARAMS_DO( cs, return );
+	CHECK_FCT_DO( sess_deregext( cs->sess_hdl ),  );
 	rg_pointers_fini(&cs->dl_handle);
 	free(cs);
 	return;
@@ -107,6 +111,14 @@
 	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw, cli);
 	CHECK_PARAMS(rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
 	
+	/* We store the identifier in the session, if any */
+	if (session) {
+		unsigned char * req_auth;
+		CHECK_MALLOC(req_auth = malloc(16));
+		memcpy(req_auth, &rad_req->hdr->authenticator[0], 16);
+		CHECK_FCT( sess_data_reg(session, cs->sess_hdl, req_auth, free) );
+	}
+	
 	/*
 	   Guidelines:
 	     http://tools.ietf.org/html/rfc4005#section-9.1
@@ -348,32 +360,10 @@
 			/*
 			      -  The Destination-Realm AVP is created from the information found
         			 in the RADIUS User-Name attribute.
+				 	-> done in rgw_msg_create_base
 			*/
 			case RADIUS_ATTR_USER_NAME:
 				CONV_STR( "User-Name" );
-				{
-					/* In addition, extract the destination-realm, if any */
-					/* We suppose the format is anything@dest-realm */
-					/* We don't care about decorated NAI here */
-					int i;
-					for (i = value.os.len - 2; i > 0; i--) {
-						if (value.os.data[i] == '@') {
-							break;
-						}
-					}
-					CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Destination-Realm", &avp_dict, ENOENT));
-					CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
-					if (i == 0) {
-						/* Not found in the User-Name => local domain */
-						value.os.data = g_pconf->diameter_realm;
-						value.os.len  = strlen(g_pconf->diameter_realm);
-					} else {
-						value.os.data += i + 1;
-						value.os.len  -= i + 1;
-					}
-					CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
-					CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
-				}
 				break;
 				
 			/*
@@ -821,10 +811,765 @@
 
 static int auth_diam_ans(struct rga_conf_state * cs, sess_id_t * session, msg_t ** diam_ans, struct radius_msg ** rad_fw, void * cli )
 {
+	msg_data_t *mdata;
+	msg_avp_t *avp, *next, *avp_x, *avp_y, *asid, *aoh;
+	msg_avp_data_t *adata, *sid, *oh;
+	dict_object_t * avp_dict;
+	char buf[254]; /* to store some attributes values (with final '\0') */
+	int ta_set = 0;
+	uint8_t	tuntag = 0;
+	unsigned char * req_auth = NULL;
+	
 	TRACE_ENTRY("%p %p %p %p %p", cs, session, diam_ans, rad_fw, cli);
-	CHECK_PARAMS(cs);
+	CHECK_PARAMS(cs && session && diam_ans && *diam_ans && rad_fw && *rad_fw);
+	
+	if (session) {
+		int ret = sess_data_dereg( session, cs->sess_hdl, (void *)&req_auth );
+		if (ret == ENOENT) {
+			TRACE_DEBUG(FULL, "No data saved in the session");
+		} else {
+			CHECK_FCT(ret); /* Return if another error occurred */
+		}
+	}
+	
+	CHECK_FCT( msg_data( *diam_ans, &mdata ) );
+	
+	/*	
+	      -  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 bellow
+		 
+	      -  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)
+	 */
+
+	/* _trunc_ = 0 => error; _trunc_ = 1 => truncate; _trunc = 2 => create several attributes */
+#define CONV2_STR( _attr_, _data_, _len_, _trunc_)	{					\
+	size_t __l = (size_t)(_len_);								\
+	size_t __off = 0;									\
+	if ((_trunc_) == 0) {									\
+		CHECK_PARAMS( __l <= 253 );							\
+	}											\
+	if ((__l > 253) && (_trunc_ == 1)) {							\
+		TRACE_DEBUG(FULL, "Attribute truncated!");					\
+		__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 CONV2_32B( _attr_, _data_)	{							\
+	uint32_t __v = htonl((uint32_t)(_data_));						\
+	CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+}
+
+#define CONV2_64B( _attr_, _data_)	{	/* Some systems do not provide htonll */	\
+	uint64_t __v = ((uint64_t)htonl((uint32_t)(((uint64_t)(_data_)) >> 32))) << 32;		\
+		__v  |= htonl((uint32_t)(_data_));						\
+	CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+}
+	/* Search the different AVPs we handle here */
+	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Session-Id", &avp_dict, ENOENT));
+	CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &asid) );
+	CHECK_FCT( msg_avp_data ( asid, &sid ) );
+	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Origin-Host", &avp_dict, ENOENT));
+	CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &aoh) );
+	CHECK_FCT( msg_avp_data ( aoh, &oh ) );
+
+	/* Check the Diameter error code */
+	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Result-Code", &avp_dict, ENOENT));
+	CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &avp) );
+	CHECK_FCT( msg_avp_data ( avp, &adata ) );
+	switch (adata->avp_data->u32) {
+		case 1001: /* DIAMETER_MULTI_ROUND_AUTH */
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_CHALLENGE;
+			break;
+		case 2001: /* DIAMETER_SUCCESS */
+		case 2002: /* DIAMETER_LIMITED_SUCCESS */
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_ACCEPT;
+			break;
+		
+		default:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_REJECT;
+			log_error("Received Diameter answer with error code '%d' from server '%.*s', session %.*s, sending Access-Reject\n",
+					adata->avp_data->u32, 
+					oh->avp_data->os.len, oh->avp_data->os.data,
+					sid->avp_data->os.len, sid->avp_data->os.data);
+			CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Error-Message", &avp_dict, ENOENT));
+			CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &avp) );
+			if (avp) {
+				CHECK_FCT( msg_avp_data ( avp, &adata ) );
+				log_error("  Error-Message content: '%.*s'\n",
+						adata->avp_data->os.len, adata->avp_data->os.data);
+			}
+			CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &avp_dict, ENOENT));
+			CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &avp) );
+			if (avp) {
+				CHECK_FCT( msg_avp_data ( avp, &adata ) );
+				log_error("  Error-Reporting-Host: '%.*s'\n",
+						adata->avp_data->os.len, adata->avp_data->os.data);
+			}
+			CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Failed-AVP", &avp_dict, ENOENT));
+			CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &avp) );
+			if (avp) {
+				log_error("  Failed-AVP was present in the message\n");
+				/* Dump its content ? */
+			}
+			return 0;
+	}
+	/* Remove this Result-Code avp */
+	CHECK_FCT( msg_free( avp, 1 ) );
+	
+	/* Process creation of the State or Class attribute with session information */
+	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Origin-Realm", &avp_dict, ENOENT));
+	CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &avp) );
+	CHECK_FCT( msg_avp_data ( avp, &adata ) );
+	
+	/* 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) < snprintf(buf, sizeof(buf), "Diameter/%.*s/%.*s/%.*s", 
+				oh->avp_data->os.len,  oh->avp_data->os.data,
+				adata->avp_data->os.len,  adata->avp_data->os.data,
+				sid->avp_data->os.len, sid->avp_data->os.data)) {
+			TRACE_DEBUG(INFO, "Data truncated in State attribute: %s", buf);
+		}
+		CONV2_STR(RADIUS_ATTR_STATE, buf, strlen(buf), 0);
+	}
+	/* The RFC text says that this should always be the case, but it seems odd... */
+	if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		if (sizeof(buf) < snprintf(buf, sizeof(buf), "Diameter/%.*s", 
+				sid->avp_data->os.len, sid->avp_data->os.data)) {
+			TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf);
+		}
+		CONV2_STR(RADIUS_ATTR_CLASS, buf, strlen(buf), 0);
+	}
+	
+	/* Unlink the Origin-Realm now; the others are unlinked at the end of this function */
+	CHECK_FCT( msg_free( avp, 1 ) );
+	
+	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Session-Timeout", &avp_dict, ENOENT));
+	CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &avp) );
+	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Authorization-Lifetime", &avp_dict, ENOENT));
+	CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &avp_x) );
+	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Re-Auth-Request-Type", &avp_dict, ENOENT));
+	CHECK_FCT( msg_search_avp (*diam_ans, avp_dict, &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( msg_avp_data ( avp, &adata ) );
+		CONV2_32B( RADIUS_ATTR_SESSION_TIMEOUT, adata->avp_data->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( msg_avp_data ( avp_x, &adata ) );
+		CONV2_32B( RADIUS_ATTR_SESSION_TIMEOUT, adata->avp_data->u32 );
+		CONV2_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( msg_avp_data ( avp_x, &adata ) );
+		CONV2_32B( RADIUS_ATTR_SESSION_TIMEOUT, adata->avp_data->u32 );
+		CONV2_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( msg_free( avp, 1 ) );
+	}
+	if (avp_x) {
+		CHECK_FCT( msg_free( avp_x, 1 ) );
+	}
+	if (avp_y) {
+		CHECK_FCT( msg_free( avp_y, 1 ) );
+	}
+	
+	
+	/*
+	      -  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
 
-	return ENOTSUP;
+	      -  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( msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
+	
+	while (next) {
+		int handled = 1;
+		avp = next;
+		CHECK_FCT( msg_browse(avp, MSG_BRW_NEXT, &next, NULL) );
+		
+		CHECK_FCT( msg_avp_data ( avp, &adata ) );
+		
+		if (adata->avp_flags & AVP_FLAG_VENDOR == 0) {
+			switch (adata->avp_code) {
+				
+		/* RFC 4005 (AVP in the order of the AA-Request/Answer AVP Table) */
+				case DIAM_ATTR_ACCT_INTERIM_INTERVAL:
+					CONV2_32B(RADIUS_ATTR_ACCT_INTERIM_INTERVAL, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_ARAP_CHALLENGE_RESPONSE:
+					CONV2_STR(RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE, adata->avp_data->os.data, adata->avp_data->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_ARAP_FEATURES:
+					CONV2_STR(RADIUS_ATTR_ARAP_FEATURES, adata->avp_data->os.data, adata->avp_data->os.len, 0);
+					break;
+					
+				/* ARAP-Password is not present in answers */
+					
+				case DIAM_ATTR_ARAP_SECURITY:
+					CONV2_32B(RADIUS_ATTR_ARAP_SECURITY, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_ARAP_SECURITY_DATA:
+					CONV2_STR(RADIUS_ATTR_ARAP_SECURITY_DATA, adata->avp_data->os.data, adata->avp_data->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_ARAP_ZONE_ACCESS:
+					CONV2_32B(RADIUS_ATTR_ARAP_ZONE_ACCESS, adata->avp_data->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 (adata->avp_data->u32 != 3) {
+						log_normal("Received Diameter answer with Auth-Request-Type set to %d (%s) from server %.*s, session %.*s.\n"
+								"  This may cause interoperability with RADIUS.\n",
+								adata->avp_data->u32,
+								(adata->avp_data->u32 == 1) ? "AUTHENTICATE_ONLY" :
+									((adata->avp_data->u32 == 2) ? "AUTHORIZE_ONLY" : "???"),
+								oh->avp_data->os.len, oh->avp_data->os.data, 
+								sid->avp_data->os.len, sid->avp_data->os.len);
+					}
+					break;
+				
+				case DIAM_ATTR_AUTH_SESSION_STATE:
+					if ((!ta_set) && (adata->avp_data->u32 == 0 /* STATE_MAINTAINED */)) {
+						CONV2_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
+					}
+					break;
+					
+				/* Authorization-Lifetime already handled */
+				
+				case DIAM_ATTR_CALLBACK_ID:
+					CONV2_STR(RADIUS_ATTR_CALLBACK_ID, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+				
+				case DIAM_ATTR_CALLBACK_NUMBER:
+					CONV2_STR(RADIUS_ATTR_CALLBACK_NUMBER, adata->avp_data->os.data, adata->avp_data->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:
+					CONV2_STR(RADIUS_ATTR_CLASS, adata->avp_data->os.data, adata->avp_data->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... */
+					CONV2_STR(RADIUS_ATTR_CONFIGURATION_TOKEN, adata->avp_data->os.data, adata->avp_data->os.len, 2);
+					break;
+				
+				/* Connect-Info is not present in answers */
+				
+				case DIAM_ATTR_FILTER_ID:
+					CONV2_STR(RADIUS_ATTR_FILTER_ID, adata->avp_data->os.data, adata->avp_data->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_LINK:
+					CONV2_32B(RADIUS_ATTR_FRAMED_APPLETALK_LINK, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_NETWORK:
+					CONV2_32B(RADIUS_ATTR_FRAMED_APPLETALK_NETWORK, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_ZONE:
+					CONV2_STR(RADIUS_ATTR_FRAMED_APPLETALK_ZONE, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_COMPRESSION:
+					CONV2_32B(RADIUS_ATTR_FRAMED_COMPRESSION, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_INTERFACE_ID:
+					CONV2_64B(RADIUS_ATTR_FRAMED_INTERFACE_ID, adata->avp_data->u64);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IP_ADDRESS:
+					CONV2_STR(RADIUS_ATTR_FRAMED_IP_ADDRESS, adata->avp_data->os.data, adata->avp_data->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IP_NETMASK:
+					CONV2_STR(RADIUS_ATTR_FRAMED_IP_NETMASK, adata->avp_data->os.data, adata->avp_data->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_PREFIX:
+					CONV2_STR(RADIUS_ATTR_FRAMED_IPV6_PREFIX, adata->avp_data->os.data, adata->avp_data->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_POOL:
+					CONV2_STR(RADIUS_ATTR_FRAMED_IPV6_POOL, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_ROUTE:
+					CONV2_STR(RADIUS_ATTR_FRAMED_IPV6_ROUTE, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPX_NETWORK:
+					CONV2_32B(RADIUS_ATTR_FRAMED_IPX_NETWORK, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_MTU:
+					CONV2_32B(RADIUS_ATTR_FRAMED_MTU, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_POOL:
+					CONV2_STR(RADIUS_ATTR_FRAMED_POOL, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_PROTOCOL:
+					CONV2_32B(RADIUS_ATTR_FRAMED_PROTOCOL, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_ROUTE:
+					CONV2_STR(RADIUS_ATTR_FRAMED_ROUTE, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_ROUTING:
+					CONV2_32B(RADIUS_ATTR_FRAMED_ROUTING, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_IDLE_TIMEOUT:
+					CONV2_32B(RADIUS_ATTR_IDLE_TIMEOUT, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_LOGIN_IP_HOST:
+					CONV2_STR(RADIUS_ATTR_LOGIN_IP_HOST, adata->avp_data->os.data, adata->avp_data->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_LOGIN_IPV6_HOST:
+					CONV2_STR(RADIUS_ATTR_LOGIN_IPV6_HOST, adata->avp_data->os.data, adata->avp_data->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_GROUP:
+					CONV2_STR(RADIUS_ATTR_LOGIN_LAT_GROUP, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_NODE:
+					CONV2_STR(RADIUS_ATTR_LOGIN_LAT_NODE, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_PORT:
+					CONV2_STR(RADIUS_ATTR_LOGIN_LAT_PORT, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_SERVICE:
+					CONV2_STR(RADIUS_ATTR_LOGIN_LAT_SERVICE, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_SERVICE:
+					CONV2_32B(RADIUS_ATTR_LOGIN_SERVICE, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_LOGIN_TCP_PORT:
+					CONV2_32B(RADIUS_ATTR_LOGIN_TCP_PORT, adata->avp_data->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:
+					CONV2_32B(RADIUS_ATTR_SESSION_TIMEOUT, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_NAS_FILTER_RULE:
+					/* This is not translatable to RADIUS */
+					log_normal("Received Diameter answer with non-translatable NAS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.\n",
+							oh->avp_data->os.len, oh->avp_data->os.data,
+							sid->avp_data->os.len, sid->avp_data->os.data);
+					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:
+					CONV2_32B(RADIUS_ATTR_PASSWORD_RETRY, adata->avp_data->u32);
+					break;
+				
+				case DIAM_ATTR_PORT_LIMIT:
+					CONV2_32B(RADIUS_ATTR_PORT_LIMIT, adata->avp_data->u32);
+					break;
+				
+				case DIAM_ATTR_PROMPT:
+					CONV2_32B(RADIUS_ATTR_PROMPT, adata->avp_data->u32);
+					break;
+					
+				case DIAM_ATTR_QOS_FILTER_RULE:
+					/* This is not translatable to RADIUS */
+					log_normal("Received Diameter answer with non-translatable QoS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.\n",
+							oh->avp_data->os.len, oh->avp_data->os.data,
+							sid->avp_data->os.len, sid->avp_data->os.data);
+					handled = 0;
+					break;
+					
+				/* Re-Auth-Request-Type already handled */
+				
+				case DIAM_ATTR_REPLY_MESSAGE:
+					CONV2_STR(RADIUS_ATTR_REPLY_MESSAGE, adata->avp_data->os.data, adata->avp_data->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_SERVICE_TYPE:
+					CONV2_32B(RADIUS_ATTR_SERVICE_TYPE, adata->avp_data->u32);
+					break;
+				
+				case DIAM_ATTR_STATE:
+					CONV2_STR(RADIUS_ATTR_STATE, adata->avp_data->os.data, adata->avp_data->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_TUNNELING:
+					{
+#define CONV2_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 CONV2_TUN_32B( _attr_, _data_)	{							\
+	uint32_t __v = htonl((uint32_t)(_data_));						\
+	         __v |= tuntag << 24;								\
+	CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+}
+						msg_avp_t *inavp, *innext;
+						tuntag++;
+						CHECK_FCT( msg_browse(avp, MSG_BRW_FIRST_CHILD, &innext, NULL) );
+						while (innext) {
+							inavp = innext;
+							CHECK_FCT( msg_browse(inavp, MSG_BRW_NEXT, &innext, NULL) );
+							CHECK_FCT( msg_avp_data ( inavp, &adata ) );
+							
+							if (adata->avp_flags & AVP_FLAG_VENDOR == 0) {
+								switch (adata->avp_code) {
+									case DIAM_ATTR_TUNNEL_TYPE:
+										CONV2_TUN_32B( RADIUS_ATTR_TUNNEL_TYPE, adata->avp_data->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_MEDIUM_TYPE:
+										CONV2_TUN_32B( RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, adata->avp_data->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_CLIENT_ENDPOINT:
+										CONV2_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_SERVER_ENDPOINT:
+										CONV2_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PREFERENCE:
+										CONV2_TUN_32B( RADIUS_ATTR_TUNNEL_PREFERENCE, adata->avp_data->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID:
+										CONV2_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_SERVER_AUTH_ID:
+										CONV2_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_ASSIGNEMENT_ID:
+										CONV2_TUN_STR(RADIUS_ATTR_TUNNEL_ASSIGNEMENT_ID, adata->avp_data->os.data, adata->avp_data->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;
+											size_t buflen;
+											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(req_auth);
+
+											/* Retrieve the shared secret */
+											CHECK_FCT((*cs->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_PARAM(adata->avp_data->os.len < 240);
+											buf[3] = adata->avp_data->os.len;
+											memcpy(&buf[4], adata->avp_data->os.data, adata->avp_data->os.len);
+											memset(&buf[4 + adata->avp_data->os.len], 0, sizeof(buf) - 4 - adata->avp_data->os.len);
+											
+											/* Initial b1 = MD5(S + R + A) */
+											addr[0] = secret;
+											len[0] = secret_len;
+											addr[1] = 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 < adata->avp_data->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;
+											}
+											
+											CONV2_STR(RADIUS_ATTR_TUNNEL_PASSWORD, &buf[0], pos + 3, 0);
+										}
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+										CONV2_TUN_STR(RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, adata->avp_data->os.data, adata->avp_data->os.len, 1);
+										break;
+									
+									default:
+										TRACE_DEBUG(FULL, "Ignored unknown AVP inside Tunneling (%d)", adata->avp_code);
+								}
+							} else {
+								TRACE_DEBUG(FULL, "Ignored unknown Vendor AVP inside Tunneling (%d, %d)", adata->avp_vendor, adata->avp_code);
+							}
+						}
+					}
+					break;
+					
+				case DIAM_ATTR_USER_NAME:
+					CONV2_STR(RADIUS_ATTR_USER_NAME, adata->avp_data->os.data, adata->avp_data->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(req_auth);
+
+						/* Retrieve the shared secret */
+						CHECK_FCT((*cs->rgw_clients_getkey)(cli, &secret, &secret_len));
+						
+						if (adata->avp_data->os.len != 64) {
+							TRACE_DEBUG(INFO, "Received EAP-Master-Session-Key attribute with length %d != 64.\n", adata->avp_data->os.len)
+						}
+						
+						CHECK_PARAMS(adata->avp_data->os.len <= 64);
+						recv_len = adata->avp_data->os.len >= 32 ? 32 : adata->avp_data->os.len;
+						send_len = adata->avp_data->os.len - recv_len;
+						
+						if ( ! radius_msg_add_mppe_keys(*rad_fw, req_auth, secret, secret_len, 
+								adata->avp_data->os.data + recv_len, send_len,
+								adata->avp_data->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:
+					CONV2_STR(RADIUS_ATTR_EAP_KEY_NAME, adata->avp_data->os.data, adata->avp_data->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, adata->avp_data->os.data, adata->avp_data->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, adata->avp_data->os.data, adata->avp_data->os.len) ) {
+						TRACE_DEBUG(INFO, "Error while converting EAP reissued payload to RADIUS message");
+						return ENOMEM;
+					}
+					
+					if ( ! radius_msg_add_attr_int32(*rad_fw, RADIUS_ATTR_ERROR_CAUSE, 202) ) {
+						TRACE_DEBUG(INFO, "Error while adding Error-Cause attribute in RADIUS message");
+						return ENOMEM;
+					}
+					break;
+			
+				default:
+					/* Leave the AVP in the message for further treatment */
+					handled = 0;
+			}
+		} else {
+			/* Vendor-specific AVPs */
+			switch (adata->avp_vendor) {
+				
+				default: /* unknown vendor */
+					handled = 0;
+			}
+		}
+		
+		if (handled) {
+			CHECK_FCT( msg_free( avp, 1 ) );
+		}
+	}
+	
+	CHECK_FCT( msg_free( asid, 1 ) );
+	CHECK_FCT( msg_free( aoh, 1 ) );
+	free(req_auth);
+
+	return 0;
 }
 
 int rga_register(int version, waaad_api_t * waaad_api, struct radius_gw_api * api)
"Welcome to our mercurial repository"