changeset 356:e203fc0c95e3

Updated the app_radgw extension to allow more souple management of sessions, and stateful gateway features.
author Sebastien Decugis <sdecugis@nict.go.jp>
date Thu, 01 Jul 2010 15:47:34 +0900
parents 1ae3f2c28737
children dda9330aa711
files extensions/app_radgw/rgw.h extensions/app_radgw/rgw_common.h extensions/app_radgw/rgw_msg.c extensions/app_radgw/rgw_plugins.c extensions/app_radgw/rgw_worker.c extensions/app_radgw/rgwx_acct.c extensions/app_radgw/rgwx_auth.c extensions/app_radgw/rgwx_debug.c extensions/app_radgw/rgwx_echodrop.c extensions/app_radgw/rgwx_sample.c
diffstat 10 files changed, 274 insertions(+), 223 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/app_radgw/rgw.h	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgw.h	Thu Jul 01 15:47:34 2010 +0900
@@ -67,7 +67,7 @@
 int rgw_msg_parse(unsigned char * buf, size_t len, struct rgw_radius_msg_meta ** msg);
 void rgw_msg_dump(struct rgw_radius_msg_meta * msg);
 int rgw_msg_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth);
-int rgw_msg_create_base(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, struct session ** session, struct msg ** diam);
+int rgw_msg_create_base(struct rgw_client * cli, struct msg ** diam);
 int rgw_msg_init(void);
 
 /* Local RADIUS server(s) configuration */
@@ -116,7 +116,7 @@
 void rgw_plg_dump(void);
 void rgw_plg_start_cache(void);
 int rgw_plg_loop_req(struct rgw_radius_msg_meta **rad, struct session **session, struct msg **diam_msg, struct rgw_client * cli);
-int rgw_plg_loop_ans(struct rgw_radius_msg_meta *req, struct session *session, struct msg **diam_ans, struct radius_msg ** rad_ans, struct rgw_client * cli);
+int rgw_plg_loop_ans(struct rgw_radius_msg_meta *req, struct session *session, struct msg **diam_ans, struct radius_msg ** rad_ans, struct rgw_client * cli, int * stateful);
 void rgw_plg_fini(void);
 
 
--- a/extensions/app_radgw/rgw_common.h	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgw_common.h	Thu Jul 01 15:47:34 2010 +0900
@@ -71,17 +71,18 @@
 	void (*rgwp_conf_free) (struct rgwp_config * state);
 
 	/* handle an incoming RADIUS message */
-	int	(*rgwp_rad_req) ( struct rgwp_config * conf, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli );
-	/* ret 0: continue; 
+	int	(*rgwp_rad_req) ( struct rgwp_config * conf, struct session ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli );
+	/* ret >0: critical error (errno), log and exit.
+	   ret 0: continue; 
 	   ret -1: stop processing this message
 	   ret -2: reply the content of rad_ans to the RADIUS client immediatly
-	   ret >0: critical error (errno), log and exit.
 	 */
 	
 	/* handle the corresponding Diameter answer */
-	int	(*rgwp_diam_ans) ( struct rgwp_config * conf, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli );
+	int	(*rgwp_diam_ans) ( struct rgwp_config * conf, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli, int * stateful );
 	/* ret 0: continue; ret >0: error; ret: -1 ... (tbd) */
-
+	/* if *stateful = 1 on return, the session will not be destroyed after RADIUS answer is sent. The extension must ensure to register a timeout on the session in this case. */
+	
 } rgwp_descriptor;
 
 
--- a/extensions/app_radgw/rgw_msg.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgw_msg.c	Thu Jul 01 15:47:34 2010 +0900
@@ -136,196 +136,35 @@
 	fd_log_debug("-----------------------------\n");
 }
 
-static struct dict_object * cache_sess_id = NULL;
-static struct dict_object * cache_dest_host = NULL;
-static struct dict_object * cache_dest_realm = NULL;
 static struct dict_object * cache_orig_host = NULL;
 static struct dict_object * cache_orig_realm = NULL;
 
 int rgw_msg_init(void)
 {
 	TRACE_ENTRY();
-	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &cache_sess_id, ENOENT) );
-	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &cache_dest_host, ENOENT) );
-	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &cache_dest_realm, ENOENT) );
 	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) );
 	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_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, struct session ** session, struct msg ** diam)
+/* Create a new Diameter msg with origin-host & realm */
+int rgw_msg_create_base(struct rgw_client * cli, struct msg ** diam)
 {
-	int idx, i;
-	const char * prefix = "Diameter/";
-	size_t pref_len;
-	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;
 	
 	struct avp *avp = NULL;
 	union avp_value avp_val;
 	
-	TRACE_ENTRY("%p %p %p %p", msg, cli, session, diam);
-	CHECK_PARAMS( msg && cli && session && (*session == NULL) && diam && (*diam == NULL) );
-	
-	pref_len = strlen(prefix);
-	
-	/* 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]);
-		char * attr_val = (char *)(attr + 1);
-		size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
-		
-		if ((attr->type == RADIUS_ATTR_USER_NAME) 
-				&& attr_len) {
-			TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", attr_len, attr_val);
-			un = attr_val;
-			un_len = attr_len;
-			continue;
-		}
-		
-		if ((attr->type == RADIUS_ATTR_STATE) 
-				&& (attr_len > pref_len + 5 /* for the '/'s and non empty strings */ ) 
-				&& ! strncmp(attr_val, prefix, pref_len)) { /* should we make it strncasecmp? */
-			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'.", 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;
-		}
-		
-		if ((attr->type == RADIUS_ATTR_CLASS) 
-				&& (attr_len > pref_len ) 
-				&& ! strncmp(attr_val, prefix, pref_len)) {
-			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;
-		}
-		
-	}
+	TRACE_ENTRY("%p %p", cli, diam);
+	CHECK_PARAMS( cli && diam && (*diam == NULL) );
 	
 	/* Get information on this peer */
 	CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) );
 	
-	/* Create the session object */
-	if (si_len) {
-		CHECK_FCT( fd_sess_fromsid ( si, si_len, session, &idx) );
-	} else {
-		if (un) {
-			int len;
-			/* If not found, create a new Session-Id. The 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(sess_str, "%.*s;%s", un_len, un, fd_g_config->cnf_diamid);
-			CHECK_FCT( fd_sess_new(session, fqdn, sess_str, len) );
-			free(sess_str);
-			idx = 1;
-		}
-	}
-	
 	/* Create an empty Diameter message so that extensions can store their AVPs */
 	CHECK_FCT(  fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam )  );
 	
-	if (*session) {
-		CHECK_FCT( fd_sess_getsid(*session, &sess_str) );
-		if (idx == 0) {
-			TRACE_DEBUG(INFO, "Another message was translated for this session ('%s') and not answered yet, discarding the new RADIUS request.", sess_str);
-			*session = NULL;
-			return EALREADY;
-		}
-		
-		TRACE_DEBUG(FULL, "Translating new message for session '%s'...", sess_str);
-		
-		/* Add the Session-Id AVP as first AVP */
-		CHECK_FCT( fd_msg_avp_new ( cache_sess_id, 0, &avp ) );
-		memset(&avp_val, 0, sizeof(avp_val));
-		avp_val.os.data = (unsigned char *)sess_str;
-		avp_val.os.len = strlen(sess_str);
-		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
-		CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_FIRST_CHILD, avp) );
-
-	} else {
-		TRACE_DEBUG(FULL, "No session has been created for this message");
-	}
-	
-	/* Add the Destination-Realm as next AVP */
-	CHECK_FCT( fd_msg_avp_new ( cache_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 => we use the local domain of this gateway */
-			avp_val.os.data = fd_g_config->cnf_diamrlm;
-			avp_val.os.len  = fd_g_config->cnf_diamrlm_len;
-		} else {
-			avp_val.os.data = un + i;
-			avp_val.os.len  = un_len - i;
-		}
-	}
-	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
-	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
-	
-	/* Add the Destination-Host as next AVP */
-	if (dh) {
-		CHECK_FCT( fd_msg_avp_new ( cache_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( fd_msg_avp_setvalue ( avp, &avp_val ) );
-		CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
-	}
-	
 	/* Add the Origin-Host as next AVP */
 	CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) );
 	memset(&avp_val, 0, sizeof(avp_val));
--- a/extensions/app_radgw/rgw_plugins.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgw_plugins.c	Thu Jul 01 15:47:34 2010 +0900
@@ -348,7 +348,7 @@
 		
 		if (plg->descriptor->rgwp_rad_req) {
 			TRACE_DEBUG(ANNOYING, "Calling next plugin: %s", plg->descriptor->rgwp_name);
-			ret = (*plg->descriptor->rgwp_rad_req)(plg->cs, *session, &(*rad)->radius, &rad_ans, diam_msg, cli);
+			ret = (*plg->descriptor->rgwp_rad_req)(plg->cs, session, &(*rad)->radius, &rad_ans, diam_msg, cli);
 			if (ret)
 				break;
 		} else {
@@ -390,13 +390,15 @@
 }
 
 /* Loop in the extension list (same as req) to convert data from diam_ans to rad_ans */
-int rgw_plg_loop_ans(struct rgw_radius_msg_meta *req, struct session *session, struct msg **diam_ans, struct radius_msg ** rad_ans, struct rgw_client * cli)
+int rgw_plg_loop_ans(struct rgw_radius_msg_meta *req, struct session *session, struct msg **diam_ans, struct radius_msg ** rad_ans, struct rgw_client * cli, int * stateful)
 {
 	int ret = 0;
 	struct fd_list * head = NULL, *li;
 	
-	TRACE_ENTRY("%p %p %p %p %p", req, session, diam_ans, rad_ans, cli);
-	CHECK_PARAMS( req && session && diam_ans && *diam_ans && rad_ans && *rad_ans && cli);
+	TRACE_ENTRY("%p %p %p %p %p %p", req, session, diam_ans, rad_ans, cli, stateful);
+	CHECK_PARAMS( req && session && diam_ans && *diam_ans && rad_ans && *rad_ans && cli && stateful);
+	
+	*stateful = 0; /* default: stateless gateway */
 	
 	/* Get the list of extensions of the RADIUS request */
 	CHECK_POSIX( pthread_rwlock_rdlock( &plg_lock) );
@@ -406,12 +408,14 @@
 	/* Loop in the list of extensions */
 	for (li = head->next; li != head; li = li->next) {
 		struct plg_descr * plg = ((struct plg_accel_item *) li)->plg;
+		int locstateful = 0;
 		
 		if (plg->descriptor->rgwp_diam_ans) {
 			TRACE_DEBUG(ANNOYING, "Calling next plugin: %s", plg->descriptor->rgwp_name);
-			ret = (*plg->descriptor->rgwp_diam_ans)(plg->cs, session, diam_ans, rad_ans, (void *)cli);
+			ret = (*plg->descriptor->rgwp_diam_ans)(plg->cs, session, diam_ans, rad_ans, (void *)cli, &locstateful);
 			if (ret)
 				break;
+			*stateful |= locstateful;
 		} else {
 			TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", plg->descriptor->rgwp_name);
 		}					
--- a/extensions/app_radgw/rgw_worker.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgw_worker.c	Thu Jul 01 15:47:34 2010 +0900
@@ -129,12 +129,10 @@
 			}  );
 		
 		/* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */
-		
-		session = NULL;
 		diam_msg = NULL;
 		
-		/* Create the session and an empty message with default common AVPs */
-		CHECK_FCT_DO( rgw_msg_create_base(msg, cli, &session, &diam_msg),
+		/* Create an empty message with only Origin information (no session, no destination -- added by the plugins) */
+		CHECK_FCT_DO( rgw_msg_create_base(cli, &diam_msg),
 			{
 				/* An error occurred, discard message */
 				rgw_msg_free(&msg);
@@ -142,6 +140,8 @@
 				continue;
 			}  );
 		
+		session = NULL;
+		
 		/* Pass the message to the list of registered plugins */
 		CHECK_FCT_DO( rgw_plg_loop_req(&msg, &session, &diam_msg, cli), 
 			{
@@ -157,26 +157,23 @@
 				rgw_clients_dispose(&cli);
 				continue;
 			}  );
-		if (msg == NULL) {
+		if (msg == NULL) { /* Error or RADIUS answer locally generated */
 			rgw_clients_dispose(&cli);
 			if (diam_msg) {
 				CHECK_FCT_DO( fd_msg_free(diam_msg), );
 				diam_msg = NULL;
 			}
-			if (session) {
-				CHECK_FCT_DO( fd_sess_destroy(&session), );
-			}
 			continue; /* the message was handled already */
 		}
 		
 		pb = 0;
 		
-		/* Check the created Diameter message */
+		/* Check the created Diameter message -- it will be invalid if no callback has handled the RADIUS message */
 		if ((diam_msg == NULL) || ( fd_msg_parse_rules(diam_msg, fd_g_config->cnf_dict, NULL) ) ) {
 			fd_log_debug("[radgw] No or invalid Diameter message was generated after processing the RADIUS command %hhd (%s).\n"
-					" This is likely an implementation problem, please report.\n",
+					" It may indicate a gateway configuration problem, or implementation issue in a plugin.\n",
 					msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
-			/* We might also dump the conflicting rule here if useful */
+			/* We should also dump the conflicting rule here to help debug? */
 			pb++;
 		}
 		
@@ -189,9 +186,6 @@
 					msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
 		}
 		
-		/* Check the session is correct (for debug) */
-		ASSERT(session != NULL);
-		
 		if (pb) {
 			/* Something went wrong during the conversion */
 			if (session) {
@@ -252,6 +246,7 @@
 	struct avp *avp;
 	struct avp_hdr  *ahdr;
 	int pb = 0;
+	int keepsession=0;
 	
 	TRACE_ENTRY("%p %p", pa, ans);
 	CHECK_PARAMS_DO( pa && ans, return );
@@ -260,7 +255,7 @@
 	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_plg_loop_ans(pa->rad, pa->sess, ans, &rad_ans, pa->cli), goto out );
+	CHECK_FCT_DO( rgw_plg_loop_ans(pa->rad, pa->sess, ans, &rad_ans, pa->cli, &keepsession), goto out );
 
 	if (*ans != NULL) {
 
@@ -292,7 +287,7 @@
 		}
 
 		if (pb) {
-			TRACE_DEBUG(INFO, "[radgw] WARNING: %d mandatory AVP in the Diameter answer have not been translated to RADIUS!\n Please use plg_debug.rgwx for more information.", pb);
+			TRACE_DEBUG(INFO, "[radgw] WARNING: %d mandatory AVP in the Diameter answer have not been translated to RADIUS!\n Please use debug.rgwx for more information.", pb);
 		}
 	}
 	
@@ -302,8 +297,10 @@
 	}
 
 out:
-	/* Destroy remaining session data (stateless gateway) */
-	CHECK_FCT_DO( fd_sess_destroy(&pa->sess),  );
+	if (!keepsession) {
+		/* Destroy remaining session data (stateless gateway) */
+		CHECK_FCT_DO( fd_sess_destroy(&pa->sess),  );
+	}
 	
 	/* Clear the Diameter message */
 	if (*ans) {
--- a/extensions/app_radgw/rgwx_acct.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgwx_acct.c	Thu Jul 01 15:47:34 2010 +0900
@@ -284,7 +284,7 @@
 }
 
 /* Incoming RADIUS request */
-static int acct_rad_req( struct rgwp_config * cs, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+static int acct_rad_req( struct rgwp_config * cs, struct session ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
 {
 	int idx;
 	int send_str=0;
@@ -299,15 +299,23 @@
 	union avp_value value;
 	struct avp ** avp_tun = NULL, *avp = NULL;
 	
+	const char * prefix = "Diameter/";
+	size_t pref_len;
+	char * si = NULL;
+	size_t si_len = 0;
+	
 	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_ACCOUNTING_REQUEST) && rad_ans && diam_fw && *diam_fw);
 	
+	pref_len = strlen(prefix);
+	
 	/*
 	      Either NAS-IP-Address or NAS-Identifier MUST be present in a
 	      RADIUS Accounting-Request.  It SHOULD contain a NAS-Port or NAS-
 	      Port-Type attribute or both unless the service does not involve a
 	      port or the NAS does not distinguish among its ports.
 	*/
+	/* We also enforce that the message contains a CLASS attribute with Diameter/ prefix containing the Session-Id. */
 	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 * v = (uint8_t *)(attr + 1);
@@ -341,6 +349,25 @@
 					           | (v[2] <<  8)
 					           |  v[3] ;
 				break;
+				
+			case RADIUS_ATTR_CLASS:
+				{
+					char * attr_val = (char *)(attr + 1);
+					size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
+					if ((attr_len > pref_len ) && ! strncmp(attr_val, prefix, pref_len)) {
+						int i;
+						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 < rad_req->attr_used; i++)
+							rad_req->attr_pos[i - 1] = rad_req->attr_pos[i];
+						rad_req->attr_used -= 1;
+						break;
+					}
+				}
+				break;
+
 		}
 	}
 	
@@ -417,6 +444,26 @@
 		return ENOTSUP;
 	}
 	
+	/* Check if we got a valid session information, otherwise the server will not be able to handle the data... */
+	if (!*session && !si) {
+		TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request from %s did not contain a CLASS attribute with Diameter session information, reject.", rgw_clients_id(cli));
+		return EINVAL;
+	}
+	
+	/* Create the Session-Id AVP if needed */
+	if (!*session) {
+		CHECK_FCT( fd_sess_fromsid ( si, si_len, session, NULL) );
+		
+		TRACE_DEBUG(FULL, "[auth.rgwx] Translating new accounting message for session '%.*s'...", si_len, si);
+		
+		/* Add the Session-Id AVP as first AVP */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
+		value.os.data = (unsigned char *)si;
+		value.os.len = si_len;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	}
+		
 	/* Add the command code */
 	{
 		struct msg_hdr * header = NULL;
@@ -1091,7 +1138,7 @@
 			st->send_str = send_str;
 		}
 		st->term_cause = str_cause;
-		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, session, &st ) );
+		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, *session, &st ) );
 	}
 	
 	return 0;
@@ -1127,7 +1174,7 @@
 	return;
 }
 
-static int acct_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+static int acct_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli, int * stateful )
 {
 	struct sess_state * st = NULL;
 	struct avp *avp, *next;
--- a/extensions/app_radgw/rgwx_auth.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgwx_auth.c	Thu Jul 01 15:47:34 2010 +0900
@@ -70,6 +70,8 @@
 		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 */
@@ -153,6 +155,8 @@
 	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) );
@@ -221,7 +225,7 @@
 }
 
 /* Handle an incoming RADIUS request */
-static int auth_rad_req( struct rgwp_config * cs, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+static int auth_rad_req( struct rgwp_config * cs, struct session ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
 {
 	int idx;
 	int got_id = 0;
@@ -229,13 +233,25 @@
 	int got_passwd = 0;
 	int got_eap = 0;
 	int got_empty_eap = 0;
+	const char * prefix = "Diameter/";
+	size_t pref_len;
+	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;
 	uint32_t status_type;
 	size_t nattr_used = 0;
 	struct avp ** avp_tun = NULL, *avp = NULL;
 	union avp_value value;
 	
 	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);
+	CHECK_PARAMS(cs && session && rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
+	
+	pref_len = strlen(prefix);
 	
 	/*
 	   Guidelines:
@@ -269,14 +285,6 @@
 		        and associated with the session state.
 		     -> sub_echo_drop should handle the Proxy-State attribute (conf issue)
 
-	      -  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.
-		     -> done in rgw_msg_create_base.
-
 	      -  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),
@@ -304,9 +312,12 @@
 	      -> the remaining is specific conversion rules
 	*/
 	
-	/* Check basic information is there */
+	/* 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]);
+		char * attr_val = (char *)(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:
@@ -326,6 +337,50 @@
 			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 */ ) 
+					&& ! strncmp(attr_val, prefix, pref_len)) { /* should we make it strncasecmp? */
+					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'.", dh_len, dh, dr_len, dr, 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;
+				}
+				break;
+		
+			case RADIUS_ATTR_USER_NAME:
+				if (attr_len) {
+					TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", attr_len, attr_val);
+					un = attr_val;
+					un_len = attr_len;
+				}
+				break;
+			
 		}
 	}
 	if (!got_id) {
@@ -346,6 +401,99 @@
 		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) {
+			/* 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 = 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, *session ? MSG_BRW_LAST_CHILD : 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, *session ? MSG_BRW_LAST_CHILD : MSG_BRW_FIRST_CHILD, avp) );
+	}
+	
+	/* Create the session if it is not already done */
+	if (*session == NULL) {
+		char * sess_str = NULL;
+		
+		if (si_len) {
+			/* We already have the Session-Id, just use it */
+			CHECK_FCT( fd_sess_fromsid ( si, si_len, session, NULL) );
+		} else {
+			/* Create a new Session-Id string */
+			
+			char * fqdn;
+			char * realm;
+			
+			/* Get information on the RADIUS client */
+			CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) );
+			
+			/* If we have a user name, create the new session with it */
+			if (un) {
+				int len;
+				/* If not found, create a new Session-Id. The 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(sess_str, "%.*s;%s", un_len, un, fd_g_config->cnf_diamid);
+				CHECK_FCT( fd_sess_new(session, fqdn, 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(*session, &sess_str) );
+		
+		TRACE_DEBUG(FULL, "[auth.rgwx] Translating new message for session '%s'...", sess_str);
+		
+		/* Add the Session-Id AVP as first AVP */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
+		value.os.data = (unsigned char *)sess_str;
+		value.os.len = strlen(sess_str);
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	}
+	
+	
 	/* Add the appropriate command code & Auth-Application-Id */
 	{
 		struct msg_hdr * header = NULL;
@@ -907,13 +1055,13 @@
 		CHECK_MALLOC(req_auth = malloc(16));
 		memcpy(req_auth, &rad_req->hdr->authenticator[0], 16);
 		
-		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, session, &req_auth ) );
+		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, *session, &req_auth ) );
 	}
 	
 	return 0;
 }
 
-static int auth_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+static int auth_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli, int * stateful )
 {
 	struct msg_hdr * hdr;
 	struct avp *avp, *next, *avp_x, *avp_y, *asid, *aoh;
--- a/extensions/app_radgw/rgwx_debug.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgwx_debug.c	Thu Jul 01 15:47:34 2010 +0900
@@ -71,7 +71,7 @@
 }
 
 /* Function called when a new RADIUS message is being converted to Diameter */
-static int debug_rad_req( struct rgwp_config * cs, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+static int debug_rad_req( struct rgwp_config * cs, struct session ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
 {
 	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw, cli);
 	
@@ -98,15 +98,24 @@
 		fd_msg_dump_walk(0, *diam_fw);
 	}
 	
+	if (!session || ! *session) {
+		fd_log_debug(" Diameter session: NULL pointer\n");
+	} else {
+		char * str;
+		CHECK_FCT( fd_sess_getsid(*session, &str) );
+
+		fd_log_debug(" Diameter session: %s\n", str);
+	}
+	
 	fd_log_debug("===========  Debug%s%s%s complete =============\n", cs ? " [" : "", cs ? (char *)cs : "", cs ? "]" : "");
 	
 	return 0;
 }
 
 /* This one, when Diameter answer is converted to RADIUS */
-static int debug_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+static int debug_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli, int * stateful )
 {
-	TRACE_ENTRY("%p %p %p %p %p", cs, session, diam_ans, rad_fw, cli);
+	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, diam_ans, rad_fw, cli, stateful);
 
 	fd_log_debug("------------- RADIUS/Diameter Answer Debug%s%s%s -------------\n", cs ? " [" : "", cs ? (char *)cs : "", cs ? "]" : "");
 	
--- a/extensions/app_radgw/rgwx_echodrop.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgwx_echodrop.c	Thu Jul 01 15:47:34 2010 +0900
@@ -118,7 +118,7 @@
 
 
 /* Handle attributes from a RADIUS request as specified in the configuration */
-static int ed_rad_req( struct rgwp_config * cs, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+static int ed_rad_req( struct rgwp_config * cs, struct session ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
 {
 	size_t nattr_used = 0;
 	int idx;
@@ -127,7 +127,7 @@
 	struct fd_list *li;
 	
 	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw, cli);
-	CHECK_PARAMS(cs && rad_req);
+	CHECK_PARAMS(cs && rad_req && session);
 	
 	/* For each attribute in the original message */
 	for (idx = 0; idx < rad_req->attr_used; idx++) {
@@ -216,7 +216,13 @@
 	
 	/* Save the echoed values in the session, if any */
 	if (!FD_IS_LIST_EMPTY(&echo_list)) {
-		CHECK_PARAMS(session);
+		CHECK_PARAMS_DO(*session,
+			{
+				fd_log_debug(	"[echodrop.rgwx] The extension is configured to echo some attributes from this message, but no session object has been created for it (yet).\n"
+						"  Please check your configuration file and include a session-generating extension BEFORE calling echodrop.rgwx to echo attributes.\n"
+						"  Please use debug.rgwx to retrieve more information.\n" );
+				return EINVAL;
+			} );
 		
 		/* Move the values in a dynamically allocated list */
 		CHECK_MALLOC( li = malloc(sizeof(struct fd_list)) );
@@ -224,19 +230,19 @@
 		fd_list_move_end(li, &echo_list);
 		
 		/* Save the list in the session */
-		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, session, &li ) );
+		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, *session, &li ) );
 	}
 	
 	return 0;
 }
 
 /* Process an answer: add the ECHO attributes back, if any */
-static int ed_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+static int ed_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli, int * stateful )
 {
 	int ret;
 	struct fd_list * list = NULL;
 	
-	TRACE_ENTRY("%p %p %p %p %p", cs, session, diam_ans, rad_fw, cli);
+	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, diam_ans, rad_fw, cli, stateful);
 	CHECK_PARAMS(cs);
 	
 	/* If there is no session associated, just give up */
--- a/extensions/app_radgw/rgwx_sample.c	Wed Jun 30 11:24:15 2010 +0900
+++ b/extensions/app_radgw/rgwx_sample.c	Thu Jul 01 15:47:34 2010 +0900
@@ -66,7 +66,7 @@
 }
 
 /* This function is called on incoming RADIUS messages. It should handle (some) RADIUS data and store into the Diameter message. */
-static int sample_rad_req( struct rgwp_config * cs, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+static int sample_rad_req( struct rgwp_config * cs, struct session ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
 {
 	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw, cli);
 	CHECK_PARAMS(cs);
@@ -75,9 +75,9 @@
 }
 
 /* This function is called when a Diameter answer is coming back. It should remove the AVPs and add the attributes in the RADIUS message. */
-static int sample_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+static int sample_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli, int * stateful )
 {
-	TRACE_ENTRY("%p %p %p %p %p", cs, session, diam_ans, rad_fw, cli);
+	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, diam_ans, rad_fw, cli, stateful);
 	CHECK_PARAMS(cs);
 	TRACE_DEBUG(INFO, "RADIUS/Diameter Sample plugin received a new Diameter answer.");
 	return 0;
"Welcome to our mercurial repository"